diff options
Diffstat (limited to 'src/Tip.c')
-rw-r--r-- | src/Tip.c | 718 |
1 files changed, 718 insertions, 0 deletions
diff --git a/src/Tip.c b/src/Tip.c new file mode 100644 index 0000000..888eeac --- /dev/null +++ b/src/Tip.c @@ -0,0 +1,718 @@ +/* + * Copyright (c) 1999 by The XFree86 Project, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of the XFree86 Project shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from the + * XFree86 Project. + * + * Author: Paulo César Pereira de Andrade + */ + +/* + * Portions Copyright (c) 2003 David J. Hawkey Jr. + * Rights, permissions, and disclaimer per the above XFree86 license. + */ + +/* $XFree86: xc/lib/Xaw/Tip.c,v 1.5 2000/05/18 16:29:53 dawes Exp $ */ + +#include "Xaw3dP.h" +#include <X11/IntrinsicP.h> +#include <X11/StringDefs.h> +#include <X11/Xos.h> +#include <X11/Xmu/Converters.h> +#include <X11/Xaw3d/TipP.h> +#include <X11/Xaw3d/XawInit.h> + +#define TIP_EVENT_MASK (ButtonPressMask | \ + ButtonReleaseMask | \ + PointerMotionMask | \ + ButtonMotionMask | \ + KeyPressMask | \ + KeyReleaseMask | \ + EnterWindowMask | \ + LeaveWindowMask) + +/* + * Types + */ +typedef struct _WidgetInfo { + Widget widget; + String label; + struct _WidgetInfo *next; +} WidgetInfo; + +typedef struct _XawTipInfo { + Screen *screen; + TipWidget tip; + Bool mapped; + WidgetInfo *widgets; + struct _XawTipInfo *next; +} XawTipInfo; + +typedef struct { + XawTipInfo *info; + WidgetInfo *winfo; +} TimeoutInfo; + +/* + * Class Methods + */ +static void XawTipClassInitialize(); +static void XawTipInitialize(); +static void XawTipDestroy(); +static void XawTipExpose(); +static void XawTipRealize(); +static Boolean XawTipSetValues(); + +/* + * Prototypes + */ +static void TipEventHandler(); +static void TipShellEventHandler(); +static WidgetInfo *CreateWidgetInfo(); +static WidgetInfo *FindWidgetInfo(); +static XawTipInfo *CreateTipInfo(); +static XawTipInfo *FindTipInfo(); +static void ResetTip(); +static void TipTimeoutCallback(); +static void TipLayout(); +static void TipPosition(); + +/* + * Initialization + */ +#define offset(field) XtOffsetOf(TipRec, tip.field) +static XtResource resources[] = { + {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), + offset(foreground), XtRString, XtDefaultForeground}, + {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct*), + offset(font), XtRString, XtDefaultFont}, +#ifdef XAW_INTERNATIONALIZATION + {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet), + offset(fontset), XtRString, XtDefaultFontSet}, +#endif + {XtNlabel, XtCLabel, XtRString, sizeof(String), + offset(label), XtRString, NULL}, + {XtNencoding, XtCEncoding, XtRUnsignedChar, sizeof(unsigned char), + offset(encoding), XtRImmediate, (XtPointer)XawTextEncoding8bit}, + {XtNinternalHeight, XtCHeight, XtRDimension, sizeof(Dimension), + offset(internal_height), XtRImmediate, (XtPointer)2}, + {XtNinternalWidth, XtCWidth, XtRDimension, sizeof(Dimension), + offset(internal_width), XtRImmediate, (XtPointer)2}, + {XtNbackingStore, XtCBackingStore, XtRBackingStore, sizeof(int), + offset(backing_store), XtRImmediate, + (XtPointer)(Always + WhenMapped + NotUseful)}, + {XtNtimeout, XtCTimeout, XtRInt, sizeof(int), + offset(timeout), XtRImmediate, (XtPointer)500}, +}; +#undef offset + +TipClassRec tipClassRec = { + /* core */ + { + (WidgetClass)&widgetClassRec, /* superclass */ + "Tip", /* class_name */ + sizeof(TipRec), /* widget_size */ + XawTipClassInitialize, /* class_initialize */ + NULL, /* class_part_initialize */ + False, /* class_inited */ + XawTipInitialize, /* initialize */ + NULL, /* initialize_hook */ + XawTipRealize, /* realize */ + NULL, /* actions */ + 0, /* num_actions */ + resources, /* resources */ + XtNumber(resources), /* num_resources */ + NULLQUARK, /* xrm_class */ + True, /* compress_motion */ + True, /* compress_exposure */ + True, /* compress_enterleave */ + False, /* visible_interest */ + XawTipDestroy, /* destroy */ + NULL, /* resize */ + XawTipExpose, /* expose */ + XawTipSetValues, /* set_values */ + NULL, /* set_values_hook */ + XtInheritSetValuesAlmost, /* set_values_almost */ + NULL, /* get_values_hook */ + NULL, /* accept_focus */ + XtVersion, /* version */ + NULL, /* callback_private */ + NULL, /* tm_table */ + XtInheritQueryGeometry, /* query_geometry */ + XtInheritDisplayAccelerator, /* display_accelerator */ + NULL, /* extension */ + }, + /* tip */ + { + NULL, /* extension */ + }, +}; + +WidgetClass tipWidgetClass = (WidgetClass)&tipClassRec; + +static XawTipInfo *TipInfoList = NULL; +static TimeoutInfo TimeoutData; + +/* + * Implementation + */ + +/* + * XmuCvtBackingStoreToString() from XFree86's distribution, because + * X.Org's distribution doesn't have it. + */ + +/*ARGSUSED*/ +static Boolean +XawCvtBackingStoreToString(dpy, args, num_args, fromVal, toVal, data) +Display *dpy; +XrmValuePtr args; +Cardinal *num_args; +XrmValuePtr fromVal, toVal; +XtPointer *data; +{ + static String buffer; + Cardinal size; + + switch (*(int *)fromVal->addr) + { + case NotUseful: + buffer = XtEnotUseful; + break; + case WhenMapped: + buffer = XtEwhenMapped; + break; + case Always: + buffer = XtEalways; + break; + case (Always + WhenMapped + NotUseful): + buffer = XtEdefault; + break; + default: + XtWarning("Cannot convert BackingStore to String"); + toVal->addr = NULL; + toVal->size = 0; + return (False); + } + + size = strlen(buffer) + 1; + if (toVal->addr != NULL) + { + if (toVal->size < size) + { + toVal->size = size; + return (False); + } + strcpy((char *)toVal->addr, buffer); + } + else + toVal->addr = (XPointer)buffer; + toVal->size = sizeof(String); + + return (True); +} + +static void +XawTipClassInitialize() +{ + XawInitializeWidgetSet(); + XtAddConverter(XtRString, XtRBackingStore, XmuCvtStringToBackingStore, + NULL, 0); + XtSetTypeConverter(XtRBackingStore, XtRString, XawCvtBackingStoreToString, + NULL, 0, XtCacheNone, NULL); +} + +/*ARGSUSED*/ +static void +XawTipInitialize(req, w, args, num_args) +Widget req, w; +ArgList args; +Cardinal *num_args; +{ + TipWidget tip = (TipWidget)w; + XGCValues values; + + tip->tip.timer = 0; + + values.foreground = tip->tip.foreground; + values.background = tip->core.background_pixel; + values.font = tip->tip.font->fid; + values.graphics_exposures = False; + + tip->tip.gc = XtAllocateGC(w, 0, GCForeground | GCBackground | GCFont | + GCGraphicsExposures, &values, GCFont, 0); +} + +static void +XawTipDestroy(w) +Widget w; +{ + XawTipInfo *info = FindTipInfo(w); + WidgetInfo *winfo; + TipWidget tip = (TipWidget)w; + + if (tip->tip.timer) + XtRemoveTimeOut(tip->tip.timer); + + XtReleaseGC(w, tip->tip.gc); + + XtRemoveEventHandler(XtParent(w), KeyPressMask, False, + TipShellEventHandler, (XtPointer)NULL); + + while (info->widgets) { + winfo = info->widgets->next; + XtFree((char *)info->widgets->label); + XtFree((char *)info->widgets); + info->widgets = winfo; + } + + if (info == TipInfoList) + TipInfoList = TipInfoList->next; + else { + XawTipInfo *p = TipInfoList; + + while (p && p->next != info) + p = p->next; + if (p) + p->next = info->next; + } + + XtFree((char *)info); +} + +static void +XawTipRealize(w, mask, attr) +Widget w; +Mask *mask; +XSetWindowAttributes *attr; +{ + TipWidget tip = (TipWidget)w; + + if (tip->tip.backing_store == Always || + tip->tip.backing_store == NotUseful || + tip->tip.backing_store == WhenMapped) { + *mask |= CWBackingStore; + attr->backing_store = tip->tip.backing_store; + } + else + *mask &= ~CWBackingStore; + *mask |= CWOverrideRedirect; + attr->override_redirect = True; + + XtWindow(w) = XCreateWindow(DisplayOfScreen(XtScreen(w)), + RootWindowOfScreen(XtScreen(w)), + XtX(w), XtY(w), + XtWidth(w) ? XtWidth(w) : 1, + XtHeight(w) ? XtHeight(w) : 1, + XtBorderWidth(w), + DefaultDepthOfScreen(XtScreen(w)), + InputOutput, CopyFromParent, *mask, attr); +} + +static void +XawTipExpose(w, event, region) +Widget w; +XEvent *event; +Region region; +{ + TipWidget tip = (TipWidget)w; + GC gc = tip->tip.gc; + char *nl, *label = tip->tip.label; + Position y = tip->tip.internal_height + tip->tip.font->max_bounds.ascent; + int len; + +#ifdef XAW_INTERNATIONALIZATION + if (tip->tip.international == True) { + Position ksy = tip->tip.internal_height; + XFontSetExtents *ext = XExtentsOfFontSet(tip->tip.fontset); + + ksy += abs(ext->max_ink_extent.y); + + while ((nl = index(label, '\n')) != NULL) { + XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset, + gc, tip->tip.internal_width, ksy, label, + (int)(nl - label)); + ksy += ext->max_ink_extent.height; + label = nl + 1; + } + len = strlen(label); + if (len) + XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset, gc, + tip->tip.internal_width, ksy, label, len); + } + else +#endif + { + while ((nl = index(label, '\n')) != NULL) { + if (tip->tip.encoding) + XDrawString16(XtDisplay(w), XtWindow(w), gc, + tip->tip.internal_width, y, + (XChar2b*)label, (int)(nl - label) >> 1); + else + XDrawString(XtDisplay(w), XtWindow(w), gc, + tip->tip.internal_width, y, + label, (int)(nl - label)); + y += tip->tip.font->max_bounds.ascent + + tip->tip.font->max_bounds.descent; + label = nl + 1; + } + len = strlen(label); + if (len) { + if (tip->tip.encoding) + XDrawString16(XtDisplay(w), XtWindow(w), gc, + tip->tip.internal_width, y, + (XChar2b*)label, len >> 1); + else + XDrawString(XtDisplay(w), XtWindow(w), gc, + tip->tip.internal_width, y, label, len); + } + } +} + +/*ARGSUSED*/ +static Boolean +XawTipSetValues(current, request, cnew, args, num_args) +Widget current, request, cnew; +ArgList args; +Cardinal *num_args; +{ + TipWidget curtip = (TipWidget)current; + TipWidget newtip = (TipWidget)cnew; + Boolean redisplay = False; + + if (curtip->tip.font->fid != newtip->tip.font->fid || + curtip->tip.foreground != newtip->tip.foreground) { + XGCValues values; + + values.foreground = newtip->tip.foreground; + values.background = newtip->core.background_pixel; + values.font = newtip->tip.font->fid; + values.graphics_exposures = False; + XtReleaseGC(cnew, curtip->tip.gc); + newtip->tip.gc = XtAllocateGC(cnew, 0, GCForeground | GCBackground | + GCFont | GCGraphicsExposures, &values, + GCFont, 0); + redisplay = True; + } + + return (redisplay); +} + +static void +TipLayout(info) +XawTipInfo *info; +{ + XFontStruct *fs = info->tip->tip.font; + int width = 0, height; + char *nl, *label = info->tip->tip.label; + +#ifdef XAW_INTERNATIONALIZATION + if (info->tip->tip.international == True) { + XFontSet fset = info->tip->tip.fontset; + XFontSetExtents *ext = XExtentsOfFontSet(fset); + + height = ext->max_ink_extent.height; + if ((nl = index(label, '\n')) != NULL) { + /*CONSTCOND*/ + while (True) { + int w = XmbTextEscapement(fset, label, (int)(nl - label)); + + if (w > width) + width = w; + if (*nl == '\0') + break; + label = nl + 1; + if (*label) + height += ext->max_ink_extent.height; + if ((nl = index(label, '\n')) == NULL) + nl = index(label, '\0'); + } + } + else + width = XmbTextEscapement(fset, label, strlen(label)); + } + else +#endif + { + height = fs->max_bounds.ascent + fs->max_bounds.descent; + if ((nl = index(label, '\n')) != NULL) { + /*CONSTCOND*/ + while (True) { + int w = info->tip->tip.encoding ? + XTextWidth16(fs, (XChar2b*)label, + (int)(nl - label) >> 1) : + XTextWidth(fs, label, (int)(nl - label)); + + if (w > width) + width = w; + if (*nl == '\0') + break; + label = nl + 1; + if (*label) + height += fs->max_bounds.ascent + fs->max_bounds.descent; + if ((nl = index(label, '\n')) == NULL) + nl = index(label, '\0'); + } + } + else + width = info->tip->tip.encoding ? + XTextWidth16(fs, (XChar2b*)label, strlen(label) >> 1) : + XTextWidth(fs, label, strlen(label)); + } + XtWidth(info->tip) = width + info->tip->tip.internal_width * 2; + XtHeight(info->tip) = height + info->tip->tip.internal_height * 2; +} + +#define DEFAULT_TIP_OFFSET 12 + +static void +TipPosition(info) +XawTipInfo *info; +{ + Window r, c; + int rx, ry, wx, wy; + unsigned mask; + Position x, y; + int bw2 = XtBorderWidth(info->tip) * 2; + int scr_width = WidthOfScreen(XtScreen(info->tip)); + int scr_height = HeightOfScreen(XtScreen(info->tip)); + int win_width = XtWidth(info->tip) + bw2; + int win_height = XtHeight(info->tip) + bw2; + + XQueryPointer(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip), + &r, &c, &rx, &ry, &wx, &wy, &mask); + x = rx + DEFAULT_TIP_OFFSET; + y = ry + DEFAULT_TIP_OFFSET; + + if (x + win_width > scr_width) + x = scr_width - win_width; + if (x < 0) + x = 0; + + if (y + win_height > scr_height) + y -= win_height + (DEFAULT_TIP_OFFSET << 1); + if (y < 0) + y = 0; + + XMoveResizeWindow(XtDisplay(info->tip), XtWindow(info->tip), + (int)(XtX(info->tip) = x), (int)(XtY(info->tip) = y), + (unsigned)XtWidth(info->tip), + (unsigned)XtHeight(info->tip)); +} + +static WidgetInfo * +CreateWidgetInfo(w) +Widget w; +{ + WidgetInfo *winfo = XtNew(WidgetInfo); + + winfo->widget = w; + winfo->label = NULL; + winfo->next = NULL; + + return (winfo); +} + +static WidgetInfo * +FindWidgetInfo(info, w) +XawTipInfo *info; +Widget w; +{ + WidgetInfo *winfo, *wlist = info->widgets; + + if (wlist == NULL) + return (info->widgets = CreateWidgetInfo(w)); + + for (winfo = wlist; wlist; winfo = wlist, wlist = wlist->next) + if (wlist->widget == w) + return (wlist); + + return (winfo->next = CreateWidgetInfo(w)); +} + +static XawTipInfo * +CreateTipInfo(w) +Widget w; +{ + XawTipInfo *info = XtNew(XawTipInfo); + Widget shell = w; + + while (XtParent(shell)) + shell = XtParent(shell); + + info->tip = (TipWidget)XtCreateWidget("tip", tipWidgetClass, + shell, NULL, 0); + XtRealizeWidget((Widget)info->tip); + info->screen = XtScreen(w); + info->mapped = False; + info->widgets = NULL; + info->next = NULL; + XtAddEventHandler(shell, KeyPressMask, False, TipShellEventHandler, + (XtPointer)NULL); + + return (info); +} + +static XawTipInfo * +FindTipInfo(w) +Widget w; +{ + XawTipInfo *info, *list = TipInfoList; + Screen *screen; + + if (list == NULL) + return (TipInfoList = CreateTipInfo(w)); + + screen = XtScreen(w); + for (info = list; list; info = list, list = list->next) + if (list->screen == screen) + return (list); + + return (info->next = CreateTipInfo(w)); +} + +static void +ResetTip(info, winfo, add_timeout) +XawTipInfo *info; +WidgetInfo *winfo; +Bool add_timeout; +{ + if (info->tip->tip.timer) { + XtRemoveTimeOut(info->tip->tip.timer); + info->tip->tip.timer = 0; + } + if (info->mapped) { + XtRemoveGrab(XtParent((Widget)info->tip)); + XUnmapWindow(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip)); + info->mapped = False; + } + if (add_timeout) { + TimeoutData.info = info; + TimeoutData.winfo = winfo; + info->tip->tip.timer = + XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)info->tip), + info->tip->tip.timeout, TipTimeoutCallback, + (XtPointer)&TimeoutData); + } +} + +static void +TipTimeoutCallback(closure, id) +XtPointer closure; +XtIntervalId *id; +{ + TimeoutInfo *cinfo = (TimeoutInfo *)closure; + XawTipInfo *info = cinfo->info; + WidgetInfo *winfo = cinfo->winfo; + Arg args[2]; + + info->tip->tip.label = winfo->label; + info->tip->tip.encoding = 0; + XtSetArg(args[0], XtNencoding, &info->tip->tip.encoding); +#ifdef XAW_INTERNATIONALIZATION + info->tip->tip.international = False; + XtSetArg(args[1], XtNinternational, &info->tip->tip.international); +#endif + XtGetValues(winfo->widget, args, 2); + + TipLayout(info); + TipPosition(info); + XMapRaised(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip)); + XtAddGrab(XtParent((Widget)info->tip), True, True); + info->mapped = True; +} + +/*ARGSUSED*/ +static void +TipShellEventHandler(w, client_data, event, continue_to_dispatch) +Widget w; +XtPointer client_data; +XEvent *event; +Boolean *continue_to_dispatch; +{ + XawTipInfo *info = FindTipInfo(w); + + ResetTip(info, FindWidgetInfo(info, w), False); +} + +/*ARGSUSED*/ +static void +TipEventHandler(w, client_data, event, continue_to_dispatch) +Widget w; +XtPointer client_data; +XEvent *event; +Boolean *continue_to_dispatch; +{ + XawTipInfo *info = FindTipInfo(w); + Boolean add_timeout; + + switch (event->type) { + case EnterNotify: + add_timeout = True; + break; + case MotionNotify: + /* If any button is pressed, timer is 0 */ + if (info->mapped) + return; + add_timeout = info->tip->tip.timer != 0; + break; + default: + add_timeout = False; + break; + } + ResetTip(info, FindWidgetInfo(info, w), add_timeout); +} + +/* + * Public routines + */ +void +XawTipEnable(w, label) +Widget w; +String label; +{ + if (XtIsWidget(w) && label && *label) { + XawTipInfo *info = FindTipInfo(w); + WidgetInfo *winfo = FindWidgetInfo(info, w); + + if (winfo->label) + XtFree((char *)winfo->label); + winfo->label = XtNewString(label); + + XtAddEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler, + (XtPointer)NULL); + } +} + +void +XawTipDisable(w) +Widget w; +{ + if (XtIsWidget(w)) { + XawTipInfo *info = FindTipInfo(w); + + XtRemoveEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler, + (XtPointer)NULL); + ResetTip(info, FindWidgetInfo(info, w), False); + } +} |