UNIXworkcode

1 /** 2 * 3 * $Id: BubbleButton.c,v 1.9 2005/12/01 14:31:43 tringali Exp $ 4 * 5 * Copyright (C) 1996 Free Software Foundation, Inc. 6 * Copyright © 1999-2001 by the LessTif developers. 7 * 8 * This file is part of the GNU LessTif Extension Library. 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Library General Public 12 * License as published by the Free Software Foundation; either 13 * version 2 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Library General Public License for more details. 19 * 20 * You should have received a copy of the GNU Library General Public 21 * License along with this library; if not, write to the Free 22 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 * 24 **/ 25 26 #ifdef HAVE_CONFIG_H 27 #include "../config.h" 28 #endif 29 30 #include <unistd.h> 31 #include <stdio.h> 32 33 #include <X11/StringDefs.h> 34 35 #include <Xm/XmosP.h> 36 #include <Xm/MessageB.h> 37 #include <Xm/Form.h> 38 #include <Xm/TextF.h> 39 #include <Xm/LabelP.h> 40 #include <Xm/DialogS.h> 41 #include <Xm/MenuShell.h> 42 #include <Xm/Display.h> 43 44 #include "BubbleButtonP.h" 45 #include "SlideC.h" 46 47 #include "../util/misc.h" 48 49 #ifdef WITH_DMALLOC 50 #include <dmalloc.h> 51 #endif 52 53 static const char rcsid[] = "$Id: BubbleButton.c,v 1.9 2005/12/01 14:31:43 tringali Exp $"; 54 55 /* 56 Widget methods, forward declarations 57 */ 58 59 static void class_initialize(void); 60 static void class_part_initialize(WidgetClass widget_class); 61 static void initialize(Widget request, Widget new_w, ArgList args, Cardinal *num_args); 62 static void destroy(Widget w); 63 static Boolean set_values(Widget old, Widget request, Widget new_w, ArgList args, Cardinal *num_args); 64 static void _XmExportLabelString(Widget w, int offset, XtArgVal *value); 65 66 /* 67 Helper functions, forward declarations 68 */ 69 70 /* 71 Widget default resources 72 */ 73 74 #define Offset(field) XtOffsetOf(XltBubbleButtonRec, bubble_button.field) 75 static XtResource resources[] = 76 { 77 { 78 XltNbubbleString, XltCBubbleString, XmRXmString, 79 sizeof(XmString), Offset(BubbleString), 80 XmRImmediate, (XtPointer)NULL 81 }, 82 { 83 XltNshowBubble, XltCShowBubble, XmRBoolean, 84 sizeof(Boolean), Offset(show_bubble), 85 XmRImmediate, (XtPointer)True 86 }, 87 { 88 XltNdelay, XltCDelay, XtRInt, 89 sizeof(int), Offset(Delay), 90 XtRImmediate, (XtPointer)1000 91 }, 92 { 93 XltNmouseOverString, XltCMouseOverString, XmRXmString, 94 sizeof(XmString), Offset(MouseOverString), 95 XtRImmediate, (XtPointer)NULL 96 }, 97 { 98 XltNmouseOverPixmap, XltCMouseOverPixmap, XmRPrimForegroundPixmap, 99 sizeof(Pixmap), Offset(MouseOverPixmap), 100 XtRImmediate, (XtPointer)None 101 }, 102 { 103 XltNbubbleDuration, XltCBubbleDuration, XtRInt, 104 sizeof(int), Offset(Duration), 105 XtRImmediate, (XtPointer)0 106 }, 107 { 108 XltNslidingBubble, XltCslidingBubble, XmRBoolean, 109 sizeof(Boolean), Offset(slidingBubble), 110 XmRImmediate, (XtPointer)True 111 }, 112 { 113 XltNautoParkBubble, XltCautoParkBubble, XmRBoolean, 114 sizeof(Boolean), Offset(autoParkBubble), 115 XmRImmediate, (XtPointer)False 116 }, 117 }; 118 119 static XmSyntheticResource syn_resources[] = 120 { 121 { 122 XltNbubbleString, 123 sizeof(XmString), Offset(BubbleString), 124 _XmExportLabelString, NULL 125 } 126 }; 127 #undef Offset 128 129 /* 130 Widget class record 131 */ 132 133 static void EnterWindow(Widget w, XEvent *event, String *params, Cardinal *num_params); 134 static void LeaveWindow(Widget w, XEvent *event, String *params, Cardinal *num_params); 135 136 static XtActionsRec actions[] = 137 { 138 {"Enter", EnterWindow}, 139 {"Leave", LeaveWindow}, 140 }; 141 142 /* *INDENT-OFF* */ 143 XltBubbleButtonClassRec xrwsBubbleButtonClassRec = { 144 /* Core class part */ 145 { 146 /* superclass */ (WidgetClass) &xmPushButtonClassRec, 147 /* class_name */ "XltBubbleButton", 148 /* widget_size */ sizeof(XltBubbleButtonRec), 149 /* class_initialize */ class_initialize, 150 /* class_part_initialize */ class_part_initialize, 151 /* class_inited */ False, 152 /* initialize */ initialize, 153 /* initialize_hook */ NULL, 154 /* realize */ XtInheritRealize, 155 /* actions */ actions, 156 /* num_actions */ XtNumber(actions), 157 /* resources */ resources, 158 /* num_resources */ XtNumber(resources), 159 /* xrm_class */ NULLQUARK, 160 /* compress_motion */ True, 161 /* compress_exposure */ XtExposeCompressMaximal, 162 /* compress_enterleave */ True, 163 /* visible_interest */ False, 164 /* destroy */ destroy, 165 /* resize */ XtInheritResize, 166 /* expose */ XtInheritExpose, 167 /* set_values */ set_values, 168 /* set_values_hook */ NULL, 169 /* set_values_almost */ XtInheritSetValuesAlmost, 170 /* get_values_hook */ NULL, 171 /* accept_focus */ NULL, 172 /* version */ XtVersion, 173 /* callback offsets */ NULL, 174 /* tm_table */ NULL, 175 /* query_geometry */ XtInheritQueryGeometry, 176 /* display_accelerator */ NULL, 177 /* extension */ (XtPointer)NULL 178 }, 179 /* Primitive Class part */ 180 { 181 /* border_highlight */ XmInheritBorderHighlight, 182 /* border_unhighlight */ XmInheritBorderUnhighlight, 183 /* translations */ XtInheritTranslations, 184 /* arm_and_activate_proc */ XmInheritArmAndActivate, 185 /* synthetic resources */ syn_resources, 186 /* num syn res */ XtNumber(syn_resources), 187 /* extension */ (XtPointer)NULL 188 }, 189 /* Label Class part */ 190 { 191 /* setOverrideCallback */ XmInheritSetOverrideCallback, 192 /* menuProcs */ XmInheritMenuProc, 193 /* translations */ XtInheritTranslations, 194 /* extension */ NULL 195 }, 196 /* PushButton Class part */ 197 { 198 /* extension */ NULL 199 }, 200 /* BubbleButton Class part */ 201 { 202 /* leave_time */ 0, 203 /* extension */ NULL 204 } 205 }; 206 /* *INDENT-ON* */ 207 208 209 210 WidgetClass xrwsBubbleButtonWidgetClass = (WidgetClass)&xrwsBubbleButtonClassRec; 211 212 /* 213 Helper routines 214 */ 215 216 /* 217 Widget methods 218 */ 219 220 static void 221 class_initialize(void) 222 { 223 xrwsBubbleButtonClassRec.bubble_button_class.leave_time = 0; 224 } 225 226 static void 227 class_part_initialize(WidgetClass widget_class) 228 { 229 } 230 231 static void 232 initialize(Widget request, Widget new_w, ArgList args, Cardinal *num_args) 233 { 234 Widget Shell; 235 Arg arg[10]; 236 int argcnt = 0; 237 238 BubbleButton_Timer(new_w) = (XtIntervalId)NULL; 239 BubbleButton_DurationTimer(new_w) = (XtIntervalId)NULL; 240 BubbleButton_Swapped(new_w) = False; 241 BubbleButton_Slider(new_w) = NULL; 242 Shell = CreatePopupShellWithBestVis("BubbleShell", 243 transientShellWidgetClass, new_w, arg, argcnt); 244 XtVaSetValues(Shell, 245 XmNoverrideRedirect, True, 246 NULL); 247 if (BubbleButton_MouseOverString(new_w) != NULL) 248 { 249 BubbleButton_MouseOverString(new_w) = XmStringCopy(BubbleButton_MouseOverString(new_w)); 250 } 251 if (BubbleButton_BubbleString(new_w) == NULL) 252 { 253 XmString xmstring; 254 255 #if XmVERSION >= 2 256 xmstring = XmeGetLocalizedString((char *)NULL, 257 new_w, 258 XmNlabelString, 259 XtName(new_w)); 260 #else 261 xmstring = _XmOSGetLocalizedString((char *)NULL, 262 new_w, 263 XmNlabelString, 264 XtName(new_w)); 265 #endif 266 BubbleButton_BubbleString(new_w) = xmstring; 267 } 268 else 269 { 270 BubbleButton_BubbleString(new_w) = XmStringCopy(BubbleButton_BubbleString(new_w)); 271 } 272 BubbleButton_Label(new_w) = XmCreateLabel(Shell, "BubbleLabel", NULL, 0); 273 XtVaSetValues(BubbleButton_Label(new_w), 274 XmNlabelString, BubbleButton_BubbleString(new_w), 275 XmNforeground, ((XltBubbleButtonWidget)new_w)->core.background_pixel, 276 XmNbackground, ((XltBubbleButtonWidget)new_w)->primitive.foreground, 277 NULL); 278 XtManageChild(BubbleButton_Label(new_w)); 279 } 280 281 static void 282 destroy(Widget w) 283 { 284 if (BubbleButton_MouseOverString(w)) 285 { 286 XmStringFree(BubbleButton_MouseOverString(w)); 287 } 288 if (BubbleButton_Timer(w)) 289 { 290 XtRemoveTimeOut(BubbleButton_Timer(w)); 291 } 292 if (BubbleButton_DurationTimer(w)) 293 { 294 XtRemoveTimeOut(BubbleButton_DurationTimer(w)); 295 } 296 } 297 298 static Boolean 299 set_values(Widget old, Widget request, Widget new_w, ArgList args, Cardinal *num_args) 300 { 301 if (BubbleButton_MouseOverString(new_w) != BubbleButton_MouseOverString(old)) 302 { 303 XmStringFree(BubbleButton_MouseOverString(old)); 304 BubbleButton_MouseOverString(new_w) = XmStringCopy(BubbleButton_MouseOverString(new_w)); 305 } 306 if (BubbleButton_BubbleString(new_w) != BubbleButton_BubbleString(old)) 307 { 308 XmStringFree(BubbleButton_BubbleString(old)); 309 BubbleButton_BubbleString(new_w) = XmStringCopy(BubbleButton_BubbleString(new_w)); 310 XtVaSetValues(BubbleButton_Label(new_w), 311 XmNlabelString, BubbleButton_BubbleString(new_w), 312 NULL); 313 } 314 if (XtIsSensitive(old) != XtIsSensitive(new_w)) 315 { 316 if (!XtIsSensitive(new_w)) 317 { 318 Cardinal num_params = 0; 319 320 LeaveWindow(new_w, NULL, NULL, &num_params); 321 } 322 } 323 return (False); 324 } 325 326 /* 327 * Short-term solution. Doesn't belong here. See SF bug #923924. 328 */ 329 extern XmString _XmStringCreateExternal(XmFontList fontlist, _XmString cs); 330 331 static void 332 _XmExportLabelString(Widget w, int offset, XtArgVal *value) 333 { 334 _XmString str; 335 XmString ret; 336 337 str = *(_XmString *)(((char *)w) + offset); 338 if (str) 339 { 340 if (XmIsLabel(w)) 341 { 342 ret = _XmStringCreateExternal(Lab_Font(w), str); 343 } 344 else 345 { 346 ret = NULL; 347 } 348 } 349 else 350 { 351 ret = NULL; 352 } 353 354 *value = (XtArgVal)ret; 355 } 356 357 static void 358 fadeOutFinish(Widget slide, Widget w, XtPointer call_data) 359 { 360 BubbleButton_Slider(w) = NULL; 361 XtPopdown(XtParent(BubbleButton_Label(w))); 362 } 363 364 static void 365 UnpostIt(Widget w) 366 { 367 BubbleButton_DurationTimer(w) = (XtIntervalId)NULL; 368 if (BubbleButton_SlidingBubble(w)) 369 { 370 BubbleButton_Slider(w) = XtVaCreateWidget("Slide", xltSlideContextWidgetClass, 371 XmGetXmDisplay(XtDisplay(w)), 372 XltNslideWidget, XtParent(BubbleButton_Label(w)), 373 XltNslideDestHeight, 1, 374 NULL); 375 XtAddCallback(BubbleButton_Slider(w), XltNslideFinishCallback, 376 (XtCallbackProc)fadeOutFinish, w); 377 } 378 else 379 { 380 XtPopdown(XtParent(BubbleButton_Label(w))); 381 } 382 } 383 384 static void 385 fadeInFinish(Widget slide, Widget w, XtPointer call_data) 386 { 387 BubbleButton_Slider(w) = NULL; 388 if (BubbleButton_Duration(w) > 0) 389 { 390 BubbleButton_DurationTimer(w) = XtAppAddTimeOut(XtWidgetToApplicationContext(w), 391 BubbleButton_Duration(w), 392 (XtTimerCallbackProc)UnpostIt, 393 w); 394 } 395 } 396 397 #define TOOLTIP_EDGE_GUARD 5 398 static void 399 PostIt(Widget w) 400 { 401 int rx, ry, x, y; 402 unsigned int key; 403 Window root, child; 404 Dimension xPos, yPos; 405 XWindowAttributes screenAttr; 406 407 BubbleButton_Timer(w) = (XtIntervalId)NULL; 408 XQueryPointer(XtDisplay(w), 409 XtWindow(w), 410 &root, 411 &child, 412 &rx, &ry, 413 &x, &y, 414 &key); 415 if (BubbleButton_DurationTimer(w) != (XtIntervalId)NULL) 416 { 417 XtRemoveTimeOut(BubbleButton_DurationTimer(w)); 418 BubbleButton_DurationTimer(w) = (XtIntervalId)NULL; 419 } 420 { 421 XtWidgetGeometry geo; 422 423 XtQueryGeometry(BubbleButton_Label(w), NULL, &geo); 424 425 xPos = rx - x + XtWidth(w) / 2 ; 426 yPos = ry - y + XtHeight(w); 427 428 if (BubbleButton_AutoParkBubble(w)) 429 { 430 xPos = rx + 3; 431 yPos = ry + 15; 432 433 /* keep tooltip within screen */ 434 XGetWindowAttributes(XtDisplay(w), 435 RootWindowOfScreen(XtScreen(w)), &screenAttr); 436 437 if (xPos + geo.width >= screenAttr.width - TOOLTIP_EDGE_GUARD) 438 xPos = screenAttr.width - geo.width - TOOLTIP_EDGE_GUARD; 439 440 if (yPos + geo.height >= screenAttr.height - TOOLTIP_EDGE_GUARD) 441 yPos = ry - 15 - geo.height; 442 } 443 444 445 if (BubbleButton_SlidingBubble(w)) 446 { 447 int xAdjust, yAdjust; 448 449 /* FIXME: slider must avoid pointer */ 450 xAdjust = rx < xPos? 1 : -1; 451 yAdjust = ry < yPos? 1 : - geo.height/2; 452 453 XtVaSetValues(XtParent(BubbleButton_Label(w)), 454 XmNx, rx + xAdjust /*- x + XtWidth(w) / 2*/, 455 XmNy, ry + yAdjust /*- y + XtHeight(w)*/, 456 XmNheight, 1, 457 XmNwidth, 1 /*geo.width*/, 458 NULL); 459 XtPopup(XtParent(BubbleButton_Label(w)), XtGrabNone); 460 461 BubbleButton_Slider(w) = XtVaCreateWidget("Slide", xltSlideContextWidgetClass, 462 XmGetXmDisplay(XtDisplay(w)), 463 XltNslideWidget, XtParent(BubbleButton_Label(w)), 464 XltNslideDestX, xPos, 465 XltNslideDestY, yPos, 466 XltNslideDestWidth, geo.width, 467 XltNslideDestHeight, geo.height, 468 NULL); 469 XtAddCallback(BubbleButton_Slider(w), XltNslideFinishCallback, (XtCallbackProc)fadeInFinish, w); 470 } 471 else 472 { 473 XtVaSetValues(XtParent(BubbleButton_Label(w)), 474 XmNx, xPos /*- x + XtWidth(w) / 2*/, 475 XmNy, yPos /*- y + XtHeight(w)*/, 476 XmNheight, geo.height, 477 XmNwidth, geo.width /*geo.width*/, 478 NULL); 479 XtPopup(XtParent(BubbleButton_Label(w)), XtGrabNone); 480 481 if (BubbleButton_Duration(w) > 0) 482 { 483 BubbleButton_DurationTimer(w) = XtAppAddTimeOut(XtWidgetToApplicationContext(w), 484 BubbleButton_Duration(w), 485 (XtTimerCallbackProc)UnpostIt, 486 w); 487 } 488 } 489 } 490 } 491 492 static void 493 SwapLabels(Widget w) 494 { 495 XmString tmp = NULL; 496 497 if (BubbleButton_MouseOverString(w)) 498 { 499 XtVaGetValues(w, 500 XmNlabelString, &tmp, 501 NULL); 502 XtVaSetValues(w, 503 XmNlabelString, BubbleButton_MouseOverString(w), 504 NULL); 505 XmStringFree(BubbleButton_MouseOverString(w)); 506 BubbleButton_MouseOverString(w) = tmp; 507 } 508 } 509 510 static void 511 SwapPixmaps(Widget w) 512 { 513 Pixmap tmp; 514 515 if (BubbleButton_MouseOverPixmap(w)) 516 { 517 XtVaGetValues(w, 518 XmNlabelPixmap, &tmp, 519 NULL); 520 XtVaSetValues(w, 521 XmNlabelPixmap, BubbleButton_MouseOverPixmap(w), 522 NULL); 523 BubbleButton_MouseOverPixmap(w) = tmp; 524 } 525 } 526 527 static void 528 Swap(Widget w) 529 { 530 if (Lab_IsText(w)) 531 { 532 SwapLabels(w); 533 } 534 else 535 { 536 SwapPixmaps(w); 537 } 538 BubbleButton_Swapped(w) = BubbleButton_Swapped(w) ? False : True; 539 } 540 541 static void 542 EnterWindow(Widget w, XEvent *event, String *params, Cardinal *num_params) 543 { 544 if (BubbleButton_ShowBubble(w) && !BubbleButton_Timer(w)) 545 { 546 unsigned long delay; 547 548 if (event && (event->xcrossing.time - BubbleButtonClass_LeaveTime(w) < BubbleButton_Delay(w))) 549 { 550 delay = 0; 551 } 552 else 553 { 554 delay = BubbleButton_Delay(w); 555 } 556 BubbleButton_Timer(w) = XtAppAddTimeOut(XtWidgetToApplicationContext(w), 557 delay, 558 (XtTimerCallbackProc)PostIt, 559 w); 560 } 561 if (!BubbleButton_Swapped(w)) 562 { 563 Swap(w); 564 } 565 } 566 567 static void 568 LeaveWindow(Widget w, XEvent *event, String *params, Cardinal *num_params) 569 { 570 if (BubbleButton_Timer(w)) 571 { 572 XtRemoveTimeOut(BubbleButton_Timer(w)); 573 BubbleButton_Timer(w) = (XtIntervalId)NULL; 574 } 575 else 576 { 577 if (BubbleButton_Slider(w) != NULL) 578 { 579 XtDestroyWidget(BubbleButton_Slider(w)); 580 BubbleButton_Slider(w) = NULL; 581 } 582 XtPopdown(XtParent(BubbleButton_Label(w))); 583 if (event) 584 { 585 if (BubbleButton_DurationTimer(w) || BubbleButton_Duration(w) == 0) 586 { 587 BubbleButtonClass_LeaveTime(w) = event->xcrossing.time; 588 } 589 } 590 } 591 if (BubbleButton_DurationTimer(w)) 592 { 593 XtRemoveTimeOut(BubbleButton_DurationTimer(w)); 594 BubbleButton_DurationTimer(w) = (XtIntervalId)NULL; 595 } 596 if (BubbleButton_Swapped(w)) 597 { 598 Swap(w); 599 } 600 } 601 602 /* 603 Public functions 604 */ 605 606 Widget 607 XltCreateBubbleButton(Widget parent, 608 char *name, 609 Arg *arglist, 610 Cardinal argCount) 611 { 612 return XtCreateWidget(name, xrwsBubbleButtonWidgetClass, parent, arglist, argCount); 613 } 614 615