Tip.c revision 775e7de9
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/* $XFree86: xc/lib/Xaw/Tip.c,v 1.4 1999/07/11 08:49:16 dawes Exp $ */
317a84e134Smrg
327a84e134Smrg#ifdef HAVE_CONFIG_H
337a84e134Smrg#include <config.h>
347a84e134Smrg#endif
357a84e134Smrg#include <X11/IntrinsicP.h>
367a84e134Smrg#include <X11/StringDefs.h>
377a84e134Smrg#include <X11/Xos.h>
387a84e134Smrg#include <X11/Xaw/TipP.h>
397a84e134Smrg#include <X11/Xaw/XawInit.h>
407a84e134Smrg#include <X11/Xmu/Converters.h>
417a84e134Smrg#include "Private.h"
427a84e134Smrg
437a84e134Smrg#define	TIP_EVENT_MASK (ButtonPressMask	  |	\
447a84e134Smrg			ButtonReleaseMask |	\
457a84e134Smrg			PointerMotionMask |	\
467a84e134Smrg			ButtonMotionMask  |	\
477a84e134Smrg			KeyPressMask	  |	\
487a84e134Smrg			KeyReleaseMask	  |	\
497a84e134Smrg			EnterWindowMask	  |	\
507a84e134Smrg			LeaveWindowMask)
517a84e134Smrg
527a84e134Smrg/*
537a84e134Smrg * Types
547a84e134Smrg */
557a84e134Smrgtypedef struct _XawTipInfo {
567a84e134Smrg    Screen *screen;
577a84e134Smrg    TipWidget tip;
587a84e134Smrg    Widget widget;
597a84e134Smrg    Bool mapped;
607a84e134Smrg    struct _XawTipInfo *next;
617a84e134Smrg} XawTipInfo;
627a84e134Smrg
637a84e134Smrg/*
647a84e134Smrg * Class Methods
657a84e134Smrg */
667a84e134Smrgstatic void XawTipClassInitialize(void);
677a84e134Smrgstatic void XawTipInitialize(Widget, Widget, ArgList, Cardinal*);
687a84e134Smrgstatic void XawTipDestroy(Widget);
697a84e134Smrgstatic void XawTipExpose(Widget, XEvent*, Region);
707a84e134Smrgstatic void XawTipRealize(Widget, Mask*, XSetWindowAttributes*);
717a84e134Smrgstatic Boolean XawTipSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
727a84e134Smrg
737a84e134Smrg/*
747a84e134Smrg * Prototypes
757a84e134Smrg */
767a84e134Smrgstatic void TipEventHandler(Widget, XtPointer, XEvent*, Boolean*);
777a84e134Smrgstatic void TipShellEventHandler(Widget, XtPointer, XEvent*, Boolean*);
787a84e134Smrgstatic XawTipInfo *CreateTipInfo(Widget);
797a84e134Smrgstatic XawTipInfo *FindTipInfo(Widget);
807a84e134Smrgstatic void ResetTip(XawTipInfo*, Bool);
817a84e134Smrgstatic void TipTimeoutCallback(XtPointer, XtIntervalId*);
827a84e134Smrgstatic void TipLayout(XawTipInfo*);
837a84e134Smrgstatic void TipPosition(XawTipInfo*);
847a84e134Smrg
857a84e134Smrg/*
867a84e134Smrg * Initialization
877a84e134Smrg */
887a84e134Smrg#define offset(field) XtOffsetOf(TipRec, tip.field)
897a84e134Smrgstatic XtResource resources[] = {
907a84e134Smrg  {
917a84e134Smrg    XtNforeground,
927a84e134Smrg    XtCForeground,
937a84e134Smrg    XtRPixel,
947a84e134Smrg    sizeof(Pixel),
957a84e134Smrg    offset(foreground),
967a84e134Smrg    XtRString,
977a84e134Smrg    XtDefaultForeground,
987a84e134Smrg  },
997a84e134Smrg  {
1007a84e134Smrg    XtNfont,
1017a84e134Smrg    XtCFont,
1027a84e134Smrg    XtRFontStruct,
1037a84e134Smrg    sizeof(XFontStruct*),
1047a84e134Smrg    offset(font),
1057a84e134Smrg    XtRString,
1067a84e134Smrg    XtDefaultFont
1077a84e134Smrg  },
1087a84e134Smrg  {
1097a84e134Smrg    XtNfontSet,
1107a84e134Smrg    XtCFontSet,
1117a84e134Smrg    XtRFontSet,
1127a84e134Smrg    sizeof(XFontSet),
1137a84e134Smrg    offset(fontset),
1147a84e134Smrg    XtRString,
1157a84e134Smrg    XtDefaultFontSet
1167a84e134Smrg  },
1177a84e134Smrg  {
1187a84e134Smrg    XtNtopMargin,
1197a84e134Smrg    XtCVerticalMargins,
1207a84e134Smrg    XtRDimension,
1217a84e134Smrg    sizeof(Dimension),
1227a84e134Smrg    offset(top_margin),
1237a84e134Smrg    XtRImmediate,
1247a84e134Smrg    (XtPointer)2
1257a84e134Smrg  },
1267a84e134Smrg  {
1277a84e134Smrg    XtNbottomMargin,
1287a84e134Smrg    XtCVerticalMargins,
1297a84e134Smrg    XtRDimension,
1307a84e134Smrg    sizeof(Dimension),
1317a84e134Smrg    offset(bottom_margin),
1327a84e134Smrg    XtRImmediate,
1337a84e134Smrg    (XtPointer)2
1347a84e134Smrg  },
1357a84e134Smrg  {
1367a84e134Smrg    XtNleftMargin,
1377a84e134Smrg    XtCHorizontalMargins,
1387a84e134Smrg    XtRDimension,
1397a84e134Smrg    sizeof(Dimension),
1407a84e134Smrg    offset(left_margin),
1417a84e134Smrg    XtRImmediate,
1427a84e134Smrg    (XtPointer)6
1437a84e134Smrg  },
1447a84e134Smrg  {
1457a84e134Smrg    XtNrightMargin,
1467a84e134Smrg    XtCHorizontalMargins,
1477a84e134Smrg    XtRDimension,
1487a84e134Smrg    sizeof(Dimension),
1497a84e134Smrg    offset(right_margin),
1507a84e134Smrg    XtRImmediate,
1517a84e134Smrg    (XtPointer)6
1527a84e134Smrg  },
1537a84e134Smrg  {
1547a84e134Smrg    XtNbackingStore,
1557a84e134Smrg    XtCBackingStore,
1567a84e134Smrg    XtRBackingStore,
1577a84e134Smrg    sizeof(int),
1587a84e134Smrg    offset(backing_store),
1597a84e134Smrg    XtRImmediate,
1607a84e134Smrg    (XtPointer)(Always + WhenMapped + NotUseful)
1617a84e134Smrg  },
1627a84e134Smrg  {
1637a84e134Smrg    XtNtimeout,
1647a84e134Smrg    XtCTimeout,
1657a84e134Smrg    XtRInt,
1667a84e134Smrg    sizeof(int),
1677a84e134Smrg    offset(timeout),
1687a84e134Smrg    XtRImmediate,
1697a84e134Smrg    (XtPointer)500
1707a84e134Smrg  },
1717a84e134Smrg  {
1727a84e134Smrg    XawNdisplayList,
1737a84e134Smrg    XawCDisplayList,
1747a84e134Smrg    XawRDisplayList,
1757a84e134Smrg    sizeof(XawDisplayList*),
1767a84e134Smrg    offset(display_list),
1777a84e134Smrg    XtRImmediate,
1787a84e134Smrg    NULL
1797a84e134Smrg  },
1807a84e134Smrg};
1817a84e134Smrg#undef offset
1827a84e134Smrg
1837a84e134SmrgTipClassRec tipClassRec = {
1847a84e134Smrg  /* core */
1857a84e134Smrg  {
1867a84e134Smrg    (WidgetClass)&widgetClassRec,	/* superclass */
1877a84e134Smrg    "Tip",				/* class_name */
1887a84e134Smrg    sizeof(TipRec),			/* widget_size */
1897a84e134Smrg    XawTipClassInitialize,		/* class_initialize */
1907a84e134Smrg    NULL,				/* class_part_initialize */
1917a84e134Smrg    False,				/* class_inited */
1927a84e134Smrg    XawTipInitialize,			/* initialize */
1937a84e134Smrg    NULL,				/* initialize_hook */
1947a84e134Smrg    XawTipRealize,			/* realize */
1957a84e134Smrg    NULL,				/* actions */
1967a84e134Smrg    0,					/* num_actions */
1977a84e134Smrg    resources,				/* resources */
1987a84e134Smrg    XtNumber(resources),		/* num_resources */
1997a84e134Smrg    NULLQUARK,				/* xrm_class */
2007a84e134Smrg    True,				/* compress_motion */
2017a84e134Smrg    True,				/* compress_exposure */
2027a84e134Smrg    True,				/* compress_enterleave */
2037a84e134Smrg    False,				/* visible_interest */
2047a84e134Smrg    XawTipDestroy,			/* destroy */
2057a84e134Smrg    NULL,				/* resize */
2067a84e134Smrg    XawTipExpose,			/* expose */
2077a84e134Smrg    XawTipSetValues,			/* set_values */
2087a84e134Smrg    NULL,				/* set_values_hook */
2097a84e134Smrg    XtInheritSetValuesAlmost,		/* set_values_almost */
2107a84e134Smrg    NULL,				/* get_values_hook */
2117a84e134Smrg    NULL,				/* accept_focus */
2127a84e134Smrg    XtVersion,				/* version */
2137a84e134Smrg    NULL,				/* callback_private */
2147a84e134Smrg    NULL,				/* tm_table */
2157a84e134Smrg    XtInheritQueryGeometry,		/* query_geometry */
2167a84e134Smrg    XtInheritDisplayAccelerator,	/* display_accelerator */
2177a84e134Smrg    NULL,				/* extension */
2187a84e134Smrg  },
2197a84e134Smrg  /* tip */
2207a84e134Smrg  {
2217a84e134Smrg    NULL,				/* extension */
2227a84e134Smrg  },
2237a84e134Smrg};
2247a84e134Smrg
2257a84e134SmrgWidgetClass tipWidgetClass = (WidgetClass)&tipClassRec;
2267a84e134Smrg
2277a84e134Smrgstatic XawTipInfo *first_tip;
2287a84e134Smrg
2297a84e134Smrg/*
2307a84e134Smrg * Implementation
2317a84e134Smrg */
2327a84e134Smrgstatic void
2337a84e134SmrgXawTipClassInitialize(void)
2347a84e134Smrg{
2357a84e134Smrg    XawInitializeWidgetSet();
2367a84e134Smrg    XtAddConverter(XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
2377a84e134Smrg		   NULL, 0);
2387a84e134Smrg    XtSetTypeConverter(XtRBackingStore, XtRString, XmuCvtBackingStoreToString,
2397a84e134Smrg		       NULL, 0, XtCacheNone, NULL);
2407a84e134Smrg}
2417a84e134Smrg
2427a84e134Smrg/*ARGSUSED*/
2437a84e134Smrgstatic void
2447a84e134SmrgXawTipInitialize(Widget req, Widget w, ArgList args, Cardinal *num_args)
2457a84e134Smrg{
2467a84e134Smrg    TipWidget tip = (TipWidget)w;
2477a84e134Smrg    XGCValues values;
2487a84e134Smrg
2497a84e134Smrg    if (!tip->tip.font) XtError("Aborting: no font found\n");
2507a84e134Smrg    if (tip->tip.international && !tip->tip.fontset)
2517a84e134Smrg	XtError("Aborting: no fontset found\n");
2527a84e134Smrg
2537a84e134Smrg    tip->tip.timer = 0;
2547a84e134Smrg
2557a84e134Smrg    values.foreground = tip->tip.foreground;
2567a84e134Smrg    values.background = tip->core.background_pixel;
2577a84e134Smrg    values.font = tip->tip.font->fid;
2587a84e134Smrg    values.graphics_exposures = False;
2597a84e134Smrg
2607a84e134Smrg    tip->tip.gc = XtAllocateGC(w, 0, GCForeground | GCBackground | GCFont |
2617a84e134Smrg			       GCGraphicsExposures, &values, GCFont, 0);
2627a84e134Smrg}
2637a84e134Smrg
2647a84e134Smrgstatic void
2657a84e134SmrgXawTipDestroy(Widget w)
2667a84e134Smrg{
2677a84e134Smrg    XawTipInfo *info = FindTipInfo(w);
2687a84e134Smrg    TipWidget tip = (TipWidget)w;
2697a84e134Smrg
2707a84e134Smrg    if (tip->tip.timer)
2717a84e134Smrg	XtRemoveTimeOut(tip->tip.timer);
2727a84e134Smrg
2737a84e134Smrg    XtReleaseGC(w, tip->tip.gc);
2747a84e134Smrg
2757a84e134Smrg    XtRemoveEventHandler(XtParent(w), KeyPressMask, False, TipShellEventHandler,
2767a84e134Smrg			 (XtPointer)NULL);
2777a84e134Smrg    if (info == first_tip)
2787a84e134Smrg	first_tip = first_tip->next;
2797a84e134Smrg    else {
2807a84e134Smrg	XawTipInfo *p = first_tip;
2817a84e134Smrg
2827a84e134Smrg	while (p && p->next != info)
2837a84e134Smrg	    p = p->next;
2847a84e134Smrg	if (p)
2857a84e134Smrg	    p->next = info->next;
2867a84e134Smrg    }
2877a84e134Smrg    XtFree((char*)info);
2887a84e134Smrg}
2897a84e134Smrg
2907a84e134Smrgstatic void
2917a84e134SmrgXawTipRealize(Widget w, Mask *mask, XSetWindowAttributes *attr)
2927a84e134Smrg{
2937a84e134Smrg    TipWidget tip = (TipWidget)w;
2947a84e134Smrg
2957a84e134Smrg    if (tip->tip.backing_store == Always ||
2967a84e134Smrg	tip->tip.backing_store == NotUseful ||
2977a84e134Smrg	tip->tip.backing_store == WhenMapped) {
2987a84e134Smrg	*mask |= CWBackingStore;
2997a84e134Smrg	attr->backing_store = tip->tip.backing_store;
3007a84e134Smrg    }
3017a84e134Smrg    else
3027a84e134Smrg	*mask &= ~CWBackingStore;
3037a84e134Smrg    *mask |= CWOverrideRedirect;
3047a84e134Smrg    attr->override_redirect = True;
3057a84e134Smrg
3067a84e134Smrg    XtWindow(w) = XCreateWindow(DisplayOfScreen(XtScreen(w)),
3077a84e134Smrg				RootWindowOfScreen(XtScreen(w)),
3087a84e134Smrg				XtX(w), XtY(w),
3097a84e134Smrg				XtWidth(w) ? XtWidth(w) : 1,
3107a84e134Smrg				XtHeight(w) ? XtHeight(w) : 1,
3117a84e134Smrg				XtBorderWidth(w),
3127a84e134Smrg				DefaultDepthOfScreen(XtScreen(w)),
3137a84e134Smrg				InputOutput,
314775e7de9Smrg				(Visual *)CopyFromParent,
3157a84e134Smrg				*mask, attr);
3167a84e134Smrg}
3177a84e134Smrg
3187a84e134Smrgstatic void
3197a84e134SmrgXawTipExpose(Widget w, XEvent *event, Region region)
3207a84e134Smrg{
3217a84e134Smrg    TipWidget tip = (TipWidget)w;
3227a84e134Smrg    GC gc = tip->tip.gc;
3237a84e134Smrg    char *nl, *label = tip->tip.label;
3247a84e134Smrg    Position y = tip->tip.top_margin + tip->tip.font->max_bounds.ascent;
3257a84e134Smrg    int len;
3267a84e134Smrg
3277a84e134Smrg    if (tip->tip.display_list)
3287a84e134Smrg	XawRunDisplayList(w, tip->tip.display_list, event, region);
3297a84e134Smrg
3307a84e134Smrg    if (tip->tip.international == True) {
3317a84e134Smrg	Position ksy = tip->tip.top_margin;
3327a84e134Smrg	XFontSetExtents *ext = XExtentsOfFontSet(tip->tip.fontset);
3337a84e134Smrg
3347a84e134Smrg	ksy += XawAbs(ext->max_ink_extent.y);
3357a84e134Smrg
3367a84e134Smrg	while ((nl = index(label, '\n')) != NULL) {
3377a84e134Smrg	    XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset,
3387a84e134Smrg			  gc, tip->tip.left_margin, ksy, label,
3397a84e134Smrg			  (int)(nl - label));
3407a84e134Smrg	    ksy += ext->max_ink_extent.height;
3417a84e134Smrg	    label = nl + 1;
3427a84e134Smrg	}
3437a84e134Smrg	len = strlen(label);
3447a84e134Smrg	if (len)
3457a84e134Smrg	    XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset, gc,
3467a84e134Smrg			  tip->tip.left_margin, ksy, label, len);
3477a84e134Smrg    }
3487a84e134Smrg    else {
3497a84e134Smrg	while ((nl = index(label, '\n')) != NULL) {
3507a84e134Smrg	    if (tip->tip.encoding)
3517a84e134Smrg		XDrawString16(XtDisplay(w), XtWindow(w), gc,
3527a84e134Smrg			      tip->tip.left_margin, y,
3537a84e134Smrg			      (XChar2b*)label, (int)(nl - label) >> 1);
3547a84e134Smrg	    else
3557a84e134Smrg		XDrawString(XtDisplay(w), XtWindow(w), gc,
3567a84e134Smrg			    tip->tip.left_margin, y, label, (int)(nl - label));
3577a84e134Smrg	    y += tip->tip.font->max_bounds.ascent +
3587a84e134Smrg		 tip->tip.font->max_bounds.descent;
3597a84e134Smrg	    label = nl + 1;
3607a84e134Smrg	}
3617a84e134Smrg	len = strlen(label);
3627a84e134Smrg	if (len) {
3637a84e134Smrg	    if (tip->tip.encoding)
3647a84e134Smrg		XDrawString16(XtDisplay(w), XtWindow(w), gc,
3657a84e134Smrg			      tip->tip.left_margin, y, (XChar2b*)label, len >> 1);
3667a84e134Smrg	    else
3677a84e134Smrg		XDrawString(XtDisplay(w), XtWindow(w), gc,
3687a84e134Smrg			    tip->tip.left_margin, y, label, len);
3697a84e134Smrg	}
3707a84e134Smrg    }
3717a84e134Smrg}
3727a84e134Smrg
3737a84e134Smrg/*ARGSUSED*/
3747a84e134Smrgstatic Boolean
3757a84e134SmrgXawTipSetValues(Widget current, Widget request, Widget cnew,
3767a84e134Smrg		ArgList args, Cardinal *num_args)
3777a84e134Smrg{
3787a84e134Smrg    TipWidget curtip = (TipWidget)current;
3797a84e134Smrg    TipWidget newtip = (TipWidget)cnew;
3807a84e134Smrg    Boolean redisplay = False;
3817a84e134Smrg
3827a84e134Smrg    if (curtip->tip.font->fid != newtip->tip.font->fid ||
3837a84e134Smrg	curtip->tip.foreground != newtip->tip.foreground) {
3847a84e134Smrg	XGCValues values;
3857a84e134Smrg
3867a84e134Smrg	values.foreground = newtip->tip.foreground;
3877a84e134Smrg	values.background = newtip->core.background_pixel;
3887a84e134Smrg	values.font = newtip->tip.font->fid;
3897a84e134Smrg	values.graphics_exposures = False;
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;
4077a84e134Smrg    char *nl, *label = info->tip->tip.label;
4087a84e134Smrg
4097a84e134Smrg    if (info->tip->tip.international == True) {
4107a84e134Smrg	XFontSet fset = info->tip->tip.fontset;
4117a84e134Smrg	XFontSetExtents *ext = XExtentsOfFontSet(fset);
4127a84e134Smrg
4137a84e134Smrg	height = ext->max_ink_extent.height;
4147a84e134Smrg	if ((nl = index(label, '\n')) != NULL) {
4157a84e134Smrg	    /*CONSTCOND*/
4167a84e134Smrg	    while (True) {
4177a84e134Smrg		int w = XmbTextEscapement(fset, label, (int)(nl - label));
4187a84e134Smrg
4197a84e134Smrg		if (w > width)
4207a84e134Smrg		    width = w;
4217a84e134Smrg		if (*nl == '\0')
4227a84e134Smrg		    break;
4237a84e134Smrg		label = nl + 1;
4247a84e134Smrg		if (*label)
4257a84e134Smrg		    height += ext->max_ink_extent.height;
4267a84e134Smrg		if ((nl = index(label, '\n')) == NULL)
4277a84e134Smrg		    nl = index(label, '\0');
4287a84e134Smrg	    }
4297a84e134Smrg	}
4307a84e134Smrg	else
4317a84e134Smrg	    width = XmbTextEscapement(fset, label, strlen(label));
4327a84e134Smrg    }
4337a84e134Smrg    else {
4347a84e134Smrg	height = fs->max_bounds.ascent + fs->max_bounds.descent;
4357a84e134Smrg	if ((nl = index(label, '\n')) != NULL) {
4367a84e134Smrg	    /*CONSTCOND*/
4377a84e134Smrg	    while (True) {
4387a84e134Smrg		int w = info->tip->tip.encoding ?
4397a84e134Smrg		    XTextWidth16(fs, (XChar2b*)label, (int)(nl - label) >> 1) :
4407a84e134Smrg		    XTextWidth(fs, label, (int)(nl - label));
4417a84e134Smrg		if (w > width)
4427a84e134Smrg		    width = w;
4437a84e134Smrg		if (*nl == '\0')
4447a84e134Smrg		    break;
4457a84e134Smrg		label = nl + 1;
4467a84e134Smrg		if (*label)
4477a84e134Smrg		    height += fs->max_bounds.ascent + fs->max_bounds.descent;
4487a84e134Smrg		if ((nl = index(label, '\n')) == NULL)
4497a84e134Smrg		    nl = index(label, '\0');
4507a84e134Smrg	    }
4517a84e134Smrg	}
4527a84e134Smrg	else
4537a84e134Smrg	    width = info->tip->tip.encoding ?
4547a84e134Smrg		XTextWidth16(fs, (XChar2b*)label, strlen(label) >> 1) :
4557a84e134Smrg		XTextWidth(fs, label, strlen(label));
4567a84e134Smrg    }
4577a84e134Smrg    XtWidth(info->tip) = width + info->tip->tip.left_margin +
4587a84e134Smrg			 info->tip->tip.right_margin;
4597a84e134Smrg    XtHeight(info->tip) = height + info->tip->tip.top_margin +
4607a84e134Smrg			  info->tip->tip.bottom_margin;
4617a84e134Smrg}
4627a84e134Smrg
4637a84e134Smrg#define	DEFAULT_TIP_Y_OFFSET	12
4647a84e134Smrgstatic void
4657a84e134SmrgTipPosition(XawTipInfo *info)
4667a84e134Smrg{
4677a84e134Smrg    Window r, c;
4687a84e134Smrg    int rx, ry, wx, wy;
4697a84e134Smrg    unsigned mask;
4707a84e134Smrg    Position x, y;
4717a84e134Smrg
4727a84e134Smrg    XQueryPointer(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip),
4737a84e134Smrg		  &r, &c, &rx, &ry, &wx, &wy, &mask);
4747a84e134Smrg    x = rx - (XtWidth(info->tip) >> 1);
4757a84e134Smrg    y = ry + DEFAULT_TIP_Y_OFFSET;
4767a84e134Smrg
4777a84e134Smrg    if (x >= 0) {
4787a84e134Smrg	int scr_width = WidthOfScreen(XtScreen(info->tip));
4797a84e134Smrg
4807a84e134Smrg	if (x + XtWidth(info->tip) + XtBorderWidth(info->tip) > scr_width)
4817a84e134Smrg	    x = scr_width - XtWidth(info->tip) - XtBorderWidth(info->tip);
4827a84e134Smrg    }
4837a84e134Smrg    if (x < 0)
4847a84e134Smrg	x = 0;
4857a84e134Smrg    if (y >= 0) {
4867a84e134Smrg	int scr_height = HeightOfScreen(XtScreen(info->tip));
4877a84e134Smrg
4887a84e134Smrg	if (y + XtHeight(info->tip) + XtBorderWidth(info->tip) > scr_height)
4897a84e134Smrg	    y -= XtHeight(info->tip) + XtBorderWidth(info->tip) +
4907a84e134Smrg		 (DEFAULT_TIP_Y_OFFSET << 1);
4917a84e134Smrg    }
4927a84e134Smrg    if (y < 0)
4937a84e134Smrg	y = 0;
4947a84e134Smrg
4957a84e134Smrg    XMoveResizeWindow(XtDisplay(info->tip), XtWindow(info->tip),
4967a84e134Smrg		      (int)(XtX(info->tip) = x), (int)(XtY(info->tip) = y),
4977a84e134Smrg		      (unsigned)XtWidth(info->tip), (unsigned)XtHeight(info->tip));
4987a84e134Smrg}
4997a84e134Smrg
5007a84e134Smrgstatic XawTipInfo *
5017a84e134SmrgCreateTipInfo(Widget w)
5027a84e134Smrg{
5037a84e134Smrg    XawTipInfo *info = XtNew(XawTipInfo);
5047a84e134Smrg    Widget shell = w;
5057a84e134Smrg
5067a84e134Smrg    info->screen = XtScreen(w);
5077a84e134Smrg
5087a84e134Smrg    while (XtParent(shell))
5097a84e134Smrg	shell = XtParent(shell);
5107a84e134Smrg
5117a84e134Smrg    info->tip = (TipWidget)XtCreateWidget("tip", tipWidgetClass, shell, NULL, 0);
5127a84e134Smrg    XtRealizeWidget((Widget)info->tip);
5137a84e134Smrg    info->widget = NULL;
5147a84e134Smrg    info->mapped = False;
5157a84e134Smrg    info->next = NULL;
5167a84e134Smrg    XtAddEventHandler(shell, KeyPressMask, False, TipShellEventHandler,
5177a84e134Smrg		      (XtPointer)NULL);
5187a84e134Smrg
5197a84e134Smrg    return (info);
5207a84e134Smrg}
5217a84e134Smrg
5227a84e134Smrgstatic XawTipInfo *
5237a84e134SmrgFindTipInfo(Widget w)
5247a84e134Smrg{
5257a84e134Smrg    XawTipInfo *ptip, *tip = first_tip;
5267a84e134Smrg    Screen *screen = XtScreenOfObject(w);
5277a84e134Smrg
5287a84e134Smrg    if (tip == NULL)
5297a84e134Smrg	return (first_tip = tip = CreateTipInfo(w));
5307a84e134Smrg
5317a84e134Smrg    for (ptip = tip; tip; ptip = tip, tip = tip->next)
5327a84e134Smrg	if (tip->screen == screen)
5337a84e134Smrg	    return (tip);
5347a84e134Smrg
5357a84e134Smrg    return (ptip->next = CreateTipInfo(w));
5367a84e134Smrg}
5377a84e134Smrg
5387a84e134Smrgstatic void
5397a84e134SmrgResetTip(XawTipInfo *info, Bool add_timeout)
5407a84e134Smrg{
5417a84e134Smrg    if (info->tip->tip.timer) {
5427a84e134Smrg	XtRemoveTimeOut(info->tip->tip.timer);
5437a84e134Smrg	info->tip->tip.timer = 0;
5447a84e134Smrg    }
5457a84e134Smrg    if (info->mapped) {
5467a84e134Smrg	XtRemoveGrab(XtParent((Widget)info->tip));
5477a84e134Smrg	XUnmapWindow(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip));
5487a84e134Smrg	info->mapped = False;
5497a84e134Smrg    }
5507a84e134Smrg    if (add_timeout) {
5517a84e134Smrg	info->tip->tip.timer =
5527a84e134Smrg	    XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)info->tip),
5537a84e134Smrg			    info->tip->tip.timeout, TipTimeoutCallback,
5547a84e134Smrg			    (XtPointer)info);
5557a84e134Smrg    }
5567a84e134Smrg}
5577a84e134Smrg
5587a84e134Smrgstatic void
5597a84e134SmrgTipTimeoutCallback(XtPointer closure, XtIntervalId *id)
5607a84e134Smrg{
5617a84e134Smrg    XawTipInfo *info = (XawTipInfo*)closure;
5627a84e134Smrg    Arg args[3];
5637a84e134Smrg
5647a84e134Smrg    info->tip->tip.label = NULL;
5657a84e134Smrg    info->tip->tip.international = False;
5667a84e134Smrg    info->tip->tip.encoding = 0;
5677a84e134Smrg    XtSetArg(args[0], XtNtip, &info->tip->tip.label);
5687a84e134Smrg    XtSetArg(args[1], XtNinternational, &info->tip->tip.international);
5697a84e134Smrg    XtSetArg(args[2], XtNencoding, &info->tip->tip.encoding);
5707a84e134Smrg    XtGetValues(info->widget, args, 3);
5717a84e134Smrg
5727a84e134Smrg    if (info->tip->tip.label) {
5737a84e134Smrg	TipLayout(info);
5747a84e134Smrg	TipPosition(info);
5757a84e134Smrg	XMapRaised(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip));
5767a84e134Smrg	XtAddGrab(XtParent((Widget)info->tip), True, True);
5777a84e134Smrg	info->mapped = True;
5787a84e134Smrg    }
5797a84e134Smrg}
5807a84e134Smrg
5817a84e134Smrg/*ARGSUSED*/
5827a84e134Smrgstatic void
5837a84e134SmrgTipShellEventHandler(Widget w, XtPointer client_data, XEvent *event,
5847a84e134Smrg		     Boolean *continue_to_dispatch)
5857a84e134Smrg{
5867a84e134Smrg    ResetTip(FindTipInfo(w), False);
5877a84e134Smrg}
5887a84e134Smrg
5897a84e134Smrg/*ARGSUSED*/
5907a84e134Smrgstatic void
5917a84e134SmrgTipEventHandler(Widget w, XtPointer client_data, XEvent *event,
5927a84e134Smrg		Boolean *continue_to_dispatch)
5937a84e134Smrg{
5947a84e134Smrg    XawTipInfo *info = FindTipInfo(w);
5957a84e134Smrg    Boolean add_timeout;
5967a84e134Smrg
5977a84e134Smrg    if (info->widget != w) {
5987a84e134Smrg	ResetTip(info, False);
5997a84e134Smrg	info->widget = w;
6007a84e134Smrg    }
6017a84e134Smrg
6027a84e134Smrg    switch (event->type) {
6037a84e134Smrg	case EnterNotify:
6047a84e134Smrg	    add_timeout = True;
6057a84e134Smrg	    break;
6067a84e134Smrg	case MotionNotify:
6077a84e134Smrg	    /* If any button is pressed, timer is 0 */
6087a84e134Smrg	    if (info->mapped)
6097a84e134Smrg		return;
6107a84e134Smrg	    add_timeout = info->tip->tip.timer != 0;
6117a84e134Smrg	    break;
6127a84e134Smrg	default:
6137a84e134Smrg	    add_timeout = False;
6147a84e134Smrg	    break;
6157a84e134Smrg    }
6167a84e134Smrg    ResetTip(info, add_timeout);
6177a84e134Smrg}
6187a84e134Smrg
6197a84e134Smrg/*
6207a84e134Smrg * Public routines
6217a84e134Smrg */
6227a84e134Smrgvoid
6237a84e134SmrgXawTipEnable(Widget w)
6247a84e134Smrg{
6257a84e134Smrg    XtAddEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler,
6267a84e134Smrg		      (XtPointer)NULL);
6277a84e134Smrg}
6287a84e134Smrg
6297a84e134Smrgvoid
6307a84e134SmrgXawTipDisable(Widget w)
6317a84e134Smrg{
6327a84e134Smrg    XawTipInfo *info = FindTipInfo(w);
6337a84e134Smrg
6347a84e134Smrg    XtRemoveEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler,
6357a84e134Smrg			 (XtPointer)NULL);
6367a84e134Smrg    if (info->widget == w)
6377a84e134Smrg	ResetTip(info, False);
6387a84e134Smrg}
639