Tip.c revision efbcb2bf
17a84e134Smrg/*
27a84e134Smrg * Copyright (c) 1999 by The XFree86 Project, Inc.
37a84e134Smrg *
47a84e134Smrg * Permission is hereby granted, free of charge, to any person obtaining a
57a84e134Smrg * copy of this software and associated documentation files (the "Software"),
67a84e134Smrg * to deal in the Software without restriction, including without limitation
77a84e134Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87a84e134Smrg * and/or sell copies of the Software, and to permit persons to whom the
97a84e134Smrg * Software is furnished to do so, subject to the following conditions:
107a84e134Smrg *
117a84e134Smrg * The above copyright notice and this permission notice shall be included in
127a84e134Smrg * all copies or substantial portions of the Software.
137a84e134Smrg *
147a84e134Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
157a84e134Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
167a84e134Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
177a84e134Smrg * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
187a84e134Smrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
197a84e134Smrg * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
207a84e134Smrg * SOFTWARE.
217a84e134Smrg *
227a84e134Smrg * Except as contained in this notice, the name of the XFree86 Project shall
237a84e134Smrg * not be used in advertising or otherwise to promote the sale, use or other
247a84e134Smrg * dealings in this Software without prior written authorization from the
257a84e134Smrg * XFree86 Project.
267a84e134Smrg *
277a84e134Smrg * Author: Paulo César Pereira de Andrade
287a84e134Smrg */
297a84e134Smrg
307a84e134Smrg#ifdef HAVE_CONFIG_H
317a84e134Smrg#include <config.h>
327a84e134Smrg#endif
337a84e134Smrg#include <X11/IntrinsicP.h>
347a84e134Smrg#include <X11/StringDefs.h>
357a84e134Smrg#include <X11/Xos.h>
367a84e134Smrg#include <X11/Xaw/TipP.h>
377a84e134Smrg#include <X11/Xaw/XawInit.h>
387a84e134Smrg#include <X11/Xmu/Converters.h>
397a84e134Smrg#include "Private.h"
407a84e134Smrg
417a84e134Smrg#define	TIP_EVENT_MASK (ButtonPressMask	  |	\
427a84e134Smrg			ButtonReleaseMask |	\
437a84e134Smrg			PointerMotionMask |	\
447a84e134Smrg			ButtonMotionMask  |	\
457a84e134Smrg			KeyPressMask	  |	\
467a84e134Smrg			KeyReleaseMask	  |	\
477a84e134Smrg			EnterWindowMask	  |	\
487a84e134Smrg			LeaveWindowMask)
497a84e134Smrg
507a84e134Smrg/*
517a84e134Smrg * Types
527a84e134Smrg */
537a84e134Smrgtypedef struct _XawTipInfo {
547a84e134Smrg    Screen *screen;
557a84e134Smrg    TipWidget tip;
567a84e134Smrg    Widget widget;
577a84e134Smrg    Bool mapped;
587a84e134Smrg    struct _XawTipInfo *next;
597a84e134Smrg} XawTipInfo;
607a84e134Smrg
617a84e134Smrg/*
627a84e134Smrg * Class Methods
637a84e134Smrg */
647a84e134Smrgstatic void XawTipClassInitialize(void);
657a84e134Smrgstatic void XawTipInitialize(Widget, Widget, ArgList, Cardinal*);
667a84e134Smrgstatic void XawTipDestroy(Widget);
677a84e134Smrgstatic void XawTipExpose(Widget, XEvent*, Region);
687a84e134Smrgstatic void XawTipRealize(Widget, Mask*, XSetWindowAttributes*);
697a84e134Smrgstatic Boolean XawTipSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
707a84e134Smrg
717a84e134Smrg/*
727a84e134Smrg * Prototypes
737a84e134Smrg */
747a84e134Smrgstatic void TipEventHandler(Widget, XtPointer, XEvent*, Boolean*);
757a84e134Smrgstatic void TipShellEventHandler(Widget, XtPointer, XEvent*, Boolean*);
767a84e134Smrgstatic XawTipInfo *CreateTipInfo(Widget);
777a84e134Smrgstatic XawTipInfo *FindTipInfo(Widget);
787a84e134Smrgstatic void ResetTip(XawTipInfo*, Bool);
797a84e134Smrgstatic void TipTimeoutCallback(XtPointer, XtIntervalId*);
807a84e134Smrgstatic void TipLayout(XawTipInfo*);
817a84e134Smrgstatic void TipPosition(XawTipInfo*);
827a84e134Smrg
837a84e134Smrg/*
847a84e134Smrg * Initialization
857a84e134Smrg */
867a84e134Smrg#define offset(field) XtOffsetOf(TipRec, tip.field)
877a84e134Smrgstatic XtResource resources[] = {
887a84e134Smrg  {
897a84e134Smrg    XtNforeground,
907a84e134Smrg    XtCForeground,
917a84e134Smrg    XtRPixel,
927a84e134Smrg    sizeof(Pixel),
937a84e134Smrg    offset(foreground),
947a84e134Smrg    XtRString,
955ec34c4cSmrg    (XtPointer)XtDefaultForeground,
967a84e134Smrg  },
977a84e134Smrg  {
987a84e134Smrg    XtNfont,
997a84e134Smrg    XtCFont,
1007a84e134Smrg    XtRFontStruct,
1017a84e134Smrg    sizeof(XFontStruct*),
1027a84e134Smrg    offset(font),
1037a84e134Smrg    XtRString,
1045ec34c4cSmrg    (XtPointer)XtDefaultFont
1057a84e134Smrg  },
1067a84e134Smrg  {
1077a84e134Smrg    XtNfontSet,
1087a84e134Smrg    XtCFontSet,
1097a84e134Smrg    XtRFontSet,
1107a84e134Smrg    sizeof(XFontSet),
1117a84e134Smrg    offset(fontset),
1127a84e134Smrg    XtRString,
1135ec34c4cSmrg    (XtPointer)XtDefaultFontSet
1147a84e134Smrg  },
1157a84e134Smrg  {
1167a84e134Smrg    XtNtopMargin,
1177a84e134Smrg    XtCVerticalMargins,
1187a84e134Smrg    XtRDimension,
1197a84e134Smrg    sizeof(Dimension),
1207a84e134Smrg    offset(top_margin),
1217a84e134Smrg    XtRImmediate,
1227a84e134Smrg    (XtPointer)2
1237a84e134Smrg  },
1247a84e134Smrg  {
1257a84e134Smrg    XtNbottomMargin,
1267a84e134Smrg    XtCVerticalMargins,
1277a84e134Smrg    XtRDimension,
1287a84e134Smrg    sizeof(Dimension),
1297a84e134Smrg    offset(bottom_margin),
1307a84e134Smrg    XtRImmediate,
1317a84e134Smrg    (XtPointer)2
1327a84e134Smrg  },
1337a84e134Smrg  {
1347a84e134Smrg    XtNleftMargin,
1357a84e134Smrg    XtCHorizontalMargins,
1367a84e134Smrg    XtRDimension,
1377a84e134Smrg    sizeof(Dimension),
1387a84e134Smrg    offset(left_margin),
1397a84e134Smrg    XtRImmediate,
1407a84e134Smrg    (XtPointer)6
1417a84e134Smrg  },
1427a84e134Smrg  {
1437a84e134Smrg    XtNrightMargin,
1447a84e134Smrg    XtCHorizontalMargins,
1457a84e134Smrg    XtRDimension,
1467a84e134Smrg    sizeof(Dimension),
1477a84e134Smrg    offset(right_margin),
1487a84e134Smrg    XtRImmediate,
1497a84e134Smrg    (XtPointer)6
1507a84e134Smrg  },
1517a84e134Smrg  {
1527a84e134Smrg    XtNbackingStore,
1537a84e134Smrg    XtCBackingStore,
1547a84e134Smrg    XtRBackingStore,
1557a84e134Smrg    sizeof(int),
1567a84e134Smrg    offset(backing_store),
1577a84e134Smrg    XtRImmediate,
1587a84e134Smrg    (XtPointer)(Always + WhenMapped + NotUseful)
1597a84e134Smrg  },
1607a84e134Smrg  {
1617a84e134Smrg    XtNtimeout,
1627a84e134Smrg    XtCTimeout,
1637a84e134Smrg    XtRInt,
1647a84e134Smrg    sizeof(int),
1657a84e134Smrg    offset(timeout),
1667a84e134Smrg    XtRImmediate,
1677a84e134Smrg    (XtPointer)500
1687a84e134Smrg  },
1697a84e134Smrg  {
1707a84e134Smrg    XawNdisplayList,
1717a84e134Smrg    XawCDisplayList,
1727a84e134Smrg    XawRDisplayList,
1737a84e134Smrg    sizeof(XawDisplayList*),
1747a84e134Smrg    offset(display_list),
1757a84e134Smrg    XtRImmediate,
1767a84e134Smrg    NULL
1777a84e134Smrg  },
1787a84e134Smrg};
1797a84e134Smrg#undef offset
1807a84e134Smrg
1817a84e134SmrgTipClassRec tipClassRec = {
1827a84e134Smrg  /* core */
1837a84e134Smrg  {
1847a84e134Smrg    (WidgetClass)&widgetClassRec,	/* superclass */
1857a84e134Smrg    "Tip",				/* class_name */
1867a84e134Smrg    sizeof(TipRec),			/* widget_size */
1877a84e134Smrg    XawTipClassInitialize,		/* class_initialize */
1887a84e134Smrg    NULL,				/* class_part_initialize */
1897a84e134Smrg    False,				/* class_inited */
1907a84e134Smrg    XawTipInitialize,			/* initialize */
1917a84e134Smrg    NULL,				/* initialize_hook */
1927a84e134Smrg    XawTipRealize,			/* realize */
1937a84e134Smrg    NULL,				/* actions */
1947a84e134Smrg    0,					/* num_actions */
1957a84e134Smrg    resources,				/* resources */
1967a84e134Smrg    XtNumber(resources),		/* num_resources */
1977a84e134Smrg    NULLQUARK,				/* xrm_class */
1987a84e134Smrg    True,				/* compress_motion */
1997a84e134Smrg    True,				/* compress_exposure */
2007a84e134Smrg    True,				/* compress_enterleave */
2017a84e134Smrg    False,				/* visible_interest */
2027a84e134Smrg    XawTipDestroy,			/* destroy */
2037a84e134Smrg    NULL,				/* resize */
2047a84e134Smrg    XawTipExpose,			/* expose */
2057a84e134Smrg    XawTipSetValues,			/* set_values */
2067a84e134Smrg    NULL,				/* set_values_hook */
2077a84e134Smrg    XtInheritSetValuesAlmost,		/* set_values_almost */
2087a84e134Smrg    NULL,				/* get_values_hook */
2097a84e134Smrg    NULL,				/* accept_focus */
2107a84e134Smrg    XtVersion,				/* version */
2117a84e134Smrg    NULL,				/* callback_private */
2127a84e134Smrg    NULL,				/* tm_table */
2137a84e134Smrg    XtInheritQueryGeometry,		/* query_geometry */
2147a84e134Smrg    XtInheritDisplayAccelerator,	/* display_accelerator */
2157a84e134Smrg    NULL,				/* extension */
2167a84e134Smrg  },
2177a84e134Smrg  /* tip */
2187a84e134Smrg  {
2197a84e134Smrg    NULL,				/* extension */
2207a84e134Smrg  },
2217a84e134Smrg};
2227a84e134Smrg
2237a84e134SmrgWidgetClass tipWidgetClass = (WidgetClass)&tipClassRec;
2247a84e134Smrg
2257a84e134Smrgstatic XawTipInfo *first_tip;
2267a84e134Smrg
2277a84e134Smrg/*
2287a84e134Smrg * Implementation
2297a84e134Smrg */
2307a84e134Smrgstatic void
2317a84e134SmrgXawTipClassInitialize(void)
2327a84e134Smrg{
2337a84e134Smrg    XawInitializeWidgetSet();
2347a84e134Smrg    XtAddConverter(XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
2357a84e134Smrg		   NULL, 0);
2367a84e134Smrg    XtSetTypeConverter(XtRBackingStore, XtRString, XmuCvtBackingStoreToString,
2377a84e134Smrg		       NULL, 0, XtCacheNone, NULL);
2387a84e134Smrg}
2397a84e134Smrg
2407a84e134Smrg/*ARGSUSED*/
2417a84e134Smrgstatic void
2425ec34c4cSmrgXawTipInitialize(Widget req _X_UNUSED, Widget w _X_UNUSED, ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
2437a84e134Smrg{
2447a84e134Smrg    TipWidget tip = (TipWidget)w;
2457a84e134Smrg    XGCValues values;
2467a84e134Smrg
2477a84e134Smrg    if (!tip->tip.font) XtError("Aborting: no font found\n");
2487a84e134Smrg    if (tip->tip.international && !tip->tip.fontset)
2497a84e134Smrg	XtError("Aborting: no fontset found\n");
250421c997bSmrg
2517a84e134Smrg    tip->tip.timer = 0;
2527a84e134Smrg
2537a84e134Smrg    values.foreground = tip->tip.foreground;
2547a84e134Smrg    values.background = tip->core.background_pixel;
2557a84e134Smrg    values.font = tip->tip.font->fid;
2567a84e134Smrg    values.graphics_exposures = False;
2577a84e134Smrg
2587a84e134Smrg    tip->tip.gc = XtAllocateGC(w, 0, GCForeground | GCBackground | GCFont |
2597a84e134Smrg			       GCGraphicsExposures, &values, GCFont, 0);
2607a84e134Smrg}
2617a84e134Smrg
2627a84e134Smrgstatic void
2637a84e134SmrgXawTipDestroy(Widget w)
2647a84e134Smrg{
2657a84e134Smrg    XawTipInfo *info = FindTipInfo(w);
2667a84e134Smrg    TipWidget tip = (TipWidget)w;
2677a84e134Smrg
2687a84e134Smrg    if (tip->tip.timer)
2697a84e134Smrg	XtRemoveTimeOut(tip->tip.timer);
2707a84e134Smrg
2717a84e134Smrg    XtReleaseGC(w, tip->tip.gc);
2727a84e134Smrg
2737a84e134Smrg    XtRemoveEventHandler(XtParent(w), KeyPressMask, False, TipShellEventHandler,
2747a84e134Smrg			 (XtPointer)NULL);
2757a84e134Smrg    if (info == first_tip)
2767a84e134Smrg	first_tip = first_tip->next;
2777a84e134Smrg    else {
2787a84e134Smrg	XawTipInfo *p = first_tip;
2797a84e134Smrg
2807a84e134Smrg	while (p && p->next != info)
2817a84e134Smrg	    p = p->next;
2827a84e134Smrg	if (p)
2837a84e134Smrg	    p->next = info->next;
2847a84e134Smrg    }
2857a84e134Smrg    XtFree((char*)info);
2867a84e134Smrg}
2877a84e134Smrg
2887a84e134Smrgstatic void
2897a84e134SmrgXawTipRealize(Widget w, Mask *mask, XSetWindowAttributes *attr)
2907a84e134Smrg{
2917a84e134Smrg    TipWidget tip = (TipWidget)w;
2927a84e134Smrg
2937a84e134Smrg    if (tip->tip.backing_store == Always ||
2947a84e134Smrg	tip->tip.backing_store == NotUseful ||
2957a84e134Smrg	tip->tip.backing_store == WhenMapped) {
2967a84e134Smrg	*mask |= CWBackingStore;
2977a84e134Smrg	attr->backing_store = tip->tip.backing_store;
2987a84e134Smrg    }
2997a84e134Smrg    else
3005ec34c4cSmrg	*mask &= (Mask)(~CWBackingStore);
3017a84e134Smrg    *mask |= CWOverrideRedirect;
3027a84e134Smrg    attr->override_redirect = True;
3037a84e134Smrg
3047a84e134Smrg    XtWindow(w) = XCreateWindow(DisplayOfScreen(XtScreen(w)),
3057a84e134Smrg				RootWindowOfScreen(XtScreen(w)),
3067a84e134Smrg				XtX(w), XtY(w),
3077a84e134Smrg				XtWidth(w) ? XtWidth(w) : 1,
3087a84e134Smrg				XtHeight(w) ? XtHeight(w) : 1,
3097a84e134Smrg				XtBorderWidth(w),
3107a84e134Smrg				DefaultDepthOfScreen(XtScreen(w)),
3117a84e134Smrg				InputOutput,
312775e7de9Smrg				(Visual *)CopyFromParent,
3137a84e134Smrg				*mask, attr);
3147a84e134Smrg}
3157a84e134Smrg
3167a84e134Smrgstatic void
3177a84e134SmrgXawTipExpose(Widget w, XEvent *event, Region region)
3187a84e134Smrg{
3197a84e134Smrg    TipWidget tip = (TipWidget)w;
3207a84e134Smrg    GC gc = tip->tip.gc;
3215ec34c4cSmrg    char *nl;
3225ec34c4cSmrg    _Xconst char * label = tip->tip.label;
3235ec34c4cSmrg    Position y = (Position)(tip->tip.top_margin + tip->tip.font->max_bounds.ascent);
3247a84e134Smrg    int len;
3257a84e134Smrg
3267a84e134Smrg    if (tip->tip.display_list)
3277a84e134Smrg	XawRunDisplayList(w, tip->tip.display_list, event, region);
3287a84e134Smrg
3297a84e134Smrg    if (tip->tip.international == True) {
3305ec34c4cSmrg	Position ksy = (Position)tip->tip.top_margin;
3317a84e134Smrg	XFontSetExtents *ext = XExtentsOfFontSet(tip->tip.fontset);
3327a84e134Smrg
333efbcb2bfSmrg	ksy = (Position) (ksy + XawAbs(ext->max_ink_extent.y));
3347a84e134Smrg
3355b16253fSmrg	while ((nl = strchr(label, '\n')) != NULL) {
3367a84e134Smrg	    XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset,
3377a84e134Smrg			  gc, tip->tip.left_margin, ksy, label,
3387a84e134Smrg			  (int)(nl - label));
339efbcb2bfSmrg	    ksy = (Position) (ksy + ext->max_ink_extent.height);
3407a84e134Smrg	    label = nl + 1;
3417a84e134Smrg	}
3425ec34c4cSmrg	len = (int)strlen(label);
3437a84e134Smrg	if (len)
3447a84e134Smrg	    XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset, gc,
3457a84e134Smrg			  tip->tip.left_margin, ksy, label, len);
3467a84e134Smrg    }
3477a84e134Smrg    else {
3485b16253fSmrg	while ((nl = strchr(label, '\n')) != NULL) {
3497a84e134Smrg	    if (tip->tip.encoding)
3507a84e134Smrg		XDrawString16(XtDisplay(w), XtWindow(w), gc,
3517a84e134Smrg			      tip->tip.left_margin, y,
3525ec34c4cSmrg			      (_Xconst XChar2b*)label, (int)(nl - label) >> 1);
3537a84e134Smrg	    else
3547a84e134Smrg		XDrawString(XtDisplay(w), XtWindow(w), gc,
3557a84e134Smrg			    tip->tip.left_margin, y, label, (int)(nl - label));
3565ec34c4cSmrg	    y = (Position)(y + (tip->tip.font->max_bounds.ascent +
3575ec34c4cSmrg		  tip->tip.font->max_bounds.descent));
3587a84e134Smrg	    label = nl + 1;
3597a84e134Smrg	}
3605ec34c4cSmrg	len = (int)strlen(label);
3617a84e134Smrg	if (len) {
3627a84e134Smrg	    if (tip->tip.encoding)
3637a84e134Smrg		XDrawString16(XtDisplay(w), XtWindow(w), gc,
3645ec34c4cSmrg			      tip->tip.left_margin, y, (_Xconst XChar2b*)label, len >> 1);
3657a84e134Smrg	    else
3667a84e134Smrg		XDrawString(XtDisplay(w), XtWindow(w), gc,
3677a84e134Smrg			    tip->tip.left_margin, y, label, len);
3687a84e134Smrg	}
3697a84e134Smrg    }
3707a84e134Smrg}
3717a84e134Smrg
3727a84e134Smrg/*ARGSUSED*/
3737a84e134Smrgstatic Boolean
3745ec34c4cSmrgXawTipSetValues(Widget current, Widget request _X_UNUSED, Widget cnew,
3755ec34c4cSmrg		ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
3767a84e134Smrg{
3777a84e134Smrg    TipWidget curtip = (TipWidget)current;
3787a84e134Smrg    TipWidget newtip = (TipWidget)cnew;
3797a84e134Smrg    Boolean redisplay = False;
3807a84e134Smrg
3817a84e134Smrg    if (curtip->tip.font->fid != newtip->tip.font->fid ||
3827a84e134Smrg	curtip->tip.foreground != newtip->tip.foreground) {
383efbcb2bfSmrg	XGCValues values = {
384efbcb2bfSmrg	    .foreground = newtip->tip.foreground,
385efbcb2bfSmrg	    .background = newtip->core.background_pixel,
386efbcb2bfSmrg	    .font = newtip->tip.font->fid,
387efbcb2bfSmrg	    .graphics_exposures = False
388efbcb2bfSmrg	};
3897a84e134Smrg
3907a84e134Smrg	XtReleaseGC(cnew, curtip->tip.gc);
3917a84e134Smrg	newtip->tip.gc = XtAllocateGC(cnew, 0, GCForeground | GCBackground |
3927a84e134Smrg				      GCFont | GCGraphicsExposures, &values,
3937a84e134Smrg				      GCFont, 0);
3947a84e134Smrg	redisplay = True;
3957a84e134Smrg    }
3967a84e134Smrg    if (curtip->tip.display_list != newtip->tip.display_list)
3977a84e134Smrg	redisplay = True;
3987a84e134Smrg
3997a84e134Smrg    return (redisplay);
4007a84e134Smrg}
4017a84e134Smrg
4027a84e134Smrgstatic void
4037a84e134SmrgTipLayout(XawTipInfo *info)
4047a84e134Smrg{
4057a84e134Smrg    XFontStruct	*fs = info->tip->tip.font;
4067a84e134Smrg    int width = 0, height;
4075ec34c4cSmrg    char *nl;
4085ec34c4cSmrg    _Xconst char *label = info->tip->tip.label;
4097a84e134Smrg
4107a84e134Smrg    if (info->tip->tip.international == True) {
4117a84e134Smrg	XFontSet fset = info->tip->tip.fontset;
4127a84e134Smrg	XFontSetExtents *ext = XExtentsOfFontSet(fset);
4137a84e134Smrg
4147a84e134Smrg	height = ext->max_ink_extent.height;
4155b16253fSmrg	if ((nl = strchr(label, '\n')) != NULL) {
4167a84e134Smrg	    /*CONSTCOND*/
4177a84e134Smrg	    while (True) {
4187a84e134Smrg		int w = XmbTextEscapement(fset, label, (int)(nl - label));
4197a84e134Smrg
4207a84e134Smrg		if (w > width)
4217a84e134Smrg		    width = w;
4227a84e134Smrg		if (*nl == '\0')
4237a84e134Smrg		    break;
4247a84e134Smrg		label = nl + 1;
4257a84e134Smrg		if (*label)
4267a84e134Smrg		    height += ext->max_ink_extent.height;
4275b16253fSmrg		if ((nl = strchr(label, '\n')) == NULL)
4285b16253fSmrg		    nl = strchr(label, '\0');
4297a84e134Smrg	    }
4307a84e134Smrg	}
4317a84e134Smrg	else
4325ec34c4cSmrg	    width = XmbTextEscapement(fset, label, (int)strlen(label));
4337a84e134Smrg    }
4347a84e134Smrg    else {
4357a84e134Smrg	height = fs->max_bounds.ascent + fs->max_bounds.descent;
4365b16253fSmrg	if ((nl = strchr(label, '\n')) != NULL) {
4377a84e134Smrg	    /*CONSTCOND*/
4387a84e134Smrg	    while (True) {
4397a84e134Smrg		int w = info->tip->tip.encoding ?
4405ec34c4cSmrg		    XTextWidth16(fs, (_Xconst XChar2b*)label, (int)(nl - label) >> 1) :
4417a84e134Smrg		    XTextWidth(fs, label, (int)(nl - label));
4427a84e134Smrg		if (w > width)
4437a84e134Smrg		    width = w;
4447a84e134Smrg		if (*nl == '\0')
4457a84e134Smrg		    break;
4467a84e134Smrg		label = nl + 1;
4477a84e134Smrg		if (*label)
4487a84e134Smrg		    height += fs->max_bounds.ascent + fs->max_bounds.descent;
4495b16253fSmrg		if ((nl = strchr(label, '\n')) == NULL)
4505b16253fSmrg		    nl = strchr(label, '\0');
4517a84e134Smrg	    }
4527a84e134Smrg	}
4537a84e134Smrg	else
4547a84e134Smrg	    width = info->tip->tip.encoding ?
4555ec34c4cSmrg		XTextWidth16(fs, (_Xconst XChar2b*)label, (int)(strlen(label) >> 1)) :
4565ec34c4cSmrg		XTextWidth(fs, label, (int)strlen(label));
4577a84e134Smrg    }
458efbcb2bfSmrg    XtWidth(info->tip) = (Dimension) (width + info->tip->tip.left_margin +
459efbcb2bfSmrg				      info->tip->tip.right_margin);
460efbcb2bfSmrg    XtHeight(info->tip) = (Dimension) (height + info->tip->tip.top_margin +
461efbcb2bfSmrg				       info->tip->tip.bottom_margin);
4627a84e134Smrg}
4637a84e134Smrg
4647a84e134Smrg#define	DEFAULT_TIP_Y_OFFSET	12
4657a84e134Smrgstatic void
4667a84e134SmrgTipPosition(XawTipInfo *info)
4677a84e134Smrg{
4687a84e134Smrg    Window r, c;
4697a84e134Smrg    int rx, ry, wx, wy;
4707a84e134Smrg    unsigned mask;
4717a84e134Smrg    Position x, y;
4727a84e134Smrg
4737a84e134Smrg    XQueryPointer(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip),
4747a84e134Smrg		  &r, &c, &rx, &ry, &wx, &wy, &mask);
4755ec34c4cSmrg    x = (Position)(rx - (XtWidth(info->tip) >> 1));
4765ec34c4cSmrg    y = (Position)(ry + DEFAULT_TIP_Y_OFFSET);
4777a84e134Smrg
4787a84e134Smrg    if (x >= 0) {
4797a84e134Smrg	int scr_width = WidthOfScreen(XtScreen(info->tip));
4807a84e134Smrg
4817a84e134Smrg	if (x + XtWidth(info->tip) + XtBorderWidth(info->tip) > scr_width)
4825ec34c4cSmrg	    x = (Position)(scr_width - XtWidth(info->tip) - XtBorderWidth(info->tip));
4837a84e134Smrg    }
4847a84e134Smrg    if (x < 0)
4857a84e134Smrg	x = 0;
4867a84e134Smrg    if (y >= 0) {
4877a84e134Smrg	int scr_height = HeightOfScreen(XtScreen(info->tip));
4887a84e134Smrg
4897a84e134Smrg	if (y + XtHeight(info->tip) + XtBorderWidth(info->tip) > scr_height)
4905ec34c4cSmrg	    y = (Position)(y - (XtHeight(info->tip) + XtBorderWidth(info->tip) +
4915ec34c4cSmrg		 (DEFAULT_TIP_Y_OFFSET << 1)));
4927a84e134Smrg    }
4937a84e134Smrg    if (y < 0)
4947a84e134Smrg	y = 0;
4957a84e134Smrg
4967a84e134Smrg    XMoveResizeWindow(XtDisplay(info->tip), XtWindow(info->tip),
4977a84e134Smrg		      (int)(XtX(info->tip) = x), (int)(XtY(info->tip) = y),
4987a84e134Smrg		      (unsigned)XtWidth(info->tip), (unsigned)XtHeight(info->tip));
4997a84e134Smrg}
5007a84e134Smrg
5017a84e134Smrgstatic XawTipInfo *
5027a84e134SmrgCreateTipInfo(Widget w)
5037a84e134Smrg{
5047a84e134Smrg    XawTipInfo *info = XtNew(XawTipInfo);
5057a84e134Smrg    Widget shell = w;
5067a84e134Smrg
5077a84e134Smrg    info->screen = XtScreen(w);
5087a84e134Smrg
5097a84e134Smrg    while (XtParent(shell))
5107a84e134Smrg	shell = XtParent(shell);
5117a84e134Smrg
5127a84e134Smrg    info->tip = (TipWidget)XtCreateWidget("tip", tipWidgetClass, shell, NULL, 0);
5137a84e134Smrg    XtRealizeWidget((Widget)info->tip);
5147a84e134Smrg    info->widget = NULL;
5157a84e134Smrg    info->mapped = False;
5167a84e134Smrg    info->next = NULL;
5177a84e134Smrg    XtAddEventHandler(shell, KeyPressMask, False, TipShellEventHandler,
5187a84e134Smrg		      (XtPointer)NULL);
5197a84e134Smrg
5207a84e134Smrg    return (info);
5217a84e134Smrg}
5227a84e134Smrg
5237a84e134Smrgstatic XawTipInfo *
5247a84e134SmrgFindTipInfo(Widget w)
5257a84e134Smrg{
5267a84e134Smrg    XawTipInfo *ptip, *tip = first_tip;
5277a84e134Smrg    Screen *screen = XtScreenOfObject(w);
5287a84e134Smrg
5297a84e134Smrg    if (tip == NULL)
530efbcb2bfSmrg	return (first_tip = CreateTipInfo(w));
5317a84e134Smrg
5327a84e134Smrg    for (ptip = tip; tip; ptip = tip, tip = tip->next)
5337a84e134Smrg	if (tip->screen == screen)
5347a84e134Smrg	    return (tip);
5357a84e134Smrg
5367a84e134Smrg    return (ptip->next = CreateTipInfo(w));
5377a84e134Smrg}
5387a84e134Smrg
5397a84e134Smrgstatic void
5407a84e134SmrgResetTip(XawTipInfo *info, Bool add_timeout)
5417a84e134Smrg{
5427a84e134Smrg    if (info->tip->tip.timer) {
5437a84e134Smrg	XtRemoveTimeOut(info->tip->tip.timer);
5447a84e134Smrg	info->tip->tip.timer = 0;
5457a84e134Smrg    }
5467a84e134Smrg    if (info->mapped) {
5477a84e134Smrg	XtRemoveGrab(XtParent((Widget)info->tip));
5487a84e134Smrg	XUnmapWindow(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip));
5497a84e134Smrg	info->mapped = False;
5507a84e134Smrg    }
5517a84e134Smrg    if (add_timeout) {
5527a84e134Smrg	info->tip->tip.timer =
5537a84e134Smrg	    XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)info->tip),
5545ec34c4cSmrg			    (unsigned long)info->tip->tip.timeout,
5555ec34c4cSmrg			    TipTimeoutCallback,
5567a84e134Smrg			    (XtPointer)info);
5577a84e134Smrg    }
5587a84e134Smrg}
5597a84e134Smrg
5607a84e134Smrgstatic void
5615ec34c4cSmrgTipTimeoutCallback(XtPointer closure, XtIntervalId *id _X_UNUSED)
5627a84e134Smrg{
5637a84e134Smrg    XawTipInfo *info = (XawTipInfo*)closure;
5647a84e134Smrg    Arg args[3];
5657a84e134Smrg
5667a84e134Smrg    info->tip->tip.label = NULL;
5677a84e134Smrg    info->tip->tip.international = False;
5687a84e134Smrg    info->tip->tip.encoding = 0;
569e1e1713cSmrg    info->tip->tip.timer = 0;
5707a84e134Smrg    XtSetArg(args[0], XtNtip, &info->tip->tip.label);
5717a84e134Smrg    XtSetArg(args[1], XtNinternational, &info->tip->tip.international);
5727a84e134Smrg    XtSetArg(args[2], XtNencoding, &info->tip->tip.encoding);
5737a84e134Smrg    XtGetValues(info->widget, args, 3);
5747a84e134Smrg
5757a84e134Smrg    if (info->tip->tip.label) {
5767a84e134Smrg	TipLayout(info);
5777a84e134Smrg	TipPosition(info);
5787a84e134Smrg	XMapRaised(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip));
5797a84e134Smrg	XtAddGrab(XtParent((Widget)info->tip), True, True);
5807a84e134Smrg	info->mapped = True;
5817a84e134Smrg    }
5827a84e134Smrg}
5837a84e134Smrg
5847a84e134Smrg/*ARGSUSED*/
5857a84e134Smrgstatic void
5865ec34c4cSmrgTipShellEventHandler(Widget w, XtPointer client_data _X_UNUSED, XEvent *event _X_UNUSED,
5875ec34c4cSmrg		     Boolean *continue_to_dispatch _X_UNUSED)
5887a84e134Smrg{
5897a84e134Smrg    ResetTip(FindTipInfo(w), False);
5907a84e134Smrg}
5917a84e134Smrg
5927a84e134Smrg/*ARGSUSED*/
5937a84e134Smrgstatic void
5945ec34c4cSmrgTipEventHandler(Widget w, XtPointer client_data _X_UNUSED, XEvent *event,
5955ec34c4cSmrg		Boolean *continue_to_dispatch _X_UNUSED)
5967a84e134Smrg{
5977a84e134Smrg    XawTipInfo *info = FindTipInfo(w);
5987a84e134Smrg    Boolean add_timeout;
5997a84e134Smrg
6007a84e134Smrg    if (info->widget != w) {
6017a84e134Smrg	ResetTip(info, False);
6027a84e134Smrg	info->widget = w;
6037a84e134Smrg    }
6047a84e134Smrg
6057a84e134Smrg    switch (event->type) {
6067a84e134Smrg	case EnterNotify:
6077a84e134Smrg	    add_timeout = True;
6087a84e134Smrg	    break;
6097a84e134Smrg	case MotionNotify:
6107a84e134Smrg	    /* If any button is pressed, timer is 0 */
6117a84e134Smrg	    if (info->mapped)
6127a84e134Smrg		return;
6137a84e134Smrg	    add_timeout = info->tip->tip.timer != 0;
6147a84e134Smrg	    break;
6157a84e134Smrg	default:
6167a84e134Smrg	    add_timeout = False;
6177a84e134Smrg	    break;
6187a84e134Smrg    }
6197a84e134Smrg    ResetTip(info, add_timeout);
6207a84e134Smrg}
6217a84e134Smrg
6227a84e134Smrg/*
6237a84e134Smrg * Public routines
6247a84e134Smrg */
6257a84e134Smrgvoid
6267a84e134SmrgXawTipEnable(Widget w)
6277a84e134Smrg{
6287a84e134Smrg    XtAddEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler,
6297a84e134Smrg		      (XtPointer)NULL);
6307a84e134Smrg}
6317a84e134Smrg
6327a84e134Smrgvoid
6337a84e134SmrgXawTipDisable(Widget w)
6347a84e134Smrg{
6357a84e134Smrg    XawTipInfo *info = FindTipInfo(w);
6367a84e134Smrg
6377a84e134Smrg    XtRemoveEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler,
6387a84e134Smrg			 (XtPointer)NULL);
6397a84e134Smrg    if (info->widget == w)
6407a84e134Smrg	ResetTip(info, False);
6417a84e134Smrg}
642