Tip.c revision 775e7de9
1/* 2 * Copyright (c) 1999 by The XFree86 Project, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 * 22 * Except as contained in this notice, the name of the XFree86 Project shall 23 * not be used in advertising or otherwise to promote the sale, use or other 24 * dealings in this Software without prior written authorization from the 25 * XFree86 Project. 26 * 27 * Author: Paulo César Pereira de Andrade 28 */ 29 30/* $XFree86: xc/lib/Xaw/Tip.c,v 1.4 1999/07/11 08:49:16 dawes Exp $ */ 31 32#ifdef HAVE_CONFIG_H 33#include <config.h> 34#endif 35#include <X11/IntrinsicP.h> 36#include <X11/StringDefs.h> 37#include <X11/Xos.h> 38#include <X11/Xaw/TipP.h> 39#include <X11/Xaw/XawInit.h> 40#include <X11/Xmu/Converters.h> 41#include "Private.h" 42 43#define TIP_EVENT_MASK (ButtonPressMask | \ 44 ButtonReleaseMask | \ 45 PointerMotionMask | \ 46 ButtonMotionMask | \ 47 KeyPressMask | \ 48 KeyReleaseMask | \ 49 EnterWindowMask | \ 50 LeaveWindowMask) 51 52/* 53 * Types 54 */ 55typedef struct _XawTipInfo { 56 Screen *screen; 57 TipWidget tip; 58 Widget widget; 59 Bool mapped; 60 struct _XawTipInfo *next; 61} XawTipInfo; 62 63/* 64 * Class Methods 65 */ 66static void XawTipClassInitialize(void); 67static void XawTipInitialize(Widget, Widget, ArgList, Cardinal*); 68static void XawTipDestroy(Widget); 69static void XawTipExpose(Widget, XEvent*, Region); 70static void XawTipRealize(Widget, Mask*, XSetWindowAttributes*); 71static Boolean XawTipSetValues(Widget, Widget, Widget, ArgList, Cardinal*); 72 73/* 74 * Prototypes 75 */ 76static void TipEventHandler(Widget, XtPointer, XEvent*, Boolean*); 77static void TipShellEventHandler(Widget, XtPointer, XEvent*, Boolean*); 78static XawTipInfo *CreateTipInfo(Widget); 79static XawTipInfo *FindTipInfo(Widget); 80static void ResetTip(XawTipInfo*, Bool); 81static void TipTimeoutCallback(XtPointer, XtIntervalId*); 82static void TipLayout(XawTipInfo*); 83static void TipPosition(XawTipInfo*); 84 85/* 86 * Initialization 87 */ 88#define offset(field) XtOffsetOf(TipRec, tip.field) 89static XtResource resources[] = { 90 { 91 XtNforeground, 92 XtCForeground, 93 XtRPixel, 94 sizeof(Pixel), 95 offset(foreground), 96 XtRString, 97 XtDefaultForeground, 98 }, 99 { 100 XtNfont, 101 XtCFont, 102 XtRFontStruct, 103 sizeof(XFontStruct*), 104 offset(font), 105 XtRString, 106 XtDefaultFont 107 }, 108 { 109 XtNfontSet, 110 XtCFontSet, 111 XtRFontSet, 112 sizeof(XFontSet), 113 offset(fontset), 114 XtRString, 115 XtDefaultFontSet 116 }, 117 { 118 XtNtopMargin, 119 XtCVerticalMargins, 120 XtRDimension, 121 sizeof(Dimension), 122 offset(top_margin), 123 XtRImmediate, 124 (XtPointer)2 125 }, 126 { 127 XtNbottomMargin, 128 XtCVerticalMargins, 129 XtRDimension, 130 sizeof(Dimension), 131 offset(bottom_margin), 132 XtRImmediate, 133 (XtPointer)2 134 }, 135 { 136 XtNleftMargin, 137 XtCHorizontalMargins, 138 XtRDimension, 139 sizeof(Dimension), 140 offset(left_margin), 141 XtRImmediate, 142 (XtPointer)6 143 }, 144 { 145 XtNrightMargin, 146 XtCHorizontalMargins, 147 XtRDimension, 148 sizeof(Dimension), 149 offset(right_margin), 150 XtRImmediate, 151 (XtPointer)6 152 }, 153 { 154 XtNbackingStore, 155 XtCBackingStore, 156 XtRBackingStore, 157 sizeof(int), 158 offset(backing_store), 159 XtRImmediate, 160 (XtPointer)(Always + WhenMapped + NotUseful) 161 }, 162 { 163 XtNtimeout, 164 XtCTimeout, 165 XtRInt, 166 sizeof(int), 167 offset(timeout), 168 XtRImmediate, 169 (XtPointer)500 170 }, 171 { 172 XawNdisplayList, 173 XawCDisplayList, 174 XawRDisplayList, 175 sizeof(XawDisplayList*), 176 offset(display_list), 177 XtRImmediate, 178 NULL 179 }, 180}; 181#undef offset 182 183TipClassRec tipClassRec = { 184 /* core */ 185 { 186 (WidgetClass)&widgetClassRec, /* superclass */ 187 "Tip", /* class_name */ 188 sizeof(TipRec), /* widget_size */ 189 XawTipClassInitialize, /* class_initialize */ 190 NULL, /* class_part_initialize */ 191 False, /* class_inited */ 192 XawTipInitialize, /* initialize */ 193 NULL, /* initialize_hook */ 194 XawTipRealize, /* realize */ 195 NULL, /* actions */ 196 0, /* num_actions */ 197 resources, /* resources */ 198 XtNumber(resources), /* num_resources */ 199 NULLQUARK, /* xrm_class */ 200 True, /* compress_motion */ 201 True, /* compress_exposure */ 202 True, /* compress_enterleave */ 203 False, /* visible_interest */ 204 XawTipDestroy, /* destroy */ 205 NULL, /* resize */ 206 XawTipExpose, /* expose */ 207 XawTipSetValues, /* set_values */ 208 NULL, /* set_values_hook */ 209 XtInheritSetValuesAlmost, /* set_values_almost */ 210 NULL, /* get_values_hook */ 211 NULL, /* accept_focus */ 212 XtVersion, /* version */ 213 NULL, /* callback_private */ 214 NULL, /* tm_table */ 215 XtInheritQueryGeometry, /* query_geometry */ 216 XtInheritDisplayAccelerator, /* display_accelerator */ 217 NULL, /* extension */ 218 }, 219 /* tip */ 220 { 221 NULL, /* extension */ 222 }, 223}; 224 225WidgetClass tipWidgetClass = (WidgetClass)&tipClassRec; 226 227static XawTipInfo *first_tip; 228 229/* 230 * Implementation 231 */ 232static void 233XawTipClassInitialize(void) 234{ 235 XawInitializeWidgetSet(); 236 XtAddConverter(XtRString, XtRBackingStore, XmuCvtStringToBackingStore, 237 NULL, 0); 238 XtSetTypeConverter(XtRBackingStore, XtRString, XmuCvtBackingStoreToString, 239 NULL, 0, XtCacheNone, NULL); 240} 241 242/*ARGSUSED*/ 243static void 244XawTipInitialize(Widget req, Widget w, ArgList args, Cardinal *num_args) 245{ 246 TipWidget tip = (TipWidget)w; 247 XGCValues values; 248 249 if (!tip->tip.font) XtError("Aborting: no font found\n"); 250 if (tip->tip.international && !tip->tip.fontset) 251 XtError("Aborting: no fontset found\n"); 252 253 tip->tip.timer = 0; 254 255 values.foreground = tip->tip.foreground; 256 values.background = tip->core.background_pixel; 257 values.font = tip->tip.font->fid; 258 values.graphics_exposures = False; 259 260 tip->tip.gc = XtAllocateGC(w, 0, GCForeground | GCBackground | GCFont | 261 GCGraphicsExposures, &values, GCFont, 0); 262} 263 264static void 265XawTipDestroy(Widget w) 266{ 267 XawTipInfo *info = FindTipInfo(w); 268 TipWidget tip = (TipWidget)w; 269 270 if (tip->tip.timer) 271 XtRemoveTimeOut(tip->tip.timer); 272 273 XtReleaseGC(w, tip->tip.gc); 274 275 XtRemoveEventHandler(XtParent(w), KeyPressMask, False, TipShellEventHandler, 276 (XtPointer)NULL); 277 if (info == first_tip) 278 first_tip = first_tip->next; 279 else { 280 XawTipInfo *p = first_tip; 281 282 while (p && p->next != info) 283 p = p->next; 284 if (p) 285 p->next = info->next; 286 } 287 XtFree((char*)info); 288} 289 290static void 291XawTipRealize(Widget w, Mask *mask, XSetWindowAttributes *attr) 292{ 293 TipWidget tip = (TipWidget)w; 294 295 if (tip->tip.backing_store == Always || 296 tip->tip.backing_store == NotUseful || 297 tip->tip.backing_store == WhenMapped) { 298 *mask |= CWBackingStore; 299 attr->backing_store = tip->tip.backing_store; 300 } 301 else 302 *mask &= ~CWBackingStore; 303 *mask |= CWOverrideRedirect; 304 attr->override_redirect = True; 305 306 XtWindow(w) = XCreateWindow(DisplayOfScreen(XtScreen(w)), 307 RootWindowOfScreen(XtScreen(w)), 308 XtX(w), XtY(w), 309 XtWidth(w) ? XtWidth(w) : 1, 310 XtHeight(w) ? XtHeight(w) : 1, 311 XtBorderWidth(w), 312 DefaultDepthOfScreen(XtScreen(w)), 313 InputOutput, 314 (Visual *)CopyFromParent, 315 *mask, attr); 316} 317 318static void 319XawTipExpose(Widget w, XEvent *event, Region region) 320{ 321 TipWidget tip = (TipWidget)w; 322 GC gc = tip->tip.gc; 323 char *nl, *label = tip->tip.label; 324 Position y = tip->tip.top_margin + tip->tip.font->max_bounds.ascent; 325 int len; 326 327 if (tip->tip.display_list) 328 XawRunDisplayList(w, tip->tip.display_list, event, region); 329 330 if (tip->tip.international == True) { 331 Position ksy = tip->tip.top_margin; 332 XFontSetExtents *ext = XExtentsOfFontSet(tip->tip.fontset); 333 334 ksy += XawAbs(ext->max_ink_extent.y); 335 336 while ((nl = index(label, '\n')) != NULL) { 337 XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset, 338 gc, tip->tip.left_margin, ksy, label, 339 (int)(nl - label)); 340 ksy += ext->max_ink_extent.height; 341 label = nl + 1; 342 } 343 len = strlen(label); 344 if (len) 345 XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset, gc, 346 tip->tip.left_margin, ksy, label, len); 347 } 348 else { 349 while ((nl = index(label, '\n')) != NULL) { 350 if (tip->tip.encoding) 351 XDrawString16(XtDisplay(w), XtWindow(w), gc, 352 tip->tip.left_margin, y, 353 (XChar2b*)label, (int)(nl - label) >> 1); 354 else 355 XDrawString(XtDisplay(w), XtWindow(w), gc, 356 tip->tip.left_margin, y, label, (int)(nl - label)); 357 y += tip->tip.font->max_bounds.ascent + 358 tip->tip.font->max_bounds.descent; 359 label = nl + 1; 360 } 361 len = strlen(label); 362 if (len) { 363 if (tip->tip.encoding) 364 XDrawString16(XtDisplay(w), XtWindow(w), gc, 365 tip->tip.left_margin, y, (XChar2b*)label, len >> 1); 366 else 367 XDrawString(XtDisplay(w), XtWindow(w), gc, 368 tip->tip.left_margin, y, label, len); 369 } 370 } 371} 372 373/*ARGSUSED*/ 374static Boolean 375XawTipSetValues(Widget current, Widget request, Widget cnew, 376 ArgList args, Cardinal *num_args) 377{ 378 TipWidget curtip = (TipWidget)current; 379 TipWidget newtip = (TipWidget)cnew; 380 Boolean redisplay = False; 381 382 if (curtip->tip.font->fid != newtip->tip.font->fid || 383 curtip->tip.foreground != newtip->tip.foreground) { 384 XGCValues values; 385 386 values.foreground = newtip->tip.foreground; 387 values.background = newtip->core.background_pixel; 388 values.font = newtip->tip.font->fid; 389 values.graphics_exposures = False; 390 XtReleaseGC(cnew, curtip->tip.gc); 391 newtip->tip.gc = XtAllocateGC(cnew, 0, GCForeground | GCBackground | 392 GCFont | GCGraphicsExposures, &values, 393 GCFont, 0); 394 redisplay = True; 395 } 396 if (curtip->tip.display_list != newtip->tip.display_list) 397 redisplay = True; 398 399 return (redisplay); 400} 401 402static void 403TipLayout(XawTipInfo *info) 404{ 405 XFontStruct *fs = info->tip->tip.font; 406 int width = 0, height; 407 char *nl, *label = info->tip->tip.label; 408 409 if (info->tip->tip.international == True) { 410 XFontSet fset = info->tip->tip.fontset; 411 XFontSetExtents *ext = XExtentsOfFontSet(fset); 412 413 height = ext->max_ink_extent.height; 414 if ((nl = index(label, '\n')) != NULL) { 415 /*CONSTCOND*/ 416 while (True) { 417 int w = XmbTextEscapement(fset, label, (int)(nl - label)); 418 419 if (w > width) 420 width = w; 421 if (*nl == '\0') 422 break; 423 label = nl + 1; 424 if (*label) 425 height += ext->max_ink_extent.height; 426 if ((nl = index(label, '\n')) == NULL) 427 nl = index(label, '\0'); 428 } 429 } 430 else 431 width = XmbTextEscapement(fset, label, strlen(label)); 432 } 433 else { 434 height = fs->max_bounds.ascent + fs->max_bounds.descent; 435 if ((nl = index(label, '\n')) != NULL) { 436 /*CONSTCOND*/ 437 while (True) { 438 int w = info->tip->tip.encoding ? 439 XTextWidth16(fs, (XChar2b*)label, (int)(nl - label) >> 1) : 440 XTextWidth(fs, label, (int)(nl - label)); 441 if (w > width) 442 width = w; 443 if (*nl == '\0') 444 break; 445 label = nl + 1; 446 if (*label) 447 height += fs->max_bounds.ascent + fs->max_bounds.descent; 448 if ((nl = index(label, '\n')) == NULL) 449 nl = index(label, '\0'); 450 } 451 } 452 else 453 width = info->tip->tip.encoding ? 454 XTextWidth16(fs, (XChar2b*)label, strlen(label) >> 1) : 455 XTextWidth(fs, label, strlen(label)); 456 } 457 XtWidth(info->tip) = width + info->tip->tip.left_margin + 458 info->tip->tip.right_margin; 459 XtHeight(info->tip) = height + info->tip->tip.top_margin + 460 info->tip->tip.bottom_margin; 461} 462 463#define DEFAULT_TIP_Y_OFFSET 12 464static void 465TipPosition(XawTipInfo *info) 466{ 467 Window r, c; 468 int rx, ry, wx, wy; 469 unsigned mask; 470 Position x, y; 471 472 XQueryPointer(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip), 473 &r, &c, &rx, &ry, &wx, &wy, &mask); 474 x = rx - (XtWidth(info->tip) >> 1); 475 y = ry + DEFAULT_TIP_Y_OFFSET; 476 477 if (x >= 0) { 478 int scr_width = WidthOfScreen(XtScreen(info->tip)); 479 480 if (x + XtWidth(info->tip) + XtBorderWidth(info->tip) > scr_width) 481 x = scr_width - XtWidth(info->tip) - XtBorderWidth(info->tip); 482 } 483 if (x < 0) 484 x = 0; 485 if (y >= 0) { 486 int scr_height = HeightOfScreen(XtScreen(info->tip)); 487 488 if (y + XtHeight(info->tip) + XtBorderWidth(info->tip) > scr_height) 489 y -= XtHeight(info->tip) + XtBorderWidth(info->tip) + 490 (DEFAULT_TIP_Y_OFFSET << 1); 491 } 492 if (y < 0) 493 y = 0; 494 495 XMoveResizeWindow(XtDisplay(info->tip), XtWindow(info->tip), 496 (int)(XtX(info->tip) = x), (int)(XtY(info->tip) = y), 497 (unsigned)XtWidth(info->tip), (unsigned)XtHeight(info->tip)); 498} 499 500static XawTipInfo * 501CreateTipInfo(Widget w) 502{ 503 XawTipInfo *info = XtNew(XawTipInfo); 504 Widget shell = w; 505 506 info->screen = XtScreen(w); 507 508 while (XtParent(shell)) 509 shell = XtParent(shell); 510 511 info->tip = (TipWidget)XtCreateWidget("tip", tipWidgetClass, shell, NULL, 0); 512 XtRealizeWidget((Widget)info->tip); 513 info->widget = NULL; 514 info->mapped = False; 515 info->next = NULL; 516 XtAddEventHandler(shell, KeyPressMask, False, TipShellEventHandler, 517 (XtPointer)NULL); 518 519 return (info); 520} 521 522static XawTipInfo * 523FindTipInfo(Widget w) 524{ 525 XawTipInfo *ptip, *tip = first_tip; 526 Screen *screen = XtScreenOfObject(w); 527 528 if (tip == NULL) 529 return (first_tip = tip = CreateTipInfo(w)); 530 531 for (ptip = tip; tip; ptip = tip, tip = tip->next) 532 if (tip->screen == screen) 533 return (tip); 534 535 return (ptip->next = CreateTipInfo(w)); 536} 537 538static void 539ResetTip(XawTipInfo *info, Bool add_timeout) 540{ 541 if (info->tip->tip.timer) { 542 XtRemoveTimeOut(info->tip->tip.timer); 543 info->tip->tip.timer = 0; 544 } 545 if (info->mapped) { 546 XtRemoveGrab(XtParent((Widget)info->tip)); 547 XUnmapWindow(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip)); 548 info->mapped = False; 549 } 550 if (add_timeout) { 551 info->tip->tip.timer = 552 XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)info->tip), 553 info->tip->tip.timeout, TipTimeoutCallback, 554 (XtPointer)info); 555 } 556} 557 558static void 559TipTimeoutCallback(XtPointer closure, XtIntervalId *id) 560{ 561 XawTipInfo *info = (XawTipInfo*)closure; 562 Arg args[3]; 563 564 info->tip->tip.label = NULL; 565 info->tip->tip.international = False; 566 info->tip->tip.encoding = 0; 567 XtSetArg(args[0], XtNtip, &info->tip->tip.label); 568 XtSetArg(args[1], XtNinternational, &info->tip->tip.international); 569 XtSetArg(args[2], XtNencoding, &info->tip->tip.encoding); 570 XtGetValues(info->widget, args, 3); 571 572 if (info->tip->tip.label) { 573 TipLayout(info); 574 TipPosition(info); 575 XMapRaised(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip)); 576 XtAddGrab(XtParent((Widget)info->tip), True, True); 577 info->mapped = True; 578 } 579} 580 581/*ARGSUSED*/ 582static void 583TipShellEventHandler(Widget w, XtPointer client_data, XEvent *event, 584 Boolean *continue_to_dispatch) 585{ 586 ResetTip(FindTipInfo(w), False); 587} 588 589/*ARGSUSED*/ 590static void 591TipEventHandler(Widget w, XtPointer client_data, XEvent *event, 592 Boolean *continue_to_dispatch) 593{ 594 XawTipInfo *info = FindTipInfo(w); 595 Boolean add_timeout; 596 597 if (info->widget != w) { 598 ResetTip(info, False); 599 info->widget = w; 600 } 601 602 switch (event->type) { 603 case EnterNotify: 604 add_timeout = True; 605 break; 606 case MotionNotify: 607 /* If any button is pressed, timer is 0 */ 608 if (info->mapped) 609 return; 610 add_timeout = info->tip->tip.timer != 0; 611 break; 612 default: 613 add_timeout = False; 614 break; 615 } 616 ResetTip(info, add_timeout); 617} 618 619/* 620 * Public routines 621 */ 622void 623XawTipEnable(Widget w) 624{ 625 XtAddEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler, 626 (XtPointer)NULL); 627} 628 629void 630XawTipDisable(Widget w) 631{ 632 XawTipInfo *info = FindTipInfo(w); 633 634 XtRemoveEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler, 635 (XtPointer)NULL); 636 if (info->widget == w) 637 ResetTip(info, False); 638} 639