2 * Copyright (c) 1999 by The XFree86 Project, Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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
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
27 * Author: Paulo César Pereira de Andrade
33 #include <X11/IntrinsicP.h>
34 #include <X11/StringDefs.h>
36 #include <X11/Xaw/TipP.h>
37 #include <X11/Xaw/XawInit.h>
38 #include <X11/Xmu/Converters.h>
41 #define TIP_EVENT_MASK (ButtonPressMask | \
53 typedef struct _XawTipInfo {
58 struct _XawTipInfo *next;
64 static void XawTipClassInitialize(void);
65 static void XawTipInitialize(Widget, Widget, ArgList, Cardinal*);
66 static void XawTipDestroy(Widget);
67 static void XawTipExpose(Widget, XEvent*, Region);
68 static void XawTipRealize(Widget, Mask*, XSetWindowAttributes*);
69 static Boolean XawTipSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
74 static void TipEventHandler(Widget, XtPointer, XEvent*, Boolean*);
75 static void TipShellEventHandler(Widget, XtPointer, XEvent*, Boolean*);
76 static XawTipInfo *CreateTipInfo(Widget);
77 static XawTipInfo *FindTipInfo(Widget);
78 static void ResetTip(XawTipInfo*, Bool);
79 static void TipTimeoutCallback(XtPointer, XtIntervalId*);
80 static void TipLayout(XawTipInfo*);
81 static void TipPosition(XawTipInfo*);
86 #define offset(field) XtOffsetOf(TipRec, tip.field)
87 static XtResource resources[] = {
101 sizeof(XFontStruct*),
129 offset(bottom_margin),
135 XtCHorizontalMargins,
144 XtCHorizontalMargins,
147 offset(right_margin),
156 offset(backing_store),
158 (XtPointer)(Always + WhenMapped + NotUseful)
173 sizeof(XawDisplayList*),
174 offset(display_list),
181 TipClassRec tipClassRec = {
184 (WidgetClass)&widgetClassRec, /* superclass */
185 "Tip", /* class_name */
186 sizeof(TipRec), /* widget_size */
187 XawTipClassInitialize, /* class_initialize */
188 NULL, /* class_part_initialize */
189 False, /* class_inited */
190 XawTipInitialize, /* initialize */
191 NULL, /* initialize_hook */
192 XawTipRealize, /* realize */
195 resources, /* resources */
196 XtNumber(resources), /* num_resources */
197 NULLQUARK, /* xrm_class */
198 True, /* compress_motion */
199 True, /* compress_exposure */
200 True, /* compress_enterleave */
201 False, /* visible_interest */
202 XawTipDestroy, /* destroy */
204 XawTipExpose, /* expose */
205 XawTipSetValues, /* set_values */
206 NULL, /* set_values_hook */
207 XtInheritSetValuesAlmost, /* set_values_almost */
208 NULL, /* get_values_hook */
209 NULL, /* accept_focus */
210 XtVersion, /* version */
211 NULL, /* callback_private */
213 XtInheritQueryGeometry, /* query_geometry */
214 XtInheritDisplayAccelerator, /* display_accelerator */
215 NULL, /* extension */
219 NULL, /* extension */
223 WidgetClass tipWidgetClass = (WidgetClass)&tipClassRec;
225 static XawTipInfo *first_tip;
231 XawTipClassInitialize(void)
233 XawInitializeWidgetSet();
234 XtAddConverter(XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
236 XtSetTypeConverter(XtRBackingStore, XtRString, XmuCvtBackingStoreToString,
237 NULL, 0, XtCacheNone, NULL);
242 XawTipInitialize(Widget req, Widget w, ArgList args, Cardinal *num_args)
244 TipWidget tip = (TipWidget)w;
247 if (!tip->tip.font) XtError("Aborting: no font found\n");
248 if (tip->tip.international && !tip->tip.fontset)
249 XtError("Aborting: no fontset found\n");
253 values.foreground = tip->tip.foreground;
254 values.background = tip->core.background_pixel;
255 values.font = tip->tip.font->fid;
256 values.graphics_exposures = False;
258 tip->tip.gc = XtAllocateGC(w, 0, GCForeground | GCBackground | GCFont |
259 GCGraphicsExposures, &values, GCFont, 0);
263 XawTipDestroy(Widget w)
265 XawTipInfo *info = FindTipInfo(w);
266 TipWidget tip = (TipWidget)w;
269 XtRemoveTimeOut(tip->tip.timer);
271 XtReleaseGC(w, tip->tip.gc);
273 XtRemoveEventHandler(XtParent(w), KeyPressMask, False, TipShellEventHandler,
275 if (info == first_tip)
276 first_tip = first_tip->next;
278 XawTipInfo *p = first_tip;
280 while (p && p->next != info)
283 p->next = info->next;
289 XawTipRealize(Widget w, Mask *mask, XSetWindowAttributes *attr)
291 TipWidget tip = (TipWidget)w;
293 if (tip->tip.backing_store == Always ||
294 tip->tip.backing_store == NotUseful ||
295 tip->tip.backing_store == WhenMapped) {
296 *mask |= CWBackingStore;
297 attr->backing_store = tip->tip.backing_store;
300 *mask &= ~CWBackingStore;
301 *mask |= CWOverrideRedirect;
302 attr->override_redirect = True;
304 XtWindow(w) = XCreateWindow(DisplayOfScreen(XtScreen(w)),
305 RootWindowOfScreen(XtScreen(w)),
307 XtWidth(w) ? XtWidth(w) : 1,
308 XtHeight(w) ? XtHeight(w) : 1,
310 DefaultDepthOfScreen(XtScreen(w)),
312 (Visual *)CopyFromParent,
317 XawTipExpose(Widget w, XEvent *event, Region region)
319 TipWidget tip = (TipWidget)w;
321 char *nl, *label = tip->tip.label;
322 Position y = tip->tip.top_margin + tip->tip.font->max_bounds.ascent;
325 if (tip->tip.display_list)
326 XawRunDisplayList(w, tip->tip.display_list, event, region);
328 if (tip->tip.international == True) {
329 Position ksy = tip->tip.top_margin;
330 XFontSetExtents *ext = XExtentsOfFontSet(tip->tip.fontset);
332 ksy += XawAbs(ext->max_ink_extent.y);
334 while ((nl = index(label, '\n')) != NULL) {
335 XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset,
336 gc, tip->tip.left_margin, ksy, label,
338 ksy += ext->max_ink_extent.height;
343 XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset, gc,
344 tip->tip.left_margin, ksy, label, len);
347 while ((nl = index(label, '\n')) != NULL) {
348 if (tip->tip.encoding)
349 XDrawString16(XtDisplay(w), XtWindow(w), gc,
350 tip->tip.left_margin, y,
351 (XChar2b*)label, (int)(nl - label) >> 1);
353 XDrawString(XtDisplay(w), XtWindow(w), gc,
354 tip->tip.left_margin, y, label, (int)(nl - label));
355 y += tip->tip.font->max_bounds.ascent +
356 tip->tip.font->max_bounds.descent;
361 if (tip->tip.encoding)
362 XDrawString16(XtDisplay(w), XtWindow(w), gc,
363 tip->tip.left_margin, y, (XChar2b*)label, len >> 1);
365 XDrawString(XtDisplay(w), XtWindow(w), gc,
366 tip->tip.left_margin, y, label, len);
373 XawTipSetValues(Widget current, Widget request, Widget cnew,
374 ArgList args, Cardinal *num_args)
376 TipWidget curtip = (TipWidget)current;
377 TipWidget newtip = (TipWidget)cnew;
378 Boolean redisplay = False;
380 if (curtip->tip.font->fid != newtip->tip.font->fid ||
381 curtip->tip.foreground != newtip->tip.foreground) {
384 values.foreground = newtip->tip.foreground;
385 values.background = newtip->core.background_pixel;
386 values.font = newtip->tip.font->fid;
387 values.graphics_exposures = False;
388 XtReleaseGC(cnew, curtip->tip.gc);
389 newtip->tip.gc = XtAllocateGC(cnew, 0, GCForeground | GCBackground |
390 GCFont | GCGraphicsExposures, &values,
394 if (curtip->tip.display_list != newtip->tip.display_list)
401 TipLayout(XawTipInfo *info)
403 XFontStruct *fs = info->tip->tip.font;
404 int width = 0, height;
405 char *nl, *label = info->tip->tip.label;
407 if (info->tip->tip.international == True) {
408 XFontSet fset = info->tip->tip.fontset;
409 XFontSetExtents *ext = XExtentsOfFontSet(fset);
411 height = ext->max_ink_extent.height;
412 if ((nl = index(label, '\n')) != NULL) {
415 int w = XmbTextEscapement(fset, label, (int)(nl - label));
423 height += ext->max_ink_extent.height;
424 if ((nl = index(label, '\n')) == NULL)
425 nl = index(label, '\0');
429 width = XmbTextEscapement(fset, label, strlen(label));
432 height = fs->max_bounds.ascent + fs->max_bounds.descent;
433 if ((nl = index(label, '\n')) != NULL) {
436 int w = info->tip->tip.encoding ?
437 XTextWidth16(fs, (XChar2b*)label, (int)(nl - label) >> 1) :
438 XTextWidth(fs, label, (int)(nl - label));
445 height += fs->max_bounds.ascent + fs->max_bounds.descent;
446 if ((nl = index(label, '\n')) == NULL)
447 nl = index(label, '\0');
451 width = info->tip->tip.encoding ?
452 XTextWidth16(fs, (XChar2b*)label, strlen(label) >> 1) :
453 XTextWidth(fs, label, strlen(label));
455 XtWidth(info->tip) = width + info->tip->tip.left_margin +
456 info->tip->tip.right_margin;
457 XtHeight(info->tip) = height + info->tip->tip.top_margin +
458 info->tip->tip.bottom_margin;
461 #define DEFAULT_TIP_Y_OFFSET 12
463 TipPosition(XawTipInfo *info)
470 XQueryPointer(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip),
471 &r, &c, &rx, &ry, &wx, &wy, &mask);
472 x = rx - (XtWidth(info->tip) >> 1);
473 y = ry + DEFAULT_TIP_Y_OFFSET;
476 int scr_width = WidthOfScreen(XtScreen(info->tip));
478 if (x + XtWidth(info->tip) + XtBorderWidth(info->tip) > scr_width)
479 x = scr_width - XtWidth(info->tip) - XtBorderWidth(info->tip);
484 int scr_height = HeightOfScreen(XtScreen(info->tip));
486 if (y + XtHeight(info->tip) + XtBorderWidth(info->tip) > scr_height)
487 y -= XtHeight(info->tip) + XtBorderWidth(info->tip) +
488 (DEFAULT_TIP_Y_OFFSET << 1);
493 XMoveResizeWindow(XtDisplay(info->tip), XtWindow(info->tip),
494 (int)(XtX(info->tip) = x), (int)(XtY(info->tip) = y),
495 (unsigned)XtWidth(info->tip), (unsigned)XtHeight(info->tip));
499 CreateTipInfo(Widget w)
501 XawTipInfo *info = XtNew(XawTipInfo);
504 info->screen = XtScreen(w);
506 while (XtParent(shell))
507 shell = XtParent(shell);
509 info->tip = (TipWidget)XtCreateWidget("tip", tipWidgetClass, shell, NULL, 0);
510 XtRealizeWidget((Widget)info->tip);
512 info->mapped = False;
514 XtAddEventHandler(shell, KeyPressMask, False, TipShellEventHandler,
521 FindTipInfo(Widget w)
523 XawTipInfo *ptip, *tip = first_tip;
524 Screen *screen = XtScreenOfObject(w);
527 return (first_tip = tip = CreateTipInfo(w));
529 for (ptip = tip; tip; ptip = tip, tip = tip->next)
530 if (tip->screen == screen)
533 return (ptip->next = CreateTipInfo(w));
537 ResetTip(XawTipInfo *info, Bool add_timeout)
539 if (info->tip->tip.timer) {
540 XtRemoveTimeOut(info->tip->tip.timer);
541 info->tip->tip.timer = 0;
544 XtRemoveGrab(XtParent((Widget)info->tip));
545 XUnmapWindow(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip));
546 info->mapped = False;
549 info->tip->tip.timer =
550 XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)info->tip),
551 info->tip->tip.timeout, TipTimeoutCallback,
557 TipTimeoutCallback(XtPointer closure, XtIntervalId *id)
559 XawTipInfo *info = (XawTipInfo*)closure;
562 info->tip->tip.label = NULL;
563 info->tip->tip.international = False;
564 info->tip->tip.encoding = 0;
565 info->tip->tip.timer = 0;
566 XtSetArg(args[0], XtNtip, &info->tip->tip.label);
567 XtSetArg(args[1], XtNinternational, &info->tip->tip.international);
568 XtSetArg(args[2], XtNencoding, &info->tip->tip.encoding);
569 XtGetValues(info->widget, args, 3);
571 if (info->tip->tip.label) {
574 XMapRaised(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip));
575 XtAddGrab(XtParent((Widget)info->tip), True, True);
582 TipShellEventHandler(Widget w, XtPointer client_data, XEvent *event,
583 Boolean *continue_to_dispatch)
585 ResetTip(FindTipInfo(w), False);
590 TipEventHandler(Widget w, XtPointer client_data, XEvent *event,
591 Boolean *continue_to_dispatch)
593 XawTipInfo *info = FindTipInfo(w);
596 if (info->widget != w) {
597 ResetTip(info, False);
601 switch (event->type) {
606 /* If any button is pressed, timer is 0 */
609 add_timeout = info->tip->tip.timer != 0;
615 ResetTip(info, add_timeout);
622 XawTipEnable(Widget w)
624 XtAddEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler,
629 XawTipDisable(Widget w)
631 XawTipInfo *info = FindTipInfo(w);
633 XtRemoveEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler,
635 if (info->widget == w)
636 ResetTip(info, False);