summaryrefslogtreecommitdiff
path: root/Build/source/texk/xdvik/gui/Tip.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/xdvik/gui/Tip.c')
-rw-r--r--Build/source/texk/xdvik/gui/Tip.c607
1 files changed, 607 insertions, 0 deletions
diff --git a/Build/source/texk/xdvik/gui/Tip.c b/Build/source/texk/xdvik/gui/Tip.c
new file mode 100644
index 00000000000..76a83f188b9
--- /dev/null
+++ b/Build/source/texk/xdvik/gui/Tip.c
@@ -0,0 +1,607 @@
+/*
+ * Copyright (c) 2001-2004 Marcin Dalecki and others
+ *
+ * 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
+ * PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE 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.
+ *
+ */
+
+#include "xdvi-config.h"
+#include "xdvi.h"
+
+#include "Tip.h"
+#include "xdvi-debug.h"
+#include "util.h"
+
+#ifdef MOTIF /* needed for `make depend' */
+
+#ifndef UNUSED
+#define UNUSED(x) ((void)(x))
+#endif
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <Xm/XmP.h>
+
+#include <Xm/PushB.h>
+#include <X11/ShellP.h>
+
+#define TIP_NUM 1024 /* FIXME: remove this hard-coded value */
+
+typedef struct {
+ int __empty;
+} TipClassPart;
+
+/* Full class record declaration */
+typedef struct _TipClassRec {
+ CoreClassPart core_class;
+ CompositeClassPart composite_class;
+ ShellClassPart shell_class;
+ OverrideShellClassPart override_shell_class;
+ TipClassPart tip_class;
+} TipClassRec;
+
+/* keep information about each widget we are keeping track of */
+struct tip_context {
+ Widget watched; /* the widget we are watching */
+ Window window; /* Window of the object we are monitoring */
+ TipWidget tw; /* pointer back to the tip widget */
+ Position abs_x, abs_y;
+ Boolean active; /* if False, tip is suppressed */
+ char *text; /* text to display */
+ short size; /* its size */
+};
+
+/* New fields for the widget record */
+typedef struct {
+ /* resources */
+ Pixel foreground;
+ XFontSet fontset; /* the font for text in box */
+ int waitPeriod; /* the delay resource - pointer must be
+ * in watched widget this long before
+ * help is popped up - in millisecs
+ */
+ unsigned int cwp; /* after help is popped down - normal
+ * wait period is cancelled for this
+ * period - in millisecs
+ */
+
+ /* private state */
+ struct tip_context twl[TIP_NUM]; /* list of widgets we are liteClue-ing */
+ Cardinal nr_twl; /* number of widgets we have attached */
+ Dimension font_width; /* width of '1' character */
+ Dimension font_height; /* height of font, rows are spaced using this */
+ Dimension font_baseline; /* relative displacement to baseline from top */
+ GC text_GC; /* for drawing text */
+ XtIntervalId tid; /* holds timer id */
+ Widget isup; /* the help popup is up on this widget */
+ Time HelpPopDownTime; /* the time at which help popup
+ was popped down */
+} TipPart;
+
+/*
+ * Full instance record declaration
+ */
+typedef struct _TipRec {
+ CorePart core;
+ CompositePart composite;
+ ShellPart shell;
+ OverrideShellPart override;
+ TipPart tip;
+} TipRec;
+
+#define CheckWidgetClass(routine) \
+ if (XtClass(w) != tipWidgetClass) \
+ wrong_widget(routine)
+
+static void initialize(Widget, Widget, ArgList, Cardinal *);
+static Boolean set_values(Widget, Widget, Widget, ArgList, Cardinal *);
+static void destroy(Widget);
+
+/*
+ * Widget resources: eg to set tip box background: *tipShell.background: yellow.
+ */
+
+#define offset(field) XtOffsetOf(TipRec, field)
+static XtResource resources[] = {
+ {XtNforeground, XtCForeground,
+ XtRPixel, sizeof(Pixel), offset(tip.foreground),
+ XtRString, "black"},
+ {XtNfontSet, XtCFontSet,
+ XtRFontSet, sizeof(XFontSet), offset(tip.fontset),
+ XtRString, "fixed"},
+ {XmNwaitPeriod, XmCWaitPeriod,
+ XtRInt, sizeof(int), offset(tip.waitPeriod),
+ XtRImmediate, (XtPointer) 800},
+ {XmNcancelWaitPeriod, XmCCancelWaitPeriod,
+ XtRInt, sizeof(int), offset(tip.cwp),
+ XtRImmediate, (XtPointer) 250},
+};
+#undef offset
+
+TipClassRec tipClassRec = {
+ {
+ /* superclass */ (WidgetClass) & overrideShellClassRec,
+ /* class_name */ "Tip",
+ /* widget size */ (Cardinal) sizeof(TipRec),
+ /* class_init */ NULL,
+ /* class_part_init */ (XtWidgetClassProc) NULL,
+ /* class_inited */ (XtEnum) FALSE,
+ /* initialize */ (XtInitProc) initialize,
+ /* init_hook */ (XtArgsProc) NULL,
+ /* realize */ XtInheritRealize,
+ /* actions */ (XtActionList) 0,
+ /* num_actions */ (Cardinal) 0,
+ /* resources */ (XtResourceList) resources,
+ /* num_resources */ (Cardinal) XtNumber(resources),
+ /* xrm_class */ NULLQUARK,
+ /* compress_motion */ TRUE,
+ /* compress_exposur */ (XtEnum) FALSE,
+ /* compress enterleave */ TRUE,
+ /* visibility_interest */ FALSE,
+ /* destroy */ destroy,
+ /* resize */ XtInheritResize,
+ /* expose, */ XtInheritExpose,
+ /* set_values */ (XtSetValuesFunc) set_values,
+ /* set_values_hook */ (XtArgsFunc) NULL,
+ /* set_values_almost */ XtInheritSetValuesAlmost,
+ /* get_values_hook */ (XtArgsProc) NULL,
+ /* accept_focus */ XtInheritAcceptFocus,
+ /* version */ XtVersion,
+ /* callback_private */ (XtPointer) NULL,
+ /* translations */ XtInheritTranslations,
+ /* query_geometry */ XtInheritQueryGeometry,
+ /* display_accelerator */ XtInheritDisplayAccelerator,
+ /* extension */ (XtPointer) 0,
+ },
+ /* composite part */
+ {
+ /* geometry_manager */ XtInheritGeometryManager,
+ /* change_managed */ XtInheritChangeManaged,
+ /* insert_child */ XtInheritInsertChild,
+ /* delete_child */ XtInheritDeleteChild,
+ /* extension */ NULL
+ },
+ /* Shell */
+ {
+ (XtPointer) NULL,
+ },
+ /* Override Shell */
+ {
+ 0,
+ },
+ /* tip */
+ {
+ 0,
+ }
+};
+
+WidgetClass tipWidgetClass = (WidgetClass) & tipClassRec;
+
+/*
+ * The font_information is derived.
+ */
+static void compute_font_info(TipWidget cw)
+{
+ XRectangle ink;
+ XRectangle logical;
+
+ if (!cw->tip.fontset)
+ return;
+ XmbTextExtents(cw->tip.fontset, "1", 1, &ink, &logical);
+
+ cw->tip.font_baseline = -logical.y; /* y offset from top to baseline, don't
+ know why this is returned as
+ negative */
+
+ cw->tip.font_width = logical.width; /* the width and height of the object */
+
+ cw->tip.font_height = logical.height;
+ TRACE_GUI((stderr, "baseline: %d, width: %d, height: %d\n",
+ cw->tip.font_baseline, cw->tip.font_width, cw->tip.font_height));
+}
+
+/*
+ * Creates the various graphic contexts we will need.
+ */
+static void create_GC(TipWidget cw)
+{
+ XtGCMask valuemask;
+ XGCValues myXGCV;
+
+ valuemask = GCForeground | GCBackground | GCFillStyle;
+ myXGCV.foreground = cw->tip.foreground;
+ myXGCV.background = cw->core.background_pixel;
+ myXGCV.fill_style = FillSolid;
+
+ if (cw->tip.text_GC)
+ XtReleaseGC((Widget) cw, cw->tip.text_GC);
+ cw->tip.text_GC = XtGetGC((Widget) cw, valuemask, &myXGCV);
+}
+
+/*
+ * A routine to halt execution and force a core dump for debugging analysis
+ * when a public routine is called with the wrong class of widget.
+ */
+
+static void wrong_widget(char *routine)
+{
+ XDVI_ABORT((stderr, "Wrong class of widget passed to %s", routine));
+}
+
+/*
+ * Global list of shells for tips that are in use.
+ */
+
+static TipWidget *shells = NULL;
+static int nr_shells = 0;
+
+/****************************************************************************
+ * Widget Methods
+ */
+
+static void initialize(Widget treq, Widget tnew, ArgList args,
+ Cardinal * nargs)
+{
+ TipWidget tw = (TipWidget) tnew;
+
+ UNUSED(treq);
+ UNUSED(args);
+ UNUSED(nargs);
+
+ tw->tip.text_GC = NULL;
+ tw->tip.isup = NULL;
+ tw->tip.HelpPopDownTime = 0;
+ tw->tip.tid = (XtIntervalId) 0;
+ tw->tip.nr_twl = 0;
+ compute_font_info(tw);
+ create_GC(tw);
+
+ /* Add to our list of tip shells.
+ */
+ if (!shells)
+ shells = (TipWidget *)XtMalloc(sizeof(TipWidget));
+ else
+ shells = (TipWidget *)XtRealloc((char *)shells,
+ sizeof(TipWidget) * (nr_shells + 1));
+
+ shells[nr_shells++] = tw;
+}
+
+static Boolean set_values(Widget _current, Widget _request, Widget _new,
+ ArgList args, Cardinal * nargs)
+{
+ TipWidget cw_new = (TipWidget) _new;
+ TipWidget cw_cur = (TipWidget) _current;
+
+ UNUSED(_request);
+ UNUSED(args);
+ UNUSED(nargs);
+
+ /* values of cw_new->tip.cwp and
+ cw_new->tip.waitPeriod are accepted without checking */
+
+ if (cw_new->tip.foreground != cw_cur->tip.foreground
+ || cw_new->core.background_pixel !=
+ cw_cur->core.background_pixel) {
+ create_GC(cw_new);
+ }
+ return FALSE;
+}
+
+static void destroy(Widget w)
+{
+ TipWidget tw = (TipWidget) w;
+ int i;
+ Boolean copy = False;
+
+ /* Remove this tip shell from our global list.
+ */
+ for (i = 0; i < nr_shells; ++i) {
+ if (shells[i] == tw) {
+ copy = True;
+ --nr_shells;
+ }
+ if (copy && nr_shells)
+ shells[i] = shells[i + 1];
+ }
+ if (!nr_shells) {
+ XtFree((char *) shells);
+ shells = NULL;
+ }
+}
+
+/****************************************************************************
+ * Event handlers
+ */
+
+/* callback to popup the tip window
+ */
+static void timeout_event(XtPointer client_data, XtIntervalId *id)
+{
+#define HBorderPix 3
+#define VBorderPix 3
+ struct tip_context *obj = (struct tip_context *) client_data;
+ TipWidget tw = obj->tw;
+ Position abs_x, abs_y;
+ int ptr_x, ptr_y;
+
+ XRectangle ink;
+ XRectangle logical;
+ Position w_height, w_width;
+ Widget w;
+
+ UNUSED(id);
+
+ TRACE_GUI((stderr, "timeout called!"));
+
+ if (tw->tip.tid == (XtIntervalId) 0)
+ return; /* timeout was removed but callback happened
+ anyway */
+
+ tw->tip.tid = (XtIntervalId) 0;
+ if (obj->active == False)
+ return;
+
+ w = obj->watched;
+
+ if (!XtIsManaged(w))
+ return;
+
+ { /* perform additional check that pointer is really still over the widget;
+ else, tooltips will sometimes pop up if window had received an Enter
+ event before (for some reason, not all Enters are followed by Leaves).
+ This is especially apparent when running xdvi from a remote display over
+ a slow connection.
+ */
+ Window root, child;
+ int root_x, root_y;
+ unsigned int keys_buttons;
+ if (!XQueryPointer(DISP, RootWindowOfScreen(SCRN), &root, &child,
+ &root_x, &root_y, &ptr_x, &ptr_y, &keys_buttons))
+ return;
+
+ TRACE_GUI((stderr, "Pointerlocate: %d, %d", root_x, root_y));
+
+ XtVaGetValues(w, XtNheight, &w_height, XtNwidth, &w_width, NULL);
+ XtTranslateCoords(w, 0, 0, &abs_x, &abs_y);
+
+ TRACE_GUI((stderr, "Window: %d,%d - %d,%d",
+ abs_x, abs_y, abs_x + w_width, abs_y + w_height));
+
+ if (root_x < abs_x || root_x > abs_x + w_width
+ || root_y < abs_y || root_y > abs_y + w_height) {
+ TRACE_GUI((stderr, "not really over toolbutton - returning!"));
+ return;
+ }
+ }
+
+ /* position just below the pointer
+ * (NOT the widget, in case the widget is large!)
+ */
+ ptr_y += 20;
+/* abs_x += w_width / 2; */
+/* abs_y += w_height; */
+
+ XmbTextExtents(tw->tip.fontset, obj->text, obj->size, &ink, &logical);
+
+ XtRealizeWidget((Widget)tw); /* so that setting the size etc. works */
+
+ XtResizeWidget((Widget) tw,
+ 2 * HBorderPix + logical.width,
+ 2 * VBorderPix + tw->tip.font_height,
+ tw->core.border_width);
+ TRACE_GUI((stderr, "Popup size: %d x %d (hborder: %d, vborder: %d)\n",
+ 2 * HBorderPix + logical.width, 2 * VBorderPix + tw->tip.font_height,
+ HBorderPix, VBorderPix));
+ XtMoveWidget((Widget)tw, ptr_x, ptr_y);
+
+ XtPopup((Widget) tw, XtGrabNone);
+ tw->tip.isup = obj->watched;
+
+ XmbDrawImageString(XtDisplay((Widget) tw),
+ XtWindow((Widget) tw),
+ tw->tip.fontset,
+ tw->tip.text_GC,
+ HBorderPix,
+ VBorderPix + tw->tip.font_baseline,
+ obj->text, obj->size);
+}
+
+/*
+ * Pointer enters watched widget, set a timer to popup the help.
+ */
+static void enter(struct tip_context *obj, XEvent * xevent,
+ XtAppContext app)
+{
+ TipWidget tw = obj->tw;
+ XEnterWindowEvent *event = &xevent->xcrossing;
+ int current_waitPeriod;
+
+ /* this doesn't help against the Enter/Leave problem mentioned above,
+ so it's not related to Widget creation ... */
+ if (!XtIsManaged(obj->watched)) {
+ TRACE_GUI((stderr, "%s:%d: Not yet managed!", __FILE__, __LINE__));
+ return;
+ }
+
+ TRACE_GUI((stderr, "%s:%d: Enter!", __FILE__, __LINE__));
+
+ if (obj->active == False)
+ return;
+
+ /* check for two enters in a row - happens when widget is
+ exposed under a pop-up */
+ if (tw->tip.tid != (XtIntervalId) 0)
+ return;
+
+ if (event->mode != NotifyNormal)
+ return;
+
+ /* it seems that this makes the tooltips somewhat unpredictable (they
+ don't show when hovering fast over several buttons, then staying on
+ one button); disabled this for the time being. */
+/* if ((event->time - tw->tip.HelpPopDownTime) > tw->tip.cwp) */
+/* current_waitPeriod = tw->tip.waitPeriod; */
+/* else */
+/* current_waitPeriod = 0; */
+
+/* current_waitPeriod = tw->tip.waitPeriod; */
+ current_waitPeriod = resource.tooltips_wait_period;
+ if (current_waitPeriod >= 0) {
+ tw->tip.tid = XtAppAddTimeOut(app, current_waitPeriod, timeout_event,
+ (XtPointer) obj);
+ }
+}
+
+/*
+ * Remove timer if its pending. Then popdown help.
+ */
+static void leave(struct tip_context *obj, XEvent * xevent)
+{
+ TipWidget tw = obj->tw;
+ XEnterWindowEvent *event = &xevent->xcrossing;
+
+ TRACE_GUI((stderr, "%s:%d: Leave!", __FILE__, __LINE__));
+
+ if (tw->tip.tid != (XtIntervalId) 0) {
+ if (globals.debug & DBG_EVENT)
+ fprintf(stderr, "%s:%d: removing timeout %ld\n", __FILE__, __LINE__, tw->tip.tid);
+ XtRemoveTimeOut(tw->tip.tid);
+ tw->tip.tid = (XtIntervalId) 0;
+ }
+
+ if (obj->active == False)
+ return;
+
+ if (tw->tip.isup) {
+ XtPopdown((Widget) tw);
+ tw->tip.isup = NULL;
+ tw->tip.HelpPopDownTime = event->time;
+ }
+}
+
+/****************************************************************************
+ * Public interface implementation.
+ */
+
+void TipAppHandle(XtAppContext app, XEvent *event)
+{
+ int i;
+
+ if (!(event->type == EnterNotify
+ || event->type == MotionNotify
+ || event->type == LeaveNotify
+ || event->type == ButtonPress)) {
+ return;
+ }
+
+ for (i = 0; i < nr_shells; ++i) {
+ unsigned int j;
+
+ for (j = 0; j < shells[i]->tip.nr_twl; ++j) {
+ if (event->xany.window == shells[i]->tip.twl[j].window) {
+ if (event->type == EnterNotify)
+ enter(shells[i]->tip.twl + j, event, app);
+ if (event->xany.type == LeaveNotify
+ || event->xany.type == MotionNotify /* FIXME: this doesn' work? */
+ /* might be useful to popdown tip when mouse is moved */
+ || event->xany.type == ButtonPress) {
+ leave(shells[i]->tip.twl + j, event);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * This has to replace the XtAppMainLoop in the application using
+ * tooltips.
+ */
+void TipAppMainLoop(XtAppContext app)
+{
+ XEvent event;
+
+ for (;;) {
+ XtAppNextEvent(app, &event);
+ TipAppHandle(app, &event);
+ XtDispatchEvent(&event);
+ }
+}
+
+/*
+ * Add a widget to be watched for tooltips.
+ *
+ * This function must be called after the widget has been realized!
+ * Further on please make sure that this function will not be called twice
+ * for one button!
+ *
+ * w - tip widget
+ * watch - the widget to give tips for
+ * text - pointer to tip text
+ */
+void TipAddWidget(Widget w, Widget watch, const String text)
+{
+#define ROUTINE "TipAddWidget"
+ TipWidget tw = (TipWidget) w;
+ int i;
+
+ CheckWidgetClass(ROUTINE); /* make sure we are called with a tip widget */
+
+ /* Make internal resource available via resource.tooltips_wait_period(_bak) and
+ * resource.show_tooltips.
+ */
+ resource.tooltips_wait_period_bak = resource.tooltips_wait_period = ABS(tw->tip.waitPeriod);
+ if (tw->tip.waitPeriod < 0) {
+ resource.show_tooltips = False;
+ }
+ else if (!resource.show_tooltips) {
+ if (resource.tooltips_wait_period == 0)
+ resource.tooltips_wait_period = -1;
+ else
+ resource.tooltips_wait_period = -resource.tooltips_wait_period;
+ }
+
+ for (i = 0; i < nr_shells; ++i)
+ if (shells[i] == tw) {
+ struct tip_context *obj;
+
+ if (tw->tip.nr_twl >= TIP_NUM) {
+ XDVI_FATAL((stderr, "Too many tip widgets, cannot add new tip"));
+ return;
+ }
+ obj = tw->tip.twl + tw->tip.nr_twl;
+ obj->text = XtNewString(text);
+ obj->size = strlen(text);
+ obj->watched = watch;
+ obj->window = XtWindow(watch);
+ obj->active = True;
+ obj->tw = tw;
+ tw->tip.nr_twl++;
+ }
+#undef ROUTINE
+}
+
+#else
+/* silence `empty compilation unit' warnings */
+static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); }
+#endif /* MOTIF */