diff options
Diffstat (limited to 'Build/source/texk/xdvik/gui')
68 files changed, 27181 insertions, 0 deletions
diff --git a/Build/source/texk/xdvik/gui/Makefile.in b/Build/source/texk/xdvik/gui/Makefile.in new file mode 100644 index 00000000000..20343fd95b3 --- /dev/null +++ b/Build/source/texk/xdvik/gui/Makefile.in @@ -0,0 +1,80 @@ +# Makefile for xdvik +# +# - 1999/05/08: janl: Removed submakes for libwww and kpathsea. That's what +# the top makefile is for! +# + + +### NOTE: ac_include paths must be relative to texk/xdvik, since that +### is from where the configure is run! +kpse_include ../make/paths.mk +kpse_include ../make/library.mk +kpse_include ../make/common.mk +kpse_include ../make/programs.mk + +kpathsea_parent = ../.. + +CPP = @CPP@ +RANLIB = @RANLIB@ + +# Make `#include <X11/...>' and `-lX...' work. +# This matches web2c (needed only for Metafont). +x_cppflags = @X_CFLAGS@ + +# Extra xdvi-specific compiler options. +ps_def = @PS_DEF@ -DXSERVER_INFO +prog_cflags = @XTRA_WARN_CFLAGS@ -I.. -I$(srcdir)/.. $(x_cppflags) $(ps_def) + +# Note: to be able to use one depend.mk file for both Motif/Xaw (which +# is needed since only maintainers are supposed to invoke `make depend') +# we include *all* object files and have tests for #ifdef MOTIF/XAW inside +# the files. +objects = \ + help-window.o \ + message-window.o \ + mag.o \ + menu.o \ + Panner.o \ + pagesel.o \ + print-dialog.o \ + print-log.o \ + search-dialog.o \ + selection.o \ + sfDir.o \ + sfDraw.o \ + sfPath.o \ + sfSelFile.o \ + statusline.o \ + Tip.o \ + topic-window.o \ + xaw_bitmaps.o \ + xaw_menu.o \ + xicon.o \ + xlwradio.o \ + xm_colorsel.o \ + xm_filesel.o \ + xm_menu.o \ + xm_prefs.o \ + xm_prefs_appearance.o \ + xm_prefs_fonts.o \ + xm_prefs_page.o \ + xm_prefs_scroll.o \ + xm_prefs_helpers.o \ + xm_toolbar.o + +libgui.a: $(objects) +### hmm, I don't understand ar ... sometimes objects were NOT replaced. +### Play it safe and always remove the archive before creating new one. + rm -f $@ + $(AR) $(ARFLAGS) $@ $(objects) + $(RANLIB) $@ + +kpse_include ../make/dist.mk +kpse_include ../make/clean.mk + +clean:: + rm -f *.o *.a + +c_auto_h_dir = .. +kpse_include ../make/rdepend.mk +kpse_include ./gui/depend.mk diff --git a/Build/source/texk/xdvik/gui/Panner.c b/Build/source/texk/xdvik/gui/Panner.c new file mode 100644 index 00000000000..1ff157b6242 --- /dev/null +++ b/Build/source/texk/xdvik/gui/Panner.c @@ -0,0 +1,1230 @@ +/* + * $XConsortium: Panner.c,v 1.52 95/01/10 14:31:26 kaleb Exp $ + * +Copyright (c) 1989, 1994 X Consortium + +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 +X CONSORTIUM 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 X Consortium 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 X Consortium. + * + * Author: Jim Fulton, MIT X Consortium + */ + +#include "xdvi-config.h" +#include "xdvi.h" +#include "string-utils.h" + +#if defined(MOTIF) && defined(USE_PANNER) && USE_XAW_PANNER /* entire file */ + +#if defined(__GNUC__) && DEVEL_MODE +#warning COMPILING Panner_c +#endif + +#include <X11/IntrinsicP.h> +#include <X11/StringDefs.h> /* for XtN and XtC defines */ +#include <X11/Xmu/CharSet.h> /* for XmuCompareISOLatin1() */ +#include "PannerP.h" /* us */ +#include <X11/Xos.h> +#include <X11/Xmu/Misc.h> /* for Min */ +#include <X11/Xmu/Drawing.h> +#include <ctype.h> /* for isascii() etc. */ + +extern Bool XmuDistinguishablePixels(); /* not defined in any Xmu headers */ + +/* + ====================================================================== + begin copy from Simple.c + ====================================================================== +*/ + +#define offset(field) XtOffsetOf(SimpleRec, simple.field) + +static XtResource resources[] = { + {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor), + offset(cursor), XtRImmediate, (XtPointer) None}, + {XtNinsensitiveBorder, XtCInsensitive, XtRPixmap, sizeof(Pixmap), + offset(insensitive_border), XtRImmediate, (XtPointer) NULL}, + {XtNpointerColor, XtCForeground, XtRPixel, sizeof(Pixel), + offset(pointer_fg), XtRString, XtDefaultForeground}, + {XtNpointerColorBackground, XtCBackground, XtRPixel, sizeof(Pixel), + offset(pointer_bg), XtRString, XtDefaultBackground}, + {XtNcursorName, XtCCursor, XtRString, sizeof(String), + offset(cursor_name), XtRString, NULL}, + {XtNinternational, XtCInternational, XtRBoolean, sizeof(Boolean), + offset(international), XtRImmediate, (XtPointer) FALSE}, +#undef offset +}; + +static void ClassPartInitialize(), ClassInitialize(),Realize(),ConvertCursor(); +static Bool SetValues(Widget current, Widget request, Widget new, ArgList args, Cardinal *num_args); +static Bool ChangeSensitive(Widget w); + +SimpleClassRec simpleClassRec = { + { /* core fields */ + /* superclass */ (WidgetClass) &widgetClassRec, + /* class_name */ "Simple", + /* widget_size */ sizeof(SimpleRec), + /* class_initialize */ ClassInitialize, + /* class_part_initialize */ ClassPartInitialize, + /* class_inited */ FALSE, + /* initialize */ NULL, + /* initialize_hook */ NULL, + /* realize */ Realize, + /* actions */ NULL, + /* num_actions */ 0, + /* resources */ resources, + /* num_resources */ XtNumber(resources), + /* xrm_class */ NULLQUARK, + /* compress_motion */ TRUE, + /* compress_exposure */ TRUE, + /* compress_enterleave */ TRUE, + /* visible_interest */ FALSE, + /* destroy */ NULL, + /* resize */ NULL, + /* expose */ NULL, +#warning FIXME: incompatible pointer type + /* set_values */ SetValues, + /* set_values_hook */ NULL, + /* set_values_almost */ XtInheritSetValuesAlmost, + /* get_values_hook */ NULL, + /* accept_focus */ NULL, + /* version */ XtVersion, + /* callback_private */ NULL, + /* tm_table */ NULL, + /* query_geometry */ XtInheritQueryGeometry, + /* display_accelerator */ XtInheritDisplayAccelerator, + /* extension */ NULL + }, + { /* simple fields */ + /* change_sensitive */ ChangeSensitive +#ifndef HAVE_OLD_XAW + , NULL +#endif + } +}; + +WidgetClass simpleWidgetClass = (WidgetClass)&simpleClassRec; + +static void ClassInitialize() +{ + static XtConvertArgRec convertArg[] = { + {XtWidgetBaseOffset, (XtPointer) XtOffsetOf(WidgetRec, core.screen), + sizeof(Screen *)}, + {XtResourceString, (XtPointer) XtNpointerColor, sizeof(Pixel)}, + {XtResourceString, (XtPointer) XtNpointerColorBackground, + sizeof(Pixel)}, + {XtWidgetBaseOffset, (XtPointer) XtOffsetOf(WidgetRec, core.colormap), + sizeof(Colormap)} + }; + + XawInitializeWidgetSet(); + XtSetTypeConverter( XtRString, XtRColorCursor, XmuCvtStringToColorCursor, + convertArg, XtNumber(convertArg), + XtCacheByDisplay, (XtDestructor)NULL); +} + +static void ClassPartInitialize(class) + WidgetClass class; +{ + SimpleWidgetClass c = (SimpleWidgetClass) class; + SimpleWidgetClass super = (SimpleWidgetClass) + c->core_class.superclass; + + if (c->simple_class.change_sensitive == NULL) { + char buf[BUFSIZ]; + + (void) sprintf(buf, + "%s Widget: The Simple Widget class method 'change_sensitive' is undefined.\nA function must be defined or inherited.", + c->core_class.class_name); + XtWarning(buf); + c->simple_class.change_sensitive = ChangeSensitive; + } + + if (c->simple_class.change_sensitive == XtInheritChangeSensitive) + c->simple_class.change_sensitive = super->simple_class.change_sensitive; +} + +static void Realize(w, valueMask, attributes) + Widget w; + Mask *valueMask; + XSetWindowAttributes *attributes; +{ + Pixmap border_pixmap = 0; + if (!XtIsSensitive(w)) { + /* change border to gray; have to remember the old one, + * so XtDestroyWidget deletes the proper one */ + if (((SimpleWidget)w)->simple.insensitive_border == None) + ((SimpleWidget)w)->simple.insensitive_border = + XmuCreateStippledPixmap(XtScreen(w), + w->core.border_pixel, + w->core.background_pixel, + w->core.depth); + border_pixmap = w->core.border_pixmap; + attributes->border_pixmap = + w->core.border_pixmap = ((SimpleWidget)w)->simple.insensitive_border; + + *valueMask |= CWBorderPixmap; + *valueMask &= ~CWBorderPixel; + } + + ConvertCursor(w); + + if ((attributes->cursor = ((SimpleWidget)w)->simple.cursor) != None) + *valueMask |= CWCursor; + + XtCreateWindow( w, (unsigned int)InputOutput, (Visual *)CopyFromParent, + *valueMask, attributes ); + + if (!XtIsSensitive(w)) + w->core.border_pixmap = border_pixmap; +} + +/* Function Name: ConvertCursor + * Description: Converts a name to a new cursor. + * Arguments: w - the simple widget. + * Returns: none. + */ + +static void +ConvertCursor(w) +Widget w; +{ + SimpleWidget simple = (SimpleWidget) w; + XrmValue from, to; + Cursor cursor; + + if (simple->simple.cursor_name == NULL) + return; + + from.addr = (XPointer) simple->simple.cursor_name; + from.size = strlen((char *) from.addr) + 1; + + to.size = sizeof(Cursor); + to.addr = (XPointer) &cursor; + + if (XtConvertAndStore(w, XtRString, &from, XtRColorCursor, &to)) { + if ( cursor != None) + simple->simple.cursor = cursor; + } + else { + XtAppErrorMsg(XtWidgetToApplicationContext(w), + "convertFailed","ConvertCursor","XawError", + "Simple: ConvertCursor failed.", + (String *)NULL, (Cardinal *)NULL); + } +} + + +/* ARGSUSED */ +static Bool +SetValues(Widget current, Widget request, Widget new, ArgList args, Cardinal *num_args) +{ + SimpleWidget s_old = (SimpleWidget) current; + SimpleWidget s_new = (SimpleWidget) new; + Boolean new_cursor = FALSE; + + UNUSED(request); + UNUSED(args); + UNUSED(num_args); + /* this disables user changes after creation*/ + s_new->simple.international = s_old->simple.international; + + if ( XtIsSensitive(current) != XtIsSensitive(new) ) + (*((SimpleWidgetClass)XtClass(new))-> + simple_class.change_sensitive) ( new ); + + if (s_old->simple.cursor != s_new->simple.cursor) { + new_cursor = TRUE; + } + +/* + * We are not handling the string cursor_name correctly here. + */ + + if ( (s_old->simple.pointer_fg != s_new->simple.pointer_fg) || + (s_old->simple.pointer_bg != s_new->simple.pointer_bg) || + (s_old->simple.cursor_name != s_new->simple.cursor_name) ) { + ConvertCursor(new); + new_cursor = TRUE; + } + + if (new_cursor && XtIsRealized(new)) + XDefineCursor(XtDisplay(new), XtWindow(new), s_new->simple.cursor); + + return False; +} + + +static Bool ChangeSensitive(Widget w) +{ + if (XtIsRealized(w)) { + if (XtIsSensitive(w)) + if (w->core.border_pixmap != XtUnspecifiedPixmap) + XSetWindowBorderPixmap( XtDisplay(w), XtWindow(w), + w->core.border_pixmap ); + else + XSetWindowBorder( XtDisplay(w), XtWindow(w), + w->core.border_pixel ); + else { + if (((SimpleWidget)w)->simple.insensitive_border == None) + ((SimpleWidget)w)->simple.insensitive_border = + XmuCreateStippledPixmap(XtScreen(w), + w->core.border_pixel, + w->core.background_pixel, + w->core.depth); + XSetWindowBorderPixmap( XtDisplay(w), XtWindow(w), + ((SimpleWidget)w)-> + simple.insensitive_border ); + } + } + return False; +} + +/* + ====================================================================== + end copy from Simple.c + ====================================================================== +*/ + + +/* following function copied from XawInit.c */ +void XawInitializeWidgetSet () +{ + static int firsttime = 1; + + if (firsttime) { + firsttime = 0; + XtInitializeWidgetClass (vendorShellWidgetClass); + } +} + +static char defaultTranslations[] = + "<Btn1Down>: start() \n\ + <Btn1Motion>: move() \n\ + <Btn1Up>: notify() stop() \n\ + <Btn2Down>: abort() \n\ + :<Key>KP_Enter: set(rubberband,toggle) \n\ + <Key>space: page(+1p,+1p) \n\ + <Key>Delete: page(-1p,-1p) \n\ + :<Key>KP_Delete: page(-1p,-1p) \n\ + <Key>BackSpace: page(-1p,-1p) \n\ + <Key>Left: page(-.5p,+0) \n\ + :<Key>KP_Left: page(-.5p,+0) \n\ + <Key>Right: page(+.5p,+0) \n\ + :<Key>KP_Right: page(+.5p,+0) \n\ + <Key>Up: page(+0,-.5p) \n\ + :<Key>KP_Up: page(+0,-.5p) \n\ + <Key>Down: page(+0,+.5p) \n\ + :<Key>KP_Down: page(+0,+.5p) \n\ + <Key>Home: page(0,0) \n\ + :<Key>KP_Home: page(0,0)"; + + +static void ActionStart(), ActionStop(), ActionAbort(), ActionMove(); +static void ActionPage(), ActionNotify(), ActionSet(); + +static XtActionsRec actions[] = { + { "start", ActionStart }, /* start tmp graphics */ + { "stop", ActionStop }, /* stop tmp graphics */ + { "abort", ActionAbort }, /* punt */ + { "move", ActionMove }, /* move tmp graphics on Motion event */ + { "page", ActionPage }, /* page around usually from keyboard */ + { "notify", ActionNotify }, /* callback new position */ + { "set", ActionSet }, /* set various parameters */ +}; + + +/* + * resources for the panner + */ +static XtResource panner_resources[] = { +#define poff(field) XtOffsetOf(PannerRec, panner.field) + { XtNallowOff, XtCAllowOff, XtRBoolean, sizeof(Boolean), + poff(allow_off), XtRImmediate, (XtPointer) FALSE }, + { XtNresize, XtCResize, XtRBoolean, sizeof(Boolean), + poff(resize_to_pref), XtRImmediate, (XtPointer) TRUE }, + { XtNreportCallback, XtCReportCallback, XtRCallback, sizeof(XtPointer), + poff(report_callbacks), XtRCallback, (XtPointer) NULL }, + { XtNdefaultScale, XtCDefaultScale, XtRDimension, sizeof(Dimension), + poff(default_scale), XtRImmediate, (XtPointer) PANNER_DEFAULT_SCALE }, + { XtNrubberBand, XtCRubberBand, XtRBoolean, sizeof(Boolean), + poff(rubber_band), XtRImmediate, (XtPointer) FALSE }, + { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), + poff(foreground), XtRString, (XtPointer) XtDefaultBackground }, + { XtNinternalSpace, XtCInternalSpace, XtRDimension, sizeof(Dimension), + poff(internal_border), XtRImmediate, (XtPointer) 4 }, + { XtNlineWidth, XtCLineWidth, XtRDimension, sizeof(Dimension), + poff(line_width), XtRImmediate, (XtPointer) 0 }, + { XtNcanvasWidth, XtCCanvasWidth, XtRDimension, sizeof(Dimension), + poff(canvas_width), XtRImmediate, (XtPointer) 0 }, + { XtNcanvasHeight, XtCCanvasHeight, XtRDimension, sizeof(Dimension), + poff(canvas_height), XtRImmediate, (XtPointer) 0 }, + { XtNsliderX, XtCSliderX, XtRPosition, sizeof(Position), + poff(slider_x), XtRImmediate, (XtPointer) 0 }, + { XtNsliderY, XtCSliderY, XtRPosition, sizeof(Position), + poff(slider_y), XtRImmediate, (XtPointer) 0 }, + { XtNsliderWidth, XtCSliderWidth, XtRDimension, sizeof(Dimension), + poff(slider_width), XtRImmediate, (XtPointer) 0 }, + { XtNsliderHeight, XtCSliderHeight, XtRDimension, sizeof(Dimension), + poff(slider_height), XtRImmediate, (XtPointer) 0 }, + { XtNshadowColor, XtCShadowColor, XtRPixel, sizeof(Pixel), + poff(shadow_color), XtRString, (XtPointer) XtDefaultForeground }, + { XtNshadowThickness, XtCShadowThickness, XtRDimension, sizeof(Dimension), + poff(shadow_thickness), XtRImmediate, (XtPointer) 2 }, + { XtNbackgroundStipple, XtCBackgroundStipple, XtRString, sizeof(String), + poff(stipple_name), XtRImmediate, (XtPointer) NULL }, +#undef poff +}; + + +/* + * widget class methods used below + */ +static void Initialize(); /* create gc's */ +static void PannerRealize(); /* create window */ +static void Destroy(); /* clean up widget */ +static void Resize(); /* need to rescale ourselves */ +static void Redisplay(); /* draw ourselves */ +static Boolean PannerSetValues(); /* set all of the resources */ +static void SetValuesAlmost(); /* deal with failed setval geom req */ +static XtGeometryResult QueryGeometry(); /* say how big we would like to be */ + +PannerClassRec pannerClassRec = { + { /* core fields */ + /* superclass */ (WidgetClass) &simpleClassRec, + /* class_name */ "Panner", + /* widget_size */ sizeof(PannerRec), + /* class_initialize */ XawInitializeWidgetSet, + /* class_part_initialize */ NULL, + /* class_inited */ FALSE, + /* initialize */ Initialize, + /* initialize_hook */ NULL, + /* realize */ PannerRealize, + /* actions */ actions, + /* num_actions */ XtNumber(actions), + /* resources */ panner_resources, + /* num_resources */ XtNumber(panner_resources), + /* xrm_class */ NULLQUARK, + /* compress_motion */ TRUE, + /* compress_exposure */ TRUE, + /* compress_enterleave */ TRUE, + /* visible_interest */ FALSE, + /* destroy */ Destroy, + /* resize */ Resize, + /* expose */ Redisplay, + /* set_values */ PannerSetValues, + /* set_values_hook */ NULL, + /* set_values_almost */ SetValuesAlmost, + /* get_values_hook */ NULL, + /* accept_focus */ NULL, + /* version */ XtVersion, + /* callback_private */ NULL, + /* tm_table */ defaultTranslations, + /* query_geometry */ QueryGeometry, + /* display_accelerator */ XtInheritDisplayAccelerator, + /* extension */ NULL + }, + { /* simple fields */ + /* change_sensitive */ XtInheritChangeSensitive +#ifndef HAVE_OLD_XAW + , NULL +#endif + }, + { /* panner fields */ + /* ignore */ 0 + } +}; + +WidgetClass pannerWidgetClass = (WidgetClass) &pannerClassRec; + + +/***************************************************************************** + * * + * panner utility routines * + * * + *****************************************************************************/ + +static void reset_shadow_gc (pw) /* used when resources change */ + PannerWidget pw; +{ + XtGCMask valuemask = GCForeground; + XGCValues values; + unsigned long pixels[3]; + + if (pw->panner.shadow_gc) XtReleaseGC ((Widget) pw, pw->panner.shadow_gc); + + pixels[0] = pw->panner.foreground; + pixels[1] = pw->core.background_pixel; + pixels[2] = pw->panner.shadow_color; + if (!pw->panner.stipple_name && + !XmuDistinguishablePixels (XtDisplay (pw), pw->core.colormap, + pixels, 3) && + XmuDistinguishablePixels (XtDisplay (pw), pw->core.colormap, + pixels, 2)) + { + valuemask = GCTile | GCFillStyle; + values.fill_style = FillTiled; + values.tile = XmuCreateStippledPixmap(XtScreen((Widget)pw), + pw->panner.foreground, + pw->core.background_pixel, + pw->core.depth); + } + else + { + if (!pw->panner.line_width && + !XmuDistinguishablePixels (XtDisplay (pw), pw->core.colormap, + pixels, 2)) + pw->panner.line_width = 1; + valuemask = GCForeground; + values.foreground = pw->panner.shadow_color; + } + if (pw->panner.line_width > 0) { + values.line_width = pw->panner.line_width; + valuemask |= GCLineWidth; + } + + pw->panner.shadow_gc = XtGetGC ((Widget) pw, valuemask, &values); +} + +static void reset_slider_gc (pw) /* used when resources change */ + PannerWidget pw; +{ + XtGCMask valuemask = GCForeground; + XGCValues values; + + if (pw->panner.slider_gc) XtReleaseGC ((Widget) pw, pw->panner.slider_gc); + + values.foreground = pw->panner.foreground; + + pw->panner.slider_gc = XtGetGC ((Widget) pw, valuemask, &values); +} + +static void reset_xor_gc (pw) /* used when resources change */ + PannerWidget pw; +{ + if (pw->panner.xor_gc) XtReleaseGC ((Widget) pw, pw->panner.xor_gc); + + if (pw->panner.rubber_band) { + XtGCMask valuemask = (GCForeground | GCFunction); + XGCValues values; + Pixel tmp; + + tmp = ((pw->panner.foreground == pw->core.background_pixel) ? + pw->panner.shadow_color : pw->panner.foreground); + values.foreground = tmp ^ pw->core.background_pixel; + values.function = GXxor; + if (pw->panner.line_width > 0) { + valuemask |= GCLineWidth; + values.line_width = pw->panner.line_width; + } + pw->panner.xor_gc = XtGetGC ((Widget) pw, valuemask, &values); + } else { + pw->panner.xor_gc = NULL; + } +} + + +static void check_knob (pw, knob) + PannerWidget pw; + Boolean knob; +{ + Position pad = pw->panner.internal_border * 2; + Position maxx = (((Position) pw->core.width) - pad - + ((Position) pw->panner.knob_width)); + Position maxy = (((Position) pw->core.height) - pad - + ((Position) pw->panner.knob_height)); + Position *x = (knob ? &pw->panner.knob_x : &pw->panner.tmp.x); + Position *y = (knob ? &pw->panner.knob_y : &pw->panner.tmp.y); + + /* + * note that positions are already normalized (i.e. internal_border + * has been subtracted out) + */ + if (*x < 0) *x = 0; + if (*x > maxx) *x = maxx; + + if (*y < 0) *y = 0; + if (*y > maxy) *y = maxy; + + if (knob) { + pw->panner.slider_x = (Position) (((double) pw->panner.knob_x) / + pw->panner.haspect + 0.5); + pw->panner.slider_y = (Position) (((double) pw->panner.knob_y) / + pw->panner.vaspect + 0.5); + pw->panner.last_x = pw->panner.last_y = PANNER_OUTOFRANGE; + } +} + + +static void move_shadow (pw) + PannerWidget pw; +{ + if (pw->panner.shadow_thickness > 0) { + int lw = pw->panner.shadow_thickness + pw->panner.line_width * 2; + int pad = pw->panner.internal_border; + + if ((int)pw->panner.knob_height > lw && (int)pw->panner.knob_width > lw) { + XRectangle *r = pw->panner.shadow_rects; + r->x = (short) (pw->panner.knob_x + pad + pw->panner.knob_width); + r->y = (short) (pw->panner.knob_y + pad + lw); + r->width = pw->panner.shadow_thickness; + r->height = (unsigned short) (pw->panner.knob_height - lw); + r++; + r->x = (short) (pw->panner.knob_x + pad + lw); + r->y = (short) (pw->panner.knob_y + pad + pw->panner.knob_height); + r->width = (unsigned short) (pw->panner.knob_width - lw + + pw->panner.shadow_thickness); + r->height = pw->panner.shadow_thickness; + pw->panner.shadow_valid = TRUE; + return; + } + } + pw->panner.shadow_valid = FALSE; +} + +static void scale_knob (pw, location, size) /* set knob size and/or loc */ + PannerWidget pw; + Boolean location, size; +{ + if (location) { + pw->panner.knob_x = (Position) PANNER_HSCALE (pw, pw->panner.slider_x); + pw->panner.knob_y = (Position) PANNER_VSCALE (pw, pw->panner.slider_y); + } + if (size) { + Dimension width, height; + + if (pw->panner.slider_width < 1) { + pw->panner.slider_width = pw->panner.canvas_width; + } + if (pw->panner.slider_height < 1) { + pw->panner.slider_height = pw->panner.canvas_height; + } + width = Min (pw->panner.slider_width, pw->panner.canvas_width); + height = Min (pw->panner.slider_height, pw->panner.canvas_height); + + pw->panner.knob_width = (Dimension) PANNER_HSCALE (pw, width); + pw->panner.knob_height = (Dimension) PANNER_VSCALE (pw, height); + } + if (!pw->panner.allow_off) check_knob (pw, TRUE); + move_shadow (pw); +} + +static void rescale (pw) + PannerWidget pw; +{ + int hpad = pw->panner.internal_border * 2; + int vpad = hpad; + + if (pw->panner.canvas_width < 1) + pw->panner.canvas_width = pw->core.width; + if (pw->panner.canvas_height < 1) + pw->panner.canvas_height = pw->core.height; + + if ((int)pw->core.width <= hpad) hpad = 0; + if ((int)pw->core.height <= vpad) vpad = 0; + + pw->panner.haspect = ((double) pw->core.width - hpad) / + (double) pw->panner.canvas_width; + pw->panner.vaspect = ((double) pw->core.height - vpad) / + (double) pw->panner.canvas_height; + scale_knob (pw, TRUE, TRUE); +} + + +static void get_default_size (pw, wp, hp) + PannerWidget pw; + Dimension *wp, *hp; +{ + Dimension pad = pw->panner.internal_border * 2; + *wp = PANNER_DSCALE (pw, pw->panner.canvas_width) + pad; + *hp = PANNER_DSCALE (pw, pw->panner.canvas_height) + pad; +} + +static Boolean get_event_xy (pw, event, x, y) + PannerWidget pw; + XEvent *event; + int *x, *y; +{ + int pad = pw->panner.internal_border; + + switch (event->type) { + case ButtonPress: + case ButtonRelease: + *x = event->xbutton.x - pad; + *y = event->xbutton.y - pad; + return TRUE; + + case KeyPress: + case KeyRelease: + *x = event->xkey.x - pad; + *y = event->xkey.y - pad; + return TRUE; + + case EnterNotify: + case LeaveNotify: + *x = event->xcrossing.x - pad; + *y = event->xcrossing.y - pad; + return TRUE; + + case MotionNotify: + *x = event->xmotion.x - pad; + *y = event->xmotion.y - pad; + return TRUE; + } + + return FALSE; +} + +static int parse_page_string (s, pagesize, canvassize, relative) + char *s; + int pagesize, canvassize; + Boolean *relative; +{ + char *cp; + double val = 1.0; + Boolean rel = FALSE; + + /* + * syntax: spaces [+-] number spaces [pc\0] spaces + */ + + for (; isascii(*s) && isspace(*s); s++) ; /* skip white space */ + + if (*s == '+' || *s == '-') { /* deal with signs */ + rel = TRUE; + if (*s == '-') val = -1.0; + s++; + } + if (!*s) { /* if null then return nothing */ + *relative = TRUE; + return 0; + } + + /* skip over numbers */ + for (cp = s; isascii(*s) && (isdigit(*s) || *s == '.'); s++) ; + val *= atof(cp); + + /* skip blanks */ + for (; isascii(*s) && isspace(*s); s++) ; + + if (*s) { /* if units */ + switch (s[0]) { + case 'p': case 'P': + val *= (double) pagesize; + break; + + case 'c': case 'C': + val *= (double) canvassize; + break; + } + } + *relative = rel; + return ((int) val); +} + + +#define DRAW_TMP(pw) \ +{ \ + XDrawRectangle (XtDisplay(pw), XtWindow(pw), \ + pw->panner.xor_gc, \ + (int) (pw->panner.tmp.x + pw->panner.internal_border), \ + (int) (pw->panner.tmp.y + pw->panner.internal_border), \ + (unsigned int) (pw->panner.knob_width - 1), \ + (unsigned int) (pw->panner.knob_height - 1)); \ + pw->panner.tmp.showing = !pw->panner.tmp.showing; \ +} + +#define UNDRAW_TMP(pw) \ +{ \ + if (pw->panner.tmp.showing) DRAW_TMP(pw); \ +} + +#define BACKGROUND_STIPPLE(pw) \ + XmuLocatePixmapFile (pw->core.screen, pw->panner.stipple_name, \ + pw->panner.shadow_color, pw->core.background_pixel, \ + pw->core.depth, NULL, 0, NULL, NULL, NULL, NULL) + +#define PIXMAP_OKAY(pm) ((pm) != None && (pm) != XtUnspecifiedPixmap) + + +/***************************************************************************** + * * + * panner class methods * + * * + *****************************************************************************/ + + +/*ARGSUSED*/ +static void Initialize (greq, gnew, args, num_args) + Widget greq, gnew; + ArgList args; + Cardinal *num_args; +{ + PannerWidget req = (PannerWidget) greq, new = (PannerWidget) gnew; + Dimension defwidth, defheight; + + UNUSED(args); + UNUSED(num_args); + + if (req->panner.canvas_width < 1) new->panner.canvas_width = 1; + if (req->panner.canvas_height < 1) new->panner.canvas_height = 1; + if (req->panner.default_scale < 1) + new->panner.default_scale = PANNER_DEFAULT_SCALE; + + get_default_size (req, &defwidth, &defheight); + if (req->core.width < 1) new->core.width = defwidth; + if (req->core.height < 1) new->core.height = defheight; + + new->panner.shadow_gc = NULL; + reset_shadow_gc (new); /* shadowColor */ + new->panner.slider_gc = NULL; + reset_slider_gc (new); /* foreground */ + new->panner.xor_gc = NULL; + reset_xor_gc (new); /* foreground ^ background */ + + rescale (new); /* does a position check */ + new->panner.shadow_valid = FALSE; + new->panner.tmp.doing = FALSE; + new->panner.tmp.showing = FALSE; +} + + +static void PannerRealize (gw, valuemaskp, attr) + Widget gw; + XtValueMask *valuemaskp; + XSetWindowAttributes *attr; +{ + PannerWidget pw = (PannerWidget) gw; + Pixmap pm = XtUnspecifiedPixmap; + Boolean gotpm = FALSE; + + if (pw->core.background_pixmap == XtUnspecifiedPixmap) { + if (pw->panner.stipple_name) pm = BACKGROUND_STIPPLE (pw); + + if (PIXMAP_OKAY(pm)) { + attr->background_pixmap = pm; + *valuemaskp |= CWBackPixmap; + *valuemaskp &= ~CWBackPixel; + gotpm = TRUE; + } + } + (*pannerWidgetClass->core_class.superclass->core_class.realize) + (gw, valuemaskp, attr); + + if (gotpm) XFreePixmap (XtDisplay(gw), pm); +} + + +static void Destroy (gw) + Widget gw; +{ + PannerWidget pw = (PannerWidget) gw; + + XtReleaseGC (gw, pw->panner.shadow_gc); + XtReleaseGC (gw, pw->panner.slider_gc); + XtReleaseGC (gw, pw->panner.xor_gc); +} + + +static void Resize (gw) + Widget gw; +{ + rescale ((PannerWidget) gw); +} + + +/* ARGSUSED */ +static void Redisplay (Widget gw, XEvent *event, Region region) +{ + PannerWidget pw = (PannerWidget) gw; + Display *dpy = XtDisplay(gw); + Window w = XtWindow(gw); + int pad = pw->panner.internal_border; + Dimension lw = pw->panner.line_width; + Dimension extra = pw->panner.shadow_thickness + lw * 2; + int kx = pw->panner.knob_x + pad, ky = pw->panner.knob_y + pad; + + UNUSED(event); + UNUSED(region); + + pw->panner.tmp.showing = FALSE; + XClearArea (XtDisplay(pw), XtWindow(pw), + (int) pw->panner.last_x - ((int) lw) + pad, + (int) pw->panner.last_y - ((int) lw) + pad, + (unsigned int) (pw->panner.knob_width + extra), + (unsigned int) (pw->panner.knob_height + extra), + False); + pw->panner.last_x = pw->panner.knob_x; + pw->panner.last_y = pw->panner.knob_y; + + XFillRectangle (dpy, w, pw->panner.slider_gc, kx, ky, + pw->panner.knob_width - 1, pw->panner.knob_height - 1); + + if (lw) + { + XDrawRectangle (dpy, w, pw->panner.shadow_gc, kx, ky, + (unsigned int) (pw->panner.knob_width - 1), + (unsigned int) (pw->panner.knob_height - 1)); + } + + if (pw->panner.shadow_valid) { + XFillRectangles (dpy, w, pw->panner.shadow_gc, + pw->panner.shadow_rects, 2); + } + if (pw->panner.tmp.doing && pw->panner.rubber_band) DRAW_TMP (pw); +} + + +/* ARGSUSED */ +static Boolean PannerSetValues (Widget gcur, Widget greq, Widget gnew, ArgList args, Cardinal *num_args) +{ + PannerWidget cur = (PannerWidget) gcur; + PannerWidget new = (PannerWidget) gnew; + Boolean redisplay = FALSE; + + UNUSED(greq); + UNUSED(args); + UNUSED(num_args); + + if (cur->panner.foreground != new->panner.foreground) { + reset_slider_gc (new); + if (cur->panner.foreground != cur->core.background_pixel) + reset_xor_gc (new); + redisplay = TRUE; + } else if (cur->panner.line_width != new->panner.line_width || + cur->core.background_pixel != new->core.background_pixel) { + reset_xor_gc (new); + redisplay = TRUE; + } + if (cur->panner.shadow_color != new->panner.shadow_color) { + reset_shadow_gc (new); + if (cur->panner.foreground == cur->core.background_pixel) + reset_xor_gc (new); + redisplay = TRUE; + } + if (cur->panner.shadow_thickness != new->panner.shadow_thickness) { + move_shadow (new); + redisplay = TRUE; + } + if (cur->panner.rubber_band != new->panner.rubber_band) { + reset_xor_gc (new); + if (new->panner.tmp.doing) redisplay = TRUE; + } + + if ((cur->panner.stipple_name != new->panner.stipple_name || + cur->panner.shadow_color != new->panner.shadow_color || + cur->core.background_pixel != new->core.background_pixel) && + XtIsRealized(gnew)) { + Pixmap pm = (new->panner.stipple_name ? BACKGROUND_STIPPLE (new) + : XtUnspecifiedPixmap); + + if (PIXMAP_OKAY(pm)) { + XSetWindowBackgroundPixmap (XtDisplay (new), XtWindow(new), pm); + XFreePixmap (XtDisplay (new), pm); + } else { + XSetWindowBackground (XtDisplay (new), XtWindow(new), + new->core.background_pixel); + } + redisplay = TRUE; + } + + if (new->panner.resize_to_pref && + (cur->panner.canvas_width != new->panner.canvas_width || + cur->panner.canvas_height != new->panner.canvas_height || + cur->panner.resize_to_pref != new->panner.resize_to_pref)) { + get_default_size (new, &new->core.width, &new->core.height); + redisplay = TRUE; + } else if (cur->panner.canvas_width != new->panner.canvas_width || + cur->panner.canvas_height != new->panner.canvas_height || + cur->panner.internal_border != new->panner.internal_border) { + rescale (new); /* does a scale_knob as well */ + redisplay = TRUE; + } else { + Boolean loc = (cur->panner.slider_x != new->panner.slider_x || + cur->panner.slider_y != new->panner.slider_y); + Boolean siz = (cur->panner.slider_width != new->panner.slider_width || + cur->panner.slider_height != new->panner.slider_height); + if (loc || siz || + (cur->panner.allow_off != new->panner.allow_off && + new->panner.allow_off)) { + scale_knob (new, loc, siz); + redisplay = TRUE; + } + } + + return redisplay; +} + +static void SetValuesAlmost (gold, gnew, req, reply) + Widget gold, gnew; + XtWidgetGeometry *req, *reply; +{ + if (reply->request_mode == 0) { /* got turned down, so cope */ + Resize (gnew); + } + (*pannerWidgetClass->core_class.superclass->core_class.set_values_almost) + (gold, gnew, req, reply); +} + +static XtGeometryResult QueryGeometry (gw, intended, pref) + Widget gw; + XtWidgetGeometry *intended, *pref; +{ + PannerWidget pw = (PannerWidget) gw; + + pref->request_mode = (CWWidth | CWHeight); + get_default_size (pw, &pref->width, &pref->height); + + if (((intended->request_mode & (CWWidth | CWHeight)) == + (CWWidth | CWHeight)) && + intended->width == pref->width && + intended->height == pref->height) + return XtGeometryYes; + else if (pref->width == pw->core.width && pref->height == pw->core.height) + return XtGeometryNo; + else + return XtGeometryAlmost; +} + + +/***************************************************************************** + * * + * panner action procs * + * * + *****************************************************************************/ + +/* ARGSUSED */ +static void ActionStart (Widget gw, XEvent *event, String *params, Cardinal *num_params) +{ + PannerWidget pw = (PannerWidget) gw; + int x, y; + + UNUSED(params); + UNUSED(num_params); + + if (!get_event_xy (pw, event, &x, &y)) { + XBell (XtDisplay(gw), 0); /* should do error message */ + return; + } + + pw->panner.tmp.doing = TRUE; + pw->panner.tmp.startx = pw->panner.knob_x; + pw->panner.tmp.starty = pw->panner.knob_y; + pw->panner.tmp.dx = (((Position) x) - pw->panner.knob_x); + pw->panner.tmp.dy = (((Position) y) - pw->panner.knob_y); + pw->panner.tmp.x = pw->panner.knob_x; + pw->panner.tmp.y = pw->panner.knob_y; + if (pw->panner.rubber_band) DRAW_TMP (pw); +} + +/* ARGSUSED */ +static void ActionStop (Widget gw, XEvent *event, String *params, Cardinal *num_params) +{ + PannerWidget pw = (PannerWidget) gw; + int x, y; + + UNUSED(params); + UNUSED(num_params); + + if (get_event_xy (pw, event, &x, &y)) { + pw->panner.tmp.x = ((Position) x) - pw->panner.tmp.dx; + pw->panner.tmp.y = ((Position) y) - pw->panner.tmp.dy; + if (!pw->panner.allow_off) check_knob (pw, FALSE); + } + if (pw->panner.rubber_band) UNDRAW_TMP (pw); + pw->panner.tmp.doing = FALSE; +} + +/* ARGSUSED */ +static void ActionAbort (Widget gw, XEvent *event, String *params, Cardinal *num_params) +{ + PannerWidget pw = (PannerWidget) gw; + + UNUSED(params); + UNUSED(num_params); + + if (!pw->panner.tmp.doing) return; + + if (pw->panner.rubber_band) UNDRAW_TMP (pw); + + if (!pw->panner.rubber_band) { /* restore old position */ + pw->panner.tmp.x = pw->panner.tmp.startx; + pw->panner.tmp.y = pw->panner.tmp.starty; + ActionNotify (gw, event, params, num_params); + } + pw->panner.tmp.doing = FALSE; +} + + +/* ARGSUSED */ +static void ActionMove (Widget gw, XEvent *event, String *params, Cardinal *num_params) +{ + PannerWidget pw = (PannerWidget) gw; + int x, y; + + UNUSED(params); + UNUSED(num_params); + + if (!pw->panner.tmp.doing) return; + + if (!get_event_xy (pw, event, &x, &y)) { + XBell (XtDisplay(gw), 0); /* should do error message */ + return; + } + + if (pw->panner.rubber_band) UNDRAW_TMP (pw); + pw->panner.tmp.x = ((Position) x) - pw->panner.tmp.dx; + pw->panner.tmp.y = ((Position) y) - pw->panner.tmp.dy; + + if (!pw->panner.rubber_band) { + ActionNotify (gw, event, params, num_params); /* does a check */ + } else { + if (!pw->panner.allow_off) check_knob (pw, FALSE); + DRAW_TMP (pw); + } +} + + +/* ARGSUSED */ +static void ActionPage (Widget gw, XEvent *event, String *params, Cardinal *num_params) +{ + PannerWidget pw = (PannerWidget) gw; + Cardinal zero = 0; + Boolean isin = pw->panner.tmp.doing; + int x, y; + Boolean relx, rely; + int pad = pw->panner.internal_border * 2; + + UNUSED(event); + UNUSED(num_params); + + if (*num_params != 2) { + XBell (XtDisplay(gw), 0); + return; + } + + x = parse_page_string (params[0], (int) pw->panner.knob_width, + ((int) pw->core.width) - pad, &relx); + y = parse_page_string (params[1], (int) pw->panner.knob_height, + ((int) pw->core.height) - pad, &rely); + + if (relx) x += pw->panner.knob_x; + if (rely) y += pw->panner.knob_y; + + if (isin) { /* if in, then use move */ + XEvent ev; + ev.xbutton.type = ButtonPress; + ev.xbutton.x = x; + ev.xbutton.y = y; + ActionMove (gw, &ev, (String *) NULL, &zero); + } else { /* else just do it */ + pw->panner.tmp.doing = TRUE; + pw->panner.tmp.x = x; + pw->panner.tmp.y = y; + ActionNotify (gw, event, (String *) NULL, &zero); + pw->panner.tmp.doing = FALSE; + } +} + + +/* ARGSUSED */ +static void ActionNotify (Widget gw, XEvent *event, String *params, Cardinal *num_params) +{ + PannerWidget pw = (PannerWidget) gw; + + UNUSED(event); + UNUSED(params); + UNUSED(num_params); + + if (!pw->panner.tmp.doing) return; + + if (!pw->panner.allow_off) check_knob (pw, FALSE); + pw->panner.knob_x = pw->panner.tmp.x; + pw->panner.knob_y = pw->panner.tmp.y; + move_shadow (pw); + + pw->panner.slider_x = (Position) (((double) pw->panner.knob_x) / + pw->panner.haspect + 0.5); + pw->panner.slider_y = (Position) (((double) pw->panner.knob_y) / + pw->panner.vaspect + 0.5); + if (!pw->panner.allow_off) { + Position tmp; + + if (pw->panner.slider_x > + (tmp = (((Position) pw->panner.canvas_width) - + ((Position) pw->panner.slider_width)))) + pw->panner.slider_x = tmp; + if (pw->panner.slider_x < 0) pw->panner.slider_x = 0; + if (pw->panner.slider_y > + (tmp = (((Position) pw->panner.canvas_height) - + ((Position) pw->panner.slider_height)))) + pw->panner.slider_y = tmp; + if (pw->panner.slider_y < 0) pw->panner.slider_y = 0; + } + + if (pw->panner.last_x != pw->panner.knob_x || + pw->panner.last_y != pw->panner.knob_y) { + XawPannerReport rep; + + Redisplay (gw, (XEvent*) NULL, (Region) NULL); + rep.changed = (XawPRSliderX | XawPRSliderY); + rep.slider_x = pw->panner.slider_x; + rep.slider_y = pw->panner.slider_y; + rep.slider_width = pw->panner.slider_width; + rep.slider_height = pw->panner.slider_height; + rep.canvas_width = pw->panner.canvas_width; + rep.canvas_height = pw->panner.canvas_height; + XtCallCallbackList (gw, pw->panner.report_callbacks, (XtPointer) &rep); + } +} + +/* ARGSUSED */ +static void ActionSet (Widget gw, XEvent *event, String *params, Cardinal *num_params) +{ + PannerWidget pw = (PannerWidget) gw; + Boolean rb; + + UNUSED(event); + + if (*num_params < 2 || + XmuCompareISOLatin1 (params[0], "rubberband") != 0) { + XBell (XtDisplay(gw), 0); + return; + } + + if (XmuCompareISOLatin1 (params[1], "on") == 0) { + rb = TRUE; + } else if (XmuCompareISOLatin1 (params[1], "off") == 0) { + rb = FALSE; + } else if (XmuCompareISOLatin1 (params[1], "toggle") == 0) { + rb = !pw->panner.rubber_band; + } else { + XBell (XtDisplay(gw), 0); + return; + } + + if (rb != pw->panner.rubber_band) { + Arg args[1]; + XtSetArg (args[0], XtNrubberBand, rb); + XtSetValues (gw, args, (Cardinal) 1); + } +} + +#endif /* MOTIF && USE_PANNER */ diff --git a/Build/source/texk/xdvik/gui/Panner.h b/Build/source/texk/xdvik/gui/Panner.h new file mode 100644 index 00000000000..7ced64ebcad --- /dev/null +++ b/Build/source/texk/xdvik/gui/Panner.h @@ -0,0 +1,108 @@ +/* + * $XConsortium: Panner.h,v 1.22 94/04/17 20:12:32 rws Exp $ + * +Copyright (c) 1989 X Consortium + +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 +X CONSORTIUM 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 X Consortium 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 X Consortium. + * + * Author: Jim Fulton, MIT X Consortium + */ + +#ifndef _XawPanner_h +#define _XawPanner_H + +#include <X11/Xaw/Reports.h> + +/***************************************************************************** + * + * Panner Widget (subclass of Simple) + * + * This widget is used to represent navigation in a 2d coordinate system. + * + * Parameters: + * + * Name Class Type Default + * ---- ----- ---- ------- + * + * allowOff AllowOff Boolean FALSE + * background Background Pixel XtDefaultBackground + * backgroundStipple BackgroundStipple String NULL + * canvasWidth CanvasWidth Dimension 0 + * canvasHeight CanvasHeight Dimension 0 + * defaultScale DefaultScale Dimension 8 percent + * foreground Foreground Pixel XtDefaultBackground + * internalSpace InternalSpace Dimension 4 + * lineWidth LineWidth Dimension 0 + * reportCallback ReportCallback XtCallbackList NULL + * resize Resize Boolean TRUE + * rubberBand RubberBand Boolean FALSE + * shadowColor ShadowColor Pixel XtDefaultForeground + * shadowThickness ShadowThickness Dimension 2 + * sliderX SliderX Position 0 + * sliderY SliderY Position 0 + * sliderWidth SliderWidth Dimension 0 + * sliderHeight SliderHeight Dimension 0 + * + *****************************************************************************/ + + /* new instance and class names */ +#ifndef _XtStringDefs_h_ +#define XtNresize "resize" +#define XtCResize "Resize" +#endif + +#define XtNallowOff "allowOff" +#define XtCAllowOff "AllowOff" +#define XtNbackgroundStipple "backgroundStipple" +#define XtCBackgroundStipple "BackgroundStipple" +#define XtNdefaultScale "defaultScale" +#define XtCDefaultScale "DefaultScale" +#define XtNcanvasWidth "canvasWidth" +#define XtCCanvasWidth "CanvasWidth" +#define XtNcanvasHeight "canvasHeight" +#define XtCCanvasHeight "CanvasHeight" +#define XtNinternalSpace "internalSpace" +#define XtCInternalSpace "InternalSpace" +#define XtNlineWidth "lineWidth" +#define XtCLineWidth "LineWidth" +#define XtNrubberBand "rubberBand" +#define XtCRubberBand "RubberBand" +#define XtNshadowThickness "shadowThickness" +#define XtCShadowThickness "ShadowThickness" +#define XtNshadowColor "shadowColor" +#define XtCShadowColor "ShadowColor" +#define XtNsliderX "sliderX" +#define XtCSliderX "SliderX" +#define XtNsliderY "sliderY" +#define XtCSliderY "SliderY" +#define XtNsliderWidth "sliderWidth" +#define XtCSliderWidth "SliderWidth" +#define XtNsliderHeight "sliderHeight" +#define XtCSliderHeight "SliderHeight" + + /* external declarations */ +extern WidgetClass pannerWidgetClass; + +typedef struct _PannerClassRec *PannerWidgetClass; +typedef struct _PannerRec *PannerWidget; + +#endif /* _XawPanner_h */ diff --git a/Build/source/texk/xdvik/gui/PannerP.h b/Build/source/texk/xdvik/gui/PannerP.h new file mode 100644 index 00000000000..5c8fbc8cee5 --- /dev/null +++ b/Build/source/texk/xdvik/gui/PannerP.h @@ -0,0 +1,104 @@ +/* + * $XConsortium: PannerP.h,v 1.19 94/04/17 20:12:33 jim Exp $ + * +Copyright (c) 1989 X Consortium + +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 +X CONSORTIUM 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 X Consortium 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 X Consortium. + * + * Author: Jim Fulton, MIT X Consortium + */ + +#ifndef _XawPannerP_h +#define _XawPannerP_h + +#include "Panner.h" +#include <X11/Xaw/SimpleP.h> /* parent */ + +typedef struct { /* new fields in widget class */ + int dummy; +} PannerClassPart; + +typedef struct _PannerClassRec { /* Panner widget class */ + CoreClassPart core_class; + SimpleClassPart simple_class; + PannerClassPart panner_class; +} PannerClassRec; + +typedef struct { /* new fields in widget */ + /* resources... */ + XtCallbackList report_callbacks; /* callback/Callback */ + Boolean allow_off; /* allowOff/AllowOff */ + Boolean resize_to_pref; /* resizeToPreferred/Boolean */ + Pixel foreground; /* foreground/Foreground */ + Pixel shadow_color; /* shadowColor/ShadowColor */ + Dimension shadow_thickness; /* shadowThickness/ShadowThickness */ + Dimension default_scale; /* defaultScale/DefaultScale */ + Dimension line_width; /* lineWidth/LineWidth */ + Dimension canvas_width; /* canvasWidth/CanvasWidth */ + Dimension canvas_height; /* canvasHeight/CanvasHeight */ + Position slider_x; /* sliderX/SliderX */ + Position slider_y; /* sliderY/SliderY */ + Dimension slider_width; /* sliderWidth/SliderWidth */ + Dimension slider_height; /* sliderHeight/SliderHeight */ + Dimension internal_border; /* internalBorderWidth/BorderWidth */ + String stipple_name; /* backgroundStipple/BackgroundStipple */ + /* private data... */ + GC slider_gc; /* background of slider */ + GC shadow_gc; /* edge of slider and shadow */ + GC xor_gc; /* for doing XOR tmp graphics */ + double haspect, vaspect; /* aspect ratio of core to canvas */ + Boolean rubber_band; /* true = rubber band, false = move */ + struct { + Boolean doing; /* tmp graphics in progress */ + Boolean showing; /* true if tmp graphics displayed */ + Position startx, starty; /* initial position of slider */ + Position dx, dy; /* offset loc for tmp graphics */ + Position x, y; /* location for tmp graphics */ + } tmp; + Position knob_x, knob_y; /* real upper left of knob in canvas */ + Dimension knob_width, knob_height; /* real size of knob in canvas */ + Boolean shadow_valid; /* true if rects are valid */ + XRectangle shadow_rects[2]; /* location of shadows */ + Position last_x, last_y; /* previous location of knob */ +} PannerPart; + +typedef struct _PannerRec { + CorePart core; + SimplePart simple; + PannerPart panner; +} PannerRec; + +#define PANNER_HSCALE(pw,val) ((pw)->panner.haspect * ((double) (val))) +#define PANNER_VSCALE(pw,val) ((pw)->panner.vaspect * ((double) (val))) + +#define PANNER_DSCALE(pw,val) (Dimension) \ + ((((unsigned long) (val)) * (unsigned long) pw->panner.default_scale) / 100L) +#define PANNER_DEFAULT_SCALE 8 /* percent */ + +#define PANNER_OUTOFRANGE -30000 + +/* + * external declarations + */ +extern PannerClassRec pannerClassRec; + +#endif /* _XawPannerP_h */ 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 */ diff --git a/Build/source/texk/xdvik/gui/Tip.h b/Build/source/texk/xdvik/gui/Tip.h new file mode 100644 index 00000000000..b4cb8a957c0 --- /dev/null +++ b/Build/source/texk/xdvik/gui/Tip.h @@ -0,0 +1,62 @@ +/* + * 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. + * + */ + +#ifndef TIP_H_ +#define TIP_H_ + +#ifdef MOTIF +/* Tooltip widget handling. This has somewhat of a library character, + * but we don't want to go into the trouble of making a library for just + * one function. + */ +#include <X11/IntrinsicP.h> +#include <X11/StringDefs.h> + +/* New resource names + */ +#define XmNcancelWaitPeriod "cancelWaitPeriod" +#define XmNwaitPeriod "waitPeriod" + +/* New resource classes + */ +#define XmCCancelWaitPeriod "CancelWaitPeriod" +#define XmCWaitPeriod "WaitPeriod" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + + extern WidgetClass tipWidgetClass; + typedef struct _TipClassRec *TipWidgetClass; + typedef struct _TipRec *TipWidget; + + extern void TipAddWidget(Widget, Widget, const String); + extern void TipAppMainLoop(XtAppContext); + extern void TipAppHandle(XtAppContext, XEvent *); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* MOTIF */ +#endif /* TIP_H_ */ diff --git a/Build/source/texk/xdvik/gui/TipP.h b/Build/source/texk/xdvik/gui/TipP.h new file mode 100644 index 00000000000..5c2f7daea24 --- /dev/null +++ b/Build/source/texk/xdvik/gui/TipP.h @@ -0,0 +1,95 @@ +/* + * 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. + * + */ + +#ifndef TIPP_h +#define TIPP_h + +#include <X11/ShellP.h> + +#include "Tip.h" + +/* Doubly Linked List Processing. + */ +struct list_thread_str { + struct list_thread_str *forw; + struct list_thread_str *back; +}; +typedef struct list_thread_str ListThread; + +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; + +extern TipClassRec xmTipClassRec; + +/* 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 + tooltip is displayed - in milliseconds + */ + int cancelWaitPeriod; /* after help is popped-down - normal + wait period is cancelled for this + period - in milliseconds + */ + + /* -------- private state --------- */ + ListThread widget_list; /* list of widgets we are liteClue-ing */ + 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 */ + XtIntervalId pid; /* holds pooler id for insensitive widgets */ + Widget parent; + 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; + +#endif diff --git a/Build/source/texk/xdvik/gui/depend.mk b/Build/source/texk/xdvik/gui/depend.mk new file mode 100644 index 00000000000..2f62ce49923 --- /dev/null +++ b/Build/source/texk/xdvik/gui/depend.mk @@ -0,0 +1,528 @@ +help-window.o: help-window.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + message-window.h ../util.h \ + $(kpathsea_srcdir)/c-stat.h $(kpathsea_srcdir)/hash.h $(kpathsea_srcdir)/tex-file.h \ + ../events.h ../x_util.h ../string-utils.h ../my-vsnprintf.h \ + topic-window.h help-window.h +mag.o: mag.c \ + ../xdvi-config.h ../c-auto.h \ + ../c-openmx.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/systypes.h \ + ../xdvi.h ../xdvi-debug.h ../version.h \ + $(kpathsea_dir)/c-auto.h $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h $(kpathsea_srcdir)/types.h \ + $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../events.h ../dvi-draw.h ../dvi-init.h \ + statusline.h ../hypertex.h mag.h xm_toolbar.h xm_menu.h menu.h \ + xaw_menu.h ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + pagesel.h +menu.o: menu.c ../xdvi-config.h ../c-auto.h ../xdvi.h ../xdvi-debug.h \ + ../version.h $(kpathsea_dir)/c-auto.h $(kpathsea_srcdir)/config.h \ + $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../events.h menu.h xm_menu.h xaw_menu.h \ + ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h +message-window.o: message-window.c \ + ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../string-utils.h ../my-vsnprintf.h \ + ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../events.h ../x_util.h message-window.h +pagesel.o: pagesel.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + xm_toolbar.h xm_menu.h menu.h ../events.h \ + xaw_menu.h ../x_util.h \ + message-window.h pagesel.h ../util.h \ + $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../string-utils.h ../my-vsnprintf.h \ + ../dvi-init.h statusline.h print-dialog.h ../search-internal.h \ + search-dialog.h ../dvisel.h ../print-internal.h ../pagehist.h +Panner.o: Panner.c ../xdvi-config.h ../c-auto.h ../xdvi.h ../xdvi-debug.h \ + ../version.h $(kpathsea_dir)/c-auto.h $(kpathsea_srcdir)/config.h \ + $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../string-utils.h ../my-vsnprintf.h +print-dialog.o: print-dialog.c \ + ../xdvi-config.h ../c-auto.h ../xdvi.h ../xdvi-debug.h ../version.h \ + $(kpathsea_dir)/c-auto.h $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h \ + $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../my-vsnprintf.h \ + print-dialog.h print-log.h ../print-internal.h ../dvi-init.h \ + ../events.h ../string-utils.h ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../x_util.h message-window.h pagesel.h \ + ../my-snprintf.h sfSelFile.h ../dvisel.h xlwradio.h statusline.h \ + search-dialog.h ../search-internal.h ../special.h +print-log.o: print-log.c \ + ../xdvi-config.h ../c-auto.h ../xdvi.h ../xdvi-debug.h ../version.h \ + $(kpathsea_dir)/c-auto.h $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h \ + $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + print-dialog.h print-log.h ../util.h \ + $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../events.h ../x_util.h xlwradio.h +search-dialog.o: search-dialog.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../events.h ../dvi-init.h search-dialog.h \ + ../search-internal.h ../dvisel.h ../print-internal.h print-dialog.h \ + xlwradio.h statusline.h ../string-utils.h ../my-vsnprintf.h ../util.h \ + $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + message-window.h ../x_util.h +selection.o: selection.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../events.h ../util.h \ + $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../encodings.h message-window.h selection.h \ + statusline.h +sfDir.o: sfDir.c ../xdvi-config.h ../c-auto.h sfDir.h \ + $(kpathsea_dir)/c-auto.h $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-stat.h +sfDraw.o: sfDraw.c ../xdvi-config.h ../c-auto.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-stat.h \ + ../xdvi.h ../xdvi-debug.h ../version.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + sfDraw.h sfDir.h sfPath.h +sfPath.o: sfPath.c ../xdvi-config.h ../c-auto.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-stat.h \ + ../xdvi.h ../xdvi-debug.h ../version.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + sfDir.h sfDraw.h sfPath.h sfSelFile.h +sfSelFile.o: sfSelFile.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../dvi-init.h message-window.h \ + $(kpathsea_srcdir)/c-stat.h \ + sfDir.h sfPath.h sfDraw.h sfSelFile.h \ + ../util.h $(kpathsea_srcdir)/hash.h $(kpathsea_srcdir)/tex-file.h \ + ../events.h $(kpathsea_srcdir)/c-fopen.h +statusline.o: statusline.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + statusline.h xm_menu.h menu.h ../events.h \ + xaw_menu.h ../x_util.h ../pagehist.h ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../my-vsnprintf.h +Tip.o: Tip.c ../xdvi-config.h ../c-auto.h ../xdvi.h ../xdvi-debug.h \ + ../version.h $(kpathsea_dir)/c-auto.h $(kpathsea_srcdir)/config.h \ + $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + Tip.h \ + ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../events.h +topic-window.o: topic-window.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + topic-window.h ../string-utils.h ../my-vsnprintf.h ../util.h \ + $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../events.h ../x_util.h +xaw_bitmaps.o: xaw_bitmaps.c xaw_bitmaps.h +xaw_menu.o: xaw_menu.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../c-openmx.h ../events.h ../dvi-draw.h \ + ../dvi-init.h statusline.h pagesel.h ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../x_util.h xaw_menu.h message-window.h \ + ../my-snprintf.h ../filehist.h +xicon.o: xicon.c ../xdvi-config.h ../c-auto.h ../xdvi.h ../xdvi-debug.h \ + ../version.h $(kpathsea_dir)/c-auto.h $(kpathsea_srcdir)/config.h \ + $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + xicon.h ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../events.h \ + ../xdvi.icon ../pixmaps/xdvi32x32.xpm ../pixmaps/xdvi16x16.xpm \ + ../pixmaps/xdvi48x48.xpm +xlwradio.o: xlwradio.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../events.h xaw_bitmaps.h xlwradioP.h \ + xlwradio.h +xm_colorsel.o: xm_colorsel.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + Tip.h \ + ../x_util.h xm_colorsel.h xm_prefsP.h \ + xm_prefs.h ../events.h ../dvi-draw.h ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + topic-window.h ../search-internal.h \ + search-dialog.h ../dvisel.h ../dvi-init.h ../print-internal.h \ + print-dialog.h +xm_filesel.o: xm_filesel.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + sfSelFile.h ../dvi.h ../string-utils.h \ + ../my-vsnprintf.h ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../events.h message-window.h ../dvi-init.h +xm_menu.o: xm_menu.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + xm_menu.h menu.h ../events.h xaw_menu.h \ + xm_toolbar.h ../my-snprintf.h ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../x_util.h statusline.h ../pagehist.h \ + ../dvi-init.h ../filehist.h ../c-openmx.h message-window.h +xm_prefs_appearance.o: xm_prefs_appearance.c ../xdvi-config.h ../c-auto.h \ + ../xdvi.h ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../x_util.h ../my-snprintf.h xm_colorsel.h \ + topic-window.h message-window.h ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../events.h xm_toolbar.h xm_menu.h menu.h \ + xaw_menu.h statusline.h pagesel.h xm_prefsP.h xm_prefs_appearance.h +xm_prefs.o: xm_prefs.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../x_util.h xm_colorsel.h topic-window.h \ + message-window.h ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../events.h xm_prefsP.h xm_prefs.h \ + xm_prefs_appearance.h xm_prefs_fonts.h xm_prefs_helpers.h \ + xm_prefs_page.h xm_prefs_scroll.h ../dvi-draw.h ../search-internal.h \ + search-dialog.h ../dvisel.h ../dvi-init.h ../print-internal.h \ + print-dialog.h statusline.h xm_toolbar.h xm_menu.h menu.h xaw_menu.h \ + pagesel.h +xm_prefs_fonts.o: xm_prefs_fonts.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../x_util.h xm_colorsel.h topic-window.h \ + ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../events.h ../hypertex.h ../my-snprintf.h \ + xm_prefsP.h xm_prefs_fonts.h +xm_prefs_helpers.o: xm_prefs_helpers.c ../xdvi-config.h ../c-auto.h \ + ../xdvi.h ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../x_util.h xm_colorsel.h topic-window.h \ + ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../events.h ../string-utils.h \ + ../my-vsnprintf.h message-window.h xm_menu.h menu.h xaw_menu.h \ + xm_prefsP.h xm_prefs.h xm_prefs_helpers.h +xm_prefs_page.o: xm_prefs_page.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../x_util.h xm_colorsel.h topic-window.h \ + ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../events.h statusline.h xm_prefsP.h \ + xm_prefs_page.h ../my-snprintf.h +xm_prefs_scroll.o: xm_prefs_scroll.c ../xdvi-config.h ../c-auto.h \ + ../xdvi.h ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + ../x_util.h xm_colorsel.h topic-window.h \ + ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + $(kpathsea_srcdir)/tex-file.h \ + ../events.h ../dvi-init.h statusline.h \ + xm_prefsP.h xm_prefs_scroll.h ../my-snprintf.h +xm_toolbar.o: xm_toolbar.c ../xdvi-config.h ../c-auto.h ../xdvi.h \ + ../xdvi-debug.h ../version.h $(kpathsea_dir)/c-auto.h \ + $(kpathsea_srcdir)/config.h $(kpathsea_srcdir)/c-std.h \ + $(kpathsea_srcdir)/c-unistd.h $(kpathsea_srcdir)/systypes.h \ + $(kpathsea_srcdir)/c-memstr.h \ + $(kpathsea_srcdir)/c-errno.h \ + $(kpathsea_srcdir)/c-minmax.h $(kpathsea_srcdir)/c-limits.h \ + $(kpathsea_srcdir)/c-proto.h $(kpathsea_srcdir)/debug.h \ + $(kpathsea_srcdir)/types.h $(kpathsea_srcdir)/lib.h $(kpathsea_srcdir)/progname.h \ + $(kpathsea_srcdir)/c-pathmx.h \ + $(kpathsea_srcdir)/c-dir.h \ + $(kpathsea_srcdir)/c-vararg.h \ + pagesel.h help-window.h message-window.h \ + $(kpathsea_srcdir)/tex-file.h $(kpathsea_srcdir)/expand.h statusline.h \ + ../dvi-init.h ../events.h ../dvi-draw.h xm_menu.h menu.h xaw_menu.h \ + xm_toolbar.h ../util.h $(kpathsea_srcdir)/c-stat.h \ + $(kpathsea_srcdir)/hash.h \ + ../x_util.h Tip.h \ + ../c-openmx.h ../pixmaps/toolbar.xpm diff --git a/Build/source/texk/xdvik/gui/help-window.c b/Build/source/texk/xdvik/gui/help-window.c new file mode 100644 index 00000000000..8f7e6612a0e --- /dev/null +++ b/Build/source/texk/xdvik/gui/help-window.c @@ -0,0 +1,1094 @@ +/* + * Copyright (c) 2004 the xdvik development team + * + * 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. + * + */ + +/* + * Help window for xdvik, using the `topics' window framework. + */ + +#include "xdvi-config.h" +#include "xdvi.h" +#include "version.h" + +#include <X11/Xatom.h> +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> + +#if MOTIF +# include <Xm/Xm.h> +# include <Xm/Form.h> +# include <Xm/Frame.h> +# include <Xm/Text.h> +# include <Xm/Protocols.h> +#else /* MOTIF */ +# include <X11/Xaw/Paned.h> +# include <X11/Xaw/Form.h> +# include <X11/Xaw/AsciiText.h> +#endif /* MOTIF */ + +#include <sys/stat.h> +#include <stdio.h> +#include <unistd.h> + +#include "message-window.h" +#include "util.h" +#include "x_util.h" +#include "string-utils.h" +#include "topic-window.h" +#include "help-window.h" + +/* missing features that will be listed in the help window */ +#if !XDVI_XT_TIMER_HACK +#define HAVE_MISSING_FEATURES 1 +#endif + +#if !HAVE_REGEX_H +#define HAVE_MISSING_FEATURES 1 +#endif + +/* + * The number of help topics - must larger or equal to actual number of items, + * also keep in sync with elements resource.help_* !!! + */ +#define NUM_HELP_TOPICS 16 + +/* + * helper routines + */ + +static Widget +create_help_text(Widget parent, const char *name, const char *value) +{ + Widget text; +#if MOTIF + Arg args[20]; + int n = 0; + + XtSetArg(args[n], XmNeditable, False); n++; + XtSetArg(args[n], XmNcursorPositionVisible, False); n++; + XtSetArg(args[n], XmNvalue, value); n++; + XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++; + XtSetArg(args[n], XmNwordWrap, True); n++; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; +/* XtSetArg(args[n], XmNtopWidget, top_widget); n++; */ +/* XtSetArg(args[n], XmNtopOffset, 10); n++; */ + XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); n++; + XtSetArg(args[n], XmNscrollBarDisplayPolicy, XmAS_NEEDED); n++; + + text = XmCreateScrolledText(parent, (char *)name, args, n); + + XtManageChild(text); +#else /* MOTIF */ + text = XtVaCreateManagedWidget(name, asciiTextWidgetClass, parent, + /* XtNfromVert, top_widget, */ + /* XtNvertDistance, 10, */ + XtNstring, value, + XtNheight, 400, + XtNwidth, 500, + /* resizing of pane by user isn't needed */ + XtNshowGrip, False, + XtNscrollVertical, XawtextScrollAlways, + XtNscrollHorizontal, XawtextScrollNever, + XtNeditType, XawtextRead, + XtNleftMargin, 5, + NULL); + XawTextDisplayCaret(text, False); +#endif /* MOTIF */ + return text; +} + + +static void +get_title_and_summary(const char *str, int len, char **title, char **summary) +{ + char *tmp = xmalloc(len + 1); + char *ptr; + + memcpy(tmp, str, len); + tmp[len] = '\0'; + + if ((ptr = strchr(tmp, '\t')) == NULL) { + XDVI_WARNING((stderr, "Help resource label `%s' doesn't contain a tab character - ignoring it.", tmp)); + *title = tmp; + *summary = NULL; + } + else { + *ptr++ = '\0'; + *title = tmp; + *summary = ptr; + } + + TRACE_GUI((stderr, "Title, Summary: |%s|%s|", *title, *summary)); +} + +static void +init_item(const char *resource, const char **resource_default, + struct topic_info *info, size_t idx, Dimension *width) +{ + const char *ptr = NULL; + char *widget_text = NULL; + Widget help_form; + Widget text; + struct topic_item *item = &(info->items[idx]); + char *translation_str = get_string_va("#override \n" + "<Key>q:close-topic-window(%p)\n" +#ifdef MOTIF + "<Key>osfCancel:close-topic-window(%p)\n" +#else + "<Key>Escape:close-topic-window(%p)\n" +#endif + "<Key>Return:close-topic-window(%p)", + info, info, info); + + if (resource != NULL) { + if ((ptr = strchr(resource, '\n')) == NULL) { + XDVI_WARNING((stderr, "Help resource text `%s' doesn't contain a newline character.", resource)); + ptr = resource; + } + else + ptr++; + get_title_and_summary(resource, ptr - resource - 1, + &(item->topic), &(item->title)); + } + else { /* resource not set; copy resource_default into malloc()ed widget_text: */ + size_t size = 0, alloc_len = 0, offset; + const size_t alloc_step = 1024; + int i; + + for (i = 0; resource_default[i] != NULL; i++) { + if (i == 0) { /* special case */ + get_title_and_summary(resource_default[i], strlen(resource_default[i]) - 1, + &(item->topic), &(item->title)); + } + else { + offset = size; + size += strlen(resource_default[i]); + /* + * allocate chunks of `alloc_step' to avoid frequent calls to malloc. + * `alloc_len' is always 1 more than `size', for the terminating NULL character. + */ + while (size + 1 > alloc_len) { + alloc_len += alloc_step; + widget_text = xrealloc(widget_text, alloc_len); + } + memcpy(widget_text + offset, resource_default[i], size - offset); + } + } + /* null-terminate string */ + widget_text[size] = '\0'; + } + + help_form = XtVaCreateManagedWidget("help_form", +#if MOTIF + xmFormWidgetClass, info->right_form, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, +#else + formWidgetClass, info->right_form, + XtNborderWidth, 0, + XtNdefaultDistance, 0, +#endif + NULL); + item->widget = help_form; + if (ptr != NULL) { + text = create_help_text(help_form, "help_text", ptr); + } + else { + text = create_help_text(help_form, "help_text", widget_text); + free(widget_text); + } + XtOverrideTranslations(text, XtParseTranslationTable(translation_str)); + free(translation_str); +#if !MOTIF + { + Dimension w; + XtVaGetValues(text, XtNwidth, &w, NULL); + if (w > *width) + *width = w; + } +#else + UNUSED(width); +#endif +} + +static void +initialize_items(struct topic_info *info) +{ + size_t k; + Dimension width; + + /* + * Define fallbacks: default_xyz is used as fallback text if + * X resource xyz isn't specified. + * + * We use arrays of strings rather than simple strings because of C's + * limitations on maximum string length; but since the resource needs to + * be a simple `char *', these have to be copied into larger buffers + * later on (which is a bit wasteful to space). OTOH, splitting the + * strings into smaller pieces would make them hard to deal with as + * X resources. They are defined as static so that they are initialized + * only once. + * + * Last elem of each array is NULL for ease of looping through it. + * + * Advantages of this method vs. putting the help texts into a file: + * - couldn't use #ifdef's as easily + * - would need to invent our own file format + * - file searching is more error-prone (needs to be installed etc.) + */ + + static const char *default_help_general[] = { + "Introduction\tAbout this version of xdvi\n", + "This is xdvik, version ", + XDVI_TERSE_VERSION_INFO + ".\nThe program's homepage is located at:\n", + "http://sourceforge.net/projects/xdvi\n", + "where you can find updates, report bugs and submit feature requests.\n", + "\n", + "\n", + "Xdvi has many command-line options, too numerous to be listed here;\n", + "see the man page for a full description.\n", + "\n", + "The most important key bindings are listed in the help sections shown\n", + "in the left window.\n", + "\n", + "Note: Unless a key binding also has an uppercase version,\n", + "all bindings are case-insensitive.\n\n", + "\n", + "The major parts of Xdvik are licensed under the X Consortium license.\n", + "Parts (encoding.c) are licensed under the GNU General Public License.\n", + "Xdvik uses the following libraries:\n", + "- The kpathsea library, licensed in part under the GNU General Public\n", + " License, in part under the GNU Library General Public License.\n", + "- t1lib, licensed in parts under the GNU Library General Public License,\n", + " in parts under the X Consortium license.\n", + "There is NO WARRANTY of anything.\n", + "\n", + "Built on ", + __DATE__, + " using these configure options:\n", +#if MOTIF + "- Motif toolkit (", + XmVERSION_STRING, + ")\n", +#else + "- Athena toolkit\n", +#endif +#ifdef A4 + "- paper: a4, units cm\n", +#else + "- paper: letter, units inches\n", +#endif +#ifdef GREY + "- anti-aliasing (grey) enabled\n", +#endif +#ifdef T1LIB + "- T1lib (direct rendering of PS fonts) enabled\n", +#endif +#if HAVE_ICONV_H + "- Iconv support compiled in\n", +#if USE_LANGINFO + "- Langinfo support compiled in\n", +#else + "- Langinfo support not compiled in\n", +#endif +#else + "- Iconv/langinfo support not compiled in\n", +#endif +#ifdef TEXXET + "- left-to-right typesetting (TeXXeT) support enabled\n", +#endif +#ifdef USE_GF + "- gf file support enabled\n", +#endif +#if HAVE_MISSING_FEATURES + "\n", + "Features not available on this platform:\n", +#if !XDVI_XT_TIMER_HACK + "- Could not redefine XtAppAddTimeOut(); some widgets may\n", + " not be updated until the mouse is moved.\n", +#endif +#if !HAVE_REGEX_H + "- regex.h header not available, regular expression support\n", + " in string search is disabled.\n", +#endif +#endif /* HAVE_MISSING_FEATURES */ + NULL + }; + + static const char *default_help_hypertex[] = { + "Hyperlinks\tNavigating links\n", + "Whenever the mouse is positioned on a link, the cursor changes\n", + "to a `hand' shape and the target of the link is displayed\n", + "in the statusline at the bottom of the window.\n", + "\n", + "The following keybindings are pre-configured:\n", + "\n", + "Mouse-1\n", + " Follow the link at the cursor position.\n", + " If the link target is not a DVI file, try to launch\n", + " an application to view the file.\n", + "Mouse-2\n", + " Open a new xdvi window displaying the link\n", + " at the cursor position if the link is a DVI file;\n", + " else, try to launch an application to view the file.\n", + "B", +#if MOTIF + ", toolbar button 9\n", +#else + "\n", +#endif + " Go back to the previous hyperlink in the history.\n", + "F", +#if MOTIF + ", toolbar button 10\n", +#else + "\n", +#endif + " Go forward to the next hyperlink in the history.\n", + "\n", + "By default, the hyperlinks are displayed in the colors \n", + "`linkColor' and `visitedLinkColor' (for visited links) and \n", + "underlined in the same colors. This can be customized \n", + "by setting the resource or command-line option `linkstyle' \n", + "to a value between 0 and 3, which have the following meaning:\n", + " 0: no highlighting of links,\n", + " 1: underline links,\n", + " 2: color links,\n", + " 3: color and underline links.\n\n", + NULL + }; + + static const char *default_help_othercommands[] = { + "Other Commands\tMiscellaneous other commands\n", + "Ctrl-f\n", + " Opens a dialog window to search for a text string\n", + " in the DVI file.\n", + "\n", + "Ctrl-g\n", + " Search for the next string match.\n", + "\n", + "Ctrl-l\n", + " Toggles fullscreen mode (which may not work with your\n", + " window manager/desktop).\n", + "\n", + "Ctrl-o", +#if MOTIF + ", toolbar button 1\n", +#else + "\n", +#endif + " Opens a popup window to select another DVI file.\n", + " With a prefix argument `n', the `n'th file from the file history\n", + " is opened instead.\n", + "\n", + "Ctrl-p", +#if MOTIF + ", toolbar button 11\n", +#else + "\n", +#endif + " Opens a popup window for printing the DVI file, or parts of it.\n", + "\n", + "Ctrl-r or Clear\n", + " Redisplays the current page.\n", + "\n", + "Ctrl-s\n", + " Opens a popup window for saving the DVI file, or parts of it.\n", + "\n", + "G\n", + " Toggles the use of greyscale anti-aliasing for\n", + " displaying shrunken bitmaps. In addition, the key\n", + " sequences `0G' and `1G' clear and set this flag,\n", + " respectively. See also the -nogrey option.\n", + "\n", + "k\n", + " Normally when xdvi switches pages, it moves to the home\n", + " position as well. The `k' keystroke toggles a `keep-\n", + " position' flag which, when set, will keep the same\n", + " position when moving between pages. Also `0k' and `1k'\n", + " clear and set this flag, respectively. See also the\n", + " -keep option.\n", + "\n", + "M\n", + " Sets the margins so that the point currently under the\n", + " cursor is the upper left-hand corner of the text in the\n", + " page. Note that this command itself does not move the\n", + " image at all. For details on how the margins are used,\n", + " see the -margins option.\n", + "\n", + "P\n", + " ``This is page number n.'' This can be used to make\n", + " the `g' keystroke refer to a different page number.\n", + " (See also `Options->Use TeX Page Numbers' and the\n", + " `T' keystroke).\n", + "\n", + "R", +#if MOTIF + ", toolbar button 2\n", +#else + "\n", +#endif + " Forces the DVI file to be reread.\n", + "\n", + "s\n", + " Changes the shrink factor to the given number.\n", + " If no number is given, the smallest factor that makes the\n", + " entire page fit in the window will be used. (Margins\n", + " are ignored in this computation.)\n", + "\n", + "S\n", + " Sets the density factor to be used when shrinking\n", + " bitmaps. This should be a number between 0 and 100;\n", + " higher numbers produce lighter characters.\n", + "\n", + "t\n", + " Switches to the next unit in a sorted list of TeX dimension\n", + " units for the popup magnifier ruler and `Ruler mode' (see the\n", + " section `Modes').\n" + "\n", + "V\n", + " Toggles Ghostscript anti-aliasing. Also `0V' and `1V' clear\n", + " and enables this mode, respectively. See also the the\n", + " -gsalpha option.\n", + "\n", + "\n", + "v\n", + " Toggles between several modes of displaying postscript specials:\n", + " Display specials, display specials with their bounding box\n", + " (if available), and display bounding boxes only (if available).\n", + " The prefix arguments 1, 2 and 0 also allow you to select one of\n" + " these states directly.\n", + "\n", + "x\n", + " Toggles expert mode (in which ", +#if MOTIF + "the menu bar, the toolbar\n", +#else + "the menu buttons,\n", +#endif + " the page list and the statusline do not appear).\n", + " `1x' toggles the display of the statusline at the bottom of the window.\n", + " `2x' toggles the scrollbars,\n", +#if MOTIF + " `3x' toggles the page list,\n", + " `4x' toggles the toolbar,\n", + " `5x' toggles the menu bar.\n", +#else + " `3x' toggles the page list and menu buttons.\n", +#endif + "\n", + "Ctrl-+", +#if MOTIF + ", toolbar button 7\n", +#else + "\n", +#endif + " Makes the display of the page larger (zooms in).\n", + "\n", + "Ctrl-+", +#if MOTIF + ", toolbar button 8\n", +#else + "\n", +#endif + " Makes the display of the page smaller (zooms out).\n", + "\n", + "Alt-Ctrl-+", +#if MOTIF + ", toolbar button 16\n", +#else + "\n", +#endif + " Makes the fonts darker (by adding to the gamma value).\n", + "\n", + "Alt-Ctrl--", +#if MOTIF + ", toolbar button 17\n", +#else + "\n", +#endif + " Makes the fonts lighter (by substracting from the gamma\n", + " value).\n", + "\n", + NULL + }; + + static const char *default_help_marking[] = { + "Printing and Saving\tMarking, printing and saving pages\n", + "The `Save' and `Print' dialogs allow you to save or print all,\n", + "pages, a range of pages, or all marked pages from a DVI file.\n", + "\n", + "Note that the page numbers for the `From ... to ...' range\n", + "refer to physical pages, not TeX pages (compare the option\n", + "`Use TeX Page Numbers' and the `T' keystroke).\n", + "\n", + "To mark a page or a range of pages, use one of the folllowing\n", + "methods:\n", + "- Click on the page in the page list with Mouse Button 2 to mark\n", + " a single page, or drag the mouse while holding down Button 2\n", + " to mark a range of pages.\n", + "- Use one of the following key combinations:\n", + " m: toggle the mark of the current page,\n", + " 1m toggle the marks of all odd pages,\n", + " 2m toggle the marks of all even pages,\n", + " 0m: unmark all pages,\n", + " Ctrl-n: toggle mark of current page, then move one page forward,\n", + " Ctrl-u: move one page back, then toggle mark of that page.\n", +#if MOTIF + "- Use one of the toobar buttons 12 to 15 to toggle the marks\n", + " of odd pages, toggle the marks of even pages, toggle the mark\n", + " of the current page, or unmark all pages, respectively.\n", +#endif + "\n", + "If the X resource or command line option `paper' has been used,\n", + "its value is inserted into the `Dvips Options' field of the printing\n", + "dialog so that the appropriate options can be passed to dvips.\n", + "This doesn't happen if the paper size has been specified explicitly\n", + "in the DVI file (e.g. by using the LaTeX `geometry' package).\n", + "Note that not all of the paper options used by xdvi\n", + "may be understood by dvips; dvips will ignore the option\n", + "in that case, and will use its default paper setting.\n", + NULL + }; + + static const char *default_help_pagemotion[] = { + "Page Motion\tMoving around in the document\n", + "\n", + "[\n", + " Moves back one item in the page history. With a prefix\n", + " argument n, move back n history items.\n" + "\n", + "]\n", + " Moves forward one item in the page history. With a prefix\n", + " argument n, move forward n history items.\n" + "\n", + "Ctr-[\n", + " Deletes current item in the page history and move\n", + " to the history item before the deleted one. With a prefix\n", + " argument n, delete n previous history items.\n", + "\n", + "Ctr-]\n", + " Deletes current item in the page history and move\n", + " to the history item after the deleted one. With a prefix\n", + " argument n, delete n next history items.\n", + "\n", + "n or f or Space or Return or LineFeed or PgDn", +#if MOTIF + ", toolbar button 5\n", +#else + "\n", +#endif + " Moves to the next page (or to the nth next page if a\n", + " number is given).\n", + "\n", + "p or b or Ctrl-h or BackSpace or Del or PgUp", +#if MOTIF + ", toolbar button 4\n", +#else + "\n", +#endif + " Moves to the previous page (or back n pages).\n", + "\n", + "Up-arrow\n", + " Scrolls page up.\n", + "\n", + "Down-arrow\n", + " Scrolls page down.\n", + "u\n", + " Moves page up two thirds of a window-full.\n", + "\n", + "d\n", + " Moves page down two thirds of a window-full.\n", + "\n", + "Left-arrow\n", + " Scrolls page left.\n", + "\n", + "Right-arrow\n", + " Scrolls page right.\n", + "\n", + "l\n", + " Moves page left two thirds of a window-full.\n", + "\n", + "r\n", + " Moves page right two thirds of a window-full.\n", + "\n", + "T\n", + " Toggle the use of TeX page numbers instead of physical\n", + " pages for the page list and the `g' command.\n", + " (See also the `Options -> Use TeX Pages' menu.)\n", + "\n", + "g\n", + " Moves to the page with the given number. Initially,\n", + " the first page is assumed to be page number 1, but this\n", + " can be changed with the `P' keystroke, described in the\n", + " section `Other Commands'. If no page number is given,\n", + " it moves to the last page.\n", + "\n", + "<, Ctrl-Home", +#if MOTIF + ", toolbar button 3\n", +#else + "\n", +#endif + " Moves to first page in the document.\n", + "\n", + ">, Ctrl-End", +#if MOTIF + ", toolbar button 6\n", +#else + "\n", +#endif + " Moves to last page in the document.\n", + "\n", + "^\n", + " Move to the ``home'' position of the page. This is\n", + " normally the upper left-hand corner of the page,\n", + " depending on the margins set via the -margins option.\n", + "\n", + "Home\n", + " Move to the ``home'' position of the page (the upper\n", + " left-hand corner), or to the top of the page if the `keep'\n", + " flag is set.\n", + "\n", + "End\n", + " Move to the end position of the page (the lower\n", + " right-hand corner), or to the bottom of the page if the\n", + " `keep' flag is set.\n", + "\n", + "c\n", + " Moves the page so that the point currently beneath the\n", + " cursor is moved to the middle of the window. It also\n", + " warps the cursor to the same place.\n", + "\n", + NULL + }; + + + static const char *default_help_mousebuttons[] = { + "Mouse Buttons\tActions bound to the mouse buttons\n", + "The mouse buttons can be customized just like the keys;\n", + "however the bindings cannot be intermixed (since\n", + "a mouse event always requires the cursor location\n", + "to be present, which a key event doesn't).\n", + "The default bindings are as follows:\n" + "\n", + "Buttons 1-3\n", + " Pops up magnifier windows of different sizes.\n", + " When the mouse is over a hyperlink, the link overrides\n", + " the magnifier. In that case, Button 1 jumps to the link\n", + " in the current xdvi window, Button 2 opens the link target\n", + " in a new instance of xdvi.\n", + " In `Ruler Mode', Button1 shows/drags the ruler instead;\n", + " in `Text Selection Mode', Button1 can be used to select\n", + " a rectangular region of text from the DVI file.\n", + "\n", + "Shift-Button1 to Shift-Button3\n", + " Drag the page in each direction (Button 1), vertically\n", + " only (Button 2) or horizontally only (Button 3).\n", + "\n", + "Ctrl-Button1\n", + " Invoke a reverse search for the text on the cursor\n", + " location (see the section SOURCE SPECIALS for more\n", + " information on this).\n", + "\n", + "The buttons 4 and 5 (wheel up and down for wheel mice)\n", + "scroll the page up and down respectively, or jump to the\n", + "next/previous page when the mouse is over the page list.", + "\n", + "In the page list, Button 2 toggles the mark a page (see\n", + "section `Marking Pages'); moving the mouse while holding\n", + "Button 2 lets you toggle a range of pages.\n", + "\n", + NULL + }; + + static const char *default_help_sourcespecials[] = { + "Source Specials\tNavigating between the TeX and the DVI file\n", + "Some TeX implementations have an option to automatically\n", + "include so-called `source specials' into a DVI file. These\n", + "contain the line number and the filename of the TeX source\n", + "and make it possible to go from a DVI file to the\n", + "(roughly) corresponding place in the TeX source and back\n", + "(this is also called `reverse search' and `forward search').\n", + "\n", + "On the TeX side, you need a TeX version that supports the `-src'\n", + "option (e.g. teTeX >= 2.0) or a macro package like srcltx.sty\n", + "to insert the specials into the DVI file.\n", + "\n", + "Source special mode can be customized for various editors\n", + "by using the command line option \"-editor\" or one of the\n", + "environment variables \"XEDITOR\", \"VISUAL\" or \"EDITOR\".\n", + "See the xdvi man page on the \"-editor\" option for details\n", + "and examples.\n", + "\n", + "Forward search can be performed by a program (i.e. your editor)\n", + "invoking xdvi with the \"-sourceposition\" option like this:\n", + "xdvi -sourceposition \"<line> <filename>\" <main file>\n", + "If there is already an instance of xdvi running that displays\n", + "<main file>, it will try to open the page specified by\n", + "<line> and <filename> an highlight this location on the page.\n", + "Else, a new instance of xdvi will be started that will try to\n", + "do the same.\n", + "\n", + "The following keybindings are pre-configured:\n", + "\n", + "Ctrl-Mouse1\n", + " [source-special()] Invoke the editor (the value\n", + " of the \"editor\" resource ) to display the line in the\n", + " TeX file corresponding to special at cursor position.\n", + "\n", + "Ctrl-v\n", + " [show-source-specials()] Show bounding boxes for every\n", + " source special on the current page, and print the strings\n", + " contained in these specials to stderr. With prefix 1,\n", + " show every bounding box on the page (for debugging purposes).\n", + "\n", + "Ctrl-x\n", + " [source-what-special()] Display information about the\n", + " source special next to the cursor, similar to\n", + " \"source-special()\", but without actually invoking\n", + " the editor (for debugging purposes).\n", + "\n", + NULL + }; + + static const char *default_help_modes[] = { + "Mouse Modes\tMagnifier Mode, Ruler Mode and Text Selection Mode\n", + "The keystroke Ctrl-m [switch-mode()] switches between\n", + "three different bindings for Mouse-1, which can also be\n", + "activated via the Modes menu (in Motif, this is a submenu\n", + "of the Options menu). The default mode at startup can be\n", + "customized via the X resource `mouseMode' or the command-line\n", + "option `-mousemode'. The default startup mode is Magnifier Mode.\n", + "\n", + "Note: The modes are activated by changing the magnifier()\n", + "action. Switching the mode will not work if Mouse-1 has\n", + "been customized to an action sequence that does not contain\n", + "the magnifier() action.\n", + "\n", + "Magnifier Mode\n", + "\n", + " In this mode, the mouse buttons 1 to 5 will pop up a\n", + " ``magnifying glass'' that shows an unshrunken image of\n", + " the page (i.e. an image at the resolution determined by\n", + " the option/X resource pixels or mfmode) at varying sizes.\n", + " When the magnifier is moved, small ruler-like tick marks\n", + " are displayed at the edges of the magnifier (unless\n", + " the X resource delayRulers is set to false, in which case\n", + " the tick marks are always displayed).\n", + "\n", + " The unit of the marks is determined by the X resource\n", + " `tickUnits' (mm by default). This unit can be changed at\n", + " runtime via the action `switch-magnifier-units()', by\n", + " default bound to the keystroke `t' (see the description\n", + " of that key, and of `switch-magnifier-units()' for more\n", + " details on the units available). The length of the tick\n", + " marks can be changed via the X resource `tickLength'\n", + " (4 by default). A zero or negative value suppresses the\n", + " tick marks.\n", + "\n", + "\n", + "Text Selection Mode\n", + "\n", + " This mode allows you to select a rectangular region of\n", + " text in the DVI file by holding down Mouse-1 and moving\n", + " the mouse. The text is put into the X primary selection\n", + " so that it can be pasted into other X applications with\n", + " Mouse-2.\n", + "\n", + " If xdvi has been compiled with locale, nl_langinfo() and\n", + " iconv support, the selected text is converted into the\n", + " character set of the current locale (see the output of\n", + " `locale -a' for a list of locale settings available on\n", + " your system). If nl_langinfo() is not available, but\n", + " iconv is, you can specify the input encoding for iconv\n", + " via the X resource `textEncoding' (see the output of\n", + " `iconv -l' for a list of valid encodings). If iconv support\n", + " is not available, only the encodings ISO-8859-1 and UTF-8\n", + " are supported (these names are case-insensitive).\n", + "\n", + " Note that UTF-8 is the only encoding that can render all\n", + " characters (e.g. mathematical symbols). If ISO-8859-1 is\n", + " active, characters that cannot be displayed are replaced\n", + " by `\' followed by the hexadecimal character code. If a\n", + " character is not recognized at all, it is replaced by\n", + " `?'. For other encodings, such characters may trigger\n", + " iconv error messages.\n", + "\n", + " If you want to extract larger portions of text, you\n", + " can also save selected pages or the entire file in\n", + " text format from the `File > Save as ...' menu.\n", + "\n", + "\n", + "Ruler Mode\n", + "\n", + " This mode provides a simple way of measuring distances\n", + " on the page. When this mode is activated, the mouse\n", + " cursor changes into a thin cross, and a larger, cross-\n", + " shaped ruler is drawn in the highlight color at the\n", + " mouse location. The ruler doesn't have units attached\n", + " to it; instead, the current distance between the ruler\n", + " and the mouse cursor is continously printed to the\n", + " statusline.\n", + "\n", + " When activating Ruler Mode, the ruler is at first\n", + " attached to the mouse and can be moved around. It can\n", + " then be positioned at a fixed place by clicking Mouse-1.\n", + " After that, the mouse cursor can be moved to measure the\n", + " horizontal (dx), vertical (dy) and direct (shortest)\n", + " (dr) distance between the ruler center point and the\n", + " mouse.\n", + "\n", + " Clicking Mouse-1 again will move the ruler to the\n", + " current mouse position, and holding down Mouse-1 will\n", + " drag the ruler around.\n", + "\n", + " In Ruler Mode, the following special keybindings extend\n", + " or replace the default bindings:\n", + "\n", + " o [ruler-snap-origin()] Snap the ruler back to\n", + " the origin coordinate (0,0).\n", + "\n", + " t [overrides switch-magnifier-units()] Toggle\n", + " between various ruler units, which can be\n", + " specified by the X resource tickUnits (`mm'\n", + " by default).\n", + "\n", + " P [overrides declare-page-number()] Print the\n", + " distances shown in the statusline to standard\n", + " output.\n", + NULL + }; + + static const char *default_help_search[] = { + "String Search\tSearching for strings in the DVI file\n", + "The keystroke Ctrl-f or the menu entry File->Find ...\n", + "opens a dialog window to search for a text string or a\n", + "regular expression in the DVI file. The keystroke Ctrl-g\n", + "jumps to the next match.\n", +#ifdef MOTIF + "(With Motif, you can also click on the `Binoculars' symbol\n", + "in the toolbar.)\n", +#endif + "\n", +#if HAVE_ICONV_H +#if USE_LANGINFO + "The search term is converted from the character set specified\n", + "by the current locale into UTF-8. (See the output of `locale -a'\n", + "for a list of locale settings available on your system).\n", +#else /* USE_LANGINFO */ + "Since langinfo() support is not available on this platform,\n", + "the character set of the search string should be specified\n", + "via the X resource/command-line option textEncoding if the\n", + "encoding is different from iso_8859-1.\n", +#endif /* USE_LANGINFO */ +#else /* HAVE_ICONV_H */ + "Since iconv() support is not available on this platform,\n", + "the search term should be a string in the encoding specified\n", + "by the X resource/command-line option textEncoding;\n", + "currently, only the values iso_8859-1 and utf-8 are suported.\n", +#endif + "Internally, the text in the DVI file is represented in\n", + "UTF-8 encoding (you can view the text by saving the DVI\n", + "file to a text file in UTF-8 encoding via the `File -> Save As ...'\n", + "dialog).\n", + "\n", + "Ideographic characters from CJKV fonts are treated specially:\n", + "All white space (spaces and newlines) before and after such\n", + "characters is ignored in the search string and in the DVI file.\n", + "\n", + "To match a newline character, use `\\n' in the search string;\n", + "to match the string `\\n', use `\\\\n'.\n", + "\n", + "If the checkbox Regular Expression is activated, the\n", + "string is teated as a regular expression in extended POSIX\n", + "format, with the following properties:\n", + "\n", + " a? matches a zero or one times\n", + "\n", + " a* matches a zero or more times\n", + "\n", + " a+ matches a one or more times. Note that * and + are\n", + " greedy, i.e. they match the longest possible\n", + " sub string.\n", + "\n", + " a{n} matches a exactly n times\n", + "\n", + " a{n,m} matches a at least n and no more than m times\n", + "\n", + " a|b matches a or b. Brackets can be used for grouping,\n", + " e.g.: (a|b)|c.\n", + "\n", + " The string matched by the nth group can be referenced\n", + " by \\n, e.g. \\1 refers to the first match.\n", + "\n", + " The characters ^ and $ match the beginning and the end\n", + " of a line, respectively.\n", + "\n", + " [abc] matches any of the letters a, b, c, and [a-z]\n", + " matches all characters from a to z.\n", + "\n", + " The patterns . and [...] without an explicit newline\n", + " don't match a newline character.\n", + "\n", + " Each item in a regular expression can also be one of\n", + " the following POSIX character classes:\n", + " [[:alnum:]] [[:alpha:]] [[:blank:]] [[:cntrl:]] [[:digit:]]\n", + " [[:graph:]] [[:lower:]] [[:print:]] [[:space:]] [[:upper:]]\n", + "\n", + " These can be negated by inserting a ^ symbol after the\n", + " first bracket: [^[:alpha:]]\n", + "\n", + " For more details on POSIX regular expressions, see\n", + " e.g. the IEEE Std 1003.1 available online from:\n", + "\n", + " http://www.opengroup.org/onlinepubs/007904975/basedefs/xbd_chap09.html\n", + "\n", + " As a non-standard extension, the following Perl-like\n", + " abbreviations can be used instead of the POSIX classes:\n", + "\n", + "\n", + " Symbol Meaning POSIX Class\n", + "\n", + " \\w an alphanumeric character [[:alnum:]]\n", + " \\W a non-alphanumeric character [^[:alnum:]]\n", + " \\d a digit character [[:digit:]]\n", + " \\D a non-digit character [^[:digit:]]\n", + " \\s a whitespace character [[:space:]]\n", + " \\S a non-whitespace character [^[:space:]]\n", + "\n", + " The following characters are special symbols; they\n", + " need to be escaped with \\ in order to match them\n", + " literally: ( ) [ ] . * ? + ^ $ \\.\n", + "\n", + "The dialog also provides checkboxes to search backwards,\n", + "to match in a case-sensitive manner (the default is to\n", + "ignore case, i.e. a search string Test will match both\n", + "the strings test and TEST in the DVI file) and to ignore\n", + "newlines and hyphens in the DVI file.\n", + "\n", + NULL + }; + + k = width = 0; + + init_item(resource.help_general, default_help_general, info, k++, &width); + init_item(resource.help_pagemotion, default_help_pagemotion, info, k++, &width); + init_item(resource.help_othercommands, default_help_othercommands, info, k++, &width); + init_item(resource.help_hypertex, default_help_hypertex, info, k++, &width); + init_item(resource.help_mousebuttons, default_help_mousebuttons, info, k++, &width); + init_item(resource.help_modes, default_help_modes, info, k++, &width); + init_item(resource.help_search, default_help_search, info, k++, &width); + init_item(resource.help_pagemotion, default_help_marking, info, k++, &width); + init_item(resource.help_sourcespecials, default_help_sourcespecials, info, k++, &width); + + ASSERT(k < NUM_HELP_TOPICS, "Too many elements in help topics!"); + + /* NULL-terminate items info */ + info->items[k].widget = 0; + info->items[k].topic = info->items[k].title = NULL; + + /* adjust width of topics label to longest text */ +#if !MOTIF + XtVaSetValues(info->topic_label, XtNwidth, width, NULL); +#endif +} + + + +/* + * Pops up the help window. If topic != NULL, also selects the topic. + */ +void +show_help(Widget toplevel, const char *topic) +{ + size_t i; + static Widget help_shell = 0; + static struct topic_info info; + static struct topic_item items[NUM_HELP_TOPICS]; + static Boolean first_time = True; + + if (help_shell == 0) { /* called 1st time; create widget */ + + /* no special callbacks for OK/Cancel buttons */ + info.ok_callback = NULL; + info.cancel_callback = NULL; + info.items = items; +/* info.items_size = NUM_HELP_TOPICS; */ + + help_shell = create_topic_window(toplevel, + "xdvik: Help", + "help_window", + &info, + initialize_items, + "Close", + /* no Cancel button needed */ + NULL); + info.shell = help_shell; + + center_window(help_shell, globals.widgets.top_level); + } + +#if MOTIF + { /* check if resources are set properly */ + Dimension w, h; + XtVaGetValues(help_shell, XtNwidth, &w, XtNheight, &h, NULL); + if (h < 200 || w < 400) { + XDVI_WARNING((stderr, "Initial help window size too small (%dx%d); overriding size.\n" + "Please check/update your application defaults file, and set both of\n" + "`XDvi*help_text.rows' and `XDvi*help_text.columns' to a realistic value.", + h, w)); + XtVaSetValues(help_shell, XtNwidth, 620, XtNheight, 520, NULL); + } + } +#endif + XtPopup(help_shell, XtGrabNone); + + if (topic != NULL) { + Boolean matched = False; + for (i = 0; info.items[i].topic != NULL; i++) { + if (strcmp(info.items[i].topic, topic) == 0) { /* match */ + select_topic(&info, i); + matched = True; + } + } + if (!matched) { + XBell(DISP, 0); + popup_message(help_shell, + MSG_WARN, + NULL, + "Shouldn't happen: Could not find topic `%s' in help list!\n" + REPORT_XDVI_BUG_TEMPLATE, + topic); + } + } + else if (first_time) { + first_time = False; + select_topic(&info, 0); + } +} diff --git a/Build/source/texk/xdvik/gui/help-window.h b/Build/source/texk/xdvik/gui/help-window.h new file mode 100644 index 00000000000..5464756395f --- /dev/null +++ b/Build/source/texk/xdvik/gui/help-window.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2001-2004 the xdvik development team + * + * 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. + * + */ + +#ifndef HELP_WINDOW_H_ +#define HELP_WINDOW_H_ + +extern void show_help(Widget parent, const char *topic); + +#endif /* HELP_WINDOW_H_ */ diff --git a/Build/source/texk/xdvik/gui/mag.c b/Build/source/texk/xdvik/gui/mag.c new file mode 100644 index 00000000000..1df6cd92cab --- /dev/null +++ b/Build/source/texk/xdvik/gui/mag.c @@ -0,0 +1,861 @@ +/* + * Copyright (c) 1990-2004 Paul Vojta 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. + * + * NOTE: xdvi is based on prior work, as noted in the modification history in + * xdvi.c. + * + */ + +/* + * Implementation of the magnifier window. + */ + +#include <math.h> + +#include "xdvi-config.h" +/* one of the following should define OPEN_MAX: */ +#include <limits.h> +#include "c-openmx.h" + +#include "xdvi.h" + +#ifdef MOTIF +#include <Xm/Xm.h> +#endif + +#include "events.h" +#include "dvi-draw.h" +#include "dvi-init.h" +#include "statusline.h" +#include "hypertex.h" +#include "mag.h" +#include "xm_toolbar.h" +#include "xm_menu.h" /* for get_last_ungrab() */ +#include "util.h" +#include "pagesel.h" + +/* to measure distance of pointer from ruler in ruler mode */ +static int g_ruler_pos_x = 0, g_ruler_pos_y = 0; + +struct WindowRec magnifier = { (Window) 0, 1, 0, 0, 0, 0, MAXDIM, 0, MAXDIM, 0 }; + +/* + * Mechanism to keep track of the magnifier window. The problems are, + * + * - if the button is released while the window is being drawn, this could + * cause an X error if we continue drawing in it after it is destroyed, and + * + * - creating and destroying the window too quickly confuses the window + * manager, which is avoided by waiting for an expose event before destroying + * it. + */ + +short magnifier_stat; /* 1 = wait for expose, -1 = destroy upon expose */ + +static Position main_x; +static Position main_y; + +static Position mag_x = 0; +static Position mag_y = 0; + +/* Under Motif the following two remain always constant */ +static int mag_conv_x = 0; +static int mag_conv_y = 0; + +static Position new_mag_x = 0; +static Position new_mag_y = 0; + +static void +can_exposures(struct WindowRec *windowrec) +{ + windowrec->min_x = windowrec->min_y = MAXDIM; + windowrec->max_x = windowrec->max_y = 0; +} + +static void +mag_motion(XEvent * event) +{ + MYTRACE((stderr, "mag_motion!\n")); + new_mag_x = event->xmotion.x + mag_conv_x; + main_x = event->xmotion.x_root - new_mag_x; + new_mag_y = event->xmotion.y + mag_conv_y; + main_y = event->xmotion.y_root - new_mag_y; + + if (new_mag_x != mag_x || new_mag_y != mag_y) + globals.ev.flags |= EV_MAG_MOVE; + else + globals.ev.flags &= ~EV_MAG_MOVE; +} + +void +mag_release(XEvent * event) +{ + UNUSED(event); + if (magnifier.win != (Window) 0) { + if (magnifier_stat) { + magnifier_stat = -1; /* destroy upon expose */ + } + else { + XDestroyWindow(DISP, magnifier.win); + if (drawing_mag) { + globals.ev.flags |= EV_MAG_GONE; + } + magnifier.win = (Window) 0; + mouse_motion = mouse_release = null_mouse; + globals.ev.flags &= ~EV_MAG_MOVE; + globals.cursor.flags &= ~CURSOR_MAG; + globals.ev.flags |= EV_CURSOR; + can_exposures(&magnifier); + /* + Workaround for bug #703304: + For obscure reasons, XFree 3.3.5 (and apparently also Solaris 8) + doesn't generate an expose event after the magnifier has beed + destroyed. But only a redraw() event caused by expose would reset + currwin.win back to mane.win, which is needed e.g. for getting the + hyperlink info updated (otherwise, the mouse will not become active + over a hyperlink). + + Forcing a redraw with + redraw(&mane); + may cause a `BadDrawable' X error with color material (e.g. from #702288), + or a `draw_part: shouldn't happen: POST encountered' error. Neither do + the following work: + globals.ev.flags |= EV_EXPOSE; (doesn't fix the bug) + draw_page(); (same effect as redraw(&mane)) + globals.ev.flags |= EV_NEWPAGE; (works, but is crude and causes flicker) + + So I decided to use expose() for the time being, which + sets mane.min_x to the current x point (which doesn't happen + with EV_EXPOSE; this causes the test for `mane.min_x < MAXDIM' + to fail in events.c; look for `see comment in mag.c'). + */ +/* fprintf(stderr, "========triggering expose!\n"); */ + expose(&mane, event->xbutton.x_root, event->xbutton.y_root, 10, 10); + } + } +} + +static int +tick_scale(int k) +{ + if (k == 0) + return 3; + else if ((k % 1000) == 0) + return 7; + else if ((k % 500) == 0) + return 6; + else if ((k % 100) == 0) + return 5; + else if ((k % 50) == 0) + return 4; + else if ((k % 10) == 0) + return 3; + else if ((k % 5) == 0) + return 2; + else + return 1; +} + +static void +draw_ticks(unsigned int width, unsigned int height, GC ourGC) +{ + int k; /* tick counter */ + double old_pixels_per_tick; + double pixels_per_tick; + int scale; + int tick_offset; /* offset along axes */ + int x; /* coordinates of top-left popup */ + int y; /* window corner */ + double xx; /* coordinates of tick */ + double yy; /* coordinates of tick */ + static char *last_tick_units = ""; /* memory of last tick units */ + + if (resource.tick_length <= 0) /* user doesn't want tick marks */ + return; + + x = 0; /* the pop-up window always has origin (0,0) */ + y = 0; + + /* We need to clear the existing window to remove old rulers. I think + that this could be avoided if draw_ticks() could be invoked earlier. + The expose argument in XClearArea() must be True to force redrawing + of the text inside the popup window. Also, it would be better to draw + the rulers before painting the text, so that rulers would not + overwrite the text, but I haven't figured out yet how to arrange + that. */ + + XClearArea(DISP, magnifier.win, x, y, width, height, True); + + /* The global resource.pixels_per_inch tells us how to find the ruler + scale. For example, 300dpi corresponds to these TeX units: + + 1 TeX point (pt) = 4.151 pixels + 1 big point (bp) = 4.167 pixels + 1 pica (pc) = 49.813 pixels + 1 cicero (cc) = 53.501 pixels + 1 didot point (dd) = 4.442 pixels + 1 millimeter (mm) = 11.811 pixels + 1 centimeter (cm) = 118.110 pixels + 1 inch (in) = 300.000 pixels + 1 scaled point (sp) = 0.00006334 pixels + + The user can select the units via a resource (e.g. XDvi*tickUnits: bp), + or a command-line option (e.g. -xrm '*tickUnits: cm'). The length of + the ticks can be controlled by a resource (e.g. XDvi*tickLength: 10), or + a command-line option (e.g. -xrm '*tickLength: 10000'). If the tick + length exceeds the popup window size, then a graph-paper grid is drawn + over the whole window. Zero, or negative, tick length completely + suppresses rulers. */ + + pixels_per_tick = (double)resource.pixels_per_inch; + if (strcmp(resource.tick_units, "pt") == 0) + pixels_per_tick /= 72.27; + else if (strcmp(resource.tick_units, "bp") == 0) + pixels_per_tick /= 72.0; + else if (strcmp(resource.tick_units, "in") == 0) + /* NO-OP */ ; + else if (strcmp(resource.tick_units, "cm") == 0) + pixels_per_tick /= 2.54; + else if (strcmp(resource.tick_units, "mm") == 0) + pixels_per_tick /= 25.4; + else if (strcmp(resource.tick_units, "dd") == 0) + pixels_per_tick *= (1238.0 / 1157.0) / 72.27; + else if (strcmp(resource.tick_units, "cc") == 0) + pixels_per_tick *= 12.0 * (1238.0 / 1157.0) / 72.27; + else if (strcmp(resource.tick_units, "pc") == 0) + pixels_per_tick *= 12.0 / 72.27; + else if (strcmp(resource.tick_units, "sp") == 0) + pixels_per_tick /= (65536.0 * 72.27); + else if (strcmp(resource.tick_units, "px") == 0) + pixels_per_tick = 10; + else { + XDVI_WARNING((stderr, "Unrecognized tickUnits [%s]: defaulting to TeX points [pt]", + resource.tick_units)); + resource.tick_units = "pt"; + pixels_per_tick /= 72.27; + } + + /* To permit accurate measurement in the popup window, we can reasonably + * place tick marks about 3 to 10 pixels apart, so we scale the computed + * pixels_per_tick by a power of ten to bring it into that range. + */ + + old_pixels_per_tick = pixels_per_tick; /* remember the original scale */ + while (pixels_per_tick < 3.0) + pixels_per_tick *= 10.0; + while (pixels_per_tick > 30.0) + pixels_per_tick /= 10.0; + + /* tell user what the ruler scale is, but only when it changes */ + if (strcmp(last_tick_units, resource.tick_units) != 0) { + if (old_pixels_per_tick != pixels_per_tick) + printf("Ruler tick interval adjusted to represent %.2f%s\n", + pixels_per_tick / old_pixels_per_tick, resource.tick_units); + else if (globals.debug & DBG_EVENT) + printf("Ruler tick interval represents 1%s\n", resource.tick_units); + } + + /* In order to make the ruler as accurate as possible, given the coarse + * screen resolution, we compute tick positions in floating-point + * arithmetic, then round to nearest integer values. + */ + + /* draw vertical ticks on top and bottom */ + for (k = 0, xx = 0.0; xx < (double)width; k++, xx += pixels_per_tick) { + tick_offset = (int)(0.5 + xx); /* round to nearest pixel */ + scale = tick_scale(k); + XDrawLine(DISP, magnifier.win, ourGC, + x + tick_offset, y, x + tick_offset, y + scale * resource.tick_length); + XDrawLine(DISP, magnifier.win, ourGC, + x + tick_offset, y + height, + x + tick_offset, y + height - scale * resource.tick_length); + } + + /* draw horizontal ticks on left and right */ + for (k = 0, yy = 0.0; yy < (double)height; k++, yy += pixels_per_tick) { + tick_offset = (int)(0.5 + yy); /* round to nearest pixel */ + scale = tick_scale(k); + XDrawLine(DISP, magnifier.win, ourGC, + x, y + tick_offset, x + scale * resource.tick_length, y + tick_offset); + XDrawLine(DISP, magnifier.win, ourGC, + x + width, y + tick_offset, + x + width - scale * resource.tick_length, y + tick_offset); + } + + last_tick_units = resource.tick_units; + + XFlush(DISP); /* bring window up-to-date */ +} + +static void +compute_mag_pos(int *xp, int *yp) +{ + int t; + + t = mag_x + main_x - magnifier.width / 2; + if (t > WidthOfScreen(SCRN) - (int)magnifier.width - 2 * MAGBORD) + t = WidthOfScreen(SCRN) - (int)magnifier.width - 2 * MAGBORD; + if (t < 0) + t = 0; + *xp = t; + t = mag_y + main_y - magnifier.height / 2; + if (t > HeightOfScreen(SCRN) - (int)magnifier.height - 2 * MAGBORD) + t = HeightOfScreen(SCRN) - (int)magnifier.height - 2 * MAGBORD; + if (t < 0) + t = 0; + *yp = t; +} + +static void +scroll_window(struct WindowRec *windowrec, int x0, int y0) +{ + int x, y; + int x2 = 0, y2 = 0; + int ww, hh; + + x = x0 - windowrec->base_x; + y = y0 - windowrec->base_y; + ww = windowrec->width - x; + hh = windowrec->height - y; + windowrec->base_x = x0; + windowrec->base_y = y0; + if (currwin.win == windowrec->win) { + currwin.base_x = x0; + currwin.base_y = y0; + } + windowrec->min_x -= x; + if (windowrec->min_x < 0) + windowrec->min_x = 0; + windowrec->max_x -= x; + if ((unsigned int)windowrec->max_x > windowrec->width) + windowrec->max_x = windowrec->width; + windowrec->min_y -= y; + if (windowrec->min_y < 0) + windowrec->min_y = 0; + windowrec->max_y -= y; + if ((unsigned int)windowrec->max_y > windowrec->height) + windowrec->max_y = windowrec->height; + if (x < 0) { + x2 = -x; + x = 0; + ww = windowrec->width - x2; + } + if (y < 0) { + y2 = -y; + y = 0; + hh = windowrec->height - y2; + } + if (ww <= 0 || hh <= 0) { + XClearWindow(DISP, windowrec->win); + windowrec->min_x = windowrec->min_y = 0; + windowrec->max_x = windowrec->width; + windowrec->max_y = windowrec->height; + } + else { + XCopyArea(DISP, windowrec->win, windowrec->win, globals.gc.copy, + x, y, (unsigned int)ww, (unsigned int)hh, x2, y2); + if (x > 0) + clearexpose(windowrec, ww, 0, (unsigned int)x, windowrec->height); + if (x2 > 0) + clearexpose(windowrec, 0, 0, (unsigned int)x2, windowrec->height); + if (y > 0) + clearexpose(windowrec, 0, hh, windowrec->width, (unsigned int)y); + if (y2 > 0) + clearexpose(windowrec, 0, 0, windowrec->width, (unsigned int)y2); + } +} + +static void +do_movemag(int x, int y) +{ + int xx, yy; + + mag_x = x; + mag_y = y; + if (mag_x == new_mag_x && mag_y == new_mag_y) + globals.ev.flags &= ~EV_MAG_MOVE; + compute_mag_pos(&xx, &yy); + XMoveWindow(DISP, magnifier.win, xx, yy); + scroll_window(&magnifier, + (x + mane_base_x) * mane.shrinkfactor - (int)magnifier.width / 2, + (y + mane_base_y) * mane.shrinkfactor - (int)magnifier.height / 2); + draw_ticks(magnifier.width, magnifier.height, globals.gc.ruler); +} + +extern jmp_buf next_env; + +void +show_distance_from_ruler(XEvent *event, Boolean to_stdout) +{ + int loc_x, loc_y; + int precision = 2; + double factor; + + if (event == NULL) /* when option is toggled */ + return; + + loc_x = event->xbutton.x; + loc_y = event->xbutton.y; + if (event->xbutton.window != mane.win) { + Window ww; + (void)XTranslateCoordinates(DISP, + RootWindowOfScreen(SCRN), mane.win, + event->xbutton.x_root, + event->xbutton.y_root, + &loc_x, + &loc_y, + &ww); /* throw away last argument */ + } + + /* map everything below 0 to the origin */ + if (loc_x < 0) + loc_x = 0; + if (loc_y < 0) + loc_y = 0; + + if (strcmp(resource.tick_units, "pt") == 0) { + factor = 72.27 * currwin.shrinkfactor / (double)resource.pixels_per_inch; + } + else if (strcmp(resource.tick_units, "bp") == 0) { + factor = 72.0 * currwin.shrinkfactor / (double)resource.pixels_per_inch; + } + else if (strcmp(resource.tick_units, "in") == 0) { + factor = currwin.shrinkfactor / (double)resource.pixels_per_inch; + } + else if (strcmp(resource.tick_units, "cm") == 0) { + factor = 2.54 * currwin.shrinkfactor / (double)resource.pixels_per_inch; + precision = 3; + } + else if (strcmp(resource.tick_units, "mm") == 0) { + factor = 25.4 * currwin.shrinkfactor / (double)resource.pixels_per_inch; + } + else if (strcmp(resource.tick_units, "dd") == 0) { + factor = 72.27 / (1238.0 / 1157.0) * currwin.shrinkfactor / (double)resource.pixels_per_inch; + } + else if (strcmp(resource.tick_units, "cc") == 0) { + factor = 72.27 / (12.0 * (1238.0 / 1157.0)) * currwin.shrinkfactor / (double)resource.pixels_per_inch; + } + else if (strcmp(resource.tick_units, "pc") == 0) { + factor = 72.27 / 12.0 * currwin.shrinkfactor / (double)resource.pixels_per_inch; + } + else if (strcmp(resource.tick_units, "sp") == 0) { + factor = 65536.0 * 72.27 * currwin.shrinkfactor / (double)resource.pixels_per_inch; + precision = 1; + } + else if (strcmp(resource.tick_units, "px") == 0) { /* pixel units */ + factor = 1; + } + else { + XDVI_WARNING((stderr, "Unrecognized tickUnits [%s]: defaulting to TeX points [pt]", + resource.tick_units)); + resource.tick_units = "pt"; + factor = 72.27 * currwin.shrinkfactor / (double)resource.pixels_per_inch; + } + + if (mouse_release != null_mouse) { + if (to_stdout) { + XDVI_INFO((stdout, "Ruler/Point: %d,%d, dx: %.*f %s, dy: %.*f %s, dr: %.*f %s", + loc_x, loc_y, + precision, 0.000, resource.tick_units, + precision, 0.000, resource.tick_units, + precision, 0.000, resource.tick_units)); + } + else { + statusline_print(STATUS_FOREVER, + "Ruler/Point: %d,%d, dx: %.*f %s, dy: %.*f %s, dt: %.*f %s", + loc_x, loc_y, + precision, 0.000, resource.tick_units, + precision, 0.000, resource.tick_units, + precision, 0.000, resource.tick_units); + } + } + else { + int d_x = loc_x - g_ruler_pos_x; + int d_y = loc_y - g_ruler_pos_y; + double d_z = sqrt((double)d_x * d_x + (double)d_y * d_y); + double unit_x = (double)d_x * factor; + double unit_y = (double)d_y * factor; + double unit_z = d_z * factor; + if (to_stdout) { + XDVI_INFO((stdout, "Ruler: %d,%d, Point: %d,%d, dx: %.*f %s, dy: %.*f %s, dr: %.*f %s", + g_ruler_pos_x, g_ruler_pos_y, loc_x, loc_y, + precision, unit_x, resource.tick_units, + precision, unit_y, resource.tick_units, + precision, unit_z, resource.tick_units)); + } + else { + statusline_print(STATUS_FOREVER, + "Ruler: %d,%d, Point: %d,%d, dx: %.*f %s, dy: %.*f %s, dr: %.*f %s", + g_ruler_pos_x, g_ruler_pos_y, loc_x, loc_y, + precision, unit_x, resource.tick_units, + precision, unit_y, resource.tick_units, + precision, unit_z, resource.tick_units); + } + } +} + +void +move_magnifier(void) +{ + if (magnifier.win == (Window) 0) + globals.ev.flags &= ~EV_MAG_MOVE; + else if (abs(new_mag_x - mag_x) > 2 * abs(new_mag_y - mag_y)) + do_movemag(new_mag_x, mag_y); + else if (abs(new_mag_y - mag_y) > 2 * abs(new_mag_x - mag_x)) + do_movemag(mag_x, new_mag_y); + else + do_movemag(new_mag_x, new_mag_y); +} + +static void +Act_href(Widget w, XEvent *event, + String *params, Cardinal *num_params) +{ + int x, y; + Window dummy; + + UNUSED(w); + UNUSED(num_params); + UNUSED(params); + + if (resource.mouse_mode == MOUSE_RULER_MODE) + return; + + (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win, + event->xkey.x, event->xkey.y, &x, &y, &dummy); + if (htex_handleref(x, y, False)) { + block_next_mouse_event(); + } +} + +static void +Act_href_newwindow(Widget w, XEvent *event, + String *params, Cardinal *num_params) +{ + int x, y; + Window dummy; + + UNUSED(w); + UNUSED(num_params); + UNUSED(params); + + if (resource.mouse_mode == MOUSE_RULER_MODE) + return; + + (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win, + event->xkey.x, event->xkey.y, &x, &y, &dummy); + if (htex_handleref(x, y, True)) { + block_next_mouse_event(); + } +} + +void +clear_ruler(void) +{ + /* maybe we should do this only for mouse-1? */ + clearexpose(&mane, 0, g_ruler_pos_y, + ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 2, 1); + clearexpose(&mane, g_ruler_pos_x, 0, + 1, ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 2); +} + +void +show_ruler(XEvent *event) +{ + if (mouse_release == null_mouse) { + if (mouse_release != null_mouse && mouse_release != drag_ruler_release) + return; + if (mouse_release == null_mouse) { + mouse_motion = drag_ruler_motion; + mouse_release = drag_ruler_release; + } + drag_ruler_motion(event); + } +} + +static void +draw_ruler(int x, int y) +{ + /* don't draw if outside page region (will be clipped automatically by Motif, + but not by Xaw, where draw widget is entire window) */ + if (x > (int)ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 1 + || y > (int)ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 1) + return; + + XFillRectangle(DISP, mane.win, globals.gc.high, + 0, y, + ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 2, 1); + XFillRectangle(DISP, mane.win, globals.gc.high, + x, 0, + 1, ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 2); +} + + +/* snap ruler back to origing (0,0) */ +void +ruler_snap_origin(XEvent *event) +{ + clear_ruler(); + g_ruler_pos_x = g_ruler_pos_y = 0; + draw_ruler(g_ruler_pos_x, g_ruler_pos_y); + /* deactivate mouse dragging */ + mouse_motion = mouse_release = null_mouse; + show_distance_from_ruler(event, False); +} + +void +redraw_ruler(void) +{ + draw_ruler(g_ruler_pos_x, g_ruler_pos_y); +} + +void +drag_ruler_motion(XEvent *event) +{ + int loc_x, loc_y; + if (event == NULL) { /* toggled via menu */ + /* hack to avoid redrawing ruler at last g_* positions when mode is + toggled on via menu, then off via keystroke */ + g_ruler_pos_x = g_ruler_pos_y = 0; + return; + } + + loc_x = event->xbutton.x; + loc_y = event->xbutton.y; + + if (event->xbutton.window != mane.win) { + Window dummy; + (void)XTranslateCoordinates(DISP, + RootWindowOfScreen(SCRN), mane.win, + event->xbutton.x_root, + event->xbutton.y_root, + &loc_x, + &loc_y, + &dummy); + } + + /* map everything below 0 to the origin */ + if (loc_x < 0) + loc_x = 0; + if (loc_y < 0) + loc_y = 0; + + clear_ruler(); + draw_ruler(loc_x, loc_y); + g_ruler_pos_x = loc_x; + g_ruler_pos_y = loc_y; +} + +void +drag_ruler_release(XEvent *event) +{ + UNUSED(event); + mouse_motion = mouse_release = null_mouse; +} + +/**************************************************************************** + * Actions specific to the handling of the magnifier + */ + +static void +Act_magnifier(Widget w, XEvent *event, + String *params, Cardinal *num_params) +{ + const char *p; + int x, y; + XSetWindowAttributes attr; +#ifndef MOTIF + Window throwaway; +#endif + UNUSED(w); + + MYTRACE((stderr, "magnifier!\n")); + + if (block_this_mouse_event()) + return; + else if (dvi_file_changed()) { + globals.ev.flags |= EV_RELOAD; + return; + } + +#ifdef MOTIF + /* see xm_menu.c for an explanation of this */ + if (pulldown_menu_active(event->xany.serial)) { + return; + } +#endif + + if (bg_current == NULL) { + /* + HACK ALERT: we can arrive here after loading a new file via the file selector + for which not all fonts have been generated. In that case, dereferencing + bg_current would bomb. Try to recover by simply returning here. + */ + return; + } + + if (resource.mouse_mode == MOUSE_RULER_MODE && event->xbutton.button == 1) { + show_ruler(event); + XDefineCursor(DISP, CURSORWIN, globals.cursor.rule); + show_distance_from_ruler(event, False); + return; + } + else if (resource.mouse_mode == MOUSE_TEXT_MODE && event->xbutton.button == 1) { + text_selection_start(event); + text_motion(event); + return; + } + + if (event->type != ButtonPress || mouse_release != null_mouse + || magnifier.win != (Window)0 || mane.shrinkfactor == 1 || *num_params != 1) { + XBell(DISP, 0); + if (mane.shrinkfactor == 1) { + statusline_print(STATUS_SHORT, + "No magnification available at shrink factor 1"); + } + return; + } + + p = *params; + if (*p == '*') { + int n = atoi(p + 1) - 1; + + if (n < 0 || n >= (int)get_magglass_items() || get_magglass_width(n) <= 0) { + XBell(DISP, 0); + return; + } + magnifier.width = get_magglass_width(n); + magnifier.height = get_magglass_height(n); + } + else { + magnifier.width = magnifier.height = atoi(p); + p = strchr(p, 'x'); + if (p != NULL) { + magnifier.height = atoi(p + 1); + if (magnifier.height == 0) + magnifier.width = 0; + } + if (magnifier.width == 0) { + XBell(DISP, 0); + return; + } + } +#ifndef MOTIF + XTranslateCoordinates(DISP, event->xbutton.window, mane.win, + 0, 0, &mag_conv_x, &mag_conv_y, &throwaway); +#endif + + globals.cursor.flags |= CURSOR_MAG; + globals.ev.flags |= EV_CURSOR; + + mag_x = event->xbutton.x + mag_conv_x; + mag_y = event->xbutton.y + mag_conv_y; + main_x = event->xbutton.x_root - mag_x; + main_y = event->xbutton.y_root - mag_y; + compute_mag_pos(&x, &y); + magnifier.base_x = (mag_x + mane_base_x) * mane.shrinkfactor - magnifier.width / 2; + magnifier.base_y = (mag_y + mane_base_y) * mane.shrinkfactor - magnifier.height / 2; + attr.save_under = True; + attr.border_pixel = resource.rule_pixel; +#if COLOR + attr.background_pixel = bg_current->pixel; +#else + attr.background_pixel = resource.back_Pixel; +#endif + attr.override_redirect = True; +#ifdef GREY + attr.colormap = G_colormap; +#endif + magnifier.win = XCreateWindow(DISP, RootWindowOfScreen(SCRN), + x, y, magnifier.width, magnifier.height, MAGBORD, + G_depth, InputOutput, G_visual, + CWSaveUnder | CWBorderPixel | CWBackPixel | +#ifdef GREY + CWColormap | +#endif + CWOverrideRedirect, &attr); + XSelectInput(DISP, magnifier.win, ExposureMask); + XMapWindow(DISP, magnifier.win); + + /* + * This call will draw the point rulers when the magnifier first pops up, + * if the XDvi*delayRulers resource is false. Some users may prefer rulers + * to remain invisible until the magnifier is moved, so the default is + * true. Rulers can be suppressed entirely by setting the XDvi*tickLength + * resource to zero or negative. + */ + + if (!resource.delay_rulers) + draw_ticks(magnifier.width, magnifier.height, globals.gc.ruler); + + magnifier_stat = 1; /* waiting for exposure */ + mouse_motion = mag_motion; + mouse_release = mag_release; +} + +static void +Act_switch_magnifier_units(Widget w, XEvent *event, + String *params, Cardinal *num_params) +{ + size_t k = 0; + static char *TeX_units[] = { + "mm", "pt", "in", "sp", "bp", "cc", "dd", "pc", "px", + }; + + UNUSED(w); + UNUSED(event); + UNUSED(params); + UNUSED(num_params); + + for (k = 0; k < XtNumber(TeX_units); ++k) + if (strcmp(resource.tick_units, TeX_units[k]) == 0) + break; + k++; + if (k >= XtNumber(TeX_units)) + k = 0; + resource.tick_units = TeX_units[k]; + if (resource.mouse_mode != MOUSE_RULER_MODE) { + statusline_print(STATUS_SHORT, "Ruler units: %s\n", resource.tick_units); + } + else { + show_distance_from_ruler(event, False); + } +} + +XtActionsRec mag_actions[] = { + {"magnifier", Act_magnifier}, + {"do-href", Act_href}, + {"do-href-newwindow", Act_href_newwindow}, + {"switch-magnifier-units", Act_switch_magnifier_units}, +}; + +/* + * This isn't creating the actual magnifier. It is created lazily on demand + * if one of the corresponding actions is taken. Therefore we are here + * just adding the record of actions related to magnifier handling to the + * application. + */ + +void +create_magnifier(void) +{ + XtAppAddActions(app, mag_actions, XtNumber(mag_actions)); +} diff --git a/Build/source/texk/xdvik/gui/mag.h b/Build/source/texk/xdvik/gui/mag.h new file mode 100644 index 00000000000..608019f3ac2 --- /dev/null +++ b/Build/source/texk/xdvik/gui/mag.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 1990-2004 Paul Vojta 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. + * + * NOTE: xdvi is based on prior work, as noted in the modification history in + * xdvi.c. + * + */ + +#ifndef MAG_H_ +#define MAG_H_ + +#define MAGBORD 1 /* border size for magnifier */ + +extern void show_distance_from_ruler(XEvent *event, Boolean to_stdout); +extern void drag_ruler_motion(XEvent *event); +extern void drag_ruler_release(XEvent *event); +extern void clear_ruler(void); +extern void show_ruler(XEvent *event); +extern void redraw_ruler(void); +extern void ruler_snap_origin(XEvent *event); + +extern void mag_release(XEvent * event); +extern void move_magnifier(void); +extern void create_magnifier(void); + +#endif /* MAG_H_ */ + diff --git a/Build/source/texk/xdvik/gui/menu.c b/Build/source/texk/xdvik/gui/menu.c new file mode 100644 index 00000000000..575ef162f9b --- /dev/null +++ b/Build/source/texk/xdvik/gui/menu.c @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2003-2004 the xdvik development team + * + * 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. + * + */ + +/* + * Common code for Xaw and Motif menu bar creation. + */ + +#include "xdvi-config.h" +#include "xdvi.h" +#include "events.h" +#include "menu.h" +#include "util.h" + +#if defined(NEW_MENU_CREATION) || defined(MOTIF) + +#ifdef MOTIF +# include <Xm/RowColumn.h> +# include <Xm/ToggleB.h> +#else +# include <X11/Intrinsic.h> +# include <X11/Xatom.h> +# include <X11/StringDefs.h> +# include <X11/Xaw/Label.h> +#endif + + +/* translate string argument into corresponding buttonTypeT */ +static buttonTypeT +get_type(const char *str) +{ + if (strcmp(str, "PUSH") == 0) + return BT_PUSH; + else if (strcmp(str, "RADIO") == 0) + return BT_RADIO; + else if (strcmp(str, "CHECK") == 0) + return BT_CHECK; + else if (strcmp(str, "SEP") == 0) + return BT_SEP; + else + return BT_INVALID; +} + +static struct button_info *m_button_info = NULL; /* toplevel node of pulldown menu structure */ + + +static void +set_menu_info(void *val, XtActionProc proc, Boolean (*cmp)(), struct button_info *item) +{ + size_t i; + ASSERT(item != NULL, "item in set_menu_info musn't be NULL!"); + for (i = 0; i < item->size; i++) { + if ((item->elems[i].type == BT_RADIO || item->elems[i].type == BT_CHECK) + && item->elems[i].action != NULL + && item->elems[i].action->proc != NULL + && item->elems[i].action->proc == proc + && item->elems[i].action->param != NULL) { + Boolean on; + ASSERT(cmp != NULL, "comparison function musn't be NULL!"); + on = cmp(val, item->elems[i].action->param); +#ifdef MOTIF + ASSERT(item->elems[i].widget != 0, "Widget musn't be NULL!"); + XmToggleButtonSetState(item->elems[i].widget, on, False); +#else +#ifdef NEW_MENU_CREATION + ASSERT(item->elems[i].widget != 0, "Widget musn't be NULL!"); + xaw_set_button_state(item->elems + i, on); +#else + static Arg args = { XtNleftBitmap, (XtArgVal) 0 }; + if (on) + args.value = (XtArgVal) menu_check_bitmap; + else + args.value = (XtArgVal) menu_uncheck_bitmap; + XtSetValues(item->elems[i].widget, &args, 1); +#endif /* NEW_MENU_CREATION */ +#endif + } + if (item->elems[i].submenu != NULL) { /* invoke it recursively */ + set_menu_info(val, proc, cmp, item->elems[i].submenu); + } + } +} + + +/* set a menu according to val and the compare function cmp */ +void +set_menu(void *val, XtActionProc proc, Boolean (*cmp)()) +{ + /* non-standard cast function ptr -> void ptr */ + /* TRACE_GUI((stderr, "set_menu_info: %d, %p, %p", *(int *)val, (void *)proc, (void *)cmp)); */ + set_menu_info(val, proc, cmp, m_button_info); +} + +static void +initialize_menus(void) +{ + int use_gs; + int shrinkval; + + /* initialize tickmarks for all possible actions */ + use_gs = resource.postscript; +#ifdef PS_GS + if (!resource.useGS) + use_gs = 0; +#endif + + set_menu(&use_gs, Act_set_ps, check_int); + set_menu(&resource.gs_alpha, Act_set_gs_alpha, check_toggle); + set_menu(&resource.keep_flag, Act_set_keep_flag, check_toggle); + shrinkval = resource.pixels_per_inch / mane.shrinkfactor; + set_menu(&shrinkval, Act_shrink_to_dpi, check_int); + set_menu(&mane.shrinkfactor, Act_set_shrink_factor, check_int); + set_menu(&resource.use_tex_pages, Act_use_tex_pages, check_toggle); + set_menu((char *)resource.paper, Act_set_paper_landscape, check_paper_landscape); + set_menu((char *)resource.paper, Act_set_papersize, check_papersize); + set_menu(&resource.mouse_mode, Act_switch_mode, check_int); + set_menu(&resource.expert_mode, Act_set_expert_mode, check_resource_expert); +} + +static void +free_items(char **items, size_t len) +{ + size_t curr = 0; + while(curr < len) { + free(items[curr++]); + } + free(items); +} + +#if 0 +static void +show_items(char *descr, char **items, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) { + fprintf(stderr, "%s %d: |%s|\n", descr, i, items[i]); + } +} +#endif + +static void +add_info(struct button_info **info, buttonTypeT bt_type, + char mnemonic, const char *title, + const char *accelerator, struct xdvi_action *action) +{ +/* fprintf(stderr, "creating new item for info %p: |%s|\n", *info, title); */ + size_t idx = (*info)->size++; + (*info)->elems = xrealloc((*info)->elems, (*info)->size * sizeof *((*info)->elems)); + (*info)->elems[idx].title = xstrdup(title); + (*info)->elems[idx].type = bt_type; + if (accelerator == NULL || accelerator[0] == '\0') + (*info)->elems[idx].accelerator = NULL; + else + (*info)->elems[idx].accelerator = xstrdup(accelerator); + (*info)->elems[idx].mnemonic = mnemonic; + (*info)->elems[idx].action = action; + (*info)->elems[idx].widget = 0; + (*info)->elems[idx].submenu = NULL; +} + +static void +insert_items(struct button_info **info, char **items, size_t num_items, + const char *button_type, const char *accelerator, const char *action) +{ + const char ENTRY_SEP = '|'; + size_t i = 0; + size_t entry_len; + size_t entry_count = 0; + char **entry_items = NULL; + size_t idx; + + Boolean found = False; + Boolean have_error = False; + +/* fprintf(stderr, "------ insert_items called for |%s|, %p, |%s|\n", *items, *info, action); */ + + if (*items == NULL) { + /* should be a separator, which is treated as a special case since + there's no menu title: */ + if (strcmp (button_type, "SEP") == 0) + add_info(info, BT_SEP, '\0', "SEP", NULL, NULL); + else + XDVI_WARNING((stderr, "Shouldn't happen: items == NULL!")); + return; + } + + entry_len = strlen(items[0]); + entry_items = split_line(items[0], ENTRY_SEP, 0, entry_len, &entry_count); + + if (entry_count < 2) { + XDVI_WARNING((stderr, "Missing Mnemonic in button info `%s'\n", items[0])); + entry_count++; + entry_items = xrealloc(entry_items, entry_count * sizeof *entry_items); + entry_items[1] = xstrdup(""); + entry_items[2] = NULL; + } + + for (i = 0; i < (*info)->size; i++) { + if (strcmp(entry_items[0], (*info)->elems[i].title) == 0) { + found = true; + break; + } + } + + idx = i; + + if (!found) { /* new item, resize info and add this item */ + struct xdvi_action *my_action = NULL; + buttonTypeT my_type = BT_NONE; + + /* if it's a `leaf' in the menu hierarchy, compile the action and set the button type */ + if (num_items == 1) { +/* fprintf(stderr, "button_type: |%s|\n", button_type); */ + + char *fmt = strchr(entry_items[0], '$'); + if (fmt != NULL + && (fmt == entry_items[0] || (fmt > entry_items[0] && *(fmt - 1) != '\\')) + && (fmt[1] == '#' || fmt[1] == '%' ||fmt[1] == '_')) { + XDVI_WARNING((stderr, "Xdvik doesn't support format characters in button labels; " + "skipping button \"%s\"", items[0])); + have_error = True; + } + + if (strlen(action) == 0 + || (my_action = compile_action(action)) == NULL) { + XDVI_WARNING((stderr, "Invalid action \"%s\" for button \"%s\" (skipping this line).", + action, items[0])); + have_error = True; + } + + if ((my_type = get_type(button_type)) == BT_INVALID) { + XDVI_WARNING((stderr, "Invalid type \"%s\" for button \"%s\" (skipping this line).", + button_type, items[0])); + have_error = True; + } + } + + if (!have_error) { + add_info(info, my_type, entry_items[1][0], entry_items[0], accelerator, my_action); + } + } + free_items(entry_items, entry_count); + + if (num_items > 1 || (num_items == 1 && strcmp(button_type, "SEP") == 0)) { /* not a leaf, invoke recursivly for next level */ +/* fprintf(stderr, "invoking recursively for %d, %p\n", idx, (*info)->elems[idx].submenu); */ + if ((*info)->elems[idx].submenu == NULL) { /* submenu didn't exist yet, create it */ + struct button_info *new_submenu = xmalloc(sizeof *new_submenu); + new_submenu->elems = NULL; + new_submenu->size = 0; + (*info)->elems[idx].submenu = new_submenu; + } + insert_items(&((*info)->elems[idx].submenu), items + 1, num_items - 1, button_type, accelerator, action); + } +} + +static void +show_button_info(int depth, struct button_info *info) +{ + size_t i; + for (i = 0; i < info->size; i++) { + TRACE_GUI((stderr, "%*c-->%s; type=%d; mnemonic=%c; accel=%s; submenu=%p; w=%lu; action: %p", + depth, ' ', + info->elems[i].title, + info->elems[i].type, + info->elems[i].mnemonic, + info->elems[i].accelerator ? info->elems[i].accelerator : "<NULL>", + (void *)info->elems[i].submenu, + (unsigned long)info->elems[i].widget, + (void *)info->elems[i].action)); + if (info->elems[i].submenu != NULL) { + show_button_info(depth + 3, info->elems[i].submenu); + } + } +} + +static void +parse_button_translations(struct button_info **info) +{ + const char *curr_p, *end_p; + + const char LINE_SEP = ':'; + const char MENU_SEP = '>'; + + for (curr_p = resource.menu_translations; + curr_p != NULL && *curr_p != '\0'; + curr_p = end_p + 1) { + end_p = strchr(curr_p, '\n'); + if (end_p != NULL) { + size_t line_len = end_p - curr_p; + size_t line_count = 0; + char **line_items; + line_items = split_line(curr_p, LINE_SEP, 0, line_len, &line_count); +/* fprintf(stderr, "length of line: %d; %d items\n", line_len, line_count); */ +/* show_items("LINE", line_items, line_count); */ + if (line_count != 4) { /* error */ + XDVI_WARNING((stderr, "Wrong number of items (%lu) in translations line:\n\"%.*s\" " + "(skipping this line).\n", + (unsigned long)line_count, (int)line_len, curr_p)); + free_items(line_items, line_count); + continue; + } + else { /* split first elem into menu description */ + size_t menu_len = strlen(line_items[0]); + size_t menu_count = 0; + char **menu_items; + if (menu_len == 0) { /* error */ + XDVI_WARNING((stderr, "Menu description (1st item) mustn't be empty:\n\"%.*s\" " + "(skipping this line).\n", + (int)line_len, curr_p)); + free_items(line_items, line_count); + continue; + } + menu_items = split_line(line_items[0], MENU_SEP, 0, menu_len, &menu_count); +/* show_items(" MENU", menu_items, menu_count); */ + + insert_items(info, menu_items, menu_count, line_items[1], line_items[2], line_items[3]); + free_items(menu_items, menu_count); + } + free_items(line_items, line_count); + } + } + show_button_info(0, *info); +} + +/* + Top-level routine: creates the pulldown menu buttons for Motif and Xaw. + For Motif, sets *globals.widgets.menu_bar to the address of the new widget created, + for Xaw, sets *width to the width of the button panel created. +*/ +void +create_menu_buttons(Widget parent, +#ifdef MOTIF + Widget *menu_bar +#else + int *width +#endif + ) +{ +#ifdef MOTIF + Widget menu = 0; +#else /* MOTIF */ + Widget panel = 0; + Boolean first_time = True; + int menu_depth = 0; +#endif + size_t i; + + m_button_info = xmalloc(sizeof *m_button_info); + m_button_info->elems = NULL; + m_button_info->size = 0; + +#ifdef MOTIF + *menu_bar = XmCreateMenuBar(parent, "menuBar", NULL, 0); + parse_button_translations(&m_button_info); +/* menu = xm_create_menu(*menu_bar, m_button_info); */ + for (i = 0; i < m_button_info->size; i++) { + menu = xm_create_menu(*menu_bar, + m_button_info->elems[i].title, + m_button_info->elems[i].mnemonic, + m_button_info->elems[i].submenu); + } + if (menu != 0) { + XtVaSetValues(*menu_bar, XmNmenuHelpWidget, menu, NULL); + } +#else /* MOTIF */ + if (first_time) { + first_time = False; + xaw_initialize_menu_bitmaps(); + panel = xaw_create_menu_widgets(parent); + parse_button_translations(&m_button_info); + } + xaw_create_menu(m_button_info, panel, menu_depth, width); +#endif /* MOTIF */ + initialize_menus(); +} +#endif /* NEW_MENU_CREATION || MOTIF */ diff --git a/Build/source/texk/xdvik/gui/menu.h b/Build/source/texk/xdvik/gui/menu.h new file mode 100644 index 00000000000..3c5f4487f1f --- /dev/null +++ b/Build/source/texk/xdvik/gui/menu.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2003-2004 the xdvik development team + * + * 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. + * + */ + +/* + * Common code for Xaw and Motif menu bar creation. + */ +#ifndef MENU_H_ +#define MENU_H_ + +#include "xdvi-config.h" +#include "events.h" +#include "xdvi.h" + +typedef enum { + BT_INVALID = -1, /* error code */ + BT_NONE = 0, + BT_PUSH, /* pushbutton */ + BT_RADIO, /* radio button (1 of n) */ + BT_CHECK, /* check button (m of n) */ + BT_SEP /* separator */ +} buttonTypeT; + + +/* structures for menu data */ +struct button_elems { + char *title; /* menu item label string */ + buttonTypeT type; /* button type */ + char mnemonic; /* 0 if none */ + char *accelerator; /* NULL if none */ + struct button_info *submenu;/* submenu, or NULL */ + Widget widget; /* the widget in this item, for later use, or 0 */ + struct xdvi_action *action; /* translated action, for later use, or NULL */ +}; + +struct button_info { + size_t size; + struct button_elems *elems; +}; + +#include "xm_menu.h" +#include "xaw_menu.h" + +extern void create_menu_buttons(Widget parent, +#ifdef MOTIF + Widget *child +#else + int *ret_panel_width +#endif + ); + +extern void set_menu(void *val, XtActionProc proc, Boolean (*cmp)()); + +#endif /* MENU_H_ */ diff --git a/Build/source/texk/xdvik/gui/message-window.c b/Build/source/texk/xdvik/gui/message-window.c new file mode 100644 index 00000000000..55e148daa1b --- /dev/null +++ b/Build/source/texk/xdvik/gui/message-window.c @@ -0,0 +1,1207 @@ +/*------------------------------------------------------------ +message-window.c: message popups for xdvi. + +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. + +------------------------------------------------------------*/ + + + +/* + +============================================ +Suggested Policy for using the GUI messages: +============================================ + +- Use the statusline for shorter messages, a message window for more + important messages or such where you'd like to give further help info + (see the `helptext' argument of popup_message()). When in doubt, + prefer the statusline (excessive use of popup windows is a nuisance + for the user). + +- Don't use any of the GUI messages to report internal workings of + the program; for important internal information, there should be a + debugging setting to print it to stderr. Use the GUI messages + only in situations such as the following: + + - to give the user feedback on actions that (s)he initiated + + - to indicate that an internal action causes a delay perceptible + by the user (as a rough guide: a delay of more than half a second) + + - to report situations that might require new actions by the user. + +*/ + +#include <ctype.h> + +#include "xdvi-config.h" +#include "xdvi.h" +#include "string-utils.h" + +/* Xaw specific stuff */ +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#ifdef MOTIF +# include <Xm/DialogS.h> +# include <Xm/MessageB.h> +# include <Xm/PushB.h> +# include <Xm/Label.h> +# include <Xm/Form.h> +# include <Xm/MenuShell.h> +# include <Xm/Protocols.h> +# include <Xm/AtomMgr.h> +#else +# include <X11/Xaw/Paned.h> +# include <X11/Xaw/Box.h> +# include <X11/Xaw/MenuButton.h> +# include <X11/Xaw/SimpleMenu.h> +# include <X11/Xaw/Sme.h> +# include <X11/Xaw/SmeBSB.h> +# include <X11/Xaw/AsciiText.h> +# include <X11/Xaw/Dialog.h> +#endif + +#include "kpathsea/c-vararg.h" +#include "xdvi.h" +#include "util.h" +#include "string-utils.h" +#include "x_util.h" +#include "message-window.h" + +/* have no more than MAX_POPUPS open simultaneously */ +#define MAX_POPUPS 10 + +/* offset for cascading popups */ +#define POPUP_OFFSET ((my_popup_num * 20)) + +#ifdef MOTIF +/* wrap messages after MSG_WRAP_LEN characters at whitespace */ +#define MSG_WRAP_LEN 60 +#endif + +/* array of active popups: */ +static int g_popup_array[MAX_POPUPS]; + +static Atom WM_DELETE_WINDOW; + +/* arrays for saving the widgets of multiple popups; + * same index as in g_popup_array: + */ +#ifdef MOTIF +static Widget popup_window[MAX_POPUPS], dialog[MAX_POPUPS]; +#else +static Widget popup_window[MAX_POPUPS], message_box[MAX_POPUPS], + message_text[MAX_POPUPS], message_paned[MAX_POPUPS], + message_ok[MAX_POPUPS], message_help[MAX_POPUPS], message_not_ok[MAX_POPUPS]; +#endif + +/* map popupMessageT's to strings/motif dialog elements */ +static struct message_map { + char *window_title; +#ifdef MOTIF + int motif_msg_type; +#endif +} my_msg_map[] = { + { "Xdvi Question" +#ifdef MOTIF + , XmDIALOG_QUESTION +#endif + }, /* MSG_QUESTION */ + { "Xdvi Help" +#ifdef MOTIF + , XmDIALOG_INFORMATION +#endif + }, /* MSG_HELP */ + { "Xdvi Info" +#ifdef MOTIF + , XmDIALOG_INFORMATION +#endif + }, /* MSG_INFO */ + { "Xdvi Warning" +#ifdef MOTIF + , XmDIALOG_WARNING +#endif + }, /* MSG_WARN */ + { "Xdvi Error" +#ifdef MOTIF + , XmDIALOG_ERROR +#endif + }, /* MSG_ERR */ +}; + +struct ok_or_cancel_cb { + message_cbT callback; /* callback function */ + XtPointer arg; /* arg for callback function */ +}; + +struct pre_ok_or_cancel_cb { + pre_message_cbT callback; /* callback function */ + XtPointer arg; /* arg for callback function */ +}; + +static struct ok_or_cancel_cb yes_callbacks[MAX_POPUPS]; +static struct ok_or_cancel_cb no_callbacks[MAX_POPUPS]; +static struct ok_or_cancel_cb cancel_callbacks[MAX_POPUPS]; +static struct pre_ok_or_cancel_cb pre_callbacks[MAX_POPUPS]; + + +static void +popdown_cancel(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + size_t idx; + + UNUSED(w); + UNUSED(event); + UNUSED(params); + UNUSED(num_params); + + ASSERT(*num_params == 1, "Wrong number of parameters in callback"); + idx = strtoul(*params, (char **)NULL, 10); + + /* First call pre_message_cb with window widget ID + * as additional parameter. + */ + if (pre_callbacks[idx].callback != NULL) { + pre_callbacks[idx].callback(popup_window[idx], pre_callbacks[idx].arg); + } + + /* Then pop down window and mark its position as free, then + * invoke the OK callback. The reason for this is that the callback + * may need to wait for open windows. + */ + XtPopdown(popup_window[idx]); + XtDestroyWidget(popup_window[idx]); + g_popup_array[idx] = 0; + XSync(DISP, True); + + /* invoke the callback if present */ + if (cancel_callbacks[idx].callback != NULL) { + cancel_callbacks[idx].callback(cancel_callbacks[idx].arg); + } +} + +#ifndef MOTIF +static void +xaw_popdown(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + size_t idx; + + UNUSED(w); + UNUSED(event); + UNUSED(params); + UNUSED(num_params); + + ASSERT(*num_params == 1, "Wrong number of parameters in callback"); + idx = strtoul(*params, (char **)NULL, 10); + + /* + NOTE: first pop down window and mark its position as free, then + invoke the callback. The reason for this is that the callback + may need to wait for open windows. + */ + XtPopdown(popup_window[idx]); + XtDestroyWidget(popup_window[idx]); + g_popup_array[idx] = 0; + XSync(DISP, True); +} + +#endif + +static XtActionsRec popdown_actions[] = { + {"close-popup-cancel", popdown_cancel }, +#if !MOTIF + {"WM_popdown", popdown_cancel }, + {"close-popup", xaw_popdown }, +#endif +}; + +static void +ok_action(Widget w, XtPointer client_data, XtPointer call_data) +{ + int idx = -1; + + UNUSED(call_data); + +#if MOTIF + UNUSED(client_data); + XtVaGetValues(w, XmNuserData, &idx, NULL); + ASSERT(idx >= 0, "Couldn't get idx from XmNuserData!"); +#else + UNUSED(w); + idx = (int)client_data; +#endif + +#if DEBUG + fprintf(stderr, "quit_action called for popup %d\n", idx); +#endif + ASSERT(idx >= 0 && idx < MAX_POPUPS, "Invalid widget index in ok_action()"); + + /* First call pre_message_cb with window widget ID + * as additional parameter. + */ + if (pre_callbacks[idx].callback != NULL) { + pre_callbacks[idx].callback(popup_window[idx], pre_callbacks[idx].arg); + } + + /* Then pop down window and mark its position as free, then + * invoke the OK callback. The reason for this is that the callback + * may need to wait for open windows. + */ + XtPopdown(popup_window[idx]); + XtDestroyWidget(popup_window[idx]); + g_popup_array[idx] = 0; + XSync(DISP, True); + + if (yes_callbacks[idx].callback != NULL) { + yes_callbacks[idx].callback(yes_callbacks[idx].arg); + } +} + +/*------------------------------------------------------------ + * help_action + * + * Arguments: + * Widget w, XtPointer call_data + * - (ignored) + * XtPointer client_data + * - the help string + * + * Returns: + * void + * + * Purpose: + * Callback for the `Help' button; opens another window + * containing the help text. The new window won't have + * another `Help' button. + *------------------------------------------------------------*/ + +static void +help_action(Widget w, XtPointer client_data, XtPointer call_data) +{ + UNUSED(call_data); + + /* open another window with the help text */ + popup_message(get_matching_parent(w, globals.widgets.top_level, "message_popup", NULL), MSG_HELP, NULL, client_data); +} + + +/* + * Callback for cancel button in choice_dialog. +*/ +static void +cancel_action(Widget w, XtPointer client_data, XtPointer call_data) +{ + int idx = -1; + + UNUSED(call_data); + +#if MOTIF + /* for motif, the index is in XmNuserData */ + UNUSED(client_data); + if (strcmp(XtName(w), Xdvi_MESSAGE_SHELL_NAME) == 0) { + /* invoked by the WM, get the messagebox child */ + Widget child; + if (get_widget_by_name(&child, w, Xdvi_MESSAGE_DIALOG_NAME, True)) { + XtVaGetValues(child, XmNuserData, &idx, NULL); + } + } + else { + XtVaGetValues(w, XmNuserData, &idx, NULL); + } + ASSERT(idx >= 0, "Couldn't get idx from XmNuserData!"); +#else + UNUSED(w); + idx = (int)client_data; +#endif + ASSERT(idx >= 0 && idx < MAX_POPUPS, "Invalid widget index in cancel_action()"); + + /* First call pre_message_cb with window widget ID + * as additional parameter. + */ + if (pre_callbacks[idx].callback != NULL) { + pre_callbacks[idx].callback(popup_window[idx], pre_callbacks[idx].arg); + } + + /* Then pop down window and mark its position as free, then + * invoke the OK callback. The reason for this is that the callback + * may need to wait for open windows. + */ + XtPopdown(popup_window[idx]); + XtDestroyWidget(popup_window[idx]); + g_popup_array[idx] = 0; + XSync(DISP, True); + + /* invoke the callback if present */ + if (cancel_callbacks[idx].callback != NULL) { + cancel_callbacks[idx].callback(cancel_callbacks[idx].arg); + } +} + +#if MOTIF +static void +not_ok_action(Widget w, XtPointer client_data, XtPointer call_data) +{ + /* Note: unmanage the parent of the button */ + int idx = -1; + + UNUSED(client_data); + + UNUSED(call_data); + XtVaGetValues(w, XmNuserData, &idx, NULL); + ASSERT(idx >= 0, "Couldn't get idx from XmNuserData!"); + ASSERT(idx >= 0 && idx < MAX_POPUPS, "Invalid widget index in ok_action()"); + + /* First call pre_message_cb with window widget ID + * as additional parameter. + */ + if (pre_callbacks[idx].callback != NULL) { + pre_callbacks[idx].callback(popup_window[idx], pre_callbacks[idx].arg); + } + + /* Then pop down window and mark its position as free, then + * invoke the OK callback. The reason for this is that the callback + * may need to wait for open windows. + */ + XtPopdown(popup_window[idx]); + XtDestroyWidget(popup_window[idx]); + g_popup_array[idx] = 0; + XSync(DISP, True); + + if (no_callbacks[idx].callback != NULL) { + no_callbacks[idx].callback(no_callbacks[idx].arg); + } +} +#endif /* MOTIF */ + +/*------------------------------------------------------------ + * create_dialogs + * + * Arguments: + * Widget toplevel - parent for the dialog window + * + * helptext - if not-NULL, create an additional + * `help' button + * + * cnt - number of current popup dialog + * + * Returns: + * void + * + * Purpose: + * Create message dialog widgets + *------------------------------------------------------------*/ + +static Widget +create_dialogs(popupMessageSizeHintT size, + Widget parent, + int cnt, + const char *helptext, + pre_message_cbT pre_cb, XtPointer arg, + const char *yes_button, message_cbT yes_cb, XtPointer yes_arg, + const char *no_button, message_cbT no_cb, XtPointer no_arg, + const char *cancel_button, message_cbT cancel_cb, XtPointer cancel_arg) +{ + Widget new_popup_window; + char *translations_str = NULL; +#ifdef MOTIF + Widget new_dialog; + UNUSED(size); +#else + char *key_translations_str = NULL; + int msg_w = 400, msg_h = 100; + Widget new_message_paned, new_message_text, new_message_box, new_message_ok, + new_message_help = 0, new_message_not_ok; + XtTranslations wm_translations, key_translations; +#endif + + /* save callbacks to global arrays */ + pre_callbacks[cnt].callback = pre_cb; + pre_callbacks[cnt].arg = arg; + + yes_callbacks[cnt].callback = yes_cb; + yes_callbacks[cnt].arg = yes_arg; + + no_callbacks[cnt].callback = no_cb; + no_callbacks[cnt].arg = no_arg; + + cancel_callbacks[cnt].callback = cancel_cb; + cancel_callbacks[cnt].arg = cancel_arg; + + XtAddActions(popdown_actions, XtNumber(popdown_actions)); + +#ifndef MOTIF + /* get index into WM_popdown arg */ + translations_str = get_string_va("<Message>WM_PROTOCOLS: WM_popdown(%d)", cnt); + wm_translations = XtParseTranslationTable(translations_str); + free(translations_str); +#endif + + if (!XtIsRealized(globals.widgets.top_level)) { + /* If toplevel window hasn't been realized yet, create a new toplevel shell + (otherwise, setting visual/color map wouldn't work); use same application names + so that resource settings will also apply to this window. + */ + new_popup_window = XtVaAppCreateShell("xdvi", "Xdvi", + transientShellWidgetClass, DISP, + NULL); + } + else { + new_popup_window = XtVaCreatePopupShell(Xdvi_MESSAGE_SHELL_NAME, +#ifdef MOTIF + xmDialogShellWidgetClass, parent, + XmNdeleteResponse, XmDO_NOTHING, /* we'll take care of that ourselves */ +#else + transientShellWidgetClass, parent, + XtNx, 60, + XtNy, 80, + XtNtranslations, wm_translations, + XtNaccelerators, G_accels_cr, +#endif + XtNtransientFor, parent, + XtNmappedWhenManaged, False, + NULL); + } + +#ifdef MOTIF + + WM_DELETE_WINDOW = XmInternAtom(XtDisplay(new_popup_window), "WM_DELETE_WINDOW", False); + XmAddWMProtocolCallback(new_popup_window, WM_DELETE_WINDOW, cancel_action, NULL); + + /* We also need to override the default ESC binding to use our internal + housekeeping functions */ + translations_str = get_string_va("#override\n<Key>osfCancel:close-popup-cancel(%d)", cnt); +/* { */ +/* XtTranslations xlats; */ +/* char *translation_str = get_string_va("<Key>osfCancel:close-popup-cancel(%d)", cnt); */ +/* xlats = XtParseTranslationTable(translation_str); */ +/* free(translation_str); */ +/* XtOverrideTranslations(new_dialog, xlats); */ +/* } */ + + new_dialog = XtVaCreateWidget(Xdvi_MESSAGE_DIALOG_NAME, xmMessageBoxWidgetClass, new_popup_window, + XmNdialogType, XmDIALOG_WARNING, /* default */ + XmNtraversalOn, True, + XmNhighlightOnEnter, True, + XmNuserData, cnt, + XmNtranslations, XtParseTranslationTable(translations_str), + NULL); + free(translations_str); + XtAddCallback(new_dialog, XmNokCallback, ok_action, NULL); + + if (no_button != NULL) { + Arg args[4]; + Widget b; + XmString b_str = XmStringCreateLocalized((char *)no_button); + XtSetArg(args[0], XmNlabelString, b_str); + b = XmCreatePushButton(new_dialog, "no_button", args, 1); + XtAddCallback(b, XmNactivateCallback, not_ok_action, NULL); + XtManageChild(b); + } + + if (cancel_button != NULL) { + XmString cancel_label = XmStringCreateLtoR((char *)cancel_button, G_charset); + XtVaSetValues(XmMessageBoxGetChild(new_dialog, XmDIALOG_CANCEL_BUTTON), + XmNlabelString, cancel_label, NULL); + XmStringFree(cancel_label); + XtAddCallback(new_dialog, XmNcancelCallback, cancel_action, NULL); + } + else { + XtUnmanageChild(XmMessageBoxGetChild(new_dialog, XmDIALOG_CANCEL_BUTTON)); + } + XtInstallAllAccelerators(new_dialog, + XmMessageBoxGetChild(new_dialog, XmDIALOG_OK_BUTTON)); + + if (helptext != NULL) { + XtAddCallback(new_dialog, XmNhelpCallback, help_action, (XtPointer)helptext); + } + else { + XtUnmanageChild(XmMessageBoxGetChild(new_dialog, XmDIALOG_HELP_BUTTON)); + } + + if (yes_button != NULL) { /* change `OK' button label */ + XmString yes_label; + yes_label = XmStringCreateLtoR((char *)yes_button, G_charset); + XtVaSetValues(XmMessageBoxGetChild(new_dialog, XmDIALOG_OK_BUTTON), + XmNlabelString, yes_label, NULL); + XmStringFree(yes_label); + } + + /* insert the new widgets into the global arrays */ + dialog[cnt] = new_dialog; + +#else /* MOTIF */ + switch (size) { + case SIZE_SMALL: + msg_w = 300; + msg_h = 100; + break; + case SIZE_MEDIUM: + msg_w = 430; + msg_h = 160; + break; + case SIZE_LARGE: + msg_w = 450; + msg_h = 180; + break; + } + WM_DELETE_WINDOW = XInternAtom(XtDisplay(new_popup_window), "WM_DELETE_WINDOW", False); + + new_message_paned = XtVaCreateManagedWidget("message_paned", panedWidgetClass, new_popup_window, + XtNaccelerators, G_accels_cr, + NULL); + + new_message_text = XtVaCreateManagedWidget("message_text", asciiTextWidgetClass, new_message_paned, +/* XtNheight, 100, */ +/* XtNwidth, 400, */ + XtNwidth, msg_w, + XtNheight, msg_h, + /* wrap horizontally instead of scrolling + * TODO: this won't work for the first widget instance? + */ + XtNwrap, XawtextWrapWord, + XtNscrollVertical, XAW_SCROLL_ALWAYS, + XtNeditType, XawtextRead, + XtNinput, True, + XtNdisplayCaret, False, + XtNleftMargin, 5, + XtNaccelerators, G_accels_cr, + NULL); + + /* box for the OK/Cancel button */ + new_message_box = XtVaCreateManagedWidget("message_box", formWidgetClass, new_message_paned, + /* resizing by user isn't needed */ + XtNshowGrip, False, + XtNdefaultDistance, 6, /* some padding */ + /* resizing the window shouldn't influence this box, + * but only the text widget + */ + XtNskipAdjust, True, + XtNaccelerators, G_accels_cr, + NULL); + + new_message_ok = XtVaCreateManagedWidget(yes_button == NULL ? "OK" : yes_button, + commandWidgetClass, new_message_box, + XtNtop, XtChainTop, + XtNbottom, XtChainBottom, + XtNleft, XtChainLeft, + XtNright, XtChainLeft, + XtNaccelerators, G_accels_cr, + NULL); + /* add quit_action callback for the "OK" button */ + /* FIXME: how to make accelerators be accepted by new_popup_window as well? */ + key_translations_str = get_string_va("<Key>q:close-popup-cancel(%d)\n" + "<Key>Return:close-popup-cancel(%d)\n" + "<Key>Escape:close-popup-cancel(%d)\n", + cnt, cnt, cnt); + key_translations = XtParseTranslationTable(key_translations_str); + free(key_translations_str); + XtOverrideTranslations(new_popup_window, key_translations); + XtOverrideTranslations(new_message_paned, key_translations); + XtOverrideTranslations(new_message_text, key_translations); + + XtInstallAllAccelerators(new_message_box, new_message_ok); + XtAddCallback(new_message_ok, XtNcallback, ok_action, (XtPointer)cnt); + + /* we create additional buttons in any case, + to make the sizing more consistent */ + new_message_help = XtVaCreateManagedWidget("Help", commandWidgetClass, new_message_box, + XtNtop, XtChainTop, + XtNfromHoriz, new_message_ok, + XtNbottom, XtChainBottom, + XtNleft, XtChainRight, + XtNright, XtChainRight, + XtNaccelerators, G_accels_cr, + NULL); + message_help[cnt] = new_message_help; + + /* add cancel button */ + new_message_not_ok = XtVaCreateManagedWidget(cancel_button == NULL ? "Cancel" : cancel_button, + commandWidgetClass, new_message_box, + XtNtop, XtChainTop, + XtNfromHoriz, new_message_ok, + XtNbottom, XtChainBottom, + XtNleft, helptext == NULL ? XtChainRight : XtChainLeft, + XtNright, helptext == NULL ? XtChainRight : XtChainLeft, + XtNaccelerators, G_accels_cr, + NULL); + message_not_ok[cnt] = new_message_not_ok; + + if (no_button != NULL) { + ASSERT(0, "third button not yet implemented in Xaw!!!"); + } + + + { /* set all buttons to same size */ + Dimension w1, w2, w3, max; + XtVaGetValues(new_message_ok, XtNwidth, &w1, NULL); + XtVaGetValues(new_message_help, XtNwidth, &w2, NULL); + XtVaGetValues(new_message_not_ok, XtNwidth, &w3, NULL); + max = w1; + if (w2 > max) + max = w2; + if (w3 > max) + max = w3; + XtVaSetValues(new_message_ok, XtNwidth, max, NULL); + XtVaSetValues(new_message_help, XtNwidth, max, NULL); + XtVaSetValues(new_message_not_ok, XtNwidth, max, NULL); + } + /* if helptext argument is not-NULL, add help_action callback, + else unmanage help button */ + if (helptext != NULL) { + XtAddCallback(new_message_help, XtNcallback, help_action, (XtPointer)helptext); + } + else { + XtUnmanageChild(new_message_help); + } + + if (cancel_button != NULL) { + XtAddCallback(new_message_not_ok, XtNcallback, cancel_action, (XtPointer)cnt); + } + else { + XtUnmanageChild(new_message_not_ok); + } + /* insert the new widgets into the global arrays */ + message_box[cnt] = new_message_box; + message_paned[cnt] = new_message_paned; + message_text[cnt] = new_message_text; + message_ok[cnt] = new_message_ok; + +#endif /* MOTIF */ + popup_window[cnt] = new_popup_window; + + return new_popup_window; +} + + +/* + * Popup a window with wrapped text in it. + * For Motif, the text is explicitly wrapped inside this method. + */ +static Widget +internal_popup_window(Widget parent, + popupMessageSizeHintT size, + popupMessageT type, + int x_coord, int y_coord, + const char *helptext, + char *msg_buf, +#ifndef MOTIF + const char *xaw_ret_action_str, +#endif + pre_message_cbT pre_cb, XtPointer arg, + const char *yes_button, message_cbT yes_cb, XtPointer yes_arg, + const char *no_button, message_cbT no_cb, XtPointer no_arg, + const char *cancel_button, message_cbT cancel_cb, XtPointer cancel_arg) +{ + char win_title[64]; + int my_popup_num = 0; +#ifdef MOTIF + XmString str; +#endif + Widget ret; + + ASSERT(type < (sizeof my_msg_map / sizeof my_msg_map[0]), "too few elements in my_msg_map"); + sprintf(win_title, my_msg_map[type].window_title); + +#if DEBUG + fprintf(stderr, "internal_popup_window called with prompt: \"%s\"\n", msg_buf); +#endif + + if (globals.widgets.top_level == 0) { + /* If toplevel window hasn't been create yet, dump messages to STDERR and return. + All text must be passed as arguments to fprintf (NOT puts), since they are + supposed to be printf-format strings (i.e. with doubled `%' to escape them) + */ + fprintf(stderr, "\n%s:\n", my_msg_map[type].window_title); + fprintf(stderr, msg_buf); + fprintf(stderr, "\n"); + if (helptext) { + fprintf(stderr, "---------- helptext ----------\n"); + fprintf(stderr, helptext); + fprintf(stderr, "\n---------- end of helptext ----------\n"); + } + return NULL; + } + /* search for first free position in g_popup_array */ + while (my_popup_num < MAX_POPUPS && (g_popup_array[my_popup_num] == 1)) { + my_popup_num++; + } + if (my_popup_num == MAX_POPUPS) { + /* already enough popups on screen, just dump it to stderr */ + fprintf(stderr, "%s:\n", win_title); + fprintf(stderr, msg_buf); + /* Note: If a mad function continues to open popups, this will + * stop after MAX_POPUPS, but open a new window for each + * window the user pops down. Maybe we ought to do something + * about this. + */ + return NULL; + } + else { + /* mark it as non-free */ + g_popup_array[my_popup_num] = 1; + } +#if DEBUG + fprintf(stderr, "first free position in g_popup_array: %d\n", my_popup_num); +#endif + + /* just to make sure ... */ + if (parent == NULL) + parent = globals.widgets.top_level; + + /* create a new set of widgets for the additional popup. */ + ret = create_dialogs(size, parent, + my_popup_num, + helptext, + pre_cb, arg, + yes_button, yes_cb, yes_arg, + no_button, no_cb, no_arg, + cancel_button, cancel_cb, cancel_arg); +#ifdef MOTIF + XtVaSetValues(popup_window[my_popup_num], XmNtitle, win_title, NULL); + XtVaSetValues(dialog[my_popup_num], XmNdialogType, my_msg_map[type].motif_msg_type, NULL); + { /* wrap message at space before MSG_WRAP_LEN */ + char *testwrap = msg_buf; + int ctr; + for (ctr = 0; *testwrap++; ctr++) { + if (*testwrap == '\n') { + ctr = 0; + } + else if (ctr > MSG_WRAP_LEN) { + size_t before_len = 0, after_len = 0; + char *before_ptr, *after_ptr; + before_ptr = after_ptr = testwrap; + /* try to find shortest sequence before or after point to wrap at; + this seems to give the most pleasing results. + */ + while (before_ptr > msg_buf && !isspace((int)*--before_ptr)) { + before_len++; + } + while (*after_ptr != '\0' && !isspace((int)*++after_ptr)) { + after_len++; + } + + if (before_len < after_len && isspace((int)*before_ptr)) { + /* use last in sequence of multiple spaces */ + while (isspace((int)*++before_ptr)) { ; } + /* back up, and wrap */ + *--before_ptr = '\n'; + ctr = 0; + } + else if (isspace((int)*after_ptr)) { + /* use last in sequence of multiple spaces */ + while (isspace((int)*++after_ptr)) { ; } + /* back up, and wrap */ + *--after_ptr = '\n'; + ctr = 0; + } + } + } + } + str = XmStringCreateLtoR((char *)msg_buf, G_charset); + XtVaSetValues(dialog[my_popup_num], + XmNmessageString, str, + XmNtraversalOn, True, + XmNhighlightOnEnter, True, + NULL); + XmStringFree(str); + + XtManageChild(dialog[my_popup_num]); + + if (x_coord > 0 && y_coord > 0) { + position_window(XtParent(dialog[my_popup_num]), (Position)x_coord, (Position)y_coord); + } + + XtPopup(XtParent(dialog[my_popup_num]), XtGrabNone); +/* XtPopup(XtParent(dialog[my_popup_num]), XtGrabExclusive); */ + +#else /* MOTIF */ + + /* add a binding of xaw_ret_action_str to <Return> to relevant widgets. + The callbacks (xaw_ret_action_str) are responsible for parsing the + passed arguments (pointers, or empty arguments). + */ + if (xaw_ret_action_str != NULL) { + XtTranslations xlats; + char *translation_str; + + if (yes_arg != NULL) + translation_str = get_string_va("<Key>Return:close-popup(%d)%s(%p)", + my_popup_num, xaw_ret_action_str, yes_arg); + else + translation_str = get_string_va("<Key>Return:close-popup(%d)%s()", + my_popup_num, xaw_ret_action_str); + + xlats = XtParseTranslationTable(translation_str); + free(translation_str); + XtOverrideTranslations(popup_window[my_popup_num], xlats); + XtOverrideTranslations(message_paned[my_popup_num], xlats); + XtOverrideTranslations(message_text[my_popup_num], xlats); + } + + XtVaSetValues(popup_window[my_popup_num], XtNtitle, win_title, NULL); + XtVaSetValues(message_text[my_popup_num], XtNstring, msg_buf, NULL); + XtRealizeWidget(popup_window[my_popup_num]); + + XSetWMProtocols(XtDisplay(popup_window[my_popup_num]), XtWindow(popup_window[my_popup_num]), + &WM_DELETE_WINDOW, 1); + + if (x_coord <= 0 || y_coord <= 0) + center_window(popup_window[my_popup_num], parent); + else + position_window(popup_window[my_popup_num], (Position)x_coord, (Position)y_coord); + + if (my_popup_num > 0) { + /* some window managers position new windows exactly above the + existing one; to prevent this, move it with some offset + from the previous one: */ + Position x = 0, y = 0; + XtVaGetValues(popup_window[my_popup_num-1], XtNx, &x, XtNy, &y, NULL); + XtVaSetValues(popup_window[my_popup_num], XtNx, x + POPUP_OFFSET, XtNy, y + POPUP_OFFSET, NULL); + + } + XtPopup(popup_window[my_popup_num], XtGrabNone); +/* XtPopup(XtParent(popup_window[my_popup_num]), XtGrabExclusive); */ + if (XtIsManaged(message_not_ok[my_popup_num]) && XtIsManaged(message_help[my_popup_num])) { + /* center the help button. This is something of a sham, since it won't + survive resizing; but in general most users won't resize dialogs ;-) */ + Position x1, x2, bw; + int w, dist; + + XtVaGetValues(message_ok[my_popup_num], XtNx, &x1, XtNwidth, &w, XtNborderWidth, &bw, NULL); + XtVaGetValues(message_help[my_popup_num], XtNx, &x2, NULL); + /* following formula is measured, not calculated - + I have no idea why it's e.g. 2 * w, not 1.5 * w ... */ + dist = (x2 - x1 - 2 * w) / 2 - 2 * bw; + XtVaSetValues(message_not_ok[my_popup_num], XtNhorizDistance, dist, NULL); + } + +#endif /* MOTIF */ + return ret; +} + + +/*------------------------------------------------------------ + * popup_message + * + * Arguments: + * popupMessageT - info, warning, error etc; see message-window.h for details + * + * char *helptext + * - if not-null, this will add a `Help' + * button to the message widget that pops + * up another message widget containing + * <helptext>. + * + * + * char *msg, ... + * - format string followed by a variable + * number of arguments to be formatted. + * + * Returns: + * void + * + * Purpose: + * Pop up a message window containing <msg>. + * If there are already n popups open, will open + * a new one unless n >= MAX_POPUPS. + *------------------------------------------------------------*/ + +Widget +popup_message(Widget parent, popupMessageT type, const char *helptext, const char *format, ...) +{ + char *msg_buf = NULL; + Widget w; + + XDVI_GET_STRING_ARGP(msg_buf, format); + + w = internal_popup_window(parent, + SIZE_SMALL, + type, + -1, -1, /* just center it */ + helptext, msg_buf, +#ifndef MOTIF + NULL, +#endif + /* no special callbacks here */ + NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + free(msg_buf); + return w; +} + +Widget +popup_message_sized(Widget parent, + popupMessageT type, + popupMessageSizeHintT sizehint, + const char *helptext, + const char *format, ...) +{ + char *msg_buf = NULL; + Widget w; + + XDVI_GET_STRING_ARGP(msg_buf, format); + + w = internal_popup_window(parent, + sizehint, + type, + -1, -1, /* just center it */ + helptext, msg_buf, +#ifndef MOTIF + NULL, +#endif + /* empty callbacks */ + NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + free(msg_buf); + return w; +} + +Widget +positioned_popup_message(Widget parent, + popupMessageT type, + int x, int y, + const char *helptext, const char *format, ...) +{ + char *msg_buf = NULL; + Widget w; + + XDVI_GET_STRING_ARGP(msg_buf, format); + + w = internal_popup_window(parent, + SIZE_SMALL, + type, + x, y, /* position at these coordinates */ + helptext, msg_buf, +#ifndef MOTIF + NULL, +#endif + /* empty callbacks */ + NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + free(msg_buf); + return w; +} + +Widget +choice_dialog(Widget parent, + popupMessageT type, + const char *helptext, +#ifndef MOTIF + const char *xaw_ret_action_str, +#endif + pre_message_cbT pre_cb, XtPointer arg, + const char *ok_label, message_cbT ok_cb, XtPointer ok_arg, + const char *cancel_label, message_cbT cancel_cb, XtPointer cancel_arg, + const char *format, ...) +{ + char *msg_buf = NULL; + Widget w; + + XDVI_GET_STRING_ARGP(msg_buf, format); + + w = internal_popup_window(parent, + SIZE_SMALL, + type, + -1, -1, /* just center it */ + helptext, msg_buf, +#ifndef MOTIF + xaw_ret_action_str, +#endif + pre_cb, arg, + ok_label, ok_cb, ok_arg, + NULL, NULL, NULL, + cancel_label, cancel_cb, cancel_arg); + free(msg_buf); + return w; +} + +#if MOTIF +Widget +choice3_dialog(Widget parent, + popupMessageT type, + const char *helptext, + pre_message_cbT pre_cb, XtPointer arg, + const char *yes_label, message_cbT yes_cb, XtPointer yes_arg, + const char *no_label, message_cbT no_cb, XtPointer no_arg, + const char *cancel_label, message_cbT cancel_cb, XtPointer cancel_arg, + const char *format, ...) +{ + char *msg_buf = NULL; + Widget w; + + XDVI_GET_STRING_ARGP(msg_buf, format); + + w = internal_popup_window(parent, + SIZE_SMALL, + type, + -1, -1, /* just center it */ + helptext, msg_buf, + pre_cb, arg, + yes_label, yes_cb, yes_arg, + no_label, no_cb, no_arg, + cancel_label, cancel_cb, cancel_arg); + free(msg_buf); + return w; +} +#endif + +Widget +choice_dialog_sized(Widget parent, + popupMessageT type, + popupMessageSizeHintT sizehint, + const char *helptext, +#ifndef MOTIF + const char *xaw_ret_action_str, +#endif + pre_message_cbT pre_cb, XtPointer arg, + const char *ok_label, message_cbT ok_cb, XtPointer ok_arg, + const char *cancel_label, message_cbT cancel_cb, XtPointer cancel_arg, + const char *format, ...) +{ + char *msg_buf = NULL; + Widget w; + + XDVI_GET_STRING_ARGP(msg_buf, format); + + w = internal_popup_window(parent, + sizehint, + type, + -1, -1, /* just center it */ + helptext, msg_buf, +#ifndef MOTIF + xaw_ret_action_str, +#endif + pre_cb, arg, + ok_label, ok_cb, ok_arg, + NULL, NULL, NULL, + cancel_label, cancel_cb, cancel_arg); + free(msg_buf); + return w; +} + +Widget +positioned_choice_dialog(Widget parent, + popupMessageT type, + int x_pos, int y_pos, + const char *helptext, +#ifndef MOTIF + const char *xaw_ret_action_str, +#endif + pre_message_cbT pre_cb, XtPointer arg, + const char *ok_label, message_cbT ok_cb, XtPointer ok_arg, + const char *cancel_label, message_cbT cancel_cb, XtPointer cancel_arg, + const char *format, ...) +{ + char *msg_buf = NULL; + Widget w; + + XDVI_GET_STRING_ARGP(msg_buf, format); + + w = internal_popup_window(parent, + SIZE_SMALL, + type, + x_pos, y_pos, + helptext, msg_buf, +#ifndef MOTIF + xaw_ret_action_str, +#endif + pre_cb, arg, + ok_label, ok_cb, ok_arg, + NULL, NULL, NULL, + cancel_label, cancel_cb, cancel_arg); + free(msg_buf); + return w; +} + +void +warn_overstrike(void) +{ + static Boolean warned_overstrike = False; + + if (!warned_overstrike) { + popup_message(globals.widgets.top_level, + MSG_WARN, + /* helptext */ + "Greyscaling is running in copy mode; this will cause overstrike characters to " + "appear incorrectly, and may result in poor display quality. " + "Possible fixes are:\n" + "- Use the ``-thorough'' command-line option.\n" + "- Quit some other color-hungry applications (e.g. Netscape).\n" + "- Use the ``-install'' command-line option.\n" + "See the section ``GREYSCALING AND COLORMAPS'' in the " + "xdvi manual page for more details.", + /* text */ + "Couldn't allocate enough colors - expect low display quality."); + warned_overstrike = True; + } +} + +Boolean +is_message_window(Widget w) +{ + int i; + for (i = 0; i < MAX_POPUPS; i++) { + if (w == popup_window[i]) + return True; + } + return False; +} + +Boolean +kill_message_window(Widget w) +{ + int i; + for (i = 0; i < MAX_POPUPS; i++) { + if (g_popup_array[i] != 0 && XtIsRealized(popup_window[i]) && w == popup_window[i]) { + g_popup_array[i] = 0; + XtPopdown(popup_window[i]); + XtDestroyWidget(popup_window[i]); + XSync(DISP, True); + return True; + } + } + return False; +} + +/* + Raise any popups that currently exist; return True iff such popups + exist, else False. +*/ +Boolean +raise_message_windows(void) +{ + int i; + Boolean have_popups = False; + + for (i = 0; i < MAX_POPUPS; i++) { + if (g_popup_array[i] != 0 && XtIsRealized(popup_window[i])) { + XRaiseWindow(DISP, XtWindow(popup_window[i])); + have_popups = True; + } + } + + return have_popups; +} diff --git a/Build/source/texk/xdvik/gui/message-window.h b/Build/source/texk/xdvik/gui/message-window.h new file mode 100644 index 00000000000..7e38197abbc --- /dev/null +++ b/Build/source/texk/xdvik/gui/message-window.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2002-2004 the xdvik development team + * + * 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. + */ + +#ifndef MESSAGE_WINDOW_H_ +#define MESSAGE_WINDOW_H_ + +#define Xdvi_MESSAGE_DIALOG_NAME "dialog" +#define Xdvi_MESSAGE_SHELL_NAME "message_popup" + + +/* + * Callback before the message window is closed. It is passed + * the message window widget ID, and the next XtPointer argument + * in the message dialog calls. + */ +typedef void (*pre_message_cbT)(Widget w, XtPointer arg); +/* + * Additional callbacks passed in as argument. It is passed + * the next XtPointer argument in the message dialog calls. + */ +typedef void (*message_cbT)(XtPointer arg); + +/* preferred size of dialogs, only used for Xaw ... not really hints, but + hard-coded values ;-) */ +typedef enum popupMessageSizeHintT_ { SIZE_SMALL, SIZE_MEDIUM, SIZE_LARGE } popupMessageSizeHintT; +typedef enum popupMessageT_ { MSG_QUESTION, MSG_HELP, MSG_INFO, MSG_WARN, MSG_ERR } popupMessageT; + +extern Widget popup_message(Widget parent, + popupMessageT type, + const char *helptext, + const char *format, ...); + +extern Widget popup_message_sized(Widget parent, + popupMessageT type, + popupMessageSizeHintT size, + const char *helptext, + const char *format, ...); + +extern Widget positioned_popup_message(Widget parent, + popupMessageT type, + int x, int y, + const char *helptext, const char *format, ...); + +extern Widget choice_dialog(Widget parent, + popupMessageT type, + const char *helptext, +#ifndef MOTIF + const char *ret_action_str, +#endif + pre_message_cbT pre_cb, XtPointer arg, + const char *ok_label, message_cbT ok_cb, XtPointer ok_args, + const char *cancel_label, message_cbT cancel_cb, XtPointer cancel_args, + const char *format, ...); + +#if MOTIF +/* 3 buttons currently only implemented for Motif */ +extern Widget choice3_dialog(Widget parent, + popupMessageT type, + const char *helptext, + pre_message_cbT pre_cb, XtPointer arg, + const char *yes_label, message_cbT yes_cb, XtPointer yes_args, + const char *no_label, message_cbT no_cb, XtPointer no_args, + const char *cancel_label, message_cbT cancel_cb, XtPointer cancel_args, + const char *format, ...); +#endif + +extern Widget choice_dialog_sized(Widget parent, + popupMessageT type, + popupMessageSizeHintT size, + const char *helptext, +#ifndef MOTIF + const char *ret_action_str, +#endif + pre_message_cbT pre_cb, XtPointer arg, + const char *ok_label, message_cbT ok_cb, XtPointer ok_args, + const char *cancel_label, message_cbT cancel_cb, XtPointer cancel_args, + const char *format, ...); + +extern Widget positioned_choice_dialog(Widget parent, + popupMessageT type, + int x_pos, int y_pos, + const char *helptext, +#ifndef MOTIF + const char *ret_action_str, +#endif + pre_message_cbT pre_cb, XtPointer arg, + const char *ok_label, message_cbT ok_cb, XtPointer ok_args, + const char *cancel_label, message_cbT cancel_cb, XtPointer cancel_args, + const char *format, ...); +extern void warn_overstrike(void); +extern Boolean raise_message_windows(void); +extern Boolean is_message_window(Widget w); +extern Boolean kill_message_window(Widget w); + +/* XawtextScrollWhenNeeded has been ditched in Xaw7 + (e.g. Xfree >= 4.x); from the man page: + + The value XawtextScrollWhenNeeded (and whenNeeded, recognized by + the converter), is accepted for backwards compatibilty with + resource specifications written for the Xaw6 Text widget, but + ignored (effectively treated as XawtextScrollNever). + + So we're forced to use `scrollAlways' here. +*/ +#define XAW_SCROLL_ALWAYS XawtextScrollAlways + +/* the reconfig stuff has been fixed + in XFree 4.1.0, vendor release 6510 (Slackware 8.0) */ +#define BROKEN_RECONFIG (( \ + (strstr(ServerVendor(DISP), "XFree") != NULL) \ + && VendorRelease(DISP) >= 4000 \ + && VendorRelease(DISP) < 4002 \ + )) + +#endif /* MESSAGE_WINDOW_H_ */ diff --git a/Build/source/texk/xdvik/gui/pagesel.c b/Build/source/texk/xdvik/gui/pagesel.c new file mode 100644 index 00000000000..b3db730e481 --- /dev/null +++ b/Build/source/texk/xdvik/gui/pagesel.c @@ -0,0 +1,1591 @@ +/* + * Page selector for xdvi + * + * Copyright (c) 2001-2004 xdvik development team + * + * This code is derived from the page selector in xdvik-j, and + * parts of it are Copyright (c) 1993, 1995 + * MATSUURA Syun syun@fuka.info.waseda.ac.jp + * HIRAHARA Atsushi hirahara@fuka.info.waseda.ac.jp + * ONO Kouichi onono@fuka.info.waseda.ac.jp + * All rights reserved. + * + * + * (SU: I was unsure how to interpret the `All rights reserved' in the + * previous line, so emailed Ono Kouichi about this. Here's a + * verbatim quote of the relevant part of his answer (which was CC'ed + * to Hirahara Atsushi - all three of them had left Waseda university + * around '95): + * + * You can modify, embed, copy and distribute a part of or the + * entire of our source code when you specify our copyright in your + * xdvik version. + * + * IANAL, but I think this is compatible with the X consortium + * license, which follows.) + * + * 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. + * + */ + +/* + BUGS: + + - with Xaw, the highlighting for the selected page (XawListHighlight) + vanishes when mouse is dragged outside the widget and then released + (but the respective page is selected, which is IMHO the desired behaviour). + + - with Xaw, scrolling the list with PgUp/PgDown until the current page + gets `out of focus' should un-highlight the current page number + + - The ASCII-based marks are *ugly*. Pixmaps (for the marked state) + would be better. The viewer gv has one (but it's Xaw only). Some + file directory widgets like + e.g. http://ftp.xfree86.org/pub/X.Org/contrib/widgets/ListTree-3.0b3.tar.gz + also have facilities for that, but most suffer from other + inadequacies (e.g. no such ting as browseSelection) and all kinds + of bitrot ... Another alternative would be using XmContainer + (see e.g. the `filemanager' example in demos/programs/filemanagers + in the openmotif distribution), but that's available for Motif >= 2.1 only. + +*/ + +#include "xdvi-config.h" +#include "xdvi.h" + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Shell.h> + +#include "xm_toolbar.h" +#include "xm_menu.h" +#include "xaw_menu.h" + +#include "x_util.h" + +#ifdef MOTIF +# include <Xm/Xm.h> +# include <Xm/List.h> +# include <Xm/ScrollBar.h> /* for XmScrollBarGetValues */ +#else /* MOTIF */ +# include <X11/Xaw/Dialog.h> +# include <X11/Xaw/Cardinals.h> +# include <X11/Xaw/Command.h> +# include <X11/Xaw/List.h> +# include <X11/Xaw/Viewport.h> +#endif /* MOTIF */ + +#include "message-window.h" +#include "pagesel.h" +#include "util.h" +#include "string-utils.h" +#include "dvi-init.h" +#include "statusline.h" +#include "events.h" +#include "print-dialog.h" +#include "search-internal.h" +#include "pagehist.h" + +#define PAGENUMLEN 128 +#define SCROLL_LIST_SCROLLBAR 0 + +#ifndef MAX_PAGE +# define MAX_PAGE (1024) +#endif /* MAX_PAGE */ + +#define LONGESTPAGENUM 55 + + +/* for saving the GC of the pagelist widget + (when un-highlighting items in highlight_page_callback, and + drawing the `current' marker) +*/ +static struct page_gc { + GC fore; + GC back; +} m_page_gc; + +#define MOTIF_IDX_OFFSET 1 /* motif index starts at 1, not 0 */ +#if !defined(LESSTIF_VERSION) +static Boolean my_list_pos_to_bounds(Widget widget, int idx, Position *x, Position *y, Dimension *w, Dimension *h); +static void refresh_highlight_marker(Widget widget, GC gc, Position x, Position y, Dimension w, Dimension h); +#endif /* !defined(LESSTIF_VERSION) */ + + + +#ifdef MOTIF + +/* make button2 mark instead of drag&drop */ +static void xm_list_set_mark(Widget widget, XEvent *event, String *params, Cardinal *num_params); +static void xm_list_drag_mark(Widget widget, XEvent *event, String *params, Cardinal *num_params); +static XtActionsRec CustomListActions[] = { + { "ListSetMark", xm_list_set_mark }, + { "ListDragMark", xm_list_drag_mark }, +}; +static char *motif_custom_translations = + "#override \n" + "s <Btn2Down>: ListDragMark(ListButtonMotion)\n" + "<Btn2Motion>: ListSetMark(ListButtonMotion)\n" + "<Btn2Up>: ListSetMark(ListButtonMotion)\n" + "<Btn2Down>: ListSetMark(ListButtonMotion)\n" +/* /\* "s ~m ~a <Btn2Down>: ListMyProcessBtn2(ListBeginExtend)\n" *\/ */ +/* /\* "s ~m ~a <Btn2Up>: ListMyProcessBtn2(ListEndExtend)\n" *\/ */ +/* /\* "~c ~s ~m ~a <Btn2Down>: ListMyProcessBtn2(ListBeginSelect)\n" *\/ */ +/* /\* "~c ~s ~m ~a <Btn2Up>: ListMyProcessBtn2(ListEndSelect)\n" *\/ */ +/* /\* "c ~s ~m ~a <Btn2Down>: ListMyProcessBtn2(ListBeginToggle)\n" *\/ */ +/* /\* "c ~s ~m ~a <Btn2Up>: ListMyProcessBtn2(ListEndToggle)\n" *\/ */ +/* /\* "c ~s ~m a <Btn2Down>: ListProcessDrag()\n" *\/ */ +/* /\* "~c s ~m a <Btn2Down>: ListProcessDrag()\n" *\/ */ +; + +#define LIST_WIDGET page_list +/* motif pagenumber is a string */ +static const char* const pageno_format = "%c %s "; + +#else /* MOTIF */ + +static int view_y; +extern Widget panel_widget; +static Widget list_widget = NULL; +static Widget viewport = NULL; +#define LIST_WIDGET list_widget +/* Xaw pagenumber is an integer, and we need to left-pad it */ +static const char* const pageno_format = "%c %*d "; + +static int xaw_maybe_scroll_pagelist(int new_page, Boolean force_recenter, int old); + +#define REDRAW_CURRENT_MARKER_HACK 1 + + +/* + The following hack tries to address the following 2 bugs with the + self-made page highlighting marker: + + - the marker overlaps with the ordinary XawListHighlight marker; + when un-highlighting a page, 1 pixel (vertically) at the edge of + the ordinary marker is overdrawn. + + - When the XawListHighlight crosses the self-drawn rectangle, + the vertical bars remain visible, but the horizontal bars + are erased. + + The hack just redraws the appropriate items whenever one of + the above can happen, i.e. when the two markers are 2 or less + pages apart from each other. +*/ +#if REDRAW_CURRENT_MARKER_HACK +/* Store index of currently marked (with our own marker) list + item, or -1 if none is marked. */ +static int g_current_highlighted = -1; + +/* redraw the default Xaw list highlight (XawListHighlight()) */ +static void +xaw_maybe_redraw_highlight(int idx) +{ + XawListReturnStruct *ret; + int high; + + if (LIST_WIDGET == NULL) + return; + + ret = XawListShowCurrent(LIST_WIDGET); + high = ret->list_index; + if (high != XAW_LIST_NONE && abs(idx - (high + MOTIF_IDX_OFFSET)) <= 2) { + /* re-highlight it */ + XawListHighlight(LIST_WIDGET, high); + } + g_current_highlighted = -1; +} + +/* redraw our own rectangle highlight marker: */ +static void +xaw_maybe_redraw_current_marker(int idx) +{ + Position x, y; + Dimension w, h; + +/* fprintf(stderr, "idx: %d, high: %d; diff: %d\n", idx + MOTIF_IDX_OFFSET, g_current_highlighted, */ +/* abs(idx + MOTIF_IDX_OFFSET - g_current_highlighted)); */ + if (abs((idx + MOTIF_IDX_OFFSET) - g_current_highlighted) <= 2 + && my_list_pos_to_bounds(LIST_WIDGET, g_current_highlighted, &x, &y, &w, &h)) { + refresh_highlight_marker(LIST_WIDGET, m_page_gc.fore, x, y, w, h); + } +} +#endif /* REDRAW_CURRENT_MARKER_HACK */ + +#endif /* MOTIF */ + +/* + * Table of page offsets in DVI file, indexed by page number - 1, + * marked pages, and page sizes. + * Initialized in prepare_pages(). + */ +struct page_index { + long offset; + int number; + Boolean marked; + unsigned int pw, ph; /* page size */ + unsigned int ww, wh; /* window size */ +}; + +struct page_index_info { + struct page_index *index; /* above struct */ + size_t index_size; /* size of currently allocated index */ + char **page_labels; /* label strings */ +}; + +static struct page_index_info page_info; + +/* access functions used by dvi-draw.c and dvi-init.c */ +long +pageinfo_get_offset(int page) +{ + ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range"); +/* fprintf(stderr, "offset for page %d is %ld\n", page, page_info.index[page].offset); */ + return page_info.index[page].offset; +} + +/* access functions used by dvi-draw.c and dvi-init.c */ +unsigned int +pageinfo_get_page_width(int page) +{ + ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range"); + return page_info.index[page].pw; +} + +unsigned int +pageinfo_get_page_height(int page) +{ + ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range"); + return page_info.index[page].ph; +} + +unsigned int +pageinfo_get_window_width(int page) +{ + ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range"); + return page_info.index[page].ww; +} + +unsigned int +pageinfo_get_window_height(int page) +{ + ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range"); + return page_info.index[page].wh; +} + +void +pageinfo_set_page_width(int page, unsigned int width) +{ + ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range"); + page_info.index[page].pw = width; +} + +void +pageinfo_set_page_height(int page, unsigned int height) +{ + ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range"); + page_info.index[page].ph = height; +} + +void +pageinfo_set_window_width(int page, unsigned int width) +{ + ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range"); + page_info.index[page].ww = width; +} + +void +pageinfo_set_window_height(int page, unsigned int height) +{ + ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range"); + page_info.index[page].wh = height; +} + +int +pageinfo_get_number(int page) +{ + ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range"); + return page_info.index[page].number; +} + +/* search for page with TeX number `number', and return its index, or -1 if it's not found. */ +int +pageinfo_get_index_of_number(int number) +{ + size_t i; + for (i = 0; i < page_info.index_size - 1; i++) { + if (number == page_info.index[i].number) + return i; + } + return -1; +} + +void +pageinfo_set_offset(int index, long offset) +{ + ASSERT(index >= 0 && index < (int)page_info.index_size, ""); + page_info.index[index].offset = offset; +} + +void +pageinfo_set_number(int index, int number) +{ + ASSERT(index >= 0 && index < (int)page_info.index_size, ""); + page_info.index[index].number = number; +} + +void +pageinfo_allocate(int total_pages) +{ + int i; + page_info.index = xmalloc(total_pages * sizeof *(page_info.index)); + for (i = 0; i < total_pages; i++) { + page_info.index[i].marked = False; + } + /* following initializations are handled by the respective Motif/Xaw functions */ + page_info.page_labels = NULL; + page_info.index_size = total_pages; +} + +/* + Deallocate page_info. NOTE: We mustn't free the page_labels here, + since the page list might survive quite some time (e.g. while fonts + for the new file are being generated) and needs the labels. +*/ +void +pageinfo_deallocate(void) +{ + free(page_info.index); + page_info.index_size = 0; + page_info.index = NULL; +} + +#ifdef MOTIF +void +toggle_pagelist(void) +{ + Dimension curr_w, curr_x; + XtVaGetValues(globals.widgets.main_window, + XmNwidth, &curr_w, + XmNx, &curr_x, + NULL); + + if ((resource.expert_mode & XPRT_SHOW_PAGELIST) != 0) { + XtManageChild(XtParent(page_list)); + XtManageChild(page_list); + + curr_x += resource.pagelist_width; + curr_w -= resource.pagelist_width; + XtVaSetValues(globals.widgets.main_window, + XtNwidth, curr_w, + XtNx, curr_x, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, XtParent(page_list), + NULL); + } + else { + XtUnmanageChild(XtParent(page_list)); + XtUnmanageChild(page_list); + + curr_x -= resource.pagelist_width; + curr_w += resource.pagelist_width; + XtVaSetValues(globals.widgets.main_window, + XmNwidth, curr_w, + XmNx, curr_x, + XmNleftAttachment, XmATTACH_FORM, + NULL); + } + +#if defined(NEW_MENU_CREATION) || defined(MOTIF) + set_menu(&resource.expert_mode, Act_set_expert_mode, check_resource_expert); +#else + set_show_pagelist_option(); +#endif +} +#endif + + +Boolean +pageinfo_have_marked_pages(void) +{ + int i; + for (i = 0; i < total_pages; i++) { + if (page_info.index[i].marked) { + return True; + } + } + return False; +} + + +/* return True if page i is marked, False else */ +Boolean +pageinfo_is_marked(int i) +{ + ASSERT(i <= (int)page_info.index_size, ""); + return page_info.index[i].marked; +} + + +typedef enum { SCROLL_UP, SCROLL_DOWN, CLICK } saveCmdT; + + +static void internal_process_button2(Widget widget, XEvent *event); +static int get_item_index(Widget w, int mouse_y); + +static int +get_page_size(void) +{ + int offset = 0; + int min_page = 0; + int max_page = 0; + int min_pageno_len = 0; + int max_pageno_len = 0; + int i; + + if (globals.dvi_file.bak_fp == NULL) + return 0; + + for (i = 0; i < total_pages; i++) { + max_page = MAX(page_info.index[i].number, max_page); + min_page = MIN(page_info.index[i].number, min_page); + } + + if (min_page >= 0) { + offset = 0; /* plus symbol is hidden */ + } else { + offset = 1; /* offset for minus symbol */ + min_page = -min_page; + } + for (min_pageno_len = offset; min_page > 0; + min_page /= 10, min_pageno_len++); + + if (max_page >= 0) { + offset = 0; /* plus symbol is hidden */ + } else { + offset = 1; /* offset for minus symbol */ + max_page = -max_page; + } + for (max_pageno_len = offset; max_page > 0; + max_page /= 10, max_pageno_len++); + + return MAX(min_pageno_len, max_pageno_len); + /* Plus 1 for minus symbol */ +} + +#if !defined(LESSTIF_VERSION) +/* draw or erase highlight marker at position x, y with widht w an height h, + using suitable offsets (for sake of consistency of the latter, and because + of the differences Xaw/Motif, this is a separate function). + */ +static void +refresh_highlight_marker(Widget widget, GC gc, + Position x, Position y, Dimension w, Dimension h) +{ + XDrawRectangle(XtDisplay(widget), XtWindow(widget), gc, +#ifdef MOTIF + x + 1, y + 1, w - 3, h - 3 +#else + x + 2, y, w - 1, h - 1 +#endif + ); +} +#endif /* !defined(LESSTIF_VERSION) */ + +#ifndef MOTIF + +/* update (redisplay) the list, saving the currently highlighted item. + * This is invoked whenever marking the page, and the re-construction of the + * entire list causes considerable flicker; but I guess that's unavoidable + * with the current simplistic labelling scheme (with changing the list items + * themselves). Gv does this considerably better (using custom widgets). + */ +static void +xaw_update_list(void) +{ + static int pagelist_width = -1; + static int total_pages_bak = -1; + + XawListReturnStruct *ret; + int idx, button_width; + + if (pagelist_width == -1 || total_pages != total_pages_bak) { + pagelist_width = xaw_get_pagelist_size(); + total_pages_bak = total_pages; + } + + /* save selected item */ + ret = XawListShowCurrent(LIST_WIDGET); + idx = ret->list_index; + button_width = get_panel_width() - 2 * (resource.btn_side_spacing + resource.btn_border_width); + /* delete and re-create list */ + ASSERT(total_pages <= (int)page_info.index_size, ""); + XawListChange(LIST_WIDGET, page_info.page_labels, 0, + MAX(button_width, pagelist_width), False); + /* restore selected item */ + if (idx != XAW_LIST_NONE) { + XawListHighlight(LIST_WIDGET, idx); + } +} + +/* + return height of a row in the list widget, and the initial offset of XtNinternalHeight + in parameters row_height and internal_h +*/ +static void +xaw_get_row_height(Widget w, Dimension *row_height, Dimension *internal_h) +{ + Dimension row_space; + XFontStruct *font; + Arg arglist[5]; + int i = 0; + + if (w == NULL || !XtIsRealized(w)) + return; + + XtSetArg(arglist[i], XtNfont, &font); ++i; + XtSetArg(arglist[i], XtNrowSpacing, &row_space); ++i; + XtSetArg(arglist[i], XtNinternalHeight, internal_h); i++; + XtGetValues(w, arglist, i); + *row_height = font->max_bounds.ascent + font->max_bounds.descent + row_space; +} + +/* + * Get pagelist width. + */ +int +xaw_get_pagelist_size(void) +{ + Widget w; + XFontStruct *font; + + w = XtVaCreateWidget("list", listWidgetClass, globals.widgets.top_level, NULL); + XtVaGetValues(w, XtNfont, &font, NULL); + XtDestroyWidget(w); + + /* have space for max. pageno + space + current-marker, + plus a few pixels for right margin */ + return (get_page_size() + 2) * get_avg_font_width(font) + 6; +} + +/* auto-scroll pagelist when mouse-1 is down and moved above top or below bottom of + page list. +*/ +static void +xaw_drag_page_callback(Widget widget, XtPointer data, XEvent *event, Boolean *cont) +{ + int y, idx, actual_idx = 0; + + UNUSED(data); + UNUSED(cont); + + if (event->xany.type == ButtonPress || ((event->xbutton.state & Button1Mask) == 0)) + return; + if (event->xany.type == MotionNotify) + y = (int)event->xmotion.y; + else + y = (int)event->xbutton.y; + + idx = get_item_index(widget, y); + + if (idx <= 0) { + idx = 1; + } + else if (idx > total_pages) { + idx = total_pages; + } + + actual_idx = xaw_maybe_scroll_pagelist(idx, False, actual_idx); + XawListHighlight(LIST_WIDGET, idx - MOTIF_IDX_OFFSET); + +/* if (event->xany.type == ButtonRelease) { */ +/* fprintf(stderr, "1\n"); */ +/* page_history_insert(idx - MOTIF_IDX_OFFSET); */ +/* goto_page(idx - MOTIF_IDX_OFFSET, resource.keep_flag ? NULL : home); */ +/* search_signal_page_changed(); */ +/* } */ +} + +static void +xaw_SendReportProc(Widget w, XtPointer closure, XtPointer call_data) +{ + XawPannerReport *rep = (XawPannerReport *) call_data; + + UNUSED(w); + UNUSED(closure); + + view_y = rep->slider_y; +} + +#endif /* not MOTIF */ + + + +/* returns the index of the current item in `Motif'-style, i.e. first item has index 1, not 0 */ +static int +get_item_index(Widget w, int mouse_y) +{ +#ifdef MOTIF + return XmListYToPos(w, mouse_y); +#else + Dimension row_height, internal_height; + + xaw_get_row_height(w, &row_height, &internal_height); + return (mouse_y - internal_height) / row_height + MOTIF_IDX_OFFSET; +#endif +} + +#if !defined(LESSTIF_VERSION) +/* idx is Motif-style index, i.e. 1 for 1st item, not 0 */ +static Boolean +my_list_pos_to_bounds(Widget widget, int idx, Position *x, Position *y, Dimension *w, Dimension *h) +{ +#ifdef MOTIF + Position x1, y1; + Dimension w1, h1; + if (XmListPosToBounds(widget, idx, &x1, &y1, &w1, &h1)) { + *x = x1; + *y = y1; + *w = w1; + *h = h1; + return True; + } + return False; +#else + Dimension row_height, internal_height; + /* FIXME: Remove this hard-coded offset! */ + const int X_OFFSET = 9; + const int RULE_OFFSET = 2; + + if (idx <= 0 || idx > total_pages) { + return False; + } + + if (viewport != NULL && XtIsRealized(viewport)) + XtVaGetValues(viewport, XtNx, x, NULL); + xaw_get_row_height(widget, &row_height, &internal_height); + XtVaGetValues(widget, XtNwidth, w, NULL); + + *x -= X_OFFSET; + *y = row_height * idx + internal_height - row_height - 1; + *w -= RULE_OFFSET; + *h = row_height + RULE_OFFSET; + + return True; +#endif +} +#endif /* !defined(LESSTIF_VERSION) */ + +/* it seems that in order to support scrolling of the page list with wheel mice, + we need to program this explicitly. + */ +static void +wheel_scroll_list_callback(Widget widget, XtPointer data, XEvent *event, Boolean *cont) +{ + int button = event->xbutton.button; + + UNUSED(widget); + UNUSED(data); + UNUSED(cont); + + if (event->xany.type == ButtonPress && (button == 4 || button == 5)) { +#if SCROLL_LIST_SCROLLBAR + Widget vert = XtNameToWidget(viewport, "vertical"); + static Dimension row_height = 0, dummy = 0; + int diff_y = 0; + + if (row_height == 0) + xaw_get_row_height(LIST_WIDGET, &row_height, &dummy); + + if (vert == NULL) { + XDVI_WARNING((stderr, "Couldn't get name of pagelist viewport widget!")); + return; + } + if (button == 5) { + diff_y = row_height; + } + else { + diff_y = -row_height; + } + XtCallCallbacks(vert, XtNscrollProc, (XtPointer)diff_y); +#else + int pageno = current_page; + if (button == 5) { + if (current_page >= total_pages - 1) { + XBell(DISP, 0); + statusline_print(STATUS_SHORT, "Last page of DVI file"); + return; + } + pageno++; + } + else { + if (current_page == 0) { + XBell(DISP, 0); + statusline_print(STATUS_SHORT, "First page of DVI file"); + return; + } + pageno--; + } + goto_page(check_goto_page(pageno), resource.keep_flag ? NULL : home, False); + search_signal_page_changed(); +#endif + } +} + + +#if !defined(LESSTIF_VERSION) +/* draw a hightlight rectangle around the page the mouse is currently over, to + make e.g. marking easier. + */ +static void +highlight_page_callback(Widget widget, XtPointer data, XEvent *event, Boolean *cont) +{ + int curr_idx = get_item_index(widget, event->xmotion.y); + Position x, y; + Dimension w, h; + static int idx_bak = -1; + + UNUSED(data); + UNUSED(cont); + + switch(event->xany.type) { + case ButtonPress: + case ButtonRelease: + case MotionNotify: + /* might need to un-highlight previous one */ + if (idx_bak >= 0 && idx_bak != curr_idx + && my_list_pos_to_bounds(widget, idx_bak, &x, &y, &w, &h)) { +/* fprintf(stderr, "index: %d, %d, h: %d, w: %d\n", x, y, h, w); */ + refresh_highlight_marker(widget, m_page_gc.back, x, y, w, h); +#if REDRAW_CURRENT_MARKER_HACK + xaw_maybe_redraw_highlight(curr_idx); +#endif + } + idx_bak = curr_idx; + /* redraw unless out of bounds (when pagelist is shorter than view area) */ + if (my_list_pos_to_bounds(widget, curr_idx, &x, &y, &w, &h)) { + refresh_highlight_marker(widget, m_page_gc.fore, x, y, w, h); +#if REDRAW_CURRENT_MARKER_HACK + g_current_highlighted = curr_idx; +#endif + } + break; + case LeaveNotify: + /* this might look overly complicated, but is neccessary to cover all + cases of 1-pixel movement up/down before leaving the list, or no + movement at all before leaving it. */ + if ((idx_bak >= 0 && idx_bak != curr_idx && my_list_pos_to_bounds(widget, idx_bak, &x, &y, &w, &h)) + || my_list_pos_to_bounds(widget, curr_idx, &x, &y, &w, &h)) { + refresh_highlight_marker(widget, m_page_gc.back, x, y, w, h); +#if REDRAW_CURRENT_MARKER_HACK + xaw_maybe_redraw_highlight(curr_idx); +#endif + } + break; + default: + break; + } +} +#endif /* !defined(LESSTIF_VERSION) */ + +/* + * invoked on Button-1 Down. + */ +static void +select_page_callback(Widget w, XtPointer closure, XtPointer call_data) +{ +#ifdef MOTIF + XmListCallbackStruct *cbs = (XmListCallbackStruct *) call_data; + int new = cbs->item_position; + + UNUSED(w); + UNUSED(closure); + + maybe_scroll_pagelist(new - MOTIF_IDX_OFFSET, False); + page_history_insert(new - MOTIF_IDX_OFFSET); + goto_page(new - MOTIF_IDX_OFFSET, resource.keep_flag ? NULL : home, False); +#else + XawListReturnStruct *item = (XawListReturnStruct *) call_data; + int new = item->list_index; + + UNUSED(w); + UNUSED(closure); + + if (globals.debug & DBG_EVENT) + fprintf(stderr, "got: button-1 for `%d'\n", new); + +#if 0 + fprintf(stderr, "select page: %d\n", new); +#endif + maybe_scroll_pagelist(new, False); + page_history_insert(new); + goto_page(new, resource.keep_flag ? NULL : home, False); +#endif + search_signal_page_changed(); +} + + +static void +init_pagelabels(int start, int end) +{ + int i; + char s[PAGENUMLEN]; +#if 0 + fprintf(stderr, "===== init_pagelabels from %d to %d\n", start, end); +#endif + ASSERT(end < (int)page_info.index_size, ""); + + page_info.page_labels = xrealloc(page_info.page_labels, sizeof *(page_info.page_labels) * (end + 2)); + for (i = start; i < end; i++) { + if (page_info.index[i].marked) + sprintf(s, "* %*d ", get_page_size(), + resource.use_tex_pages ? page_info.index[i].number : i + 1); + else + sprintf(s, " %*d ", get_page_size(), + resource.use_tex_pages ? page_info.index[i].number : i + 1); + + page_info.page_labels[i] = xstrdup(s); + } + page_info.page_labels[i] = NULL; /* terminate - important for creating the widget. */ +} + + +#ifdef MOTIF + + +static int +xm_get_top_visible(int start) +{ + int top = start; + while (top < total_pages && !XmListPosToBounds(LIST_WIDGET, top, NULL, NULL, NULL, NULL)) + top++; + return top; +} + +static int +xm_get_bottom_visible(int start) +{ + int bot = start; + while (bot < total_pages && XmListPosToBounds(LIST_WIDGET, bot, NULL, NULL, NULL, NULL)) + bot++; + bot--; + return bot; +} + +/* Scroll pagelist down or up if needed, and update top_visible and + bot_visible. List is always scrolled so that 1 element is still + visible below or above pointer, to make it possible to flip through + document by repeatedly clicking on first/last. + */ +static void +xm_maybe_scroll_pagelist(int current, saveCmdT curr_cmd, int *top_visible, int *bot_visible) +{ +#if 0 + fprintf(stderr, "topmost visible: %d, bottom: %d, current: %d, total: %d\n", + top_visible, bottom_visible, current, total_pages); +#endif + + if (current < *top_visible && curr_cmd != SCROLL_DOWN) { + XmListSetPos(LIST_WIDGET, + current < 1 + ? 1 + : current); + (*top_visible)--; + (*bot_visible)--; + } + else if (current + MOTIF_IDX_OFFSET >= *bot_visible && curr_cmd != SCROLL_UP) { + XmListSetBottomPos(LIST_WIDGET, + current + MOTIF_IDX_OFFSET >= total_pages + ? current + MOTIF_IDX_OFFSET + : current + MOTIF_IDX_OFFSET + 1); + (*top_visible)++; + (*bot_visible)++; + } +} + +static void +xm_set_page_labels(void) +{ + int i; + char buf[PAGENUMLEN]; + + XmString *motif_page_labels = xmalloc((total_pages + 2) * sizeof *motif_page_labels); + + for (i = 0; i < total_pages; ++i) { + sprintf(buf, pageno_format, ' ', page_info.page_labels[i]); + motif_page_labels[i] = XmStringCreateLocalized(buf); + } + XmListDeleteAllItems(LIST_WIDGET); + XmListAddItems(LIST_WIDGET, motif_page_labels, total_pages, 0); + + XmListSelectPos(LIST_WIDGET, current_page + MOTIF_IDX_OFFSET, False); + for (i = 0; i < total_pages; ++i) { + XmStringFree(motif_page_labels[i]); + } + free(motif_page_labels); +} + +static void +xm_toggle_label(Widget widget, int idx, Boolean update) +{ + /* TODO: use `update' to update all labels at once when toggling multiple */ + XmString str; + char *mark_font, buf[128]; + + if (widget == NULL) + return; + + UNUSED(update); + +/* ensure_labelinfo_size(idx); */ + ASSERT(idx < (int)page_info.index_size, ""); + if (!page_info.index[idx].marked) { + sprintf(buf, pageno_format, '*', page_info.page_labels[idx]); + mark_font = "MARKED"; + page_info.index[idx].marked = True; + } + else { + sprintf(buf, pageno_format, ' ', page_info.page_labels[idx]); + mark_font = "UNMARKED"; + page_info.index[idx].marked = False; + } +/* str = XmStringCreateLocalized(buf); */ + str = XmStringCreateLtoR(buf, mark_font); + XmListReplaceItemsPos(widget, &str, 1, idx + MOTIF_IDX_OFFSET); + XmStringFree(str); +} + + +#else /* MOTIF */ + +static void +mark_page_callback(Widget w, XtPointer data, XEvent *event, Boolean *cont) +{ + UNUSED(data); + UNUSED(cont); + + /* moving button2 generates MotionNotify events for button0 */ + if (event->type == MotionNotify || event->xbutton.button == Button2) + internal_process_button2(w, event); + + if (pageinfo_have_marked_pages()) { + notify_print_dialog_have_marked(True); + } + else { + notify_print_dialog_have_marked(False); + } + +} + +void +xaw_create_pagelist_widgets(Dimension height, Dimension width, Position y, Widget parent) +{ + viewport = XtVaCreateWidget("viewport", + viewportWidgetClass, parent, + XtNallowVert, True, + /* this is not related to the scroll bar: */ + /* XtNforceBars, True, */ + XtNx, resource.btn_side_spacing, + XtNy, y, + XtNheight, height, + XtNwidth, width, + NULL); + LIST_WIDGET = XtVaCreateWidget("list", + listWidgetClass, viewport, + XtNlist, page_info.page_labels, + XtNdefaultColumns, 1, + XtNforceColumns, True, + XtNx, 10, + XtNy, 10, + XtNheight, height, + XtNwidth, width - 10, + XtNlongest, LONGESTPAGENUM, + XtNverticalList, True, + NULL); + XtManageChild(LIST_WIDGET); + XtManageChild(viewport); + XtAddCallback(LIST_WIDGET, XtNcallback, select_page_callback, + (XtPointer) NULL); + /* for scrolling the list */ + XtAddCallback(viewport, XtNreportCallback, xaw_SendReportProc, + (XtPointer) NULL); + XtAddEventHandler(LIST_WIDGET, + ButtonPressMask | ButtonReleaseMask | Button2MotionMask, + False, mark_page_callback, (XtPointer)NULL); + + if (resource.pagelist_highlight_current) + XtAddEventHandler(LIST_WIDGET, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask | LeaveWindowMask, + False, highlight_page_callback, (XtPointer)NULL); + + + XtAddEventHandler(LIST_WIDGET, ButtonPressMask | ButtonReleaseMask, + False, wheel_scroll_list_callback, (XtPointer)NULL); + + + XtAddEventHandler(LIST_WIDGET, + /* FIXME: We should add PointerMotionMask here, but handling PointerMotionMask + currently doesn't work with the Xaw list widget: the auto-scrolling code doesn't + realize when the mouse direction of the pointer movement changes, and continues + to scroll into the same direction. This will be rather annoying for users, so + we disabled PointerMotionMask for the time being. + */ + ButtonReleaseMask /* | PointerMotionMask */ | Button1MotionMask, + False, xaw_drag_page_callback, (XtPointer)NULL); +} + +static void +xaw_toggle_label(Widget w, int idx, Boolean update) +{ + if (w == NULL) + return; + +/* ensure_labelinfo_size(idx); */ + ASSERT(idx < (int)page_info.index_size, ""); + if (!page_info.index[idx].marked) { +/* sprintf(toc[idx], "* %*d ", get_page_size(), page_index[idx].number); */ + sprintf(page_info.page_labels[idx], pageno_format, '*', get_page_size(), + resource.use_tex_pages ? page_info.index[idx].number : idx + 1); + page_info.index[idx].marked = True; + } + else { + sprintf(page_info.page_labels[idx], pageno_format, ' ', get_page_size(), + resource.use_tex_pages ? page_info.index[idx].number : idx + 1); +/* sprintf(toc[idx], " %*d ", get_page_size(), page_index[idx].number); */ + page_info.index[idx].marked = False; + } + + if (update) + xaw_update_list(); +} + +static int +xaw_maybe_scroll_pagelist(int new_page, Boolean force_recenter, int idx_bak) +{ + Position x; + Position y, new_y, bot_y; + Dimension view_height, row_height, internal_height; + /* Dimension clip_height; */ + /* static Widget list_clip = 0; */ + + if (LIST_WIDGET == NULL || (resource.expert_mode & XPRT_SHOW_BUTTONS) == 0) + return idx_bak; + +/* if (list_clip == 0) { */ +/* list_clip = XtNameToWidget(viewport, "clip"); */ +/* } */ +/* if (XtIsRealized(list_clip)) */ +/* XtVaGetValues(list_clip, XtNheight, &clip_height, XtNx, &cx, XtNy, &y1, NULL); */ + if (viewport != NULL && XtIsRealized(viewport)) + XtVaGetValues(viewport, XtNheight, &view_height, XtNx, &x, NULL); +/* fprintf(stderr, "diff: %d, %d, %d, %d, %d\n", cx - x, clip_height, view_height, y1, (int)y2); */ + xaw_get_row_height(LIST_WIDGET, &row_height, &internal_height); + y = row_height * new_page; + +#if DEBUG + fprintf(stderr, "###### xaw_maybe_scroll_pagelist: y %d, view_y %d, view_height %d, row_height %d, internal_height %d; actual %d\n", + y, view_y, view_height, row_height, internal_height, idx_bak); +#endif + /*FIXME: when page list is destroyed, view_y will be 0 until user scrolls page list */ + bot_y = view_y + view_height; + if (force_recenter || ((y >= bot_y - row_height))) { +#if DEBUG + fprintf(stderr, "scrolled below bottom; incrementing %d to %d\n", view_y, view_y + row_height); +#endif + y += row_height; + XawViewportSetCoordinates(viewport, x, y - view_height > 0 ? y - view_height : 0); + return new_page + 1; + } + else if (force_recenter || ((y <= view_y + row_height + internal_height))) { +#if DEBUG + fprintf(stderr, "scrolled over top; new_y: %d\n", y - row_height); +#endif + new_y = y - 2 * row_height; + XawViewportSetCoordinates(viewport, x, new_y); + return new_page - 1; + } + /* not scrolled */ + return -2; +} + +#endif /* MOTIF */ + + +/* idx is C-style index (0-based), not Motif one (1-based) */ +static void +toggle_label(Widget widget, int idx, Boolean update) +{ + if (idx >= total_pages) + return; + ASSERT(idx < total_pages, ""); + ASSERT(idx >= 0, ""); +#ifdef MOTIF + xm_toggle_label(widget, idx, update); +#else + xaw_toggle_label(widget, idx, update); +#endif +} + + +void +list_toggle_marks(int arg) +{ + + int i; + + if (arg < 0) { /* mark all */ + for (i = 0; i < total_pages; i++) { + ASSERT(i < (int)page_info.index_size, ""); +/* ensure_labelinfo_size(i); */ + if (!page_info.index[i].marked) { + toggle_label(LIST_WIDGET, i, False); + } + } + } + else if (arg == 0) { /* unmark all */ + for (i = 0; i < total_pages; i++) { + ASSERT(i < (int)page_info.index_size, ""); +/* ensure_labelinfo_size(i); */ + if (page_info.index[i].marked) { + toggle_label(LIST_WIDGET, i, False); + } + } + } + else { /* toggle odd/even */ + if (arg == 2) /* toggle even */ + arg = 0; + for (i = 0; i < total_pages; i++) { + if ((i + 1) % 2 == arg) { + toggle_label(LIST_WIDGET, i, False); + } + } + } + /* TODO: update widget once for Motif as well! */ +#ifndef MOTIF + xaw_update_list(); +#endif +} + +static Boolean PagelistInitialized = False; + +#ifndef MOTIF +void +handle_pagelist_resize(void) +{ + /* TODO: the following will mess up the geometry of the list + (doesn't increase height, and incrementally decreases width): + if (list_widget) { + Dimension height; + --- without the (un)manage, I get an X Error: + XtMakeGeometryRequest - parent has no geometry manager + --- + XtUnmanageChild(viewport); + XtUnmanageChild(LIST_WIDGET); + XtVaGetValues(globals.widgets.clip_widget, XtNheight, &height, NULL); + height -= resource.btn_top_spacing + resource.btn_border_width + global_y_pos; + XtVaSetValues(viewport, XtNheight, height, NULL); + XtManageChild(LIST_WIDGET); + XtManageChild(viewport); + } + ... so we use brute force instead: */ + handle_destroy_pagelist(LIST_WIDGET, NULL, NULL); + create_pagelist(); +} + +void +handle_destroy_pagelist(Widget w, XtPointer client_data, XtPointer call_data) +{ + UNUSED(w); + UNUSED(client_data); + UNUSED(call_data); + + if (viewport != NULL) { + XtDestroyWidget(viewport); + viewport = NULL; + LIST_WIDGET = NULL; + } + PagelistInitialized = False; +} +#endif /* MOTIF */ + +void +create_pagelist(void) +{ + Pixel background, foreground; +#ifdef MOTIF + +/* items = xrealloc(items, sizeof *items * (total_pages + 2)); */ + init_pagelabels(0, total_pages); + xm_set_page_labels(); + if (!PagelistInitialized) { + XtAppContext app; + + XtVaGetValues(LIST_WIDGET, XmNforeground, &foreground, XmNbackground, &background, NULL); + m_page_gc.back = set_or_make_gc(NULL, GXcopy, background, foreground); + m_page_gc.fore = set_or_make_gc(NULL, GXcopy, foreground, background); + XtManageChild(LIST_WIDGET); + + XtAddCallback(LIST_WIDGET, XmNbrowseSelectionCallback, select_page_callback, NULL); +#if !defined(LESSTIF_VERSION) + /* + Don't use the highlighting hack with LessTif, since its XmListPosToBounds() + is too broken to be usable (as of 0.93.36): + - it returns generally too low values, apparently it doesn't take + XmNlistSpacing into account; + - it doesn't take scrollbar position into account. + */ + if (resource.pagelist_highlight_current) + XtAddEventHandler(LIST_WIDGET, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask | LeaveWindowMask, + False, highlight_page_callback, (XtPointer)NULL); +#endif /* !defined(LESSTIF_VERSION) */ + XtAddEventHandler(LIST_WIDGET, ButtonPressMask | ButtonReleaseMask, + False, wheel_scroll_list_callback, (XtPointer)NULL); + + app = XtWidgetToApplicationContext(globals.widgets.top_level); + XtAppAddActions(app, CustomListActions, XtNumber(CustomListActions)); + XtOverrideTranslations(LIST_WIDGET, XtParseTranslationTable(motif_custom_translations)); + PagelistInitialized = True; + } +#else /* MOTIF */ + if ((resource.expert_mode & XPRT_SHOW_BUTTONS) == 0) { + PagelistInitialized = False; /* might need to re-create widgets in this case */ + return; + } + + if (globals.debug & DBG_GUI) + fprintf(stderr, "allocating list with %d pages\n", total_pages); + + init_pagelabels(0, total_pages); + if (!PagelistInitialized) { + xaw_create_pagelist(); + XtVaGetValues(LIST_WIDGET, XtNforeground, &foreground, XtNbackground, &background, NULL); + m_page_gc.back = set_or_make_gc(NULL, GXcopy, background, foreground); + m_page_gc.fore = set_or_make_gc(NULL, GXcopy, foreground, background); + PagelistInitialized = True; + } +#endif /* MOTIF */ + /* scroll to the current page if needed */ + maybe_scroll_pagelist(current_page, False); +} + +#ifndef MOTIF +static void +free_pagelabels(void) +{ + int i; + for (i = 0; page_info.page_labels != NULL && page_info.page_labels[i] != NULL; i++) { + free(page_info.page_labels[i]); + } + free(page_info.page_labels); + page_info.page_labels = NULL; +} +#endif /* not MOTIF */ + + +void +refresh_pagelist(int newsize, int newpage) +{ + if ( +#ifndef MOTIF + (resource.expert_mode & XPRT_SHOW_BUTTONS) == 0 || +#endif + !XtIsRealized(globals.widgets.top_level)) + return; + +#ifdef DEBUG + fprintf(stderr, "=== refresh_pagelist: newsize %d, newpage %d\n", newsize, newpage); +#endif +#ifdef MOTIF +/* items = xrealloc(items, sizeof *items * (newsize + 2)); */ + init_pagelabels(0, newsize); + xm_set_page_labels(); +#else /* MOTIF */ + if ((resource.expert_mode & XPRT_SHOW_BUTTONS) == 0) + return; + + /* FIXME - is this really neccessary?? The alternative: + XawListChange(LIST_WIDGET, page_info.page_labels, newsize, 0, True); + has problems when freeing the page labels afterwards. + */ + handle_destroy_pagelist(LIST_WIDGET, NULL, NULL); + + free_pagelabels(); + init_pagelabels(0, newsize); + + xaw_create_pagelist(); +#endif /* MOTIF */ + /* `True' since the pagelist is newly created */ + maybe_scroll_pagelist(newpage, True); +} + +void +maybe_scroll_pagelist(int newpage, Boolean force_recenter) +{ +#ifdef MOTIF + int top_visible, bot_visible; + UNUSED(force_recenter); +#endif + + if ( +#ifndef MOTIF + (resource.expert_mode & XPRT_SHOW_BUTTONS) == 0 || +#endif + !XtIsRealized(globals.widgets.top_level)) + return; + +#ifdef MOTIF + XmListSelectPos(LIST_WIDGET, newpage + MOTIF_IDX_OFFSET, False); + + top_visible = xm_get_top_visible(1); + bot_visible = xm_get_bottom_visible(top_visible); + + xm_maybe_scroll_pagelist(newpage, CLICK, &top_visible, &bot_visible); +#if HAVE_XPM + tb_check_navigation_sensitivity(current_page); +#endif +#else + + if (LIST_WIDGET == NULL) + return; + + (void)xaw_maybe_scroll_pagelist(newpage + 1, force_recenter, 0); + + XawListHighlight(LIST_WIDGET, newpage); +#if REDRAW_CURRENT_MARKER_HACK + /* if the XawListHighlight happens adjacent to the page that was + last highlighted with our home-made `current selected' + rectangle, it might overdraw that rectangle. In this case, + restore it: + */ + xaw_maybe_redraw_current_marker(newpage); +#endif +#endif +} + +#ifdef MOTIF +static void +set_all_marks(int from, int to) +{ + int i; + for (i = from; i < to; i++) { +/* ensure_labelinfo_size(i); */ + page_info.index[i].marked = True; + toggle_label(LIST_WIDGET, i, False); + } +} + +static void +internal_process_button2_drag(Widget widget, XEvent *event) +{ + int i, idx, min = total_pages, max = 0; + idx = get_item_index(widget, event->xbutton.y); + + for (i = 0; i < total_pages; i++) { +/* ensure_labelinfo_size(i); */ + if (page_info.index[i].marked && i > max) + max = i; + } + for (i = total_pages; i > 0; i--) { +/* ensure_labelinfo_size(i); */ + if (page_info.index[i].marked && i < min) + min = i; + } + + if (min == total_pages) + min = 0; + if (max == 0) + max = total_pages; + + if (idx < min) { + set_all_marks(idx, min); + } + else if (idx > max) { + set_all_marks(max + 1, idx); + } + else { + set_all_marks(0, idx); + } +} +#endif /* MOTIF */ + +static void +internal_process_button2(Widget widget, XEvent *event) +{ + int curr_idx; + static int prev_idx = 0; +#ifndef MOTIF + static int actual_idx = -2; +#endif + static int top_visible = 0, bot_visible = 0; + static int prev_y = 0, curr_y = 0; + static saveCmdT prev_cmd = 0; /* previous command (CLICK/SCROLL_UP/SCROLL_DOWN) */ + static saveCmdT curr_cmd = 0; /* current command (CLICK/SCROLL_UP/SCROLL_DOWN) */ + static saveCmdT bak_cmd = 0; /* last command that started inside the pagelist (CLICK/SCROLL_UP/SCROLL_DOWN) */ + static Boolean change_scroll_direction = False; + + switch(event->xany.type) { + case ButtonPress: + prev_y = event->xbutton.y; + prev_idx = curr_idx = get_item_index(widget, prev_y); +#ifdef MOTIF + top_visible = xm_get_top_visible(1); + bot_visible = xm_get_bottom_visible(top_visible); +#endif + toggle_label(widget, curr_idx - 1, True); + prev_cmd = CLICK; +#ifdef MOTIF + xm_maybe_scroll_pagelist(curr_idx - 1, CLICK, &top_visible, &bot_visible); +#else + actual_idx = xaw_maybe_scroll_pagelist(curr_idx, False, actual_idx); +#endif + break; + case ButtonRelease: + prev_cmd = CLICK; + break; + case MotionNotify: + curr_y = (int)event->xmotion.y; + curr_idx = get_item_index(widget, event->xmotion.y); +#ifndef MOTIF + if (actual_idx > 0) { + curr_idx = actual_idx; + } +#endif + + if (curr_y < prev_y) + curr_cmd = SCROLL_UP; + else if (curr_y > prev_y) + curr_cmd = SCROLL_DOWN; + prev_y = curr_y; + + if (prev_cmd != CLICK && curr_cmd != prev_cmd) + change_scroll_direction = True; + + if ((curr_idx != prev_idx && !change_scroll_direction) + || (change_scroll_direction + /* last or first visible are always spared, unless they are really + the first or last page; this way, always 1 more page is visible + than is currently marked/selected + */ + && !(curr_idx == top_visible) && !(curr_idx == bot_visible))) { + if (curr_idx <= 0) { + /* When user has scrolled off, mark this by setting curr_idx to 1 more or less + than the pagelist has so that the last/first page don't oscillate between + marked/unmarked state when user continues to scroll. + Also, we continue scrolling as long as user drags in the same direction as + the last `real' scrolling event (saved as bak_cmd). + */ + if (curr_cmd == SCROLL_DOWN && bak_cmd == SCROLL_DOWN && prev_idx <= total_pages) { + curr_idx = prev_idx + 1; + } + else if (curr_cmd == SCROLL_UP && bak_cmd == SCROLL_UP && prev_idx > 0) { + curr_idx = prev_idx - 1; + } + } + + + if (curr_idx > 0 && curr_idx <= total_pages) { + toggle_label(widget, curr_idx - 1, True); +#ifdef MOTIF + xm_maybe_scroll_pagelist(curr_idx - 1, curr_cmd, &top_visible, &bot_visible); +#else + actual_idx = xaw_maybe_scroll_pagelist(curr_idx, False, actual_idx); +#endif + prev_idx = curr_idx; + bak_cmd = curr_cmd; + change_scroll_direction = False; + } + else { +#ifndef MOTIF + if (curr_idx > total_pages) + actual_idx = -2; + else + actual_idx = xaw_maybe_scroll_pagelist(curr_idx, False, actual_idx); +#endif + } + prev_cmd = curr_cmd; + } + break; + default: + break; + } +} + +void +list_toggle_current(int arg) +{ + toggle_label(LIST_WIDGET, arg, True); + + if (pageinfo_have_marked_pages()) { + notify_print_dialog_have_marked(True); + } + else { + notify_print_dialog_have_marked(False); + } +} + +#ifdef MOTIF + +static void +xm_list_set_mark(Widget widget, + XEvent *event, + String *params, + Cardinal *num_params) +{ + UNUSED(params); + + if ((*num_params != 1) || !XmIsList(widget)) + return; + + internal_process_button2(widget, event); + + if (pageinfo_have_marked_pages()) { + notify_print_dialog_have_marked(True); + } + else { + notify_print_dialog_have_marked(False); + } +} + +static void +xm_list_drag_mark(Widget widget, + XEvent *event, + String *params, + Cardinal *num_params) +{ + UNUSED(params); + + if ((*num_params != 1) || !XmIsList(widget)) + return; + + internal_process_button2_drag(widget, event); + + if (pageinfo_have_marked_pages()) { + notify_print_dialog_have_marked(True); + } + else { + notify_print_dialog_have_marked(False); + } +} + +#endif /* MOTIF */ diff --git a/Build/source/texk/xdvik/gui/pagesel.h b/Build/source/texk/xdvik/gui/pagesel.h new file mode 100644 index 00000000000..aad1b591456 --- /dev/null +++ b/Build/source/texk/xdvik/gui/pagesel.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2001-2004 the xdvik development team + * + * Page selector for xdvi + * + * Copyright (c) 1993, 1995 + * MATSUURA Syun syun@fuka.info.waseda.ac.jp + * HIRAHARA Atsushi hirahara@fuka.info.waseda.ac.jp + * ONO Kouichi onono@fuka.info.waseda.ac.jp + * + * All rights reserved. + */ + +/* SU: I was unsure how to interpret the `All rights reserved' in the + * previous line, so emailed Ono Kouichi about this. Here's (the + * relevant part of) his answer (which was CC'ed to Hirahara Atsushi - + * all three of them had left Waseda university around '95): + * + * You can modify, embed, copy and distribute a part of or the + * entire of our source code when you specify our copyright in your + * xdvik version. + * + * IANAL, but I think this is compatible with the X consortium + * license as specified in the other files. + */ + +#ifndef PAGESEL_H_ +#define PAGESEL_H_ + +#include "xdvi.h" + +extern void refresh_pagelist(int newsize, int newpage); +extern void maybe_scroll_pagelist(int newpage, Boolean force_recenter); +extern void create_pagelist(void); +extern void list_toggle_current(int arg); +extern void list_toggle_marks(int arg); + +# ifdef MOTIF +extern Widget page_list; +extern void toggle_pagelist(void); +# else +extern int xaw_get_pagelist_size(void); +extern void xaw_create_pagelist_widgets(Dimension height, Dimension width, Position y, Widget parent); +extern void handle_destroy_pagelist(Widget w, XtPointer client_data, XtPointer call_data); +extern void handle_pagelist_resize(void); +# endif + +/* pageinfo access methods */ +extern long pageinfo_get_offset(int page); +extern int pageinfo_get_number(int page); +extern int pageinfo_get_index_of_number(int number); + +extern unsigned int pageinfo_get_page_width(int page); +extern unsigned int pageinfo_get_page_height(int page); +extern unsigned int pageinfo_get_window_width(int page); +extern unsigned int pageinfo_get_window_height(int page); + +extern void pageinfo_set_page_width(int page, unsigned int width); +extern void pageinfo_set_page_height(int page, unsigned int height); +extern void pageinfo_set_window_width(int page, unsigned int width); +extern void pageinfo_set_window_height(int page, unsigned int height); + +extern void pageinfo_set_offset(int index, long offset); +extern void pageinfo_set_number(int index, int number); + +extern void pageinfo_allocate(int total_pages); +extern void pageinfo_deallocate(void); +extern Boolean pageinfo_is_marked(int i); +extern Boolean pageinfo_have_marked_pages(void); + +#endif /* PAGESEL_H_ */ diff --git a/Build/source/texk/xdvik/gui/print-dialog.c b/Build/source/texk/xdvik/gui/print-dialog.c new file mode 100644 index 00000000000..c7187be6100 --- /dev/null +++ b/Build/source/texk/xdvik/gui/print-dialog.c @@ -0,0 +1,2973 @@ +/* + * Copyright (c) 2002-2004 Paul Vojta and the xdvik development team + * + * 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. + */ + +/* + SU: Adapted from non-k xdvi's popups.c + + This implements a print popup (and a `save to file' popup). My idea + was to use the same window layout framework for both tasks, since + there's a big overlap in the widgets needed. However after looking + at the mess I've created, I'm no longer convinced this was a good + idea. (I reckon it's slightly better than the code duplication when + doing it in separate files, but only *slightly* ...) + + We need a more intelligent approach that bans this ugly kind of + `data proliferation' (those global arrays of widgets and helper + structures). Custom Widgets? Factory methods? Needs investigation. + + Also, in the long run, I guess we want the `Save' window to be more + similar to the file browser (i.e. to contain a file browser e.g. + in the upper part); in that case, the code will be moved into another + module anyway. + + Moreover, the GUI code should be completely separated from the backend + code. +*/ + +#include <string.h> +#include "xdvi-config.h" +#include "xdvi.h" +#include "my-vsnprintf.h" +#include <ctype.h> + +#include "print-dialog.h" +#include "print-log.h" +#include "print-internal.h" +#include "events.h" +#include "dvi-init.h" +#include "string-utils.h" +#include "util.h" +#include "x_util.h" +#include "message-window.h" +#include "pagesel.h" +#include "my-snprintf.h" +#include "sfSelFile.h" +#include "dvisel.h" /* for select_marked_pages() */ +#include "xlwradio.h" +#include "statusline.h" +#include "search-dialog.h" +#include "search-internal.h" +#include "special.h" + +/* Xlib and Xutil are already included */ + + +#include <X11/Xatom.h> +#include <X11/StringDefs.h> + +#ifdef MOTIF +# include <Xm/BulletinB.h> +# include <Xm/DialogS.h> +# include <Xm/PanedW.h> +# include <Xm/MessageB.h> +# include <Xm/LabelG.h> +# include <Xm/Form.h> +# include <Xm/Frame.h> +# include <Xm/ToggleBG.h> +# include <Xm/Text.h> +# include <Xm/TextF.h> +# include <Xm/PushB.h> +# include <Xm/Protocols.h> +# include <Xm/AtomMgr.h> +#define XTranslations XmNtranslations +#else /* MOTIF */ +# include <X11/Shell.h> +# include <X11/Xaw/Paned.h> +# include <X11/Xaw/Box.h> +# include <X11/Xaw/Form.h> +# include <X11/Xaw/Label.h> +# include <X11/Xaw/Command.h> +# include <X11/Xaw/Toggle.h> +# include <X11/Xaw/AsciiText.h> +# include <X11/Xaw/SimpleMenu.h> +# include <X11/Xaw/MenuButton.h> +# include <X11/Xaw/Sme.h> +# include <X11/Xaw/SmeBSB.h> +#if 0 +#include "xaw_bitmaps.h" +static Pixmap menu_double_arrow_bitmap; +#endif +#define XTranslations XtNtranslations +#endif /* MOTIF */ + +struct output_format_mapping { + const char *fmt_string; + outputFormatT fmt; + const char *extension; +} output_format_mapping[] = { + { "Postscript", FMT_PS , ".ps" }, + { "PDF", FMT_PS2PDF, ".pdf" }, + { "DVI", FMT_DVI, ".dvi" }, + { "Plain Text (ISO_8859-1)",FMT_ISO_8859_1, ".txt" }, + { "Plain Text (UTF-8)", FMT_UTF8, ".txt" }, + { NULL, FMT_NONE, NULL } +}; + +typedef enum printRadioT_ { + NO_PRINT_VAL = -1, + PRINT_TO_PRINTER = 1, + PRINT_TO_FILE, + SAVE_TO_FILE +} printRadioT; + + +typedef enum pageRadioT_ { + NO_PAGE_VAL = -1, + PRINT_ALL = 1, + PRINT_MARKED, + PRINT_RANGE, + SAVE_ALL, + SAVE_MARKED, + SAVE_RANGE +} pageRadioT; + +static Boolean is_print_button(pageRadioT flag) { + return (flag <= PRINT_RANGE); +} + +struct dialog_info { + char *printer_name; + char *file_name; + /* fully canonicalized filename, not visible in window, but used in error messages. + It is either NULL, or a malloc()ed buffer (which is free()d immediately before use). */ + char *expanded_filename; + char *dvips_options; + char *from_page; + char *to_page; + printRadioT print_radio; + pageRadioT range_radio; + outputFormatT output_format; +}; + +/* + global widgets, and arrays of global widgets (mosly, one + for the printing and one for the saving dialog). Needs + cleanup! +*/ + +#define FORMAT_SEL_CNT 5 + +/* top-level widgets: one for printing, one for saving to file */ +static Widget print_shell = NULL; +static Widget save_shell = NULL; + +/* global structures for saving print info between invocations */ +static struct dialog_info save_pr_info = { + NULL, NULL, NULL, NULL, NULL, NULL, NO_PRINT_VAL, NO_PAGE_VAL, FMT_NONE +}; +static struct dialog_info save_save_info = { + NULL, NULL, NULL, NULL, NULL, NULL, NO_PRINT_VAL, NO_PAGE_VAL, FMT_NONE +}; + +/* + these contain the currently selected values, or defaults; + used in communication between callbacks +*/ +static struct dialog_info curr_pr_info = { + NULL, NULL, NULL, NULL, NULL, NULL, PRINT_TO_PRINTER, PRINT_ALL, FMT_NONE +}; +static struct dialog_info curr_save_info = { + NULL, NULL, NULL, NULL, NULL, NULL, SAVE_TO_FILE, SAVE_ALL, FMT_NONE +}; + +static void print_precheck1(XtPointer info); +static void print_precheck2(XtPointer info); + +/* access methods ... */ +const char * +get_printer_options(void) +{ + return save_pr_info.printer_name; +} + +char * +get_dvips_options(printOrSaveActionT act) +{ + if (act == FILE_PRINT) + return save_pr_info.dvips_options; + else + return save_save_info.dvips_options; +} + +#ifdef MOTIF +static Widget from_label[FILE_ALL] = { None, None }; +static Widget format_selection_option; +XmString format_selection_texts[FORMAT_SEL_CNT]; + +#else /* MOTIF */ + +static Widget format_selection_label; +static Widget format_selection_button; +#if 0 +static Widget format_selection_b1; +#endif +static Widget format_selection_popup; +static Widget format_selection_menu[FORMAT_SEL_CNT]; + +static void xaw_print_save_act_go(Widget, XEvent *, String *, Cardinal *); + +#endif /* MOTIF */ + +static void print_cancel(Widget, XEvent *, String *, Cardinal *); +static void save_cancel(Widget, XEvent *, String *, Cardinal *); + +static Widget to_printer_radio; +static Widget to_file_radio_or_label[FILE_ALL] = { None, None }; +static Widget to_label[FILE_ALL] = { None, None }; +static Widget of_label[FILE_ALL] = { None, None }; +static Widget range_all_radio[FILE_ALL] = { None, None }; +static Widget range_marked_radio[FILE_ALL] = { None, None }; +static Widget range_from_to_radio[FILE_ALL] = { None, None }; +static Widget cancel_button[FILE_ALL] = { None, None }; +static Widget ok_button[FILE_ALL] = { None, None }; +static Widget print_to_printer_text; +static Widget print_to_file_text[FILE_ALL] = { None, None }; +static Widget print_to_file_button[FILE_ALL] = { None, None }; +static Widget dvips_options_text[FILE_ALL] = { None, None }; +static Widget page_from_text[FILE_ALL] = { None, None }; +static Widget page_to_text[FILE_ALL] = { None, None }; + +static int print_is_active = False; /* False if window is unmanaged or not created yet, True if managed */ +static int save_is_active = False; /* False if window is unmanaged or not created yet, True if managed */ + + +static XtActionsRec print_actions[] = { +#ifndef MOTIF + {"saveInternal", xaw_print_save_act_go }, + {"printInternal", xaw_print_save_act_go}, +#endif + {"WM_print_cancel", print_cancel }, + {"WM_save_cancel", save_cancel }, +}; + +/* + ============================================================ + generic utility functions + ============================================================ +*/ + +/* + Return the printer name, eventually followed by `-P$PRINTER', + since the `-o' option of dvips doesn't evaluate the environment + variable. +*/ +static char * +get_printer_name(void) +{ + char *printcmd, *printer; + + if (resource.dvips_printer_str != NULL && strlen(resource.dvips_printer_str) > 0) { + return xstrdup(resource.dvips_printer_str); + } + printcmd = xstrdup("lpr"); + printer = getenv("PRINTER"); + if (printer != NULL) { + printcmd = xstrcat(printcmd, " -P"); + printcmd = xstrcat(printcmd, printer); + } + return printcmd; +} + +/* return basename of `old_name' with `old_ext' replaced by `new_ext', in fresh memory */ +static char * +get_new_file_name(const char *old_name, const char *old_ext, const char *new_ext) +{ + /* old_name contains the normalized DVI file name, with path and extension */ + char *basename, *new_name; + size_t len; + + basename = strrchr(old_name, DIR_SEPARATOR); + + if (basename != NULL) { + basename++; + new_name = xmalloc(strlen(basename) + strlen(new_ext) + 1); + new_name = strcpy(new_name, basename); + } + else { + new_name = xmalloc(strlen(old_name) + strlen(new_ext) + 1); + new_name = strcpy(new_name, old_name); + } + + len = strlen(new_name); + strcpy(new_name + len - strlen(old_ext), new_ext); + return new_name; +} + +/* + * Translate `non-standard' paper sizes for xdvi into options suitable + * for dvips. We don't do this always, since it would override the papersize + * specials inserted by e.g. + * \usepackage[dvips]{geometry} + * which is the preferred way to specify the papersize. If the papersize + * has been explicitly set by such a special, this function returns NULL. + * Else it returns the value of the `paper' resource, without the trailing + * `r', and sets the `landscape' argument to True if a trailing `r' was + * present. + * + * Note that we don't check/warn about whether the current paper + * resource is valid for dvips; dvips will ignore invalid paper sizes. + */ +static char * +get_dvips_papersize(Boolean *landscape) +{ + char *papersize; + if (have_papersize_special()) + return NULL; + + papersize = xstrdup(resource.paper); + *landscape = False; + if (papersize[strlen(papersize) - 1] == 'r') { /* landscape size */ + papersize[strlen(papersize) - 1] = '\0'; + *landscape = True; + } + TRACE_GUI((stderr, "Using dvips arguments: `%s', %s", papersize, *landscape ? "landscape" : "portrait")); + return papersize; +} + +static void +set_outfile_name(struct dialog_info *info, struct dialog_info *save_info, printOrSaveActionT act) +{ + free(info->file_name); + if (act == FILE_PRINT) { + info->file_name = get_new_file_name(globals.dvi_name, ".dvi", ".ps"); + } + else { /* saving to file */ + if (save_info->range_radio == SAVE_ALL) { + info->file_name = get_new_file_name(globals.dvi_name, ".dvi", + output_format_mapping[resource.default_saving_format].extension); + } + else { + char *tmp = xstrdup("_pp"); + tmp = xstrcat(tmp, output_format_mapping[resource.default_saving_format].extension); + + info->file_name = get_new_file_name(globals.dvi_name, ".dvi", tmp); + free(tmp); + } + } +} + +/* get initial values for dialog texts */ +static void +get_initial_dialog_values(struct dialog_info *info, printOrSaveActionT act) +{ + if (act == FILE_PRINT && info->printer_name == NULL) + info->printer_name = get_printer_name(); + + if (info->dvips_options == NULL) { + info->dvips_options = xstrdup(""); + + /* add -t option if not already present */ + if (resource.dvips_options_str != NULL + && strstr(resource.dvips_options_str, "-t ") == NULL) { + Boolean dvips_landscape = False; + char *dvips_paper_size = get_dvips_papersize(&dvips_landscape); + if (dvips_paper_size != NULL) { + info->dvips_options = xstrdup("-t "); + info->dvips_options = xstrcat(info->dvips_options, dvips_paper_size); + free(dvips_paper_size); + if (dvips_landscape) { + info->dvips_options = xstrcat(info->dvips_options, " -t landscape"); + } + } + } + + if (resource.dvips_options_str != NULL) { + if (strlen(info->dvips_options) > 0) + info->dvips_options = xstrcat(info->dvips_options, " "); + info->dvips_options = xstrcat(info->dvips_options, resource.dvips_options_str); + } + } + + if (info->from_page == NULL) { + info->from_page = xmalloc(LENGTH_OF_INT + 1); + sprintf(info->from_page, "%d", current_page + globals.pageno_correct); + } + + if (info->to_page == NULL) { + info->to_page = xmalloc(LENGTH_OF_INT + 1); + sprintf(info->to_page, "%d", current_page + globals.pageno_correct); + } +} + + +#if MOTIF +static void +set_sensitivity(int i) +{ + Boolean sensitivity; + +/* if (i == curr_pr_info.print_radio) /\* if no change *\/ */ +/* return; */ + + curr_pr_info.print_radio = i; + + sensitivity = (i == PRINT_TO_PRINTER); + XtSetSensitive(print_to_printer_text, sensitivity); + +/* sensitivity ^= (True ^ False); */ + XtSetSensitive(print_to_file_text[FILE_PRINT], !sensitivity); + XtSetSensitive(print_to_file_button[FILE_PRINT], !sensitivity); +} +#endif /* MOTIF */ + +static void +update_dvips_options_sensitivity(int idx, Widget top) +{ + Widget dvips_label; + +#if MOTIF + Widget dvips_options; + + if (get_widget_by_name(&dvips_label, top, "dvips_options_name", True) && + get_widget_by_name(&dvips_options, top, "dvips_options_text", True)) { + if (output_format_mapping[idx].fmt == FMT_PS || + output_format_mapping[idx].fmt == FMT_PS2PDF) { + XtSetSensitive(dvips_label, True); + XtSetSensitive(dvips_options, True); + } + else { + XtSetSensitive(dvips_label, False); + XtSetSensitive(dvips_options, False); + } + } +#else + if (get_widget_by_name(&dvips_label, top, "dvips_options_name", True)) { + XtSetSensitive(XtParent(dvips_label), + output_format_mapping[idx].fmt == FMT_PS || + output_format_mapping[idx].fmt == FMT_PS2PDF); + } +#endif +} + +/* callbacks */ + +static void +select_format_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + Widget file_text, top; + + char *filename; + int i; +#ifdef MOTIF + Widget f1, f2; + char *ptr; + + i = (int)client_data; + UNUSED(call_data); + f1 = XtParent(w); + ASSERT(f1 != 0, "Parent of widget musn't be NULL!\n"); + f2 = XtParent(f1); + ASSERT(f2 != 0, "Parent of f1 musn't be NULL!\n"); + top = XtParent(f2); + ASSERT(top != 0, "Parent of f2 musn't be NULL!\n"); + + if ((file_text = XtNameToWidget(top, "*file_text")) == 0) { + XDVI_WARNING((stderr, "Couldn't find \"file_text\" widget!")); + return; + } + XtVaGetValues(file_text, XmNvalue, &filename, NULL); + if ((ptr = strrchr(filename, '.')) != NULL) { + char buf[1024]; + replace_extension(filename, output_format_mapping[i].extension, buf, sizeof buf); + XtVaSetValues(file_text, XmNvalue, buf, NULL); + } +#else /* MOTIF */ + + Widget popup, button; + char *ptr1, *ptr2; + char buf[1024]; + + UNUSED(call_data); + popup = XtParent(w); + ASSERT(popup != 0, "Parent of menu musn't be NULL!\n"); + button = XtParent(popup); + ASSERT(button != 0, "Parent of popup musn't be NULL!\n"); + + top = (Widget)client_data; + + if ((file_text = XtNameToWidget(top, "*file_text")) == 0) { + XDVI_WARNING((stderr, "Couldn't find \"file_text\" widget!")); + return; + } + + XtVaGetValues(file_text, XtNstring, &filename, NULL); +/* fprintf(stderr, "file text: |%s|\n", filename); */ + + /* swap title strings */ + XtVaGetValues(button, XtNlabel, &ptr1, NULL); + XtVaGetValues(w, XtNlabel, &ptr2, NULL); + strncpy(buf, ptr1, sizeof buf); + + for (i = 0; output_format_mapping[i].fmt_string != NULL; i++) { + if (strcmp(ptr2, output_format_mapping[i].fmt_string) == 0) { + char buf2[1024]; + replace_extension(filename, output_format_mapping[i].extension, buf2, sizeof buf2); + XtVaSetValues(file_text, XtNstring, buf2, NULL); + + break; + } + } + if (output_format_mapping[i].fmt_string == NULL) { + popup_message(get_matching_parent(w, globals.widgets.top_level, "print_popup", "save_popup", NULL), + MSG_WARN, + REPORT_XDVI_BUG_TEMPLATE, + "Unrecognized string in format selector: |%s|", ptr2); + } + XtVaSetValues(button, XtNlabel, ptr2, NULL); + XtVaSetValues(w, XtNlabel, buf, NULL); + +#endif /* MOTIF */ + + save_save_info.output_format = curr_save_info.output_format = output_format_mapping[i].fmt; + + update_dvips_options_sensitivity(i, save_shell); + + resource.default_saving_format = save_save_info.output_format; + set_outfile_name(&save_save_info, &curr_save_info, FILE_SAVE); +} + +static void +cb_print_cancel(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct save_or_print_info *info = (struct save_or_print_info *)client_data; + UNUSED(w); + UNUSED(call_data); + + ASSERT(info != NULL, "client_data in cb_print_cancel musn't be NULL!"); + if (info->message_popup != 0) { + kill_message_window(info->message_popup); + } + XtPopdown(print_shell); + print_is_active = False; +} + +static void +cb_save_cancel(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct save_or_print_info *info = (struct save_or_print_info *)client_data; + UNUSED(w); + UNUSED(call_data); + + ASSERT(info != NULL, "client_data in cb_save_cancel musn't be NULL!"); + if (info->message_popup != 0) { + kill_message_window(info->message_popup); + } + XtPopdown(save_shell); + save_is_active = False; +} + +static void +cb_print_or_save(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct save_or_print_info *info = (struct save_or_print_info *)client_data; + UNUSED(w); + UNUSED(call_data); + + print_precheck1((XtPointer)info); +} + +/* access from outside the module */ +void +cancel_saving(struct save_or_print_info *info) +{ + cb_save_cancel(NULL, info, NULL); +} + + +static void +print_cancel(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + struct save_or_print_info *info = NULL; + void *ptr; + + UNUSED(w); + UNUSED(event); + + ASSERT(*num_params > 0, "params in print_cancel must be > 0!"); + ASSERT(*params != NULL, "params in print_cancel mustn't be NULL!"); + + TRACE_GUI((stderr, "Pointer string value: |%s|", *params)); + sscanf(*params, "%p", &ptr); + info = (struct save_or_print_info *)ptr; + + cb_print_cancel(NULL, info, NULL); +} + +static void +save_cancel(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + struct save_or_print_info *info = NULL; + void *ptr; + + UNUSED(w); + UNUSED(event); + + ASSERT(*num_params > 0, "params in save_cancel must be > 0!"); + ASSERT(*params != NULL, "params in save_cancel mustn't be NULL!"); + + TRACE_GUI((stderr, "Pointer string value: |%s|", *params)); + sscanf(*params, "%p", &ptr); + info = (struct save_or_print_info *)ptr; + + cb_save_cancel(NULL, info, NULL); +} + +#ifndef MOTIF + +/* + enable the `print to' button if user presses key or mouse button + inside that input field +*/ +static void +xaw_print_handle_key(Widget widget, XtPointer closure, XEvent *ev, Boolean *cont) +{ + char *number; + int i; + + number = (char *)closure; + i = atoi(number); + ASSERT(i == 2 || i == 3, "Button index should be 2 or 3"); + UNUSED(widget); + UNUSED(ev); + UNUSED(cont); + + if (i == 2) { + XawToggleSetCurrent(to_printer_radio, (XtPointer)PRINT_TO_PRINTER); + } + else { + ASSERT(to_file_radio_or_label[FILE_PRINT] != NULL, ""); + XawToggleSetCurrent(to_file_radio_or_label[FILE_PRINT], (XtPointer)PRINT_TO_FILE); + } +} + +/* + enable the `print range' button if user presses key or mouse button + inside that input field +*/ +static void +xaw_range_handle_key(Widget widget, XtPointer closure, XEvent *ev, Boolean *cont) +{ + printOrSaveActionT act = (printOrSaveActionT)closure; + UNUSED(widget); + UNUSED(ev); + UNUSED(cont); + + if (act == FILE_PRINT && curr_pr_info.range_radio != PRINT_RANGE) { + ASSERT(range_all_radio[act] != NULL, "button should have been initialized"); + XawToggleSetCurrent(range_all_radio[act], (XtPointer)PRINT_RANGE); + } + else if (act == FILE_SAVE && curr_save_info.range_radio != SAVE_RANGE) { + ASSERT(range_all_radio[act] != NULL, "button should have been initialized"); + XawToggleSetCurrent(range_all_radio[act], (XtPointer)SAVE_RANGE); + } +} + + +static void +xaw_print_save_act_go(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + struct save_or_print_info *info = NULL; + void *ptr; + + UNUSED(w); + UNUSED(event); + + ASSERT(*num_params > 0, "params in search_cancel must be > 0!"); + ASSERT(*params != NULL, "params in search_cancel mustn't be NULL!"); + + TRACE_GUI((stderr, "Pointer string value: |%s|", *params)); + sscanf(*params, "%p", &ptr); + info = (struct save_or_print_info *)ptr; + + print_precheck1((XtPointer)info); +} + +#if 0 +static void +xaw_popup_menu_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + Position x, y; + Dimension h; + int pos_x, pos_y; + Window dummy; + + Widget popup = (Widget)client_data; + fprintf(stderr, "popup: %p\n", popup); + XtVaGetValues(w, XtNx, &x, XtNy, &y, XtNheight, &h, NULL); + fprintf(stderr, "x: %d, y: %d, h: %d\n", x, y, h); + XTranslateCoordinates(DISP, XtWindow(w), RootWindowOfScreen(SCRN), + x, y, &pos_x, &pos_y, &dummy); + XtRealizeWidget(popup); + /* WRONG - would need to use x position of its left widget ... */ + XtVaSetValues(popup, XtNx, pos_x, XtNy, pos_y + h + 1, NULL); + XtPopup(popup, XtGrabNone); +} +#endif /* 0 */ + +#endif /* not MOTIF */ + +static void +cb_print_vs_file(Widget w, XtPointer client_data, XtPointer call_data) +{ +#ifdef MOTIF + Boolean to_printer = False, to_file = False; + if (((XmToggleButtonCallbackStruct *)call_data)->set) { + if (w == to_printer_radio) + to_printer = True; + else + to_file = True; + + } + else { + if (w == to_printer_radio) + to_file = True; + else + to_printer = True; + } + + XmToggleButtonGadgetSetState(to_file_radio_or_label[FILE_PRINT], to_file, False); + XmToggleButtonGadgetSetState(to_printer_radio, to_printer, False); + + set_sensitivity((int)client_data); + XmProcessTraversal(curr_pr_info.print_radio == PRINT_TO_PRINTER + ? print_to_printer_text + : print_to_file_text[FILE_PRINT], + XmTRAVERSE_CURRENT); +#else /* MOTIF */ + int i = (int) XawToggleGetCurrent(to_printer_radio); + + UNUSED(w); + UNUSED(client_data); + UNUSED(call_data); + + /* + NOTE: Unlike non-k xdvi, we don't use set_sensitivity for the + Xaw version, since the text inside the textfield cannot be made + insensitive, so it won't be obvious to users why they cannot + change the text. Also, the page range isn't set insensitive + either. + */ + if (i != 0) + curr_pr_info.print_radio = i; +#endif /* MOTIF */ + +} + + + +static void +cb_range(Widget w, XtPointer client_data, XtPointer call_data) +{ + pageRadioT flag = (pageRadioT)client_data; + printOrSaveActionT act; + +#ifdef MOTIF + + Boolean sensitivity; + UNUSED(call_data); + ASSERT(flag != PRINT_MARKED || flag != SAVE_MARKED || pageinfo_have_marked_pages(), + "PRINT_MARKED or SAVE_MARKED shouldn't be togglable if there are no marked pages!"); + + if (is_print_button(flag)) { + curr_pr_info.range_radio = (int)client_data; + sensitivity = (curr_pr_info.range_radio == PRINT_RANGE); + act = FILE_PRINT; + } + else { + curr_save_info.range_radio = (int)client_data; + sensitivity = (curr_save_info.range_radio == SAVE_RANGE); + act = FILE_SAVE; + } + + /* switch off other radio buttons, switch on current one */ + XmToggleButtonGadgetSetState(range_all_radio[act], False, False); + XmToggleButtonGadgetSetState(range_marked_radio[act], False, False); + XmToggleButtonGadgetSetState(range_from_to_radio[act], False, False); + XmToggleButtonGadgetSetState(w, True, False); + + /* set from-to range to sensitive if appropriate */ + XtSetSensitive(from_label[act], sensitivity); + XtSetSensitive(page_from_text[act], sensitivity); + XtSetSensitive(to_label[act], sensitivity); + XtSetSensitive(page_to_text[act], sensitivity); + XtSetSensitive(of_label[act], sensitivity); + +#else /* MOTIF */ + + int i; + UNUSED(call_data); + if (is_print_button(flag)) { + act = FILE_PRINT; + } + else { + act = FILE_SAVE; + } + + UNUSED(w); + + i = (int) XawToggleGetCurrent(range_all_radio[act]); + + if (i != 0) { + ASSERT(i != PRINT_MARKED || i != SAVE_MARKED || pageinfo_have_marked_pages(), + "PRINT_MARKED or SAVE_MARKED shouldn't be togglable if there are no marked pages!"); + + if (act == FILE_PRINT) + curr_pr_info.range_radio = i; + else + curr_save_info.range_radio = i; + } + +#endif /* MOTIF */ +} + +static void +set_filename_callback(const char *fname, void *data) +{ + if (fname != NULL) { + Widget w = (Widget)data; + XtVaSetValues(w, +#ifdef MOTIF + XmNvalue, fname, + XmNcursorPosition, strlen(fname), +#else + XtNstring, fname, +#endif + NULL); + } +} + + +static void +cb_browse(Widget w, XtPointer client_data, XtPointer call_data) +{ +/* char *fname; */ + struct save_or_print_info *info = (struct save_or_print_info *)client_data; + printOrSaveActionT act = info->act; +/* Widget ret_widget; */ + Widget parent; + + UNUSED(call_data); +#ifdef MOTIF + UNUSED(w); +#else + if (act == FILE_PRINT) + xaw_print_handle_key(w, (XtPointer)"3", NULL, NULL); +#endif + + if (act == FILE_SAVE) { + static struct filesel_callback cb; /* static so that we can pass its address */ + cb.title = "Xdvi: Save to file"; + cb.prompt = "Save to file:"; + cb.ok = "OK"; + cb.cancel = "Cancel"; + cb.init_path = NULL; + cb.filemask = "*.dvi"; + cb.must_exist = False; + cb.exit_on_cancel = False; + cb.func_ptr = set_filename_callback; + cb.data = print_to_file_text[act]; + + if (!get_widget_by_name(&parent, globals.widgets.top_level, Xdvi_SAVE_SHELL_NAME, True)) + parent = globals.widgets.top_level; + XsraSelFile(parent, &cb); +/* fprintf(stderr, "done saving: return widget = %p\n", ret_widget); */ + } + else { + static struct filesel_callback cb; /* static so that we can pass its address */ + cb.title = "Xdvi: Print to file"; + cb.prompt = "Print to file:"; + cb.ok = "OK"; + cb.cancel = "Cancel"; + cb.init_path = NULL; + cb.filemask = "*.ps"; + cb.must_exist = False; + cb.exit_on_cancel = False; + cb.func_ptr = set_filename_callback; + cb.data = print_to_file_text[act]; + + if (!get_widget_by_name(&parent, globals.widgets.top_level, Xdvi_PRINT_SHELL_NAME, True)) + parent = globals.widgets.top_level; + XsraSelFile(parent, &cb); +/* fname = XsraSelFile(parent, "Xdvi: Print to file", "Print to file:", */ +/* "OK", "Cancel", */ +/* NULL, */ +/* "*.ps", False, &ret_widget); */ +/* fprintf(stderr, "done printing: return widget = %p\n", ret_widget); */ + } + +/* if (fname != NULL) { */ +/* XtVaSetValues(print_to_file_text[act], */ +/* #ifdef MOTIF */ +/* XmNvalue, fname, XmNcursorPosition, strlen(fname), */ +/* #else */ +/* XtNstring, fname, */ +/* #endif */ +/* NULL); */ +/* } */ +/* free(fname); */ +} + +/* create a popup dialog for printing a DVI file OR saving pages from a DVI file */ +static Widget +create_print_or_save_window(struct save_or_print_info *info, char *ofstring) +{ + printOrSaveActionT act = info->act; + Widget top_level_shell; + Atom WM_DELETE_WINDOW; +#ifndef MOTIF + Widget form, paned, box; + Widget f01form, f02form; /* dummy forms to get indentation consistent */ + Widget r1form; + Widget r1label; + Widget r2form, r3form, r4form = 0; + Widget r4label; + Widget r5label; + Widget r6form; + Widget r61form; + Widget r7form; + char *str; + + XtTranslations xlats, xlats2, wm_translations; + XtAccelerators accels2; + int ddist; + + static Dimension w_avg = 220; + Dimension w_curr; + + /* handy abbrevs */ +#define HORIZONTAL_RESIZING_NO XtNleft, XtChainLeft, XtNright, XtChainLeft +#define HORIZONTAL_RESIZING_YES XtNleft, XtChainLeft, XtNright, XtChainRight +#define VERTICAL_RESIZING_NO XtNtop, XtChainTop, XtNbottom, XtChainTop + + XtAddActions(print_actions, XtNumber(print_actions)); + + if (act == FILE_SAVE) { + str = get_string_va("<Message>WM_PROTOCOLS: WM_save_cancel(%p)\n" + "<Key>Escape:WM_save_cancel(%p)\n" + "<Key>q:WM_save_cancel(%p)", + info, info, info); + wm_translations = XtParseTranslationTable(str); + free(str); + } + else { + str = get_string_va("<Message>WM_PROTOCOLS: WM_print_cancel(%p)\n" + "<Key>Escape:WM_print_cancel(%p)\n" + "<Key>q:WM_print_cancel(%p)", + info, info, info); + wm_translations = XtParseTranslationTable(str); + free(str); + } + + top_level_shell = XtVaCreatePopupShell(act == FILE_SAVE ? Xdvi_SAVE_SHELL_NAME : Xdvi_PRINT_SHELL_NAME, + transientShellWidgetClass, + globals.widgets.top_level, + XtNtitle, act == FILE_SAVE ? "xdvik: Save file" : "xdvik: Print DVI file", + XtNmappedWhenManaged, False, + XtNtransientFor, globals.widgets.top_level, + XtNallowShellResize, True, + XtNtranslations, wm_translations, + NULL); + + WM_DELETE_WINDOW = XInternAtom(XtDisplay(top_level_shell), "WM_DELETE_WINDOW", False); + + paned = XtVaCreateManagedWidget(act == FILE_SAVE ? "save_paned" : "print_paned", + panedWidgetClass, top_level_shell, NULL); + + form = XtVaCreateManagedWidget("form", formWidgetClass, + paned, + XtNallowResize, True, + NULL); + XtVaGetValues(form, XtNdefaultDistance, &ddist, NULL); +/* fprintf(stderr, "form1: %ld\n", (long)form); */ + + xlats = XtParseTranslationTable("<EnterWindow>:highlight(Always)\n" + "<LeaveWindow>:unhighlight()\n" + "<Btn1Down>,<Btn1Up>:set()notify()"); + + if (act == FILE_PRINT) { + str = get_string_va("<Key>Return:printInternal(%p)", info); + xlats2 = XtParseTranslationTable(str); + } + else { + str = get_string_va("<Key>Return:saveInternal(%p)", info); + xlats2 = XtParseTranslationTable(str); + } + free(str); + + if (act == FILE_PRINT) { /* selection `to printer/to file' */ + /* FIXME: set real sizes here */ +/* Dimension w_curr; */ + f01form = XtVaCreateManagedWidget("dummy_print_to_label_form", formWidgetClass, + form, + XtNresizable, True, + XtNborderWidth, 0, + /* XtNvertDistance, 10, */ + HORIZONTAL_RESIZING_YES, + NULL); + r1label = XtVaCreateManagedWidget("print_to", labelWidgetClass, + f01form, + XtNlabel, "Print to: ", + XtNborderWidth, 0, + HORIZONTAL_RESIZING_NO, + NULL); + + r2form = XtVaCreateManagedWidget("print_to_printer_form", formWidgetClass, + form, + XtNresizable, True, + XtNborderWidth, 0, + XtNvertDistance, 0, + XtNfromVert, f01form, + HORIZONTAL_RESIZING_YES, + XtNallowResize, True, + NULL); + + accels2 = XtParseAcceleratorTable("<Btn1Down>,<Btn1Up>:set()notify()"); + + to_printer_radio = XtVaCreateManagedWidget("to_printer", +#ifdef XAW + radioWidgetClass, +#else + toggleWidgetClass, +#endif + r2form, + XtNlabel, "Printer: ", + XtNborderWidth, 0, + XtNhighlightThickness, 1, + XtNradioData, PRINT_TO_PRINTER, + XtNstate, resource.default_printing_target == PRINT_TO_PRINTER, + XtNtranslations, xlats, + XtNaccelerators, accels2, + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, + NULL); + + XtAddCallback(to_printer_radio, XtNcallback, cb_print_vs_file, NULL); + XtInstallAccelerators(r2form, to_printer_radio); + + print_to_printer_text = XtVaCreateManagedWidget("printer_text", asciiTextWidgetClass, + r2form, + XtNwidth, w_avg, + XtNdataCompression, False, + XtNeditType, XawtextEdit, + /* XtNresize, XawtextResizeWidth, */ + XtNfromHoriz, to_printer_radio, + HORIZONTAL_RESIZING_YES, + VERTICAL_RESIZING_NO, + /* XtNscrollHorizontal, XawtextScrollAlways, */ + NULL); + XtOverrideTranslations(print_to_printer_text, xlats2); + XtAddEventHandler(print_to_printer_text, KeyPressMask | ButtonPressMask, False, + xaw_print_handle_key, (XtPointer)"2"); + + adjust_vertically(to_printer_radio, print_to_printer_text, ddist); + + r3form = XtVaCreateManagedWidget("print_to_file_form", formWidgetClass, + form, + XtNborderWidth, 0, + XtNvertDistance, 0, + XtNfromVert, r2form, + HORIZONTAL_RESIZING_YES, + NULL); + + to_file_radio_or_label[act] = XtVaCreateManagedWidget("to_file", +#ifdef XAW + radioWidgetClass, +#else + toggleWidgetClass, +#endif + r3form, + XtNhighlightThickness, 1, + XtNborderWidth, 0, + XtNlabel, "File: ", + XtNstate, resource.default_printing_target == PRINT_TO_FILE, + XtNradioGroup, to_printer_radio, + XtNradioData, PRINT_TO_FILE, + XtNtranslations, xlats, + XtNaccelerators, accels2, + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, + NULL); + XtAddCallback(to_file_radio_or_label[act], XtNcallback, cb_print_vs_file, NULL); + XtInstallAccelerators(r3form, to_file_radio_or_label[act]); + + print_to_file_text[act] = XtVaCreateManagedWidget("file_text", asciiTextWidgetClass, + r3form, + XtNwidth, w_avg, + XtNdataCompression, False, + XtNeditType, XawtextEdit, + XtNfromHoriz, to_file_radio_or_label[act], + HORIZONTAL_RESIZING_YES, + VERTICAL_RESIZING_NO, + NULL); + XtOverrideTranslations(print_to_file_text[act], xlats2); + XtAddEventHandler(print_to_file_text[act], KeyPressMask | ButtonPressMask, False, + xaw_print_handle_key, (XtPointer)"3"); + } + else { /* not printing, but saving */ + int i; + Dimension max_entry_width = 0; + f01form = XtVaCreateManagedWidget("dummy_save_as_form", formWidgetClass, + form, + XtNresizable, True, + XtNborderWidth, 0, + /* XtNvertDistance, 10, */ + HORIZONTAL_RESIZING_YES, + NULL); + r1label = XtVaCreateManagedWidget("save_as", labelWidgetClass, + f01form, + XtNlabel, "Save as: ", + XtNborderWidth, 0, + HORIZONTAL_RESIZING_NO, + NULL); + r1form = XtVaCreateManagedWidget("save_to_file_form", formWidgetClass, + form, + XtNborderWidth, 0, + XtNfromVert, f01form, + HORIZONTAL_RESIZING_YES, + VERTICAL_RESIZING_NO, + NULL); +/* fprintf(stderr, "formr1: %ld\n", (long)r1form); */ +#if 0 + menu_double_arrow_bitmap + = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level), + RootWindowOfScreen(XtScreen(globals.widgets.top_level)), + (char *)menu_double_arrow_bits, MENU_DOUBLE_ARROW_W, MENU_DOUBLE_ARROW_H); +#endif + format_selection_label = XtVaCreateManagedWidget("format_selection_label", labelWidgetClass, + r1form, + XtNlabel, "Format: ", + XtNborderWidth, 0, + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, + NULL); + format_selection_button = XtVaCreateManagedWidget("format_selection_button", menuButtonWidgetClass, + r1form, + XtNmenuName, "format_selection_popup", + XtNlabel, output_format_mapping[resource.default_saving_format].fmt_string, + XtNjustify, XtJustifyLeft, + XtNborderWidth, resource.btn_border_width, + XtNfromHoriz, format_selection_label, +#if 0 + XtNhighlightThickness, 0, +#endif + NULL); +#if 0 /* Hack for arrow - menuButtonWidget cannot have a XtNrightBitmap!!! */ + format_selection_b1 = XtVaCreateManagedWidget("format_selection_b1", commandWidgetClass, + r1form, + XtNborderWidth, resource.btn_border_width, + XtNfromHoriz, format_selection_button, + XtNhorizDistance, -1, + XtNhighlightThickness, 0, + XtNbitmap, menu_double_arrow_bitmap, + NULL); + adjust_heights(format_selection_button, format_selection_b1, NULL); +#endif + format_selection_popup = XtCreatePopupShell("format_selection_popup", simpleMenuWidgetClass, + format_selection_button, +/* globals.widgets.top_level, */ + NULL, 0); +#if 0 + XtAddCallback(format_selection_b1, XtNcallback, xaw_popup_menu_cb, format_selection_popup); +#endif + for (i = 1; i < FORMAT_SEL_CNT; i++) { + char name[1024]; + Dimension curr_entry_width; + SNPRINTF(name, sizeof name, "format_selection_pulldown_%d", i); + format_selection_menu[i] = XtVaCreateManagedWidget(name, smeBSBObjectClass, + format_selection_popup, + XtNjustify, XtJustifyLeft, + XtNlabel, + i == resource.default_saving_format + ? output_format_mapping[0].fmt_string + : output_format_mapping[i].fmt_string, + NULL); + XtVaGetValues(format_selection_menu[i], XtNwidth, &curr_entry_width, NULL); + if (curr_entry_width > max_entry_width) + max_entry_width = curr_entry_width; + XtAddCallback(format_selection_menu[i], XtNcallback, select_format_cb, form); + } + XtVaSetValues(format_selection_button, XtNwidth, max_entry_width, NULL); + + r3form = XtVaCreateManagedWidget("to_file_form", formWidgetClass, + form, + XtNborderWidth, 0, + XtNfromVert, r1form, + HORIZONTAL_RESIZING_YES, + VERTICAL_RESIZING_NO, + NULL); + to_file_radio_or_label[act] = XtVaCreateManagedWidget("to_file", labelWidgetClass, + r3form, + XtNlabel, "File name: ", + XtNborderWidth, 0, + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, + NULL); + print_to_file_text[act] = XtVaCreateManagedWidget("file_text", asciiTextWidgetClass, + r3form, + XtNwidth, w_avg, + XtNdataCompression, False, + /* XtNvertDistance, ddist + 5, */ + XtNeditType, XawtextEdit, + XtNfromHoriz, to_file_radio_or_label[act], + HORIZONTAL_RESIZING_YES, + VERTICAL_RESIZING_NO, + NULL); + XtOverrideTranslations(print_to_file_text[act], xlats2); + + + { /* align left edges of format selection pulldown and filename field */ + Dimension w1, w2, w_max; + XtVaGetValues(format_selection_label, XtNwidth, &w1, NULL); + XtVaGetValues(to_file_radio_or_label[act], XtNwidth, &w2, NULL); + w_max = MAX(w1, w2); + XtVaSetValues(format_selection_button, XtNhorizDistance, ddist + (w_max - w1), NULL); + XtVaSetValues(print_to_file_text[act], XtNhorizDistance, ddist + (w_max - w2), NULL); + } + } + +#define DIST_1 8 /* FIXME */ + print_to_file_button[act] = XtVaCreateManagedWidget("file_button", commandWidgetClass, + r3form, + XtNlabel, "Browse ...", + XtNfromHoriz, print_to_file_text[act], + XtNhorizDistance, DIST_1, + /* attach to right of form, no resizing: */ + XtNleft, XtChainRight, XtNright, XtChainRight, + VERTICAL_RESIZING_NO, + NULL); + + XtVaGetValues(print_to_file_button[act], XtNwidth, &w_curr, NULL); + XtVaSetValues(print_to_file_text[act], XtNwidth, w_avg - w_curr - DIST_1 - 2, NULL); +#undef DIST_1 + + adjust_vertically(to_file_radio_or_label[act], print_to_file_text[act], ddist + 5); + adjust_vertically(print_to_file_text[act], to_file_radio_or_label[act], ddist + 5); + adjust_vertically(print_to_file_button[act], to_file_radio_or_label[act], ddist + 5); + + XtAddCallback(print_to_file_button[act], XtNcallback, cb_browse, (XtPointer)info); + + /* other dvips options */ + r4form = XtVaCreateManagedWidget("dvips_options_form", formWidgetClass, + form, + XtNborderWidth, 0, + XtNfromVert, r3form, + XtNvertDistance, 0, + HORIZONTAL_RESIZING_YES, + NULL); + + r4label = XtVaCreateManagedWidget("dvips_options_name", labelWidgetClass, + r4form, + XtNlabel, "Dvips Options:", + XtNborderWidth, 0, + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, + NULL); + + dvips_options_text[act] = XtVaCreateManagedWidget("dvips_options_text", asciiTextWidgetClass, + r4form, + XtNwidth, w_avg, + XtNdataCompression, False, + XtNeditType, XawtextEdit, + XtNfromHoriz, r4label, + HORIZONTAL_RESIZING_YES, + VERTICAL_RESIZING_NO, + NULL); + XtOverrideTranslations(dvips_options_text[act], xlats2); + + { /* align left edges of input fields */ + Dimension w1, w2, w3, w_max; + + if (act == FILE_PRINT) + XtVaGetValues(to_printer_radio, XtNwidth, &w1, NULL); + else + XtVaGetValues(format_selection_label, XtNwidth, &w1, NULL); + + XtVaGetValues(to_file_radio_or_label[act], XtNwidth, &w2, NULL); + XtVaGetValues(r4label, XtNwidth, &w3, NULL); + + w_max = MAX(MAX(w1, w2), w3); + + if (act == FILE_PRINT) + XtVaSetValues(print_to_printer_text, XtNhorizDistance, ddist + (w_max - w1), NULL); + else + XtVaSetValues(format_selection_button, XtNhorizDistance, ddist + (w_max - w1), NULL); + + XtVaSetValues(print_to_file_text[act], XtNhorizDistance, ddist + (w_max - w2), NULL); + XtVaSetValues(dvips_options_text[act], XtNhorizDistance, ddist + (w_max - w3), NULL); + } + + /* page selection */ + + f02form = XtVaCreateManagedWidget("dummy_pages_form", formWidgetClass, + form, + XtNfromVert, r4form, + XtNresizable, True, + XtNborderWidth, 0, + /* XtNvertDistance, 10, */ + HORIZONTAL_RESIZING_NO, + NULL); + + r5label = XtVaCreateManagedWidget("range_lab", labelWidgetClass, + f02form, + XtNlabel, "Pages:", + XtNborderWidth, 0, + HORIZONTAL_RESIZING_NO, + NULL); + + r6form = XtVaCreateManagedWidget("page_range_form", formWidgetClass, + form, + XtNborderWidth, 0, + XtNfromVert, f02form, + /* XtNvertDistance, 0, */ + HORIZONTAL_RESIZING_NO, + NULL); + + accels2 = XtParseAcceleratorTable("<Btn1Down>,<Btn1Up>:set()notify()"); + range_all_radio[act] = XtVaCreateManagedWidget("range_all", +#ifdef XAW + radioWidgetClass, +#else + toggleWidgetClass, +#endif + r6form, + XtNlabel, "All ", + XtNborderWidth, 0, + XtNhighlightThickness, 1, + XtNradioData, act == FILE_PRINT ? PRINT_ALL : SAVE_ALL, + XtNstate, True, /* enable this button by default */ + XtNtranslations, xlats, + XtNaccelerators, accels2, + /* XtNvertDistance, 0, */ + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, + NULL); + if (act == FILE_PRINT) + XtAddCallback(range_all_radio[act], XtNcallback, cb_range, (XtPointer)PRINT_ALL); + else + XtAddCallback(range_all_radio[act], XtNcallback, cb_range, (XtPointer)SAVE_ALL); + XtInstallAccelerators(r6form, range_all_radio[act]); + + r61form = XtVaCreateManagedWidget("range_marked_form", formWidgetClass, + form, + XtNborderWidth, 0, + XtNfromVert, r6form, + /* XtNvertDistance, 0, */ + HORIZONTAL_RESIZING_NO, + NULL); + + range_marked_radio[act] = XtVaCreateManagedWidget("range_marked", +#ifdef XAW + radioWidgetClass, +#else + toggleWidgetClass, +#endif + + r61form, + XtNlabel, "Marked ", + XtNborderWidth, 0, + XtNhighlightThickness, 1, + XtNradioGroup, range_all_radio[act], + XtNradioData, act == FILE_PRINT ? PRINT_MARKED : SAVE_MARKED, + XtNtranslations, xlats, + XtNaccelerators, accels2, + /* XtNvertDistance, 0, */ + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, + NULL); + if (act == FILE_PRINT) + XtAddCallback(range_marked_radio[act], XtNcallback, cb_range, (XtPointer)PRINT_RANGE); + else + XtAddCallback(range_marked_radio[act], XtNcallback, cb_range, (XtPointer)SAVE_RANGE); + XtInstallAccelerators(r61form, range_marked_radio[act]); + + r7form = XtVaCreateManagedWidget("range_from_to_form", formWidgetClass, + form, + XtNborderWidth, 0, + XtNfromVert, r61form, + /* XtNvertDistance, 0, */ + XtNresizable, True, + /* HORIZONTAL_RESIZING_NO, */ + NULL); + + range_from_to_radio[act] = XtVaCreateManagedWidget("range_from_to", +#ifdef XAW + radioWidgetClass, +#else + toggleWidgetClass, +#endif + r7form, + XtNlabel, "From: ", + XtNborderWidth, 0, + XtNhighlightThickness, 1, + XtNradioGroup, range_all_radio[act], + XtNradioData, act == FILE_PRINT ? PRINT_RANGE : SAVE_RANGE, + XtNtranslations, xlats, + XtNaccelerators, accels2, + /* XtNvertDistance, 0, */ + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, + NULL); + if (act == FILE_PRINT) + XtAddCallback(range_from_to_radio[act], XtNcallback, cb_range, (XtPointer)PRINT_RANGE); + else + XtAddCallback(range_from_to_radio[act], XtNcallback, cb_range, (XtPointer)SAVE_RANGE); + XtInstallAccelerators(r7form, range_from_to_radio[act]); + + page_from_text[act] = XtVaCreateManagedWidget("range_from", asciiTextWidgetClass, + r7form, + XtNdataCompression, False, + XtNeditType, XawtextEdit, + XtNwidth, 50, + XtNfromHoriz, range_from_to_radio[act], + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, + NULL); + XtOverrideTranslations(page_from_text[act], xlats2); + XtAddEventHandler(page_from_text[act], KeyPressMask | ButtonPressMask, False, + xaw_range_handle_key, (XtPointer)act); + + to_label[act] = XtVaCreateManagedWidget("range_to_lab", labelWidgetClass, + r7form, + XtNlabel, "to: ", + XtNborderWidth, 0, + XtNfromHoriz, page_from_text[act], + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, + NULL); + + page_to_text[act] = XtVaCreateManagedWidget("range_to", asciiTextWidgetClass, + r7form, + XtNdataCompression, False, + XtNeditType, XawtextEdit, + XtNwidth, 50, + XtNfromHoriz, to_label[act], + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, + NULL); + XtOverrideTranslations(page_to_text[act], xlats2); + XtAddEventHandler(page_to_text[act], KeyPressMask | ButtonPressMask, False, + xaw_range_handle_key, (XtPointer)act); + + of_label[act] = XtVaCreateManagedWidget("range_of", labelWidgetClass, + r7form, + XtNlabel, ofstring, + XtNborderWidth, 0, + XtNresizable, True, + XtNfromHoriz, page_to_text[act], + /* HORIZONTAL_RESIZING_NO, */ + XtNleft, XtChainLeft, + VERTICAL_RESIZING_NO, + NULL); + + adjust_vertically(range_from_to_radio[act], page_from_text[act], ddist); + adjust_vertically(to_label[act], page_from_text[act], ddist); + adjust_vertically(of_label[act], page_from_text[act], ddist); + + /* box for the OK/Cancel button */ + box = XtVaCreateManagedWidget("box", formWidgetClass, + paned, + /* resizing by user isn't needed */ + XtNshowGrip, False, + XtNdefaultDistance, 6, /* some padding */ + /* resizing the window shouldn't influence this box, + * but only the pane widget + */ + XtNskipAdjust, True, + XtNaccelerators, G_accels_cr, + NULL); + + ok_button[act] = XtVaCreateManagedWidget(act == FILE_PRINT ? "print_button" : "save_button", + commandWidgetClass, box, + XtNlabel, act == FILE_PRINT ? "Print" : "Save", + XtNaccelerators, G_accels_cr, + XtNtop, XtChainTop, + XtNbottom, XtChainBottom, + HORIZONTAL_RESIZING_NO, + NULL); + if (act == FILE_PRINT) + XtAddCallback(ok_button[act], XtNcallback, cb_print_or_save, (XtPointer)info); + else + XtAddCallback(ok_button[act], XtNcallback, cb_print_or_save, (XtPointer)info); + + XtInstallAccelerators(form, ok_button[act]); + if (act == FILE_PRINT) + XtInstallAccelerators(print_to_printer_text, ok_button[act]); + XtInstallAccelerators(print_to_file_text[act], ok_button[act]); + + cancel_button[act] = XtVaCreateManagedWidget("cancel", commandWidgetClass, + box, + XtNlabel, "Cancel", + XtNfromHoriz, ok_button[act], + XtNbottom, XtChainBottom, + XtNjustify, XtJustifyRight, + XtNleft, XtChainRight, + XtNright, XtChainRight, + NULL); + if (act == FILE_PRINT) + XtAddCallback(cancel_button[act], XtNcallback, cb_print_cancel, (XtPointer)info); + else + XtAddCallback(cancel_button[act], XtNcallback, cb_save_cancel, (XtPointer)info); + + XtManageChild(top_level_shell); + center_window(top_level_shell, globals.widgets.top_level); + XtMapWidget(top_level_shell); + XSetWMProtocols(XtDisplay(top_level_shell), XtWindow(top_level_shell), &WM_DELETE_WINDOW, 1); + + if (pageinfo_have_marked_pages()) { + XawToggleSetCurrent(range_all_radio[act], + act == FILE_PRINT ? (XtPointer)PRINT_MARKED : (XtPointer)SAVE_MARKED); + } + else if ((act == FILE_PRINT && save_pr_info.range_radio == PRINT_MARKED) + || (act == FILE_SAVE && save_save_info.range_radio == SAVE_MARKED)) { + XawToggleSetCurrent(range_all_radio[act], + act == FILE_PRINT ? (XtPointer)PRINT_ALL : (XtPointer)SAVE_ALL); + } + +#else /* not MOTIF */ + Widget form, pane, box; + Widget frame1, f1label, f1child; + Widget r4label; + Widget frame2, f2label, f2child; + XmString str; + XtTranslations xlats; + + XtAddActions(print_actions, XtNumber(print_actions)); + + if (act == FILE_SAVE) { + char *ptr = get_string_va("#override \n<Key>osfCancel:WM_save_cancel(%p)", info); + xlats = XtParseTranslationTable(ptr); + free(ptr); + } + else { + char *ptr = get_string_va("#override \n<Key>osfCancel:WM_print_cancel(%p)", info); + xlats = XtParseTranslationTable(ptr); + free(ptr); + } + + top_level_shell = XtVaCreatePopupShell(act == FILE_SAVE ? "save_popup" : "print_popup", + xmDialogShellWidgetClass, globals.widgets.top_level, + XmNtitle, act == FILE_SAVE ? "Save file" : "Print DVI file", + XmNallowShellResize, True, + XmNdeleteResponse, XmDO_NOTHING, /* we'll take care of that ourselves */ + XmNmappedWhenManaged, False, /* so that we can center it first */ + NULL); + + /* make the window manager destroy action just pop down the dialog */ + WM_DELETE_WINDOW = XmInternAtom(XtDisplay(top_level_shell), "WM_DELETE_WINDOW", False); + if (act == FILE_SAVE) + XmAddWMProtocolCallback(top_level_shell, WM_DELETE_WINDOW, cb_save_cancel, (XtPointer)info); + else + XmAddWMProtocolCallback(top_level_shell, WM_DELETE_WINDOW, cb_print_cancel, (XtPointer)info); + + + pane = XtVaCreateWidget(act == FILE_SAVE ? "save_paned" : "print_paned", + xmPanedWindowWidgetClass, top_level_shell, + /* make sashes invisible */ + XmNsashWidth, 1, + XmNsashHeight, 1, + /* turn separator off, since it gives visual noise with the frames */ + XmNseparatorOn, False, + NULL); + + + form = XtVaCreateWidget("form", xmFormWidgetClass, + pane, + XmNhorizontalSpacing, DDIST_MAJOR, + XmNverticalSpacing, DDIST_MAJOR, + XmNautoUnmanage, False, + XTranslations, xlats, + NULL); + + if (act == FILE_PRINT) { + /* First frame: print to printer or file */ + + frame1 = XtVaCreateWidget("print_to_frame", xmFrameWidgetClass, form, + XmNmarginWidth, DDIST, + XmNmarginHeight, DDIST, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XTranslations, xlats, + NULL); + + str = XmStringCreateLocalized("Print to:"); + f1label = XtVaCreateManagedWidget("title", xmLabelGadgetClass, + frame1, + XmNchildType, XmFRAME_TITLE_CHILD, + XmNlabelString, str, + NULL); + XmStringFree(str); + + f1child = XtVaCreateWidget("form", xmFormWidgetClass, frame1, + XmNhorizontalSpacing, DDIST, + XmNverticalSpacing, DDIST, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XTranslations, xlats, + NULL); + + str = XmStringCreateLocalized("Printer:"); + to_printer_radio = XtVaCreateManagedWidget("to_printer", + xmToggleButtonGadgetClass, f1child, + XmNlabelString, str, + XmNindicatorType, XmONE_OF_MANY, + XmNset, resource.default_printing_target == PRINT_TO_PRINTER, + XmNtopAttachment, XmATTACH_FORM, + XmNtopOffset, 0, + XmNleftAttachment, XmATTACH_FORM, + XmNleftOffset, 0, + NULL); + XmStringFree(str); + XtAddCallback(to_printer_radio, XmNvalueChangedCallback, cb_print_vs_file, (XtPointer)1); + + XtOverrideTranslations(to_printer_radio, xlats); + + print_to_printer_text = XtVaCreateManagedWidget("print_text", xmTextFieldWidgetClass, + f1child, + XmNtopAttachment, XmATTACH_FORM, + XmNtopOffset, 0, + XmNleftAttachment, XmATTACH_WIDGET, + XmNrightAttachment, XmATTACH_FORM, + XmNleftWidget, to_printer_radio, + XTranslations, xlats, + NULL); + XtAddCallback(print_to_printer_text, XmNactivateCallback, cb_print_or_save, info); + + adjust_heights(to_printer_radio, print_to_printer_text, NULL); + + str = XmStringCreateLocalized("File:"); + to_file_radio_or_label[act] = XtVaCreateManagedWidget("to_file", xmToggleButtonGadgetClass, + f1child, + XmNlabelString, str, + XmNindicatorType, XmONE_OF_MANY, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, print_to_printer_text, + XmNleftAttachment, XmATTACH_FORM, + XmNset, resource.default_printing_target == PRINT_TO_FILE, + XmNleftOffset, 0, + NULL); + XmStringFree(str); + XtAddCallback(to_file_radio_or_label[act], XmNvalueChangedCallback, cb_print_vs_file, (XtPointer)2); + + str = XmStringCreateLocalized("Browse ..."); + print_to_file_button[act] = XtVaCreateManagedWidget("file_button", xmPushButtonWidgetClass, + f1child, + XmNlabelString, str, + XmNsensitive, resource.default_printing_target == PRINT_TO_FILE, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, print_to_printer_text, + XmNrightAttachment, XmATTACH_FORM, + NULL); + XmStringFree(str); + + XtOverrideTranslations(to_file_radio_or_label[act], xlats); + XtOverrideTranslations(print_to_file_button[act], xlats); + + print_to_file_text[act] = XtVaCreateManagedWidget("file_text", xmTextFieldWidgetClass, + f1child, + XmNsensitive, resource.default_printing_target == PRINT_TO_FILE, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, print_to_printer_text, + XmNrightAttachment, XmATTACH_WIDGET, + XmNrightWidget, print_to_file_button[act], + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, to_file_radio_or_label[act], + XTranslations, xlats, + NULL); + + adjust_heights(to_file_radio_or_label[act], print_to_file_button[act], print_to_file_text[act], NULL); + + XtAddCallback(print_to_file_text[act], XmNactivateCallback, cb_print_or_save, (XtPointer)info); + XtAddCallback(print_to_file_button[act], XmNactivateCallback, cb_browse, (XtPointer)info); + + XtManageChild(f1child); + XtManageChild(frame1); + + } /* saving, not printing */ + else { + frame1 = XtVaCreateWidget("save_as_frame", xmFrameWidgetClass, + form, + XmNmarginWidth, DDIST, + XmNmarginHeight, DDIST, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + NULL); + + str = XmStringCreateLocalized("Save as:"); + f1label = XtVaCreateManagedWidget("title", xmLabelGadgetClass, + frame1, + XmNchildType, XmFRAME_TITLE_CHILD, + XmNlabelString, str, + NULL); + XmStringFree(str); + + f1child = XtVaCreateWidget("form", xmFormWidgetClass, frame1, + XmNhorizontalSpacing, DDIST, + XmNverticalSpacing, DDIST, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XTranslations, xlats, + NULL); +/* fprintf(stderr, "CHILD: 0x%x\n", (long)f1child); */ + str = XmStringCreateLocalized("Format:"); + format_selection_texts[0] = XmStringCreateLocalized((char *)output_format_mapping[0].fmt_string); + format_selection_texts[1] = XmStringCreateLocalized((char *)output_format_mapping[1].fmt_string); + format_selection_texts[2] = XmStringCreateLocalized((char *)output_format_mapping[2].fmt_string); + format_selection_texts[3] = XmStringCreateLocalized((char *)output_format_mapping[3].fmt_string); + format_selection_texts[4] = XmStringCreateLocalized((char *)output_format_mapping[4].fmt_string); + format_selection_option + = XmVaCreateSimpleOptionMenu(f1child, "format_selection_option", + str, 'F', + resource.default_saving_format, /*initial menu selection*/ + select_format_cb, /* callback */ + XmVaPUSHBUTTON, format_selection_texts[0], 'P', NULL, NULL, + XmVaPUSHBUTTON, format_selection_texts[1], 'F', NULL, NULL, + XmVaPUSHBUTTON, format_selection_texts[2], 'D', NULL, NULL, + XmVaPUSHBUTTON, format_selection_texts[3], 'T', NULL, NULL, + XmVaPUSHBUTTON, format_selection_texts[4], 'U', NULL, NULL, + NULL); + + XmStringFree(str); + XmStringFree(format_selection_texts[0]); + XmStringFree(format_selection_texts[1]); + XmStringFree(format_selection_texts[2]); + XmStringFree(format_selection_texts[3]); + XmStringFree(format_selection_texts[4]); + + str = XmStringCreateLocalized("File name:"); + to_file_radio_or_label[act] = XtVaCreateManagedWidget("to_file", xmLabelGadgetClass, + f1child, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, format_selection_option, + XmNleftAttachment, XmATTACH_FORM, + /* XmNchildType, XmFRAME_TITLE_CHILD, */ + XmNlabelString, str, + NULL); + XmStringFree(str); + str = XmStringCreateLocalized("Browse ..."); + print_to_file_button[act] = XtVaCreateManagedWidget("file_button", xmPushButtonWidgetClass, + f1child, + XmNlabelString, str, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, format_selection_option, + XmNrightAttachment, XmATTACH_FORM, + XTranslations, xlats, + NULL); + print_to_file_text[act] = XtVaCreateManagedWidget("file_text", xmTextFieldWidgetClass, + f1child, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, format_selection_option, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, to_file_radio_or_label[act], + XmNrightAttachment, XmATTACH_WIDGET, + XmNrightWidget, print_to_file_button[act], + XTranslations, xlats, + NULL); + + adjust_heights(to_file_radio_or_label[act], print_to_file_button[act], print_to_file_text[act], NULL); + XtAddCallback(print_to_file_button[act], XmNactivateCallback, cb_browse, (XtPointer)info); + } + +#if 1 + { /* align left edges of widgets */ + Dimension w1, w2; + + XtVaGetValues(to_file_radio_or_label[act], XmNwidth, &w2, NULL); + if (act == FILE_PRINT) + XtVaGetValues(to_printer_radio, XmNwidth, &w1, NULL); + else { + Widget w; + if (get_widget_by_name(&w, format_selection_option, "OptionLabel", True)) { + XtVaGetValues(w, XmNwidth, &w1, NULL); + } + else { + w1 = w2; + } + } + + if (w1 > w2) { + Dimension offset = DDIST; + offset += w1 - w2; + XtVaSetValues(print_to_file_text[act], XmNleftOffset, offset, NULL); + } + else if (w2 > w1) { + Dimension offset = DDIST; + offset += w2 - w1; + if (act == FILE_PRINT) + XtVaSetValues(print_to_printer_text, XmNleftOffset, offset, NULL); + else { + Widget w; + if (get_widget_by_name(&w, format_selection_option, "OptionLabel", True)) { + /* FIXME: Setting width would be cleaner, but it doesn't work ...? + Dimension curr_w; + XtVaGetValues(w, XmNwidth, &curr_w, NULL); + XtVaSetValues(w, XmNwidth, curr_w + offset, NULL); + */ + /* fprintf(stderr, "Setting width from %d to: %d\n", curr_w, curr_w + offset); */ + XtVaSetValues(w, + XmNmarginRight, offset, + XmNalignment, XmALIGNMENT_BEGINNING, + NULL); + } + + } + } + } +#endif /* 0 */ + + if (act == FILE_SAVE) { + XtManageChild(format_selection_option); + XtManageChild(f1child); + XtManageChild(frame1); + } + + /* additional dvips options */ + str = XmStringCreateLocalized("Dvips Options:"); + r4label = XtVaCreateManagedWidget("dvips_options_name", xmLabelGadgetClass, + form, + XmNlabelString, str, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, frame1, + XmNleftAttachment, XmATTACH_FORM, + NULL); + XmStringFree(str); + + dvips_options_text[act] = XtVaCreateManagedWidget("dvips_options_text", xmTextFieldWidgetClass, + form, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, frame1, + XmNleftAttachment, XmATTACH_WIDGET, + XmNrightAttachment, XmATTACH_FORM, + XmNleftWidget, r4label, + XmNcolumns, 20, + XTranslations, xlats, + NULL); + XtAddCallback(dvips_options_text[act], XmNactivateCallback, cb_print_or_save, (XtPointer)info); + adjust_heights(r4label, dvips_options_text[act], NULL); + + /* page selection */ + frame2 = XtVaCreateWidget(act == FILE_PRINT ? "print_page_frame" : "save_page_frame", xmFrameWidgetClass, + form, + XmNmarginWidth, DDIST, + XmNmarginHeight, DDIST, + XmNresizable, True, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, dvips_options_text[act], + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XTranslations, xlats, + NULL); + + str = XmStringCreateLocalized("Pages:"); + f2label = XtVaCreateManagedWidget("title", xmLabelGadgetClass, + frame2, + XmNchildType, XmFRAME_TITLE_CHILD, + XmNlabelString, str, + NULL); + XmStringFree(str); + + f2child = XtVaCreateWidget("form", xmFormWidgetClass, frame2, + XmNhorizontalSpacing, DDIST, + XmNverticalSpacing, DDIST, + XmNresizable, True, + XTranslations, xlats, + XTranslations, xlats, + NULL); + + str = XmStringCreateLocalized("All"); + range_all_radio[act] = XtVaCreateManagedWidget("all", + xmToggleButtonGadgetClass, f2child, + XmNlabelString, str, + XmNindicatorType, XmONE_OF_MANY, + XmNset, True, + XmNtopAttachment, XmATTACH_FORM, + XmNtopOffset, 0, + XmNleftAttachment, XmATTACH_FORM, + XmNleftOffset, 0, + XTranslations, xlats, + NULL); + XmStringFree(str); + if (act == FILE_PRINT) { + XtAddCallback(range_all_radio[act], XmNvalueChangedCallback, cb_range, (XtPointer)PRINT_ALL); + } + else { + XtAddCallback(range_all_radio[act], XmNvalueChangedCallback, cb_range, (XtPointer)SAVE_ALL); + } + + str = XmStringCreateLocalized("Marked"); + range_marked_radio[act] = XtVaCreateManagedWidget("marked", + xmToggleButtonGadgetClass, f2child, + XmNlabelString, str, + XmNindicatorType, XmONE_OF_MANY, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, range_all_radio[act], + XmNleftAttachment, XmATTACH_FORM, + XmNleftOffset, 0, + XTranslations, xlats, + NULL); + XmStringFree(str); + if (act == FILE_PRINT) { + XtAddCallback(range_marked_radio[act], XmNvalueChangedCallback, cb_range, (XtPointer)PRINT_MARKED); + } + else { + XtAddCallback(range_marked_radio[act], XmNvalueChangedCallback, cb_range, (XtPointer)SAVE_MARKED); + } + + str = XmStringCreateLocalized("Range:"); + range_from_to_radio[act] = XtVaCreateManagedWidget("range", + xmToggleButtonGadgetClass, f2child, + XmNlabelString, str, + XmNindicatorType, XmONE_OF_MANY, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, range_marked_radio[act], + XmNleftAttachment, XmATTACH_FORM, + XmNleftOffset, 0, + XTranslations, xlats, + NULL); + XmStringFree(str); + + if (act == FILE_PRINT) { + XtAddCallback(range_from_to_radio[act], XmNvalueChangedCallback, cb_range, (XtPointer)PRINT_RANGE); + } + else { + XtAddCallback(range_from_to_radio[act], XmNvalueChangedCallback, cb_range, (XtPointer)SAVE_RANGE); + } + str = XmStringCreateLocalized("From"); + from_label[act] = XtVaCreateManagedWidget("from", xmLabelGadgetClass, + f2child, + XmNlabelString, str, + XmNsensitive, False, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, range_marked_radio[act], + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, range_from_to_radio[act], + NULL); + XmStringFree(str); + + page_from_text[act] = XtVaCreateManagedWidget("from_page", + xmTextFieldWidgetClass, f2child, + XmNcolumns, 5, + XmNsensitive, False, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, range_marked_radio[act], + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, from_label[act], + XTranslations, xlats, + NULL); + XtAddCallback(page_from_text[act], XmNactivateCallback, cb_print_or_save, (XtPointer)info); + + str = XmStringCreateLocalized("to"); + to_label[act] = XtVaCreateManagedWidget("to", xmLabelGadgetClass, + f2child, + XmNlabelString, str, + XmNsensitive, False, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, range_marked_radio[act], + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, page_from_text[act], + NULL); + XmStringFree(str); + + page_to_text[act] = XtVaCreateManagedWidget("to_page", xmTextFieldWidgetClass, + f2child, + XmNcolumns, 5, + XmNsensitive, False, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, range_marked_radio[act], + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, to_label[act], + XTranslations, xlats, + NULL); + XtAddCallback(page_to_text[act], XmNactivateCallback, cb_print_or_save, (XtPointer)info); + + str = XmStringCreateLocalized(ofstring); + of_label[act] = XtVaCreateManagedWidget("of", xmLabelGadgetClass, + f2child, + XmNlabelString, str, + XmNsensitive, False, + XmNresizable, True, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, range_marked_radio[act], + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, page_to_text[act], + NULL); + XmStringFree(str); + + adjust_heights(range_from_to_radio[act], + from_label[act], page_from_text[act], + to_label[act], page_to_text[act], + of_label[act], + NULL); + + XtManageChild(f2child); + XtManageChild(frame2); + + /* box for the OK/Cancel button */ + box = XtVaCreateManagedWidget("box", xmFormWidgetClass, + pane, + XmNskipAdjust, True, /* don't resize this area */ + XTranslations, xlats, + NULL); + + if (act == FILE_PRINT) + str = XmStringCreateLocalized("Print"); + else + str = XmStringCreateLocalized("Save"); + ok_button[act] = XtVaCreateManagedWidget(act == FILE_PRINT ? "print_button" : "save_button", + xmPushButtonWidgetClass, box, + XmNlabelString, str, + XmNshowAsDefault, True, + XmNdefaultButtonShadowThickness, 1, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + /* to mimick appearance of native dialog buttons: */ + XmNmarginWidth, 6, + XmNmarginHeight, 4, + XmNtopOffset, 10, + XmNbottomOffset, 10, + XmNleftOffset, 10, + NULL); + XmStringFree(str); + if (act == FILE_PRINT) + XtAddCallback(ok_button[act], XmNactivateCallback, cb_print_or_save, (XtPointer)info); + else + XtAddCallback(ok_button[act], XmNactivateCallback, cb_print_or_save, (XtPointer)info); + + XtOverrideTranslations(ok_button[act], XtParseTranslationTable("<Key>Return:ArmAndActivate()")); + + str = XmStringCreateLocalized("Cancel"); + cancel_button[act] = XtVaCreateManagedWidget("cancel", + xmPushButtonWidgetClass, box, + XmNlabelString, str, + XmNdefaultButtonShadowThickness, 1, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + /* to mimick appearance of native dialog buttons: */ + XmNmarginWidth, 6, + XmNmarginHeight, 4, + XmNtopOffset, 10, + XmNbottomOffset, 10, + XmNrightOffset, 10, + NULL); + XmStringFree(str); + + XtOverrideTranslations(range_all_radio[act], xlats); + XtOverrideTranslations(range_marked_radio[act], xlats); + XtOverrideTranslations(range_from_to_radio[act], xlats); + XtOverrideTranslations(ok_button[act], xlats); + XtOverrideTranslations(cancel_button[act], xlats); + + if (act == FILE_PRINT) + XtAddCallback(cancel_button[act], XmNactivateCallback, cb_print_cancel, (XtPointer)info); + else + XtAddCallback(cancel_button[act], XmNactivateCallback, cb_save_cancel, (XtPointer)info); + + { /* make the buttons the same size: */ + Dimension w1, w2; + XtVaGetValues(ok_button[act], XtNwidth, &w1, NULL); + XtVaGetValues(cancel_button[act], XtNwidth, &w2, NULL); + if (w1 < w2) + XtVaSetValues(ok_button[act], XtNwidth, w2, NULL); + else if (w2 > w1) + XtVaSetValues(cancel_button[act], XtNwidth, w1, NULL); + } + + if (pageinfo_have_marked_pages()) { + XmToggleButtonGadgetSetState(range_marked_radio[act], True, True); + } + else if (save_pr_info.range_radio == PRINT_MARKED) { + XmToggleButtonGadgetSetState(range_all_radio[act], True, True); + } + + XtManageChild(form); + XtManageChild(pane); + center_window(top_level_shell, globals.widgets.top_level); + XtMapWidget(top_level_shell); + XmProcessTraversal(ok_button[act], XmTRAVERSE_CURRENT); +#endif /* not MOTIF */ + + /* set default output format */ + save_save_info.output_format = curr_save_info.output_format = + output_format_mapping[resource.default_saving_format].fmt; + + if (act == FILE_SAVE) + update_dvips_options_sensitivity(resource.default_saving_format, top_level_shell); + + return top_level_shell; +} +#undef HORIZONTAL_RESIZING_NO +#undef HORIZONTAL_RESIZING_YES +#undef VERTICAL_RESIZING_NO + + +static void +set_target_radio(struct dialog_info *save_info, struct dialog_info *curr_info, printOrSaveActionT act) +{ +#ifdef MOTIF + if (save_info->print_radio > 0 && save_info->print_radio != curr_info->print_radio) { + XmToggleButtonGadgetSetState(save_info->print_radio == PRINT_TO_PRINTER + ? to_printer_radio + : to_file_radio_or_label[act], True, True); + } +#else /* MOTIF */ + UNUSED(act); + UNUSED(curr_info); + XawToggleSetCurrent(to_printer_radio, (XtPointer)save_info->print_radio); +#endif /* MOTIF */ +} + +static void +set_range_radio(struct dialog_info *save_info, struct dialog_info *curr_info, printOrSaveActionT act) +{ +#ifdef MOTIF + if (save_info->range_radio > 0 && save_info->range_radio != curr_info->range_radio) { + Widget selected = 0; + switch (save_info->range_radio) { + case PRINT_ALL: + case SAVE_ALL: + selected = range_all_radio[act]; + break; + case PRINT_MARKED: + case SAVE_MARKED: + selected = range_marked_radio[act]; + break; + case PRINT_RANGE: + case SAVE_RANGE: + selected = range_from_to_radio[act]; + break; + default: + ASSERT(0, "wrong button type in set_range_radio()"); + } + XmToggleButtonGadgetSetState(selected, True, True); + } +#else /* MOTIF */ + UNUSED(curr_info); + XawToggleSetCurrent(range_all_radio[act], (XtPointer)save_info->range_radio); +#endif /* MOTIF */ +} + +void +save_or_print_callback(struct save_or_print_info *info) +{ + printOrSaveActionT act = info->act; + static char ofstring[1024]; /* should be ample */ + struct dialog_info *save_info = NULL, *curr_info = NULL; + + if (act == FILE_PRINT) { + save_info = &save_pr_info; + curr_info = &curr_pr_info; + if (print_is_active) { + XBell(DISP, 0); + XRaiseWindow(DISP, XtWindow(print_shell)); + return; + } + + if (printlog_raise_active()) { + XBell(DISP, 0); + return; + } + } + else if (act == FILE_SAVE) { + save_info = &save_save_info; + curr_info = &curr_save_info; + if (save_is_active) { + XBell(DISP, 0); + XRaiseWindow(DISP, XtWindow(save_shell)); + return; + } + } + else { + ASSERT(0, "wrong action type in save_or_print_callback()"); + } + + if (globals.dvi_file.bak_fp == NULL) { + popup_message(globals.widgets.top_level, + MSG_ERR, + NULL, + "Empty or incomplete DVI file. " + "Please select a new DVI file via File -> Open."); + return; + } + + if (globals.pageno_correct == 1) + sprintf(ofstring, "of %d physical pages", total_pages); + else + sprintf(ofstring, "of %d to %d pages", globals.pageno_correct, total_pages + globals.pageno_correct - 1); + + if (act == FILE_PRINT && print_shell == NULL) { + curr_info->print_radio = resource.default_printing_target; + print_shell = create_print_or_save_window(info, ofstring); +#ifdef MOTIF + set_sensitivity(resource.default_printing_target); +#endif + } + else if (act == FILE_SAVE && save_shell == NULL) + save_shell = create_print_or_save_window(info, ofstring); + else { /* if the window was already created */ +#ifdef MOTIF + XmString str; +#endif + /* XXX: is this actually needed??? */ + /* XXX: To test, remove the following */ + if (!pageinfo_have_marked_pages()) { + if (act == FILE_PRINT && save_pr_info.range_radio == PRINT_MARKED) { + save_pr_info.range_radio = PRINT_ALL; + } + else if (act == FILE_SAVE && save_save_info.range_radio == SAVE_MARKED) { + save_save_info.range_radio = SAVE_ALL; + } + } + + if (act == FILE_PRINT) + set_target_radio(save_info, curr_info, act); + set_range_radio(save_info, curr_info, act); +#ifdef MOTIF + str = XmStringCreateLocalized(ofstring); + XtVaSetValues(of_label[act], XmNlabelString, str, NULL); + XmStringFree(str); + + /* FIXME: this seems neccessary, otherwise labels are misaligned + again if window is closed and opened a second time? */ + adjust_heights(range_from_to_radio[act], + from_label[act], page_from_text[act], + to_label[act], page_to_text[act], + of_label[act], + NULL); + + if (save_info->dvips_options != NULL) { + XtVaSetValues(dvips_options_text[act], XmNvalue, save_info->dvips_options, + XmNcursorPosition, strlen(save_info->dvips_options), NULL); + } + else { + XtVaSetValues(dvips_options_text[act], XmNvalue, "", NULL); + } +#else /* MOTIF */ + XtVaSetValues(of_label[act], XtNlabel, ofstring, NULL); + + if (save_info->dvips_options != NULL) { + XtVaSetValues(dvips_options_text[act], XtNstring, save_info->dvips_options, NULL); + XawTextSetInsertionPoint(dvips_options_text[act], strlen(save_info->dvips_options)); + } + else { + XtVaSetValues(dvips_options_text[act], XtNstring, "", NULL); + } +#endif /* MOTIF */ + } + + get_initial_dialog_values(save_info, act); + +#ifdef MOTIF + if (act == FILE_PRINT) + XtVaSetValues(print_to_printer_text, XmNvalue, save_info->printer_name, + XmNcursorPosition, strlen(save_info->printer_name), NULL); + + XtVaSetValues(dvips_options_text[act], XmNvalue, save_info->dvips_options, + XmNcursorPosition, strlen(save_info->dvips_options), NULL); + + XtVaSetValues(page_from_text[act], XmNvalue, save_info->from_page, + XmNcursorPosition, strlen(save_info->from_page), NULL); + + XtVaSetValues(page_to_text[act], XmNvalue, save_info->to_page, + XmNcursorPosition, strlen(save_info->to_page), NULL); +#else /* MOTIF */ + if (act == FILE_PRINT) { + XtVaSetValues(print_to_printer_text, XtNstring, save_info->printer_name, NULL); + XawTextSetInsertionPoint(print_to_printer_text, strlen(save_info->printer_name)); + } + + XtVaSetValues(dvips_options_text[act], XtNstring, save_info->dvips_options, NULL); + XawTextSetInsertionPoint(dvips_options_text[act], strlen(save_info->dvips_options)); + + XtVaSetValues(page_from_text[act], XtNstring, save_info->from_page, NULL); + XawTextSetInsertionPoint(page_from_text[act], strlen(save_info->from_page)); + + XtVaSetValues(page_to_text[act], XtNstring, save_info->to_page, NULL); + XawTextSetInsertionPoint(page_to_text[act], strlen(save_info->to_page)); +#endif /* MOTIF */ + + if (act == FILE_PRINT) { + if (pageinfo_have_marked_pages()) { + XtSetSensitive(range_marked_radio[FILE_PRINT], True); + } + else { + XtSetSensitive(range_marked_radio[FILE_PRINT], False); + } + ASSERT(save_pr_info.range_radio != PRINT_MARKED || pageinfo_have_marked_pages(), + "PRINT_MARKED shouldn't be togglable if there are no marked pages!"); + } + else if (act == FILE_SAVE) { + if (pageinfo_have_marked_pages()) { + XtSetSensitive(range_marked_radio[FILE_SAVE], True); + } + else { + XtSetSensitive(range_marked_radio[FILE_SAVE], False); + } + ASSERT(save_save_info.range_radio != SAVE_MARKED || pageinfo_have_marked_pages(), + "SAVE_MARKED shouldn't be togglable if there are no marked pages!"); + } + + set_outfile_name(save_info, &curr_save_info, act); + +#ifdef MOTIF + XtVaSetValues(print_to_file_text[act], XmNvalue, save_info->file_name, + XmNcursorPosition, strlen(save_info->file_name), NULL); +#else /* MOTIF */ + XtVaSetValues(print_to_file_text[act], XtNstring, save_info->file_name, NULL); + XawTextSetInsertionPoint(print_to_file_text[act], strlen(save_info->file_name)); +#endif /* MOTIF */ + + if (act == FILE_PRINT) { + XtPopup(print_shell, XtGrabNone); + print_is_active = True; + } + else { + XtPopup(save_shell, XtGrabNone); + save_is_active = True; + } +} + +static int +getpageno(Widget w) +{ + char *s, *p; + int pageno; +#ifndef MOTIF + XtVaGetValues(w, XtNstring, &s, NULL); +#else /* MOTIF */ + s = XmTextFieldGetString(w); +#endif + p = s; + if (*p == '-') + ++p; + if (!isdigit((int)*p)) { + return 0; + } + do { + ++p; + } while (isdigit((int)*p)); + if (*p != '\0') + return 0; + + pageno = atoi(s) - globals.pageno_correct; +#ifdef MOTIF + XtFree(s); +#endif + return pageno; +} + + +/* + return text value of Widget w in newly allocated memory, + free()ing old_val as appropriate. Returns NULL if + text value starts with '\0'. +*/ +static char * +get_saved_command(char **old_val, Widget w) +{ + char *val = NULL; + + if (*old_val != NULL) { +#if MOTIF + XtFree((char *)*old_val); +#else + free((char*)*old_val); +#endif + *old_val = NULL; + } +#if MOTIF + val = XmTextFieldGetString(w); + if (*val == '\0') { + XtFree((char *)val); + val = NULL; + } +#else + XtVaGetValues(w, XtNstring, &val, NULL); + if (*val == '\0') { + val = NULL; + } + else { + val = xstrdup(val); + } +#endif + return val; +} + +/* fill curr_print_info and save_print_info with current values */ +static void +get_save_window_state(struct dialog_info *save_info, struct dialog_info *curr_info, printOrSaveActionT act) +{ + save_info->print_radio = curr_info->print_radio; + save_info->range_radio = curr_info->range_radio; + + if (act == FILE_PRINT) { + save_info->printer_name = get_saved_command(&(save_info->printer_name), print_to_printer_text); + curr_info->printer_name = get_saved_command(&(curr_info->printer_name), print_to_printer_text); + } + save_info->file_name = get_saved_command(&(save_info->file_name), print_to_file_text[act]); + curr_info->file_name = get_saved_command(&(curr_info->file_name), print_to_file_text[act]); + + save_info->dvips_options = get_saved_command(&(save_info->dvips_options), dvips_options_text[act]); + curr_info->dvips_options = get_saved_command(&(curr_info->dvips_options), dvips_options_text[act]); + + save_info->from_page = get_saved_command(&(save_info->from_page), page_from_text[act]); + save_info->to_page = get_saved_command(&(save_info->to_page), page_to_text[act]); + + TRACE_GUI((stderr, "saved options:\nprinter: |%s|, file: |%s|, dvips: |%s|, from: |%s|, to: |%s|\n", + save_info->printer_name ? save_info->printer_name : "<NULL>", + save_info->file_name ? save_info->file_name : "<NULL>", + save_info->dvips_options ? save_info->dvips_options : "<NULL>", + save_info->from_page ? save_info->from_page : "<NULL>", + save_info->to_page ? save_info->to_page : "<NULL>")); +} + +static void +popdown_dialog_and_print_or_save(XtPointer myinfo) +{ + struct save_or_print_info *info = (struct save_or_print_info *)myinfo; + struct select_pages_info *pinfo = (struct select_pages_info *)info->pinfo; + struct dialog_info *curr_info = NULL; + + /* pop down the dialog */ + if (pinfo->act == FILE_PRINT) { + curr_info = &save_pr_info; + cb_print_cancel(NULL, info, NULL); + /* when printing to file, set ps_out.fname to non-NULL; this will + notify called routines that printing should go to the file */ + if (curr_info->print_radio == PRINT_TO_FILE) { + pinfo->finfo->ps_out.fname = xstrdup(curr_info->expanded_filename); + } + } + else { + curr_info = &save_save_info; + cb_save_cancel(NULL, info, NULL); + } + + if (globals.dvi_file.bak_fp == NULL) { /* shouldn't happen */ + info->message_popup = popup_message(globals.widgets.top_level, + MSG_ERR, NULL, + "No active DVI file"); + return; + } + + ASSERT(pinfo->finfo->dvi_in.fp != NULL, "DVI input FILE * must have been set!"); + + /* when saving, set the final file names */ + if (curr_info->range_radio == SAVE_RANGE + || curr_info->range_radio == SAVE_MARKED + || curr_info->range_radio == SAVE_ALL) { + switch (curr_info->output_format) { + case FMT_DVI: + pinfo->finfo->dvi_out.fname = xstrdup(curr_info->expanded_filename); + TRACE_GUI((stderr, "DVI out_name: |%s|", pinfo->finfo->dvi_out.fname)); + break; + case FMT_PS: + pinfo->finfo->ps_out.fname = xstrdup(curr_info->expanded_filename); + TRACE_GUI((stderr, "PS out_name: |%s|", pinfo->finfo->ps_out.fname)); + break; + case FMT_PS2PDF: + pinfo->finfo->pdf_out.fname = xstrdup(curr_info->expanded_filename); + TRACE_GUI((stderr, "PDF out_name: |%s|", pinfo->finfo->pdf_out.fname)); + break; + case FMT_ISO_8859_1: + case FMT_UTF8: + pinfo->finfo->txt_out.fname = xstrdup(curr_info->expanded_filename); + TRACE_GUI((stderr, "TXT out_name: |%s|", pinfo->finfo->txt_out.fname)); + break; + case FMT_NONE: + break; + } + } + + /* add info about page ranges */ + switch (curr_info->range_radio) { + case SAVE_RANGE: + case PRINT_RANGE: + /* convert from 1-based to 0-based */ + pinfo->from = strtoul(curr_info->from_page, (char **)NULL, 10) - 1; + pinfo->to = strtoul(curr_info->to_page, (char **)NULL, 10) - 1; + pinfo->callback = check_pagerange; + break; + case SAVE_MARKED: + case PRINT_MARKED: + pinfo->callback = check_marked; + break; + case SAVE_ALL: + case PRINT_ALL: + pinfo->callback = NULL; + break; + case NO_PAGE_VAL: + ASSERT(0, "Shouldn't happen: NO_PAGE_VAL passed to popdown_dialog_and_print_or_save()"); + break; + } + + store_preference(NULL, "dvipsOptionsString", "%s", save_pr_info.dvips_options ? save_pr_info.dvips_options : ""); + if (pinfo->act == FILE_PRINT) { + store_preference(NULL, "defaultPrintingTarget", "%d", save_pr_info.print_radio); + store_preference(NULL, "dvipsPrinterString", "%s", save_pr_info.printer_name ? save_pr_info.printer_name : ""); + internal_print(pinfo); + } + else { + store_preference(NULL, "defaultSavingFormat", "%d", save_save_info.output_format); + internal_save(pinfo, curr_info->output_format); + } +} + +/* Clean up after user has aborted printing/saving */ +static void +do_cleanup(XtPointer arg) +{ + struct save_or_print_info *info = (struct save_or_print_info *)arg; + struct select_pages_info *pinfo = (struct select_pages_info *)info->pinfo; + struct file_info *finfo = pinfo->finfo; + + cancel_saving(info); + unlink(finfo->dvi_tmp.fname); +} + +/* XXX: is the save_* stuff actually *needed*??? */ +static Boolean +validate_and_save_values(struct save_or_print_info *info) +{ + int page_range_begin, page_range_end; + printOrSaveActionT act = info->act; + + if ((act == FILE_PRINT && curr_pr_info.range_radio == PRINT_RANGE) + || (act == FILE_SAVE && curr_save_info.range_radio == SAVE_RANGE)) { + page_range_begin = getpageno(page_from_text[act]); + page_range_end = getpageno(page_to_text[act]); + if (page_range_begin + 1 < 1) { + info->message_popup = + popup_message(act == FILE_PRINT ? print_shell : save_shell, + MSG_ERR, + "Please specify a valid page range.", + "Invalid page range: start (%d) < 1.", + page_range_begin + 1); + return False; + } + else if (page_range_begin > page_range_end) { + info->message_popup = + popup_message(act == FILE_PRINT ? print_shell : save_shell, + MSG_ERR, + "Please specify a valid page range.", + "Invalid page range: start (%d) > end (%d).", + page_range_begin + 1, page_range_end + 1); + return False; + } + else if (page_range_end + 1 > total_pages) { + info->message_popup = + popup_message(act == FILE_PRINT ? print_shell : save_shell, + MSG_ERR, + "Please specify a valid page range.", + "Invalid page range: end (%d) > total_pages (%d).", + page_range_end + 1, total_pages); + return False; + } + + if (act == FILE_PRINT) { + curr_pr_info.from_page = xrealloc(curr_pr_info.from_page, LENGTH_OF_INT + 1); + curr_pr_info.to_page = xrealloc(curr_pr_info.to_page, LENGTH_OF_INT + 1); + sprintf(curr_pr_info.from_page, "%d", page_range_begin); + sprintf(curr_pr_info.to_page, "%d", page_range_end); + } + else { + curr_save_info.from_page = xrealloc(curr_save_info.from_page, LENGTH_OF_INT + 1); + curr_save_info.to_page = xrealloc(curr_save_info.to_page, LENGTH_OF_INT + 1); + sprintf(curr_save_info.from_page, "%d", page_range_begin); + sprintf(curr_save_info.to_page, "%d", page_range_end); + } + } + + if (act == FILE_PRINT) + get_save_window_state(&save_pr_info, &curr_pr_info, act); + else + get_save_window_state(&save_save_info, &curr_save_info, act); + + return True; +} + +/* + * Routines for freeing allocated resources in file_info: + * close file pointers, and free file name memory. + */ +static void +free_file_info(struct file_IO *info) +{ + if (info->fp != NULL) { + fclose(info->fp); + info->fp = NULL; + } + free(info->fname); + info->fname = NULL; +} + +static void +reset_file_info(struct file_info *finfo) +{ + free_file_info(&(finfo->dvi_in)); + free_file_info(&(finfo->dvi_tmp)); + free_file_info(&(finfo->dvi_out)); + free_file_info(&(finfo->ps_out)); + free_file_info(&(finfo->pdf_out)); + free_file_info(&(finfo->txt_out)); +} + +/* + First round of sanity checks. Note the use of curr_pr_info here; + this is before the values are saved to save_pr_info. +*/ +static void +print_precheck1(XtPointer myinfo) +{ + /* Initialize structures with defaults. Note: structures are + static, so that they survive between calls, and to make memory + management with callbacks (and all the stuff in print-internal.c) + easier. + */ + struct save_or_print_info *info = (struct save_or_print_info *)myinfo; + printOrSaveActionT act = info->act; + static struct select_pages_info pinfo = { + FILE_ALL, 0, 0, NULL, check_marked, { 0, NULL }, NO_ERROR + }; + static struct file_info finfo = { + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL } + }; + char *ok_button_str = NULL; + FILE *fin = NULL; + + /* Return if there are active popups; otherwise, the print process + may get messed up */ + if (raise_message_windows()) { + XBell(DISP, 0); +/* popup_message(pinfo.act == FILE_PRINT ? print_shell : save_shell, */ +/* MSG_WARN, NULL, */ +/* "Please close other open message windows first!"); */ + return; + } + + /* free all pointers in case we've been called again */ + reset_file_info(&finfo); + + pinfo.finfo= &finfo; /* pass finfo in data field of pinfo to callbacks etc. */ + pinfo.act = act; + info->pinfo = &pinfo; + + /* Validate page ranges, and if they are correct, save them to the + * appropriate save_save_info/save_print_info structure. + * NOTE: From here on, always use the save_* variants to retrieve values! + */ + if (!validate_and_save_values(info)) + return; + + /* We need to create a temporary DVI file containing the selected or all pages + if we want to do one of the following: + - print the marked pages + - save to a DVI file + - print only the marked pages to a PS or a PDF file. + */ + if ((pinfo.act == FILE_PRINT && curr_pr_info.range_radio == PRINT_MARKED) + || (pinfo.act == FILE_SAVE + && (save_save_info.output_format == FMT_DVI + || ((save_save_info.output_format == FMT_PS || save_save_info.output_format == FMT_PS2PDF) + && save_save_info.range_radio == SAVE_MARKED)))) { + /* need to create a temporary DVI file containing the selected pages */ + int tmp_fd = xdvi_temp_fd(&(finfo.dvi_tmp.fname)); /* this allocates finfo.dvi_tmp.fname */ + + if (tmp_fd == -1) { + info->message_popup = + popup_message(pinfo.act == FILE_PRINT ? print_shell : save_shell, + MSG_ERR, NULL, + "Couldn't create temporary DVI file for printing: %s", strerror(errno)); + return; + } + /* don't use XFOPEN here, since we don't want to treat an error in opening the file as fatal. */ + /* `b' in mode won't hurt ... */ + if ((finfo.dvi_tmp.fp = try_fdopen(tmp_fd, "wb+")) == NULL) { /* failure */ + info->message_popup = + popup_message(pinfo.act == FILE_PRINT ? print_shell : save_shell, + MSG_ERR, + "Xdvi needs to create a temporary file containing the " + "currently marked pages, but creating that file failed. " + "Try to fix the cause of the problem, or choose " + "\"Print selected pages\" as a workaround.", + "Could not open temp file for writing: %s.\n", + strerror(errno)); + return; + } + } + /* We try to use the original DVI file unless it's corrupted, in which case + we ask the user if they want to save the cached copy instead */ + if ((fin = fopen(globals.dvi_name, "rb")) == NULL + || !find_postamble(fin, &(pinfo.errflag))) { + Boolean could_not_open = False; + if (fin == NULL) + could_not_open = True; + if (fin) + fclose(fin); + + /* if we can't use the temporary file as source for our copy, this is a fatal error */ + if (!resource.use_temp_fp) { + info->message_popup = + popup_message(pinfo.act == FILE_PRINT ? print_shell : save_shell, + MSG_ERR, NULL, + "Could not copy DVI file %s: The file %s", + globals.dvi_name, could_not_open ? "doesn't exist any more" : "seems to be corrupted"); + return; + } + + /* else, we'll try to use the cached copy of the DVI file */ + if ((fin = fopen(get_tmp_dvi_name(), "rb")) == NULL) { + info->message_popup = + popup_message(pinfo.act == FILE_PRINT ? print_shell : save_shell, + MSG_ERR, NULL, + "Something's very wrong here - opening of both the " + "original DVI file and the cached copy failed!"); + return; + } + + if (!find_postamble(fin, &(pinfo.errflag))) { + info->message_popup = + popup_message(pinfo.act == FILE_PRINT ? print_shell : save_shell, + MSG_ERR, NULL, + "Shouldn't happen: Cached copy of the DVI file seems corrupted!"); + return; + } + finfo.dvi_in.fp = fin; + + /* tell user about it */ + if (pinfo.act == FILE_PRINT) + ok_button_str = "Print Copy"; + else + ok_button_str = "Save Copy"; + + info->message_popup = + choice_dialog(pinfo.act == FILE_PRINT ? print_shell : save_shell, + MSG_QUESTION, NULL, +#ifndef MOTIF + NULL, /* TODO: binding for RET? */ +#endif /* MOTIF */ + NULL, NULL, /* no pre_callbacks */ + ok_button_str, print_precheck2, (XtPointer)info, + "Cancel", do_cleanup, (XtPointer)info, + "The DVI file %s %s. Do you want to %s a cached copy of the file?", + globals.dvi_name, + could_not_open ? "doesn't exist any more" : "seems to be corrupted", + pinfo.act == FILE_PRINT ? "print" : "save"); + } + else { /* original DVI file is OK, copy it */ + finfo.dvi_in.fp = fin; + print_precheck2((XtPointer)info); + } +} + +static void +print_precheck2(XtPointer myinfo) +{ + struct save_or_print_info *info = (struct save_or_print_info *)myinfo; + char *expanded_filename = NULL; + struct select_pages_info *pinfo = (struct select_pages_info *)info->pinfo; + printOrSaveActionT act = pinfo->act; + const char *fname; + + /* Return if there are active popups; otherwise, the print process + will be messed up. */ + if (raise_message_windows()) { + XBell(DISP, 0); + return; + } + + /* expand and canonicalize path name */ + if (act == FILE_PRINT) + fname = save_pr_info.file_name; + else + fname = save_save_info.file_name; + + if (fname == NULL) { + info->message_popup = + popup_message(act == FILE_PRINT ? print_shell : save_shell, + MSG_WARN, + NULL, + "No filename specified!"); + return; + } + expanded_filename = expand_homedir(fname); /* this allocates expanded_filename */ + if (expanded_filename == NULL) { + info->message_popup = + popup_message(act == FILE_PRINT ? print_shell : save_shell, + MSG_WARN, + "Please specify either a filename or a full path, without using \"~\" or \"~user\".", + "Couldn't expand filename \"%s\" to a full path.", + fname); + return; + } + + if (act == FILE_PRINT) { + free(save_pr_info.expanded_filename); + save_pr_info.expanded_filename = expanded_filename; + } + else { + free(save_save_info.expanded_filename); + save_save_info.expanded_filename = expanded_filename; + } + + + if (act == FILE_SAVE && save_save_info.output_format == FMT_DVI) { /* canonicalize filename */ + char *tmp = expand_filename_append_dvi(save_save_info.expanded_filename, USE_CWD_PATH, False); + if (strcmp(tmp, globals.dvi_name) == 0) { + info->message_popup = + popup_message(act == FILE_PRINT ? print_shell : save_shell, + MSG_ERR, + NULL, + "Cannot overwrite the current DVI file (%s). " + "Please select a different file name.", + save_save_info.expanded_filename); + free(tmp); + return; + } + free(save_save_info.expanded_filename); + save_save_info.expanded_filename = tmp; + } + if (act == FILE_SAVE || (act == FILE_PRINT && curr_pr_info.print_radio == PRINT_TO_FILE)) { + /* check whether to clobber existing file */ + struct stat statbuf; + + if ((act == FILE_SAVE + && stat(save_save_info.expanded_filename, &statbuf) == 0 + && S_ISREG(statbuf.st_mode)) + || (act == FILE_PRINT + && stat(save_pr_info.expanded_filename, &statbuf) == 0 + && S_ISREG(statbuf.st_mode))) { + + info->message_popup = + choice_dialog(act == FILE_PRINT ? print_shell : save_shell, + MSG_QUESTION, NULL, +#ifndef MOTIF + NULL, /* TODO: binding for RET */ +#endif + NULL, NULL, /* no pre_callbacks */ + "Replace", popdown_dialog_and_print_or_save, (XtPointer)info, + /* + do nothing if user selects `cancel'; this will return to the + printing dialog, since user hasn't changed their mind about + printing, but would probably just like to choose another filename + */ + "Cancel", NULL, (XtPointer)NULL, + "%s already exists.\nDo you want to replace it?", + act == FILE_SAVE + ? save_save_info.expanded_filename + : save_pr_info.expanded_filename); + return; + } + else { + popdown_dialog_and_print_or_save(info); + } + } + else { + popdown_dialog_and_print_or_save(info); + } +} + +/* called from pagesel.c if user marks or unmarks a page in the page list */ +void +notify_print_dialog_have_marked(Boolean flag) +{ + /* Note: check for *both* windows, save and print: */ + + static pageRadioT old_value = NO_PAGE_VAL; + + if (save_is_active) { + if (!flag && curr_save_info.range_radio == SAVE_MARKED) { + /* switch button from `marked' to its previous value */ + switch (old_value) { + case SAVE_ALL: +#ifdef MOTIF + XmToggleButtonGadgetSetState(range_all_radio[FILE_SAVE], True, True); +#else + XawToggleSetCurrent(range_all_radio[FILE_SAVE], (XtPointer)old_value); +#endif + break; + case SAVE_RANGE: +#ifdef MOTIF + XmToggleButtonGadgetSetState(range_from_to_radio[FILE_SAVE], True, True); +#else + XawToggleSetCurrent(range_from_to_radio[FILE_SAVE], (XtPointer)old_value); +#endif + break; + default: break; + } + } + else if (flag) { + /* switch button to `marked', saving the current value */ + if (curr_save_info.range_radio != SAVE_MARKED) /* guard for second invocation on mouse release */ + old_value = curr_save_info.range_radio; +#ifdef MOTIF + XmToggleButtonGadgetSetState(range_marked_radio[FILE_SAVE], True, True); +#else + XawToggleSetCurrent(range_marked_radio[FILE_SAVE], (XtPointer)SAVE_MARKED); +#endif /* MOTIF */ + } + XtSetSensitive(range_marked_radio[FILE_SAVE], flag); + } + + if (print_is_active) { + if (!flag && curr_pr_info.range_radio == PRINT_MARKED) { + /* switch button from `marked' to its previous value */ + switch (old_value) { + case PRINT_ALL: +#ifdef MOTIF + XmToggleButtonGadgetSetState(range_all_radio[FILE_PRINT], True, True); +#else + XawToggleSetCurrent(range_all_radio[FILE_PRINT], (XtPointer)old_value); +#endif + break; + case PRINT_RANGE: +#ifdef MOTIF + XmToggleButtonGadgetSetState(range_from_to_radio[FILE_PRINT], True, True); +#else + XawToggleSetCurrent(range_from_to_radio[FILE_PRINT], (XtPointer)old_value); +#endif + break; + default: break; + } + } + else if (flag) { + /* switch button to `marked', saving the current value */ + if (curr_pr_info.range_radio != PRINT_MARKED) /* guard for second invocation on mouse release */ + old_value = curr_pr_info.range_radio; +#ifdef MOTIF + XmToggleButtonGadgetSetState(range_marked_radio[FILE_PRINT], True, True); +#else + XawToggleSetCurrent(range_marked_radio[FILE_PRINT], (XtPointer)PRINT_MARKED); +#endif /* MOTIF */ + } + XtSetSensitive(range_marked_radio[FILE_PRINT], flag); + } +} diff --git a/Build/source/texk/xdvik/gui/print-dialog.h b/Build/source/texk/xdvik/gui/print-dialog.h new file mode 100644 index 00000000000..95f73de3e18 --- /dev/null +++ b/Build/source/texk/xdvik/gui/print-dialog.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2003-2004 the xdvik development team + * + * 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. + */ + +#ifndef PRINT_DIALOG_H_ +#define PRINT_DIALOG_H_ + +#define Xdvi_SAVE_SHELL_NAME "save_popup" +#define Xdvi_PRINT_SHELL_NAME "print_popup" + +typedef enum { FILE_PRINT = 0, FILE_SAVE = 1, FILE_ALL = 2 } printOrSaveActionT; + +struct save_or_print_info { + printOrSaveActionT act; + Widget message_popup; + struct select_pages_info *pinfo; +}; + +extern void save_or_print_callback(struct save_or_print_info *info); +extern void cancel_saving(struct save_or_print_info *info); +extern const char *get_printer_options(void); +extern char *get_dvips_options(printOrSaveActionT act); + +extern void notify_print_dialog_have_marked(Boolean flag); + +#endif /* PRINT_DIALOG_H_ */ diff --git a/Build/source/texk/xdvik/gui/print-log.c b/Build/source/texk/xdvik/gui/print-log.c new file mode 100644 index 00000000000..dd1c26bc008 --- /dev/null +++ b/Build/source/texk/xdvik/gui/print-log.c @@ -0,0 +1,552 @@ +/* + * Copyright (c) 2002-2004 Paul Vojta and the xdvik development team + * + * 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 <string.h> +#include "xdvi-config.h" +#include "xdvi.h" + +#include "xdvi-debug.h" +#include "print-dialog.h" +#include "print-log.h" /* for adjust_vertically() */ +#include "util.h" +#include "x_util.h" +#include "xlwradio.h" + +#include <X11/Xatom.h> +#include <X11/StringDefs.h> + +/* FIXME: change #if XAW to #if !defined(MOTIF) to make it consistent with other files */ + +#ifdef MOTIF +# include <Xm/BulletinB.h> +# include <Xm/DialogS.h> +# include <Xm/MessageB.h> +# include <Xm/LabelG.h> +# include <Xm/Form.h> +# include <Xm/Frame.h> +# include <Xm/ToggleBG.h> +# include <Xm/PanedW.h> +# include <Xm/Text.h> +# include <Xm/TextF.h> +# include <Xm/PushB.h> +# include <Xm/Protocols.h> +# include <Xm/AtomMgr.h> +#else +# include <X11/Shell.h> +# include <X11/Xaw/Paned.h> +# include <X11/Xaw/Box.h> +# include <X11/Xaw/Form.h> +# include <X11/Xaw/Label.h> +# include <X11/Xaw/Command.h> +# include <X11/Xaw/Toggle.h> +# include <X11/Xaw/AsciiText.h> + +/* XawFmt8Bit is only available starting with X11R6: */ +# if XtSpecificationRelease < 6 +# define XawFmt8Bit FMT8BIT +# endif + +#endif /* MOTIF */ + +static Boolean printlog_active = False; /* if the print log is active */ +static Widget printlog_shell = NULL; + +/* global callbacks - FIXME: avoid this, by passing the callback as argument to the action? */ +static void (*g_close_callback)(Widget w, XtPointer client_data, XtPointer call_data); +static void (*g_cancel_callback)(Widget w, XtPointer client_data, XtPointer call_data); +static void (*g_destroy_callback)(Widget w, XtPointer client_data, XtPointer call_data); + +static void printlog_act_close(Widget, XEvent *, String *, Cardinal *); +static void printlog_act_keep(Widget, XEvent *, String *, Cardinal *); +static void printlog_act_unkeep(Widget, XEvent *, String *, Cardinal *); +static void printlog_act_cancel(Widget, XEvent *, String *, Cardinal *); +static void printlog_act_destroy(Widget, XEvent *, String *, Cardinal *); + +#ifdef MOTIF +static void xm_printlog_act_destroy(Widget w, XtPointer a, XtPointer b) +{ + UNUSED(w); + UNUSED(a); + UNUSED(b); + ASSERT(g_destroy_callback != NULL, "Callback not initialized"); + (g_destroy_callback)(NULL, NULL, NULL); +} +#endif + +static Atom WM_DELETE_WINDOW; + +static XtActionsRec printlog_actions[] = { + {"printlogIntClose", printlog_act_close}, + {"printlogIntKeep", printlog_act_keep}, + {"printlogIntUnkeep", printlog_act_unkeep}, + {"printlogIntCancel", printlog_act_cancel}, + {"WM_destroy", printlog_act_destroy }, +}; + +static char *text_translations = \ + "<Key>Return:printlogIntClose()\n" + "^<Key>c:printlogIntCancel()\n" + "^<Key>s:printlogIntKeep()\n" + "^<Key>q:printlogIntUnkeep()"; + +static Widget printlog_text; +static Widget printlog_close; +static Widget printlog_unkeep; +static Widget printlog_cancel; + + +#ifdef MOTIF +static XmTextPosition printlog_length; +#else +static XawTextPosition printlog_length; +#endif + +Boolean +printlog_raise_active(void) +{ + if (printlog_active) { + XRaiseWindow(DISP, XtWindow(printlog_shell)); + return True; + } + return False; +} + +void +printlog_popup(void) +{ + printlog_length = 0; + center_window(printlog_shell, globals.widgets.top_level); + XtMapWidget(printlog_shell); +/* XtPopup(printlog_shell, XtGrabNone); */ + printlog_active = True; +#ifndef MOTIF + XSetWMProtocols(XtDisplay(printlog_shell), XtWindow(printlog_shell), &WM_DELETE_WINDOW, 1); +#else + XmProcessTraversal(printlog_cancel, XmTRAVERSE_CURRENT); + XtOverrideTranslations(printlog_text, XtParseTranslationTable(text_translations)); +#endif +} + +void +printlog_reset(void) +{ + printlog_length = 0; +} + + + +void +printlog_create(const char *title, + const char *close_label, + void (*close_callback)(Widget w, XtPointer client_data, XtPointer call_data), + void (*cancel_callback)(Widget w, XtPointer client_data, XtPointer call_data), + void (*destroy_callback)(Widget w, XtPointer client_data, XtPointer call_data), + void (*unkeep_callback)(Widget w, XtPointer client_data, XtPointer call_data)) +{ + /* + FIXME BETA: The printlog presents too much detail and is confusing for users. + Re-implement it using an `printing page n of m' message, + i.e. according to the spec on: + http://xdvi.sourceforge.net/gui.html#file-print + Possible save the detailed log to a different window that can be viewed + via `File -> logs', as described on: + http://xdvi.sourceforge.net/gui.html#file-logs + */ + XtAddActions(printlog_actions, XtNumber(printlog_actions)); + g_destroy_callback = destroy_callback; + +#ifndef MOTIF + /* Create popup window */ + if (printlog_shell == NULL) { + Widget form, box, paned; +/* int ddist; */ +/* Dimension w0, w1, w2, w3, b; */ + + XtTranslations wm_translations = XtParseTranslationTable("<Message>WM_PROTOCOLS: WM_destroy()"); + printlog_shell = XtVaCreatePopupShell("printlog", + transientShellWidgetClass, globals.widgets.top_level, + XtNtitle, title, + XtNtransientFor, globals.widgets.top_level, + XtNmappedWhenManaged, False, + XtNtranslations, wm_translations, + XtNtransientFor, globals.widgets.top_level, + NULL); + + WM_DELETE_WINDOW = XInternAtom(XtDisplay(printlog_shell), "WM_DELETE_WINDOW", False); + + paned = XtVaCreateManagedWidget("paned", panedWidgetClass, printlog_shell, NULL); + + form = XtVaCreateManagedWidget("form", formWidgetClass, + paned, + XtNdefaultDistance, 6, + NULL); + printlog_text = XtVaCreateManagedWidget("text", + asciiTextWidgetClass, form, + XtNstring, "", + XtNdataCompression, False, + XtNeditType, XawtextAppend, + XtNscrollHorizontal, XawtextScrollAlways, + XtNscrollVertical, XawtextScrollAlways, + XtNwidth, 600, + XtNheight, 400, + XtNleft, XawChainLeft, + XtNright, XawChainRight, + XtNtop, XawChainTop, + XtNbottom, XawChainBottom, + NULL); + XtOverrideTranslations(printlog_text, XtParseTranslationTable(text_translations)); + + printlog_unkeep = XtVaCreateManagedWidget("keep", +#ifdef XAW + radioWidgetClass, +#else + toggleWidgetClass, +#endif + form, + XtNlabel, close_label, + XtNborderWidth, 0, + XtNisRadio, False, + XtNhighlightThickness, 1, + XtNfromVert, printlog_text, + XtNleft, XawChainLeft, + XtNright, XawChainLeft, + XtNtop, XawChainBottom, + XtNbottom, XawChainBottom, + NULL); + XtVaSetValues(printlog_unkeep, + XtNstate, resource.dvips_hang > 0 && resource.dvips_fail_hang > 0, + NULL); + XtAddCallback(printlog_unkeep, XtNcallback, unkeep_callback, NULL); + + /* box for the Close/Cancel button */ + box = XtVaCreateManagedWidget("box", formWidgetClass, + paned, + /* resizing by user isn't needed */ + XtNshowGrip, False, + XtNdefaultDistance, 6, /* some padding */ + /* resizing the window shouldn't influence this box, + * but only the pane widget + */ + XtNskipAdjust, True, + XtNaccelerators, G_accels_cr, + NULL); + + printlog_close = XtVaCreateManagedWidget("close", + commandWidgetClass, box, + XtNlabel, "Close", + XtNsensitive, False, + XtNleft, XawChainLeft, + XtNright, XawChainLeft, + XtNtop, XawChainBottom, + XtNbottom, XawChainBottom, + NULL); + XtAddCallback(printlog_close, XtNcallback, close_callback, NULL); + g_close_callback = close_callback; + + printlog_cancel = XtVaCreateManagedWidget("cancel", + commandWidgetClass, box, + XtNlabel, "Cancel", + XtNfromHoriz, printlog_unkeep, + XtNleft, XawChainRight, + XtNright, XawChainRight, + XtNtop, XawChainBottom, + XtNbottom, XawChainBottom, + NULL); + XtAddCallback(printlog_cancel, XtNcallback, cancel_callback, NULL); + g_cancel_callback = cancel_callback; + XtManageChild(printlog_shell); + } + else { + XtSetSensitive(printlog_close, False); + XtSetSensitive(printlog_cancel, True); + } + +#else /* MOTIF */ + + /* Create popup window */ + if (printlog_shell == NULL) { + Widget form, pane, box; + XmString str; + Arg args[10]; + int n; + + printlog_shell = XtVaCreatePopupShell("printlog", + xmDialogShellWidgetClass, globals.widgets.top_level, + XmNdeleteResponse, XmDO_NOTHING, /* we'll take care of that ourselves */ + XmNtitle, title, + XtNmappedWhenManaged, False, + NULL); + + WM_DELETE_WINDOW = XmInternAtom(XtDisplay(printlog_shell), "WM_DELETE_WINDOW", False); + XmAddWMProtocolCallback(printlog_shell, WM_DELETE_WINDOW, xm_printlog_act_destroy, NULL); + + pane = XtVaCreateWidget("printlog_pane", xmPanedWindowWidgetClass, printlog_shell, + /* make sashes invisible */ + XmNsashWidth, 1, + XmNsashHeight, 1, + NULL); + + form = XtVaCreateWidget("form", xmFormWidgetClass, pane, + XmNhorizontalSpacing, DDIST, + XmNverticalSpacing, DDIST, + XmNautoUnmanage, False, + NULL); + /* force vertical scrollbars. Under Motif 2.x (2.1.0 and 2.2.2 at least), + XmNeditMode must be set early in order to have an effect. */ + n = 0; + XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++; + XtSetArg(args[n], XmNeditable, False); n++; + XtSetArg(args[n], XmNrows, 24); n++; + XtSetArg(args[n], XmNcolumns, 80); n++; + + printlog_text = XmCreateScrolledText(form, "text", args, n); + + XtVaSetValues(XtParent(printlog_text), + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + NULL); + + XtManageChild(printlog_text); + + str = XmStringCreateLocalized((char *)close_label); + printlog_unkeep = XtVaCreateManagedWidget("keep", + xmToggleButtonGadgetClass, form, + XmNlabelString, str, + XmNnavigationType, XmTAB_GROUP, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, XtParent(printlog_text), + XmNleftAttachment, XmATTACH_FORM, + NULL); + XmStringFree(str); + XtAddCallback(printlog_unkeep, XmNvalueChangedCallback, unkeep_callback, NULL); + XmToggleButtonGadgetSetState(printlog_unkeep, resource.dvips_hang > 0 && resource.dvips_fail_hang > 0, False); + + /* box for Close/Cancel button */ + box = XtVaCreateManagedWidget("box", xmFormWidgetClass, + pane, + NULL); + + str = XmStringCreateLocalized("Close"); + printlog_close = XtVaCreateManagedWidget("close", + xmPushButtonWidgetClass, box, + XmNlabelString, str, + XmNshowAsDefault, True, + XmNsensitive, False, + XmNdefaultButtonShadowThickness, 1, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + /* to mimick appearance of native dialog buttons: */ + XmNmarginWidth, 6, + XmNmarginHeight, 4, + XmNtopOffset, 10, + XmNbottomOffset, 10, + XmNleftOffset, 10, + NULL); + XmStringFree(str); + XtAddCallback(printlog_close, XmNactivateCallback, close_callback, NULL); + g_close_callback = close_callback; + + str = XmStringCreateLocalized("Cancel"); + printlog_cancel = XtVaCreateManagedWidget("cancel", + xmPushButtonWidgetClass, box, + XmNlabelString, str, + XmNdefaultButtonShadowThickness, 1, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + /* to mimick appearance of native dialog buttons: */ + XmNmarginWidth, 6, + XmNmarginHeight, 4, + XmNtopOffset, 10, + XmNbottomOffset, 10, + XmNrightOffset, 10, + NULL); + XmStringFree(str); + XtAddCallback(printlog_cancel, XmNactivateCallback, cancel_callback, NULL); + g_cancel_callback = cancel_callback; + + XtManageChild(box); + XtManageChild(form); + XtManageChild(pane); +/* center_window(printlog_shell, globals.widgets.top_level); */ +/* XtMapWidget(printlog_shell); */ + /* this doesn't help */ + /* XtOverrideTranslations(form, XtParseTranslationTable(text_translations)); */ + /* XtOverrideTranslations(pane, XtParseTranslationTable(text_translations)); */ + } + else { + XtSetSensitive(printlog_close, False); + XtSetSensitive(printlog_cancel, True); + } +#endif + adjust_width(printlog_close, printlog_cancel); +} + +void +printlog_append(const char *str, size_t len) +{ +#ifndef MOTIF + + static XawTextBlock block = {0, 0, NULL, 0}; + + block.ptr = (char *) str; + block.length = len; + block.format = XawFmt8Bit; + while (XawTextReplace(printlog_text, printlog_length, printlog_length, &block) + != XawEditDone) { + int length; + + XtVaGetValues(printlog_text, XtNlength, &length, NULL); + printlog_length = length; + } + printlog_length += len; + XawTextSetInsertionPoint(printlog_text, printlog_length); + +#else /* MOTIF */ + + XmTextInsert(printlog_text, printlog_length, (char *) str); + printlog_length += len; + XtVaSetValues(printlog_text, XmNcursorPosition, printlog_length, NULL); + XmTextShowPosition(printlog_text, printlog_length); + +#endif /* MOTIF */ +} + +void +printlog_append_str(const char *str) +{ + printlog_append(str, strlen(str)); +} + +/* disable cancel button, enable close button */ +void +printlog_enable_closebutton(void) +{ + XtSetSensitive(printlog_close, True); + XtSetSensitive(printlog_cancel, False); +#if MOTIF + XmProcessTraversal(printlog_close, XmTRAVERSE_CURRENT); +#endif +} + +static void +printlog_internal_close(void) +{ + XtUnmapWidget(printlog_shell); + printlog_active = False; +#ifndef MOTIF + XtVaSetValues(printlog_text, XtNstring, "", NULL); +#else + XmTextSetString(printlog_text, ""); +#endif +} + +void +printlog_popdown(Boolean force) +{ + if (force) { /* user clicked on `Close' */ + printlog_internal_close(); + } + else { /* timer elapsed */ +#ifndef MOTIF + Boolean state; + XtVaGetValues(printlog_unkeep, XtNstate, &state, NULL); + if (state) { + printlog_internal_close(); + } +#else + if (XmToggleButtonGadgetGetState(printlog_unkeep)) { + printlog_internal_close(); + } +#endif + } +} + + +static void +printlog_act_close(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + UNUSED(w); + UNUSED(event); + UNUSED(params); + UNUSED(num_params); + + ASSERT(g_close_callback != NULL, "Callback not initialized"); + (g_close_callback)(NULL, NULL, NULL); +} + +static void +printlog_act_keep(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + UNUSED(w); + UNUSED(event); + UNUSED(params); + UNUSED(num_params); + +#ifdef MOTIF + XmToggleButtonGadgetSetState(printlog_unkeep, False, False); +#else + XtVaSetValues(printlog_unkeep, XtNstate, False, NULL); +#endif +} + + + +static void +printlog_act_unkeep(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + UNUSED(w); + UNUSED(event); + UNUSED(params); + UNUSED(num_params); +#ifdef MOTIF + XmToggleButtonGadgetSetState(printlog_unkeep, True, False); +#else + XtVaSetValues(printlog_unkeep, XtNstate, True, NULL); +#endif +} + +static void +printlog_act_cancel(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + UNUSED(w); + UNUSED(event); + UNUSED(params); + UNUSED(num_params); + + ASSERT(g_cancel_callback != NULL, "Callback not initialized"); + (g_cancel_callback)(NULL, NULL, NULL); +} + +static void +printlog_act_destroy(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + UNUSED(w); + UNUSED(event); + UNUSED(params); + UNUSED(num_params); + + ASSERT((g_destroy_callback) != NULL, "Callback not initialized"); + (g_destroy_callback)(NULL, NULL, NULL); +} diff --git a/Build/source/texk/xdvik/gui/print-log.h b/Build/source/texk/xdvik/gui/print-log.h new file mode 100644 index 00000000000..90e69be8343 --- /dev/null +++ b/Build/source/texk/xdvik/gui/print-log.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2002-2004 Paul Vojta and the xdvik development team + * + * 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. + */ + +#ifndef PRINT_LOG_H_ +#define PRINT_LOG_H_ + +/* printlog access functions */ + +extern void printlog_create(const char *title, + const char *close_label, + /* callback for `close' button */ + void (*close_callback)(Widget w, XtPointer client_data, XtPointer call_data), + /* callback for `cancel' button */ + void (*cancel_callback)(Widget w, XtPointer client_data, XtPointer call_data), + /* callback for destroying window via window manager */ + void (*destroy_callback)(Widget w, XtPointer client_data, XtPointer call_data), + /* callback for `close automatically' checkbox */ + void (*unkeep_callback)(Widget w, XtPointer client_data, XtPointer call_data)); +extern Boolean printlog_raise_active(void); +extern void printlog_popup(void); +extern void printlog_reset(void); +extern void printlog_append(const char *str, size_t len); +extern void printlog_append_str(const char *str); +extern void printlog_enable_closebutton(void); +extern void printlog_popdown(Boolean override_timer); + +#endif /* PRINT_LOG_H_ */ diff --git a/Build/source/texk/xdvik/gui/search-dialog.c b/Build/source/texk/xdvik/gui/search-dialog.c new file mode 100644 index 00000000000..c77fa9d219e --- /dev/null +++ b/Build/source/texk/xdvik/gui/search-dialog.c @@ -0,0 +1,1072 @@ +/* + * Copyright (c) 2003-2004 Stefan Ulrich + * + * 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 "events.h" +#include "dvi-init.h" +#include "search-dialog.h" +#include "search-internal.h" +#include "xlwradio.h" +#include "statusline.h" +#include "string-utils.h" +#include "util.h" +#include "message-window.h" +#include "x_util.h" + +#include <X11/Xatom.h> +#include <X11/StringDefs.h> + +#ifdef MOTIF +# include <Xm/BulletinB.h> +# include <Xm/DialogS.h> +# include <Xm/PanedW.h> +# include <Xm/MessageB.h> +# include <Xm/LabelG.h> +# include <Xm/Form.h> +# include <Xm/Frame.h> +# include <Xm/ToggleBG.h> +# include <Xm/Text.h> +# include <Xm/TextF.h> +# include <Xm/PushB.h> +# include <Xm/Protocols.h> +# include <Xm/AtomMgr.h> +#else /* MOTIF */ +# include <X11/Shell.h> +# include <X11/Xaw/Paned.h> +# include <X11/Xaw/Box.h> +# include <X11/Xaw/Form.h> +# include <X11/Xaw/Label.h> +# include <X11/Xaw/Command.h> +# include <X11/Xaw/Toggle.h> +# include <X11/Xaw/AsciiText.h> +#endif /* MOTIF */ + +/* #defines for widgets that are used in common code */ +#ifdef MOTIF +# define SHELL_WIDGET xmDialogShellWidgetClass +# define PANED_WIDGET xmPanedWindowWidgetClass +# define FORM_WIDGET xmFormWidgetClass +# define LABEL_WIDGET xmLabelGadgetClass +# define TEXT_WIDGET xmTextFieldWidgetClass +# define CHECKBOX_WIDGET xmToggleButtonGadgetClass +# define CHECKBUTTON_IS_SET XmNset +# define PUSHBUTTON_WIDGET xmPushButtonWidgetClass +# define VALUE_CALLBACK_NAME XmNvalueChangedCallback +# define ACTIVATE_CALLBACK_NAME XmNactivateCallback +#else /* MOTIF */ +# define SHELL_WIDGET transientShellWidgetClass +# define PANED_WIDGET panedWidgetClass +# define FORM_WIDGET formWidgetClass +# define LABEL_WIDGET labelWidgetClass +# define TEXT_WIDGET asciiTextWidgetClass +# ifdef XAW +# define CHECKBOX_WIDGET radioWidgetClass +# else /* XAW */ +# define CHECKBOX_WIDGET toggleWidgetClass +# endif /* XAW */ +# define CHECKBUTTON_IS_SET XtNstate +# define PUSHBUTTON_WIDGET commandWidgetClass +# define VALUE_CALLBACK_NAME XtNcallback +# define ACTIVATE_CALLBACK_NAME XtNcallback +#endif /* MOTIF */ + +static Boolean m_find_popup_active = False; + +/* flags for setting single bits in resource.search_window_defaults */ +static const int SETTINGS_USE_REGEXP_FLAG = 1; +static const int SETTINGS_CASE_SENSITIVE_FLAG = 2; +static const int SETTINGS_BACKWARDS_FLAG = 4; +static const int SETTINGS_IGNORE_LINEBREAKS_FLAG = 8; + + +static void +cb_search_go(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct search_settings *settings = (struct search_settings *)client_data; + Widget find_popup; + Position x = 0, y = 0; + + ASSERT(settings != NULL, "client_data cb_search_go mustn't be NULL!"); + UNUSED(call_data); + + TRACE_FIND((stderr, "cb_search_go: settings: searchterm=|%s|, down = %d, re = %d, case = %d", + settings->term, settings->direction, settings->use_regexp, settings->case_sensitive)); + + /* retrieve the find popup window */ + if ((find_popup = XtNameToWidget(globals.widgets.top_level, "*find_popup")) == 0) { + XDVI_WARNING((stderr, "Couldn't find \"find_popup\" widget!")); + x = y = 0; + } + else { + XtVaGetValues(find_popup, XtNx, &x, XtNy, &y, NULL); + /* add some offsets */ + settings->x_pos = x + 10; + settings->y_pos = y + 10; + TRACE_GUI((stderr, "SHELL: %ld; x: %d, y: %d", (unsigned long)w, x, y)); + } + + settings->from_page = current_page; + settings->message_window = 0; + TRACE_FIND((stderr, "starting search from page: %d", settings->from_page)); + search_dvi((XtPointer)settings); +} + +static void +show_settings(const char *func_name, struct search_settings *settings) +{ + TRACE_FIND((stderr, "%s: settings: str=|%s|, down = %d, re = %d, " + "case = %d, ignore_hyphens = %d, ignore_linebreaks = %d\n", + func_name, + settings->term, + settings->direction, + settings->use_regexp, + settings->case_sensitive, + settings->ignore_hyphens, + settings->ignore_linebreaks)); + +} + + +static void +cb_regexp_search(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct search_settings *settings = (struct search_settings *)client_data; + /* Hack: Since XawToggleUnsetCurrent() will invoke this callback again, + return every second time to avoid multiple popups. This is done by + setting the flag `do_return'. + */ +#if !HAVE_REGEX_H + static Boolean do_return = False; +#endif + ASSERT(settings != NULL, "client_data cb_search_go mustn't be NULL!"); + UNUSED(call_data); + +#if !HAVE_REGEX_H + if (do_return) { + do_return = False; + return; + } + + XBell(DISP, 0); + popup_message(get_matching_parent(w, globals.widgets.top_level, "find_popup", NULL), + MSG_ERR, + NULL, + "Sorry, regular expression support (regex.h) is not available on this platform."); + do_return = True; +#ifdef MOTIF + XmToggleButtonGadgetSetState(w, True, False); +#else + XawToggleUnsetCurrent(w); +#endif + +#else + UNUSED(w); + settings->use_regexp = !(settings->use_regexp); + + /* force re-initialization of search term by resetting utf8_term: */ + free(settings->utf8_term); + settings->utf8_term = NULL; + + show_settings("cb_regexp_search", settings); +#endif + + if (settings->use_regexp) + resource.search_window_defaults |= SETTINGS_USE_REGEXP_FLAG; + else + resource.search_window_defaults &= ~SETTINGS_USE_REGEXP_FLAG; + store_preference(NULL, "searchWindowDefaults", "%u", resource.search_window_defaults); +} + +static void +cb_backwards_search(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct search_settings *settings = (struct search_settings *)client_data; + ASSERT(settings != NULL, "client_data cb_search_go mustn't be NULL!"); + UNUSED(w); + UNUSED(call_data); + + switch (settings->direction) { + case SEARCH_UP: settings->direction = SEARCH_DOWN; break; + case SEARCH_DOWN: settings->direction = SEARCH_UP; break; + default: ASSERT(0, "shouldn't happen: settings->direction is neither SEARCH_UP nor SEARCH_DOWN!"); break; + } + + show_settings("cb_backwards_search", settings); + + if (settings->direction == SEARCH_UP) + resource.search_window_defaults |= SETTINGS_BACKWARDS_FLAG; + else + resource.search_window_defaults &= ~SETTINGS_BACKWARDS_FLAG; + store_preference(NULL, "searchWindowDefaults", "%u", resource.search_window_defaults); +} + +static void +cb_match_case(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct search_settings *settings = (struct search_settings *)client_data; + ASSERT(settings != NULL, "client_data cb_search_go mustn't be NULL!"); + UNUSED(w); + UNUSED(call_data); + + settings->case_sensitive = !(settings->case_sensitive); + + show_settings("cb_match_case", settings); + + if (settings->case_sensitive) + resource.search_window_defaults |= SETTINGS_CASE_SENSITIVE_FLAG; + else + resource.search_window_defaults &= ~SETTINGS_CASE_SENSITIVE_FLAG; + store_preference(NULL, "searchWindowDefaults", "%u", resource.search_window_defaults); +} + +static void +cb_linebreaks(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct search_settings *settings = (struct search_settings *)client_data; + ASSERT(settings != NULL, "client_data cb_search_go mustn't be NULL!"); + UNUSED(w); + UNUSED(call_data); + + settings->ignore_linebreaks = !(settings->ignore_linebreaks); + settings->ignore_hyphens = !(settings->ignore_hyphens); + + show_settings("cb_linebreaks", settings); + + if (settings->ignore_linebreaks) + resource.search_window_defaults |= SETTINGS_IGNORE_LINEBREAKS_FLAG; + else + resource.search_window_defaults &= ~SETTINGS_IGNORE_LINEBREAKS_FLAG; + store_preference(NULL, "searchWindowDefaults", "%u", resource.search_window_defaults); +} + +static void +cb_search_cancel(Widget w, XtPointer client_data, XtPointer call_data) +{ + Widget find_popup; + struct search_settings *settings = (struct search_settings *)client_data; + + UNUSED(w); + UNUSED(call_data); + + /* retrieve the find popup window */ + if ((find_popup = XtNameToWidget(globals.widgets.top_level, "*find_popup")) == 0) { + XDVI_WARNING((stderr, "Couldn't find \"find_popup\" widget!")); + return; + } + + /* This flag is checked in the scanning routines, and + will be eventually reset by do_pages() in events.c. + (The scanning routines musn't reset it, since they + might not be called again after this point!) */ + globals.ev.flags |= EV_FIND_CANCEL; + + search_signal_page_changed(); /* Hack to make search restart anew */ + search_erase_highlighting(True); + if (settings->message_window != 0) { + TRACE_GUI((stderr, "kill_message_window: %p\n", (void *)settings->message_window)); + kill_message_window(settings->message_window); + } + XtPopdown(find_popup); + m_find_popup_active = False; +} + +static void +cb_search_get_term_button(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct search_settings *settings = (struct search_settings *)client_data; + char *searchterm = NULL; + + Widget searchbox_input, paned, box; + UNUSED(call_data); + + box = XtParent(w); + ASSERT(box != 0, "Parent of button widget mustn't be NULL!"); + paned = XtParent(box); + ASSERT(paned != 0, "Parent of box widget mustn't be NULL!"); + + if ((searchbox_input = XtNameToWidget(paned, "*searchbox_input")) == 0) { + XDVI_WARNING((stderr, "Couldn't find \"searchbox_input\" widget!")); + return; + } + +#ifdef MOTIF + XtVaGetValues(searchbox_input, XmNvalue, &searchterm, NULL); +#else + XtVaGetValues(searchbox_input, XtNstring, &searchterm, NULL); +#endif + if (searchterm == NULL) { + XDVI_WARNING((stderr, "Searchterm in cb_search_get_term callback shouldn't be NULL!")); + return; + } + TRACE_FIND((stderr, "searchterm1: |%s|", searchterm)); + settings->term = searchterm; + + cb_search_go(w, settings, NULL); +} + +static void +search_cancel(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + struct search_settings *settings = NULL; + void *ptr; + UNUSED(event); + + ASSERT(*num_params > 0, "params in search_cancel must be > 0!"); + ASSERT(*params != NULL, "params in search_cancel mustn't be NULL!"); + + /* + * the *params char pointer contains the pointer value (address) of `settings'; + * convert it back to a pointer: + */ + TRACE_GUI((stderr, "Pointer string value: |%s|", *params)); + sscanf(*params, "%p", &ptr); + settings = (struct search_settings *)ptr; + TRACE_GUI((stderr, "Pointer address: |%p|", (void *)settings)); + ASSERT(settings != NULL, "Shouldn't happen: Couldn't get string representation of argument pointer."); + + cb_search_cancel(w, settings, NULL); +} + +#ifdef MOTIF + +static void +cb_search_get_term(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct search_settings *settings = (struct search_settings *)client_data; + char *searchterm = NULL; + Widget form, paned, searchbox_form; + Widget button; + XEvent ev; + UNUSED(call_data); + + /* retrieve the `Find' button widget */ + searchbox_form = XtParent(w); + ASSERT(searchbox_form != 0, "Parent of button widget mustn't be NULL!"); + form = XtParent(searchbox_form); + ASSERT(form != 0, "Parent of searchbox_form widget mustn't be NULL!"); + paned = XtParent(form); + ASSERT(paned != 0, "Parent of form widget mustn't be NULL!"); + if ((button = XtNameToWidget(paned, "*find_button")) == 0) { + XDVI_WARNING((stderr, "Couldn't find \"find_button\" widget!")); + return; + } + + if (settings != NULL) { + /* retrieve search term from text input field */ + XtVaGetValues(w, XmNvalue, &searchterm, NULL); + if (searchterm == NULL) { + XDVI_WARNING((stderr, "Searchterm in cb_search_get_term callback shouldn't be NULL!")); + return; + } + + TRACE_FIND((stderr, "searchterm2: |%s|", searchterm)); + settings->term = (const char *)searchterm; + } + + /* make the `Find' button think it got pushed. + Also synthetisize an event, just to be sure ... */ + synthetisize_event(&ev, button); + + XtCallActionProc(button, "ArmAndActivate", &ev, NULL, 0); + /* the following don't make the button appear visually pressed: */ + /* XtCallCallbacks(button, XmNarmCallback, NULL); */ + /* XtCallCallbacks(button, XmNactivateCallback, NULL); */ +} + +static void +xm_search_go(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + struct search_settings *settings = NULL; + void *ptr; + Widget input; + + UNUSED(w); + UNUSED(event); + + if (params != NULL) { /* re-initialize search term */ + ASSERT(*num_params == 1, "num_params in xm_search_go should be 1 if *params != NULL"); + /* + * the *params char pointer contains the pointer value (address) of `settings'; + * convert it back to a pointer: + */ + TRACE_GUI((stderr, "Pointer string value: |%s|", *params)); + sscanf(*params, "%p", &ptr); + settings = (struct search_settings *)ptr; + TRACE_GUI((stderr, "Pointer address: |%p|", (void *)settings)); + ASSERT(settings != NULL, "Shouldn't happen: Couldn't get string representation of argument pointer."); + } + + if ((input = XtNameToWidget(globals.widgets.top_level, "*searchbox_input")) == 0) { + XDVI_WARNING((stderr, "Couldn't find \"searchbox_input\" widget!")); + return; + } + cb_search_get_term(input, settings, NULL); +} + +#else /* MOTIF */ + +static void +xaw_unset_button(XtPointer client_data, XtIntervalId *id) +{ + XEvent ev; + Widget button = (Widget)client_data; + + UNUSED(id); + + synthetisize_event(&ev, button); + XtCallActionProc(button, "unset", &ev, NULL, 0); +} + +static void +xaw_search_go(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + struct search_settings *settings = NULL; + void *ptr; + char *searchterm = NULL; + Widget button, input; + XtIntervalId timeout; + + UNUSED(w); + + /* retrieve the `Find' button widget */ + if ((button = XtNameToWidget(globals.widgets.top_level, "*find_button")) == 0) { + XDVI_WARNING((stderr, "Couldn't find \"find_button\" widget!")); + return; + } + if ((input = XtNameToWidget(globals.widgets.top_level, "*searchbox_input")) == 0) { + XDVI_WARNING((stderr, "Couldn't find \"searchbox_input\" widget!")); + return; + } + + XtVaGetValues(input, XtNstring, &searchterm, NULL); + if (searchterm == NULL) { + XDVI_WARNING((stderr, "Searchterm in xaw_search_go callback shouldn't be NULL!")); + return; + } + + if (params != NULL) { /* re-initialize search term */ + ASSERT(*num_params == 1, "num_params in xaw_search_go should be 1 if *params != NULL"); + /* + * the *params char pointer contains the pointer value (address) of `settings'; + * convert it back to a pointer: + */ + TRACE_GUI((stderr, "Pointer string value: |%s|", *params)); + sscanf(*params, "%p", &ptr); + settings = (struct search_settings *)ptr; + TRACE_GUI((stderr, "Pointer address: |%p|", (void *)settings)); + ASSERT(settings != NULL, "Shouldn't happen: Couldn't get string representation of argument pointer."); + + settings->term = (const char *)searchterm; + } + + /* + * Again, we want the button to appear activated when <RETURN> is pressed. + * For this, we explicitly invoke set()notify(), and unset() after a timeout + * of 150 milliseconds, which seems to correspond to the Motif default. + */ + XtCallActionProc(button, "set", event, NULL, 0); + XtCallActionProc(button, "notify", event, NULL, 0); + XSync(DISP, False); + timeout = XtAppAddTimeOut(app, 150, xaw_unset_button, (XtPointer)button); +} + + +/* + * Restart search from within another popup (confirmation) window. + */ +static void +xaw_search_restart(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + void *ptr; + struct search_settings *settings; + + UNUSED(event); + UNUSED(w); + + ASSERT(*num_params > 0, "params in xaw_search_restart must be > 0!"); + ASSERT(*params != NULL, "params in xaw_search_restart mustn't be NULL!"); + + TRACE_GUI((stderr, "Pointer string value: |%s|", *params)); + sscanf(*params, "%p", &ptr); + settings = (struct search_settings *)ptr; + + search_restart((XtPointer)settings); +} + +static XtActionsRec xaw_search_actions[] = { + { "do-search-restart", xaw_search_restart }, + { "do-search", xaw_search_go }, +}; +#endif /* MOTIF */ + +static XtActionsRec search_actions[] = { + { "do-search", +#ifdef MOTIF + xm_search_go +#else + xaw_search_go +#endif + }, + { "cancel-search", search_cancel }, +}; + +static Widget +create_search_window(struct search_settings *settings) +{ + Widget top_level_shell; + Atom WM_DELETE_WINDOW; + Widget form, find_paned, box; + unsigned char curr_state; + + Widget searchbox_form, searchbox_label, searchbox_input; + /* left and right column */ + Widget options_left_form, options_right_form; + /* checkboxes in left column */ + Widget regexp_checkbox, matchcase_checkbox, backwards_checkbox; + /* checkboxes in right column */ + Widget linebreaks_checkbox; + Widget find_button, cancel_button; + + XtTranslations xlats; + char *translation_str = NULL; +#ifdef MOTIF + XmString str; +#else /* MOTIF */ + XtTranslations wm_translations; + int ddist; +#endif + + XtAddActions(search_actions, XtNumber(search_actions)); + +#ifndef MOTIF + + XtAddActions(xaw_search_actions, XtNumber(xaw_search_actions)); +#define HORIZONTAL_RESIZING_NO XtNleft, XtChainLeft, XtNright, XtChainLeft +#define HORIZONTAL_RESIZING_YES XtNleft, XtChainLeft, XtNright, XtChainRight +#define VERTICAL_RESIZING_NO XtNtop, XtChainTop, XtNbottom, XtChainTop + + /* + * hacky: Since translations can only contain strings, pass + * the pointer address as string in the callback. But the only + * portability problem should be the size of the pointer + * representation, and we deal with that by using VSNPRINTF(). + */ + translation_str = get_string_va("<Message>WM_PROTOCOLS: cancel-search(%p)\nCtrl<Key>g:find-next()", + (void *)settings); + wm_translations = XtParseTranslationTable(translation_str); + free(translation_str); + translation_str = NULL; +#endif /* not MOTIF */ + + top_level_shell = XtVaCreatePopupShell("find_popup", + SHELL_WIDGET, + globals.widgets.top_level, + XtNtitle, "xdvik: Find in DVI file", + XtNmappedWhenManaged, False, /* so that we can center it first */ + XtNtransientFor, globals.widgets.top_level, + XtNallowShellResize, True, +#ifdef MOTIF + XmNdeleteResponse, XmDO_NOTHING, /* we'll take care of that ourselves */ +#else + XtNtranslations, wm_translations, +#endif + NULL); + + TRACE_GUI((stderr, "toplevel: %ld", (unsigned long)top_level_shell)); + + WM_DELETE_WINDOW = XInternAtom(XtDisplay(top_level_shell), "WM_DELETE_WINDOW", False); +#ifdef MOTIF + XmAddWMProtocolCallback(top_level_shell, WM_DELETE_WINDOW, cb_search_cancel, settings); +#endif + + find_paned = XtVaCreateWidget("find_paned", + PANED_WIDGET, + top_level_shell, +#ifdef MOTIF + /* make sashes invisible */ + XmNsashWidth, 1, + XmNsashHeight, 1, +#endif + NULL); + + form = XtVaCreateWidget("form", + FORM_WIDGET, + find_paned, +#ifdef MOTIF + XmNhorizontalSpacing, DDIST_MAJOR, + XmNverticalSpacing, DDIST_MAJOR, + XmNautoUnmanage, False, +#else + XtNallowResize, True, +#endif + NULL); + +#ifdef MOTIF + str = XmStringCreateLocalized("Find:"); +#else + XtVaGetValues(form, XtNdefaultDistance, &ddist, NULL); +#endif + + /* search term input field */ + searchbox_form = XtVaCreateWidget("searchbox_form", + FORM_WIDGET, + form, +#ifdef MOTIF + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNhorizontalSpacing, DDIST, + XmNverticalSpacing, DDIST, +#else + XtNborderWidth, 0, + HORIZONTAL_RESIZING_YES, +#endif + NULL); + searchbox_label = XtVaCreateManagedWidget("searchbox_label", + LABEL_WIDGET, + searchbox_form, +#ifdef MOTIF + XmNlabelString, str, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNalignment, XmALIGNMENT_BEGINNING, +#else + XtNlabel, "Find:", + XtNborderWidth, 0, + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, +#endif + NULL); + + searchbox_input = XtVaCreateManagedWidget("searchbox_input", + TEXT_WIDGET, + searchbox_form, +#ifdef MOTIF + XmNtopAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, searchbox_label, + XmNbottomAttachment, XmATTACH_FORM, +#else + XtNwidth, 260, + XtNdataCompression, False, + XtNeditType, XawtextEdit, + XtNfromHoriz, searchbox_label, + HORIZONTAL_RESIZING_YES, + VERTICAL_RESIZING_NO, +#endif + NULL); + + + if (settings->term != NULL) { + XtVaSetValues(searchbox_input, +#ifdef MOTIF + XmNvalue, +#else + XtNstring, +#endif + settings->term, NULL); + } + + XtManageChild(searchbox_form); + +#ifdef MOTIF + XtAddCallback(searchbox_input, XmNactivateCallback, cb_search_get_term, settings); +#endif + + /* + * form for left row of options checkbuttons + */ + options_left_form = XtVaCreateManagedWidget("options_left_form", + FORM_WIDGET, + form, +#ifdef MOTIF + XmNhorizontalSpacing, DDIST, + XmNverticalSpacing, DDIST, + XmNresizable, True, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, searchbox_form, + XmNleftAttachment, XmATTACH_FORM, +/* XmNrightAttachment, XmATTACH_FORM, */ +/* XmNbottomAttachment, XmATTACH_FORM, */ +#else + XtNborderWidth, 0, + XtNfromVert, searchbox_form, + HORIZONTAL_RESIZING_NO, +#endif + NULL); + /* + * form for right row of options checkbuttons + */ + options_right_form = XtVaCreateManagedWidget("options_right_form", + FORM_WIDGET, + form, +#ifdef MOTIF + XmNhorizontalSpacing, DDIST, + XmNverticalSpacing, DDIST, + XmNresizable, True, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, searchbox_form, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, options_left_form, + XmNrightAttachment, XmATTACH_FORM, +/* XmNbottomAttachment, XmATTACH_FORM, */ +#else + XtNborderWidth, 0, + XtNfromVert, searchbox_form, + XtNfromHoriz, options_left_form, + HORIZONTAL_RESIZING_NO, +#endif + NULL); +#ifdef MOTIF + XmStringFree(str); + str = XmStringCreateLocalized("Regular expression"); +#endif + + regexp_checkbox = XtVaCreateManagedWidget("regexp_checkbox", + CHECKBOX_WIDGET, + options_left_form, +#ifdef MOTIF + XmNlabelString, str, + XmNindicatorType, XmN_OF_MANY, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, +#else + XtNlabel, "Regular expression", + XtNborderWidth, 0, + XtNisRadio, False, + XtNhighlightThickness, 1, + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, +#endif + NULL); + + XtAddCallback(regexp_checkbox, VALUE_CALLBACK_NAME, cb_regexp_search, settings); + +#ifdef MOTIF + XmStringFree(str); + str = XmStringCreateLocalized("Find backwards"); +#endif + + backwards_checkbox = XtVaCreateManagedWidget("backwards_checkbox", + CHECKBOX_WIDGET, + options_left_form, +#ifdef MOTIF + XmNlabelString, str, + XmNindicatorType, XmN_OF_MANY, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, regexp_checkbox, + XmNleftAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, +#else + XtNlabel, "Find backwards", + XtNfromVert, regexp_checkbox, + XtNborderWidth, 0, + XtNisRadio, False, + XtNhighlightThickness, 1, + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, +#endif + NULL); + + XtAddCallback(backwards_checkbox, VALUE_CALLBACK_NAME, cb_backwards_search, settings); + +#ifdef MOTIF + XmStringFree(str); + str = XmStringCreateLocalized("Case sensitive"); +#endif + + matchcase_checkbox = XtVaCreateManagedWidget("matchcase_checkbox", + CHECKBOX_WIDGET, + options_right_form, +#ifdef MOTIF + XmNlabelString, str, + XmNindicatorType, XmN_OF_MANY, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, +#else + XtNlabel, "Case sensitive", + XtNborderWidth, 0, + XtNisRadio, False, + XtNhighlightThickness, 1, + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, +#endif + NULL); + + XtAddCallback(matchcase_checkbox, VALUE_CALLBACK_NAME, cb_match_case, settings); + +#ifdef MOTIF + XmStringFree(str); + str = XmStringCreateLocalized("Ignore newlines/hyphens"); +#endif + + linebreaks_checkbox = XtVaCreateManagedWidget("linebreaks_checkbox", + CHECKBOX_WIDGET, + options_right_form, +#ifdef MOTIF + XmNlabelString, str, + XmNindicatorType, XmN_OF_MANY, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, matchcase_checkbox, + XmNleftAttachment, XmATTACH_FORM, +#else + XtNlabel, "Ignore newlines/hyphens", + XtNfromVert, matchcase_checkbox, + XtNborderWidth, 0, + XtNisRadio, False, + XtNhighlightThickness, 1, + HORIZONTAL_RESIZING_NO, + VERTICAL_RESIZING_NO, +#endif + NULL); + + XtAddCallback(linebreaks_checkbox, VALUE_CALLBACK_NAME, cb_linebreaks, settings); + + /* + * box for Find/Cancel buttons + */ + box = XtVaCreateManagedWidget("box", + FORM_WIDGET, + find_paned, +#ifdef MOTIF + XmNskipAdjust, True, /* don't resize this area */ +#else + /* resizing by user isn't needed */ + XtNshowGrip, False, + XtNdefaultDistance, 6, /* some padding */ + /* resizing the window shouldn't influence this box, + * but only the pane widget + */ + XtNskipAdjust, True, + XtNaccelerators, G_accels_cr, +#endif + NULL); + +#ifdef MOTIF + XmStringFree(str); + str = XmStringCreateLocalized("Find"); +#endif + + find_button = XtVaCreateManagedWidget("find_button", + PUSHBUTTON_WIDGET, + box, +#ifdef MOTIF + XmNlabelString, str, + XmNshowAsDefault, True, + XmNdefaultButtonShadowThickness, 1, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + /* to mimick appearance of native dialog buttons: */ + XmNmarginWidth, 6, + XmNmarginHeight, 4, + XmNtopOffset, 10, + XmNbottomOffset, 10, + XmNleftOffset, 10, +#else + XtNlabel, "Find", + XtNaccelerators, G_accels_cr, + XtNtop, XtChainTop, + XtNbottom, XtChainBottom, + HORIZONTAL_RESIZING_NO, +#endif + NULL); +#ifdef MOTIF + XmStringFree(str); + str = XmStringCreateLocalized("Cancel"); +#endif + + cancel_button = XtVaCreateManagedWidget("cancel_button", + PUSHBUTTON_WIDGET, + box, +#ifdef MOTIF + XmNlabelString, str, + XmNdefaultButtonShadowThickness, 1, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + /* to mimick appearance of native dialog buttons: */ + XmNmarginWidth, 6, + XmNmarginHeight, 4, + XmNtopOffset, 10, + XmNbottomOffset, 10, + XmNrightOffset, 10, +#else + XtNlabel, "Cancel", + XtNfromHoriz, find_button, + XtNbottom, XtChainBottom, + XtNjustify, XtJustifyRight, + XtNleft, XtChainRight, + XtNright, XtChainRight, +#endif + NULL); + +#ifdef MOTIF + XmStringFree(str); +#endif + + XtAddCallback(cancel_button, ACTIVATE_CALLBACK_NAME, cb_search_cancel, settings); + XtAddCallback(find_button, ACTIVATE_CALLBACK_NAME, cb_search_get_term_button, settings); + + translation_str = get_string_va( +#ifdef MOTIF + "<Key>osfCancel:cancel-search(%p)\n" +#else + "<Key>Escape:cancel-search(%p)\n" +#endif + "<Key>Return:do-search(%p)\n" + "Ctrl<Key>g:find-next()", + (void *)settings, (void *)settings); + + xlats = XtParseTranslationTable(translation_str); + free(translation_str); + + XtOverrideTranslations(searchbox_form, xlats); + XtOverrideTranslations(options_left_form, xlats); + XtOverrideTranslations(options_right_form, xlats); + XtOverrideTranslations(regexp_checkbox, xlats); + XtOverrideTranslations(matchcase_checkbox, xlats); + XtOverrideTranslations(backwards_checkbox, xlats); + XtOverrideTranslations(box, xlats); + XtOverrideTranslations(find_button, xlats); + XtOverrideTranslations(cancel_button, xlats); + XtOverrideTranslations(searchbox_input, xlats); + + /* following doesn't help to force input to textbox in xaw: */ + /* XtInstallAllAccelerators(find_paned, find_paned); */ + /* XtInstallAllAccelerators(searchbox_input, find_paned); */ + + XtManageChild(form); + XtManageChild(find_paned); + + { /* set all buttons to same size */ + Dimension w1, w2, max; + XtVaGetValues(find_button, XtNwidth, &w1, NULL); + XtVaGetValues(cancel_button, XtNwidth, &w2, NULL); + max = MAX(w1, w2); + XtVaSetValues(find_button, XtNwidth, max, NULL); + XtVaSetValues(cancel_button, XtNwidth, max, NULL); + } + + XtManageChild(top_level_shell); + /* don't center this one - would just get in the way in this case. */ + /* center_window(top_level_shell, globals.widgets.top_level); */ + XtPopup(top_level_shell, XtGrabNone); + m_find_popup_active = True; + +#ifdef MOTIF + XmProcessTraversal(searchbox_input, XmTRAVERSE_CURRENT); +/* XmProcessTraversal(find_button, XmTRAVERSE_CURRENT); */ +#endif + XSetWMProtocols(XtDisplay(top_level_shell), XtWindow(top_level_shell), &WM_DELETE_WINDOW, 1); + + /* check if we have default values from resource.search_window_defaults */ + if (resource.search_window_defaults & SETTINGS_USE_REGEXP_FLAG) + XtVaSetValues(regexp_checkbox, CHECKBUTTON_IS_SET, True, NULL); + if (resource.search_window_defaults & SETTINGS_CASE_SENSITIVE_FLAG) + XtVaSetValues(matchcase_checkbox, CHECKBUTTON_IS_SET, True, NULL); + if (resource.search_window_defaults & SETTINGS_BACKWARDS_FLAG) + XtVaSetValues(backwards_checkbox, CHECKBUTTON_IS_SET, True, NULL); + if (resource.search_window_defaults & SETTINGS_IGNORE_LINEBREAKS_FLAG) + XtVaSetValues(linebreaks_checkbox, CHECKBUTTON_IS_SET, True, NULL); + + /* initialize `settings' values according to the checkbox states + (in case user has assigned values via X defaults): */ + XtVaGetValues(regexp_checkbox, CHECKBUTTON_IS_SET, &curr_state, NULL); + settings->use_regexp = curr_state; + XtVaGetValues(matchcase_checkbox, CHECKBUTTON_IS_SET, &curr_state, NULL); + settings->case_sensitive = curr_state; + XtVaGetValues(backwards_checkbox, CHECKBUTTON_IS_SET, &curr_state, NULL); + settings->direction = curr_state ? SEARCH_UP : SEARCH_DOWN; + XtVaGetValues(linebreaks_checkbox, CHECKBUTTON_IS_SET, &curr_state, NULL); + settings->ignore_hyphens = settings->ignore_linebreaks = curr_state; + +#ifndef MOTIF + +#undef HORIZONTAL_RESIZING_NO +#undef HORIZONTAL_RESIZING_YES +#undef VERTICAL_RESIZING_NO + +#endif /* MOTIF */ + return top_level_shell; +} + +void +dvi_find_string(const char *str, Boolean find_next) +{ + /* Synthesize a RET keystroke for the find dialog. + * Also pops up the find dialog, to make it easier for user to + * edit options, change direction etc. */ + Widget find_popup; + if ((find_popup = XtNameToWidget(globals.widgets.top_level, "*find_popup")) == 0) { + static struct search_settings settings; + static struct search_info searchinfo = { False, False, False, 0, 0, 0, 0 }; + settings.term = str; + settings.use_regexp = False; + settings.case_sensitive = False; + settings.direction = SEARCH_DOWN; + settings.ignore_hyphens = False; + settings.ignore_linebreaks = False; + settings.x_pos = -1; + settings.y_pos = -1; + settings.searchinfo = &searchinfo; + settings.hyphen_delta = 0; + + find_popup = create_search_window(&settings); + + if (find_next) + return; + } + else if (str != NULL) { /* change the search term */ + Widget searchbox_input; + if ((searchbox_input = XtNameToWidget(find_popup, "*searchbox_input")) == 0) { + XDVI_WARNING((stderr, "Couldn't find \"searchbox_input\" widget!")); + return; + } + XtVaSetValues(searchbox_input, +#ifdef MOTIF + XmNvalue, +#else + XtNstring, +#endif + str, NULL); + } + if (m_find_popup_active) { + XRaiseWindow(DISP, XtWindow(find_popup)); + } + else { + XtPopup(find_popup, XtGrabNone); + m_find_popup_active = True; + } + + if (str != NULL || find_next) { +#ifdef MOTIF + xm_search_go(NULL, NULL, NULL, NULL); +#else + xaw_search_go(NULL, NULL, NULL, NULL); +#endif + } +} + diff --git a/Build/source/texk/xdvik/gui/search-dialog.h b/Build/source/texk/xdvik/gui/search-dialog.h new file mode 100644 index 00000000000..c272658d021 --- /dev/null +++ b/Build/source/texk/xdvik/gui/search-dialog.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2003-2004 Stefan Ulrich + * + * 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. + */ + +#ifndef SEARCH_DIALOG_H_ +#define SEARCH_DIALOG_H_ + +#include "xdvi-config.h" +#include "xdvi.h" + +typedef enum { SEARCH_UP = 0, SEARCH_DOWN, SEARCH_UNINITIALIZED } searchDirectionT; + +/* bounding box info for words. */ +struct bbox { + int ulx, uly, lrx, lry; +}; + +struct search_info { + Boolean have_match; + Boolean locked; /* to block multiple searches */ + Boolean search_wrapped; /* if restarting from start (or end) */ + int from_pos; + int to_pos; + int scan_page; + int page_offset; /* start of current scan */ +}; + + +struct search_settings { + const char *term; /* original search term */ + char *utf8_term; /* term in UTF-8 encoding */ + char *posix_term; /* utf8_term with Perl abbreviations mapped to POSIX */ + Boolean use_regexp; + Boolean case_sensitive; + Boolean ignore_hyphens; + Boolean ignore_linebreaks; + searchDirectionT direction; + struct search_info *searchinfo; + /* internal state management */ + int x_pos, y_pos; /* position of the search popup window */ + int from_page; /* page to start search from */ + int curr_page; /* current page of search search from */ + int to_page; /* highest page already scanned */ + int hyphen_delta; /* difference in offsets when hyphenation is removed */ + Widget message_window; /* warning popup, or NULL if it doesn't exist */ +}; + +extern void dvi_find_string(const char *str, Boolean find_next); + +#endif /* SEARCH_DIALOG_H_ */ diff --git a/Build/source/texk/xdvik/gui/selection.c b/Build/source/texk/xdvik/gui/selection.c new file mode 100644 index 00000000000..e0bef1e5f1e --- /dev/null +++ b/Build/source/texk/xdvik/gui/selection.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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 <string.h> + +#include <X11/Xatom.h> +#include <X11/StringDefs.h> + +#if HAVE_X11_XMU_XMU_H +#include <X11/Xmu/Atoms.h> +#include <X11/Xmu/Xmu.h> +#endif + +#include "events.h" +#include "util.h" +#include "encodings.h" +#include "message-window.h" +#include "selection.h" +#include "statusline.h" + +/* + * Set the current X selection (i.e. the primary selection AKA XA_PRIMARY). + * Most of this is copied from Asente/Converse/Swick: X Window System Toolkit Manual + */ + +/* This is file scope since deliver_selection_cb() needs access to it. + * I guess we could also store it in an Atom, but why bother ... + */ +static char *m_selection_text = NULL; +static char *m_cvt_selection_text = NULL; +static size_t m_selection_size = 0; + +/* helper routine */ +static Atom +fetch_atom(Widget w, const char *name) +{ + Atom a; + XrmValue source, dest; + + source.size = strlen(name) + 1; + source.addr = (char *)name; + dest.size = sizeof a; + dest.addr = (caddr_t)&a; + + (void)XtConvertAndStore(w, XtRString, &source, XtRAtom, &dest); + return a; +} + +/* Lose the selection */ +static void +lose_selection_cb(Widget w, Atom *selection) +{ + UNUSED(w); + UNUSED(selection); + + text_change_region(TEXT_SEL_CLEAR, NULL); +} + +static char * +utf8_to_native_encoding(const char *utf8) +{ + static const char *text_encoding = NULL; + + /* convert to user encoding, similar to search-internal.c */ + if (text_encoding == NULL) { + text_encoding = get_text_encoding(); + } + + if (memicmp(text_encoding, "iso-8859-1", strlen("iso-8859-1")) == 0 + || memicmp(text_encoding, "iso8859-1", strlen("iso8859-1")) == 0) { + return str_utf8_to_iso_8859_1(utf8); + } + else if (memicmp(text_encoding, "utf-8", strlen("utf-8")) != 0 + && memicmp(text_encoding, "utf8", strlen("utf8")) != 0) { + /* some other encoding */ + return iconv_convert_string("utf-8", text_encoding, utf8); + } + /* fallback */ + return xstrdup(utf8); +} + +/* Deliver the selection. Only supports XA_STRING. */ +static Boolean +deliver_selection_cb(Widget w, + Atom *selection, + Atom *target, + Atom *type, + XtPointer *value, + unsigned long *length, + int *format) +{ + Atom targets = fetch_atom(w, "TARGETS"); + Atom utf8_string = fetch_atom(w, "UTF8_STRING"); + + TRACE_GUI((stderr, "selection target = %lu (%s)", + *target, XGetAtomName(DISP, *target))); + + if (m_selection_text == NULL) /* paranoia */ + return False; + +#if HAVE_X11_XMU_XMU_H + if (*target == targets) { + /* TARGETS handling copied from xclipboard.c */ + Atom* targetP; + Atom* std_targets; + unsigned long std_length; + XSelectionRequestEvent* req = XtGetSelectionRequest(w, *selection, (XtRequestId)NULL); + + TRACE_GUI((stderr, "Selection type: targets")); + XmuConvertStandardSelection(w, req->time, selection, + target, type, (XPointer*)&std_targets, &std_length, format); + *value = XtMalloc(sizeof(Atom)*(std_length + 2)); + targetP = *(Atom**)value; + *length = std_length + 2; + *targetP++ = XA_COMPOUND_TEXT(DISP); + *targetP++ = XA_STRING; + memcpy((char*)targetP, (char*)std_targets, sizeof(Atom)*std_length); + XtFree((char*)std_targets); + *type = XA_ATOM; + *format = sizeof(Atom) * 8; + return True; + } + else +#endif + if (*target == utf8_string) { + TRACE_GUI((stderr, "Selection type: UTF8_STRING")); + *type = *target; + *value = (XtPointer)XtNewString(m_selection_text); + /* *value = (XtPointer)m_selection_text; */ + *length = strlen(m_selection_text); + *format = 8; + return True; + } + else if (*target == XA_STRING) { + char *ptr = utf8_to_native_encoding(m_selection_text); + TRACE_GUI((stderr, "Selection type: XA_STRING")); + strncpy(m_cvt_selection_text, ptr, m_selection_size); + m_cvt_selection_text[m_selection_size - 1] = '\0'; /* ensure termination */ + free(ptr); + *type = *target; + *value = (XtPointer)XtNewString(m_cvt_selection_text); + /* *value = (XtPointer)m_selection_text; */ + *length = strlen(m_cvt_selection_text); + *format = 8; + return True; + } +#if HAVE_X11_XMU_XMU_H + else if (*target == XA_COMPOUND_TEXT(DISP) || *target == XA_TEXT(DISP)) { + const char *cl[1]; + char *ptr; + int retval; + + XTextProperty ct; + XICCEncodingStyle style = XStdICCTextStyle; + + TRACE_GUI((stderr, "Selection type: XA_COMPOUND_TEXT")); + ptr = utf8_to_native_encoding(m_selection_text); + strncpy(m_cvt_selection_text, ptr, m_selection_size); + m_cvt_selection_text[m_selection_size - 1] = '\0'; /* ensure termination */ + cl[0] = m_cvt_selection_text; + + *type = *target; + retval = XmbTextListToTextProperty(DISP, (char **)cl, 1, style, &ct); + + if (retval == XNoMemory || retval == XLocaleNotSupported || retval == XConverterNotFound) { + statusline_print(STATUS_MEDIUM, "XmbTextListToTextProperty failed: %d", retval); + return False; + } + + *value = ct.value; + *length = ct.nitems; + *format = 8; + return True; + } +#endif + else { +#if HAVE_X11_XMU_XMU_H + TRACE_GUI((stderr, "Selection type: standard selection")); + if (XmuConvertStandardSelection(w, CurrentTime, selection, + target, type, (XPointer *)value, length, format)) + return True; + else { +#endif + TRACE_GUI((stderr, "Selection type unsupported: %lu (%s)", + (unsigned long)*target, XGetAtomName(DISP, *target))); + statusline_print(STATUS_MEDIUM, + "X client asked for an unsupported selection target type: %lu (%s)", + (unsigned long)*target, XGetAtomName(DISP, *target)); + return False ; +#if HAVE_X11_XMU_XMU_H + } +#endif + } +} + +/* do it */ +Boolean +set_selection(const char *text, Widget w) +{ + /* caller should make sure that text is never longer than 4 * XMaxRequestSize(DISP) - 32 */ + if (m_selection_text == NULL) { + m_selection_size = 4 * XMaxRequestSize(DISP); + m_selection_text = xmalloc(m_selection_size); + m_cvt_selection_text = xmalloc(m_selection_size); + } + strncpy(m_selection_text, text, m_selection_size); + m_selection_text[m_selection_size - 1] = '\0'; /* ensure termination */ + + return XtOwnSelection(w, XA_PRIMARY, XtLastTimestampProcessed(XtDisplay(w)), + deliver_selection_cb, + lose_selection_cb, + (XtSelectionDoneProc)NULL); +} + +void +unset_selection(Widget w) +{ + XtDisownSelection(w, XA_PRIMARY, XtLastTimestampProcessed(XtDisplay(w))); +} + diff --git a/Build/source/texk/xdvik/gui/selection.h b/Build/source/texk/xdvik/gui/selection.h new file mode 100644 index 00000000000..1dcb8416cbb --- /dev/null +++ b/Build/source/texk/xdvik/gui/selection.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + */ + +#ifndef SELECTION_H_ +#define SELECTION_H_ + +#include "xdvi-config.h" +#include "xdvi.h" + +Boolean set_selection(const char *text, Widget w); +void unset_selection(Widget w); + +#endif /* SELECTION_H_ */ diff --git a/Build/source/texk/xdvik/gui/sfDir.c b/Build/source/texk/xdvik/gui/sfDir.c new file mode 100644 index 00000000000..de39ccf3678 --- /dev/null +++ b/Build/source/texk/xdvik/gui/sfDir.c @@ -0,0 +1,165 @@ +/* + * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Software Research Associates not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Software Research Associates + * makes no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Author: Erik M. van der Poel + * Software Research Associates, Inc., Tokyo, Japan + * erik@sra.co.jp + */ + +#include "xdvi-config.h" +#include "sfDir.h" + +#if !defined(MOTIF) /* entire file */ + +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Xos.h> +#include <X11/Xaw/Text.h> +#include <X11/Xaw/AsciiText.h> +#undef wchar_t + +#include <stdio.h> + +#ifdef SEL_FILE_IGNORE_CASE +#include <ctype.h> +#endif /* def SEL_FILE_IGNORE_CASE */ + + +#ifdef SEL_FILE_IGNORE_CASE +int +SFcompareEntries(const void *vp, const void *vq) +{ + SFEntry *p = vp; + SFEntry *q = vq; + char *r, *s; + char c1, c2; + + r = p->real; + s = q->real; + + c1 = *r++; + if (islower(c1)) { + c1 = toupper(c1); + } + c2 = *s++; + if (islower(c2)) { + c2 = toupper(c2); + } + + while (c1 == c2) { + if (!c1) { + return strcmp(p->real, q->real); + } + c1 = *r++; + if (islower(c1)) { + c1 = toupper(c1); + } + c2 = *s++; + if (islower(c2)) { + c2 = toupper(c2); + } + } + + return c1 - c2; +} +#else /* def SEL_FILE_IGNORE_CASE */ +int +SFcompareEntries(const void *vp, const void *vq) +{ + const SFEntry *p = vp; + const SFEntry *q = vq; + return strcmp(p->real, q->real); +} +#endif /* def SEL_FILE_IGNORE_CASE */ + +int +SFgetDir(SFDir *dir) +{ + SFEntry *result = NULL; + int alloc = 0; + int i; + DIR *dirp; + struct dirent *dp; + char *str; + int len; + int maxChars; + struct stat statBuf; + + maxChars = strlen(dir->dir) - 1; + + dir->entries = NULL; + dir->nEntries = 0; + dir->nChars = 0; + + result = NULL; + i = 0; + + dirp = opendir("."); + if (!dirp) { + return 1; + } + + (void)stat(".", &statBuf); + dir->mtime = statBuf.st_mtime; + + (void)readdir(dirp); /* throw away "." */ + +#ifndef S_IFLNK + (void)readdir(dirp); /* throw away ".." */ +#endif /* ndef S_IFLNK */ + + while ((dp = readdir(dirp))) { + if (i >= alloc) { + alloc = 2 * (alloc + 1); + result = (SFEntry *) XtRealloc((char *)result, + (unsigned)(alloc * sizeof(SFEntry))); + } + result[i].statDone = 0; + str = dp->d_name; + len = strlen(str); + result[i].real = XtMalloc((unsigned)(len + 2)); + (void)strcat(strcpy(result[i].real, str), " "); + if (len > maxChars) { + maxChars = len; + } + result[i].shown = result[i].real; + i++; + } + +#if defined(SVR4) || defined(SYSV) || defined(USG) + qsort((char *)result, (unsigned)i, sizeof(SFEntry), SFcompareEntries); +#else /* defined(SVR4) || defined(SYSV) || defined(USG) */ + qsort((char *)result, i, sizeof(SFEntry), SFcompareEntries); +#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */ + + dir->entries = result; + dir->nEntries = i; + dir->nChars = maxChars + 1; + + closedir(dirp); + + return 0; +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* !defined(MOTIF) */ diff --git a/Build/source/texk/xdvik/gui/sfDir.h b/Build/source/texk/xdvik/gui/sfDir.h new file mode 100644 index 00000000000..0207ec4f1a0 --- /dev/null +++ b/Build/source/texk/xdvik/gui/sfDir.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2002-2004 the xdvik development team + * + * 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. + * + */ + +#ifndef SFDIR_H_ +#define SFDIR_H_ + +#include "xdvi-config.h" + +#include "kpathsea/c-auto.h" +#include "kpathsea/config.h" +#include "kpathsea/c-dir.h" +#include "kpathsea/c-stat.h" + +#ifndef MOTIF + +typedef struct { + int statDone; + char *real; + char *shown; +} SFEntry; + +typedef struct { + char *dir; + char *path; + SFEntry *entries; + int nEntries; + int vOrigin; + int nChars; + int hOrigin; + int changed; + int beginSelection; + int endSelection; + time_t mtime; +} SFDir; + +extern int SFcompareEntries(const void *vp, const void *vq); +extern int SFgetDir(SFDir *dir); + +extern SFDir *SFdirs; + +#endif /* MOTIF */ + +#endif /* SFDIR_H_ */ diff --git a/Build/source/texk/xdvik/gui/sfDraw.c b/Build/source/texk/xdvik/gui/sfDraw.c new file mode 100644 index 00000000000..7a3ddd8cf06 --- /dev/null +++ b/Build/source/texk/xdvik/gui/sfDraw.c @@ -0,0 +1,819 @@ +/* + * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Software Research Associates not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Software Research Associates + * makes no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Author: Erik M. van der Poel + * Software Research Associates, Inc., Tokyo, Japan + * erik@sra.co.jp + */ + +#include "xdvi-config.h" + +#include "kpathsea/c-auto.h" +#include "kpathsea/config.h" +#include "kpathsea/c-stat.h" +#include "xdvi.h" + +#include "sfDraw.h" +#include "sfDir.h" +#include "sfPath.h" + +#if !defined(MOTIF) /* for xdvik */ + +#define SF_DEFAULT_FONT "9x15" + +typedef struct { + char *font; +} TextData, *textPtr; + +int SFcharWidth, SFcharAscent, SFcharHeight; + +int SFcurrentInvert[3] = { -1, -1, -1 }; + +static GC SFlineGC, SFscrollGC, SFinvertGC, SFtextGC; + +static XtResource textResources[] = { + {XtNfont, XtCFont, XtRString, sizeof(char *), + XtOffset(textPtr, font), XtRString, SF_DEFAULT_FONT}, +}; + +static XFontStruct *SFfont; + +static int SFcurrentListY; + +static XtIntervalId SFscrollTimerId; + +void +SFinitFont(void) +{ + TextData *data; + + data = XtNew(TextData); + + XtGetApplicationResources(selFileForm, (XtPointer) data, textResources, + XtNumber(textResources), (Arg *) NULL, ZERO); + + SFfont = XLoadQueryFont(SFdisplay, data->font); + if (!SFfont) { + SFfont = XLoadQueryFont(SFdisplay, SF_DEFAULT_FONT); + if (!SFfont) { + char sbuf[256]; + + (void)sprintf(sbuf, "XsraSelFile: can't get font %s", + SF_DEFAULT_FONT); + + XtAppError(SFapp, sbuf); + } + } + + TRACE_GUI((stderr, "FONT: width %d, %d\n", SFfont->max_bounds.width, SFfont->min_bounds.width)); + SFcharWidth = (SFfont->max_bounds.width + SFfont->min_bounds.width) / 2; + SFcharAscent = SFfont->max_bounds.ascent; + SFcharHeight = SFcharAscent + SFfont->max_bounds.descent; + if (SFcharWidth == 0) { + /* if min_bounds.width = -max_bounds.width, we probably + have a scalable TT font; try to determine its actual + width by measuring the letter `x': + */ + SFcharWidth = XTextWidth(SFfont, "x", 1); + } + if (SFcharWidth == 0) { /* last resort */ + SFcharWidth = SFfont->max_bounds.width / 2; + } + TRACE_GUI((stderr, + "Using font measures: charwidth %d, ascent %d, height %d", + SFcharWidth, SFcharAscent, SFcharHeight)); +} + +void +SFcreateGC(void) +{ + XGCValues gcValues; + XRectangle rectangles[1]; + +/* XtVaGetValues(selFileLists[0], */ +/* XtNforeground, &(gcValues.foreground), */ +/* XtNbackground, &(gcValues.background), */ +/* NULL); */ + gcValues.foreground = SFfore; + + SFlineGC = XtGetGC(selFileLists[0], (XtGCMask) + GCForeground | 0, &gcValues); + + SFscrollGC = XtGetGC(selFileLists[0], (XtGCMask) + 0, &gcValues); + + gcValues.function = GXinvert; +/* gcValues.plane_mask = (gcValues.foreground ^ gcValues.background); */ + gcValues.plane_mask = (SFfore ^ SFback); + + SFinvertGC = XtGetGC(selFileLists[0], (XtGCMask) + GCFunction | GCPlaneMask | 0, &gcValues); + + gcValues.foreground = SFfore; + gcValues.background = SFback; + gcValues.font = SFfont->fid; + + SFtextGC = XCreateGC(SFdisplay, XtWindow(selFileLists[0]), (unsigned long) + GCForeground | GCBackground | GCFont | 0, &gcValues); + + rectangles[0].x = SFlineToTextH + SFbesideText; + rectangles[0].y = 0; + rectangles[0].width = SFcharsPerEntry * SFcharWidth; + rectangles[0].height = SFupperY + 1; + + XSetClipRectangles(SFdisplay, SFtextGC, 0, 0, rectangles, 1, Unsorted); +} + +void +SFclearList(int n, int doScroll) +{ + SFDir *dir; + + SFcurrentInvert[n] = -1; + + XClearWindow(SFdisplay, XtWindow(selFileLists[n])); + + XDrawSegments(SFdisplay, XtWindow(selFileLists[n]), SFlineGC, SFsegs, 2); + + if (doScroll) { + dir = &(SFdirs[SFdirPtr + n]); + + if ((SFdirPtr + n < SFdirEnd) && dir->nEntries && dir->nChars) { + XawScrollbarSetThumb(selFileVScrolls[n], + (double)dir->vOrigin / dir->nEntries, + (double)(dir->nEntries < SFlistSize + ? dir->nEntries + : SFlistSize) + / dir->nEntries); + + XawScrollbarSetThumb(selFileHScrolls[n], + (double)(dir->hOrigin) / dir->nChars, + (double)(dir->nChars < SFcharsPerEntry + ? dir-> nChars : SFcharsPerEntry) + / dir->nChars); + } + else { + XawScrollbarSetThumb(selFileVScrolls[n], 0.0, 1.0); + XawScrollbarSetThumb(selFileHScrolls[n], 0.0, 1.0); + } + } +} + +static void +SFdeleteEntry(SFDir *dir, SFEntry *entry) +{ + SFEntry *e; + SFEntry *end; + int n; + int idx; + + idx = entry - dir->entries; + + if (idx < dir->beginSelection) { + dir->beginSelection--; + } + if (idx <= dir->endSelection) { + dir->endSelection--; + } + if (dir->beginSelection > dir->endSelection) { + dir->beginSelection = dir->endSelection = -1; + } + + if (idx < dir->vOrigin) { + dir->vOrigin--; + } + + XtFree(entry->real); + + end = &(dir->entries[dir->nEntries - 1]); + + for (e = entry; e < end; e++) { + *e = *(e + 1); + } + + if (!(--dir->nEntries)) { + return; + } + + n = dir - &(SFdirs[SFdirPtr]); + if ((n < 0) || (n > 2)) { + return; + } + + XawScrollbarSetThumb(selFileVScrolls[n], + (double)(dir->vOrigin) / dir->nEntries, + (double)(dir->nEntries < SFlistSize + ? dir->nEntries : SFlistSize) + / dir->nEntries); +} + +static void +SFwriteStatChar(char *name, int last, struct stat *statBuf) +{ + name[last] = SFstatChar(statBuf); +} + +static int +SFstatAndCheck(SFDir *dir, SFEntry *entry) +{ + struct stat statBuf; + char save; + int last; + extern int SFchdir(); + + /* + * must be restored before returning + */ + save = *(dir->path); + *(dir->path) = 0; + + if (!SFchdir(SFcurrentPath)) { + last = strlen(entry->real) - 1; + entry->real[last] = 0; + entry->statDone = 1; + if ((!stat(entry->real, &statBuf)) +#ifdef S_IFLNK + || (!lstat(entry->real, &statBuf)) +#endif /* ndef S_IFLNK */ + ) { + if (SFfunc) { + char *shown; + + shown = NULL; + if (SFfunc(entry->real, &shown, &statBuf)) { + if (shown) { + int len; + + len = strlen(shown); + entry->shown = XtMalloc((unsigned)(len + 2) + ); + (void)strcpy(entry->shown, shown); + SFwriteStatChar(entry->shown, len, &statBuf); + entry->shown[len + 1] = 0; + } + } + else { + SFdeleteEntry(dir, entry); + + *(dir->path) = save; + return 1; + } + } + SFwriteStatChar(entry->real, last, &statBuf); + } + else { + entry->real[last] = ' '; + } + } + + *(dir->path) = save; + return 0; +} + +static void +SFdrawStrings(Window w, SFDir *dir, int from, int to) +{ + int i; + SFEntry *entry; + int x; + + x = SFtextX - dir->hOrigin * SFcharWidth; + + if (dir->vOrigin + to >= dir->nEntries) { + to = dir->nEntries - dir->vOrigin - 1; + } + for (i = from; i <= to; i++) { + entry = &(dir->entries[dir->vOrigin + i]); + if (!(entry->statDone)) { + if (SFstatAndCheck(dir, entry)) { + if (dir->vOrigin + to >= dir->nEntries) { + to = dir->nEntries - dir->vOrigin - 1; + } + i--; + continue; + } + } + XDrawImageString(SFdisplay, + w, + SFtextGC, + x, + SFtextYoffset + i * SFentryHeight, + entry->shown, strlen(entry->shown)); + if (dir->vOrigin + i == dir->beginSelection) { + XDrawLine(SFdisplay, + w, + SFlineGC, + SFlineToTextH + 1, + SFlowerY + i * SFentryHeight, + SFlineToTextH + SFentryWidth - 2, + SFlowerY + i * SFentryHeight); + } + if ( + (dir->vOrigin + i >= dir->beginSelection) && + (dir->vOrigin + i <= dir->endSelection)) { + SFcompletionSegs[0].y1 = SFcompletionSegs[1].y1 = + SFlowerY + i * SFentryHeight; + SFcompletionSegs[0].y2 = SFcompletionSegs[1].y2 = + SFlowerY + (i + 1) * SFentryHeight - 1; + XDrawSegments(SFdisplay, w, SFlineGC, SFcompletionSegs, 2); + } + if (dir->vOrigin + i == dir->endSelection) { + XDrawLine(SFdisplay, + w, + SFlineGC, + SFlineToTextH + 1, + SFlowerY + (i + 1) * SFentryHeight - 1, + SFlineToTextH + SFentryWidth - 2, + SFlowerY + (i + 1) * SFentryHeight - 1); + } + } +} + +void +SFdrawList(int n, int doScroll) +{ + SFDir *dir; + Window w; + + SFclearList(n, doScroll); + + if (SFdirPtr + n < SFdirEnd) { + dir = &(SFdirs[SFdirPtr + n]); + w = XtWindow(selFileLists[n]); + XDrawImageString(SFdisplay, + w, + SFtextGC, + SFtextX - dir->hOrigin * SFcharWidth, + SFlineToTextV + SFaboveAndBelowText + SFcharAscent, + dir->dir, strlen(dir->dir) + ); + SFdrawStrings(w, dir, 0, SFlistSize - 1); + } +} + +void +SFdrawLists(int doScroll) +{ + int i; + + for (i = 0; i < 3; i++) { + SFdrawList(i, doScroll); + } +} + +static void +SFinvertEntry(int n) +{ + XFillRectangle(SFdisplay, + XtWindow(selFileLists[n]), + SFinvertGC, + SFlineToTextH, + SFcurrentInvert[n] * SFentryHeight + SFlowerY, + SFentryWidth, SFentryHeight); +} + +static unsigned long +SFscrollTimerInterval(void) +{ + static int maxVal = 200; + static int varyDist = 50; + static int minDist = 50; + int t; + int dist; + + if (SFcurrentListY < SFlowerY) { + dist = SFlowerY - SFcurrentListY; + } + else if (SFcurrentListY > SFupperY) { + dist = SFcurrentListY - SFupperY; + } + else { + return (unsigned long)1; + } + + t = maxVal - ((maxVal / varyDist) * (dist - minDist)); + + if (t < 1) { + t = 1; + } + + if (t > maxVal) { + t = maxVal; + } + + return (unsigned long)t; +} + +static void +SFscrollTimer(XtPointer p, XtIntervalId *id) +{ + SFDir *dir; + int save; + int n; + + UNUSED(id); + + n = (int)p; + + fprintf(stderr, "SFscrollTimer called!\n"); + + dir = &(SFdirs[SFdirPtr + n]); + save = dir->vOrigin; + + if (SFcurrentListY < SFlowerY) { + if (dir->vOrigin > 0) { + SFvSliderMovedCallback(selFileVScrolls[n], n, dir->vOrigin - 1); + } + } + else if (SFcurrentListY > SFupperY) { + if (dir->vOrigin < dir->nEntries - SFlistSize) { + SFvSliderMovedCallback(selFileVScrolls[n], n, dir->vOrigin + 1); + } + } + + if (dir->vOrigin != save) { + if (dir->nEntries) { + XawScrollbarSetThumb(selFileVScrolls[n], + (double)(dir->vOrigin) / dir->nEntries, + (double)(dir->nEntries < SFlistSize + ? dir-> nEntries : SFlistSize) + / dir->nEntries); + } + } + + if (SFbuttonPressed) { + SFscrollTimerId = XtAppAddTimeOut(SFapp, + SFscrollTimerInterval(), + SFscrollTimer, (XtPointer) n); + } +} + +static int +SFnewInvertEntry(int n, XMotionEvent *event) +{ + int x, y; + int new; + static int SFscrollTimerAdded = 0; + + x = event->x; + y = event->y; + + if (SFdirPtr + n >= SFdirEnd) { + return -1; + } + else if ((x >= 0) && (x <= SFupperX) && (y >= SFlowerY) && (y <= SFupperY) + ) { + SFDir *dir = &(SFdirs[SFdirPtr + n]); + + if (SFscrollTimerAdded) { + SFscrollTimerAdded = 0; + XtRemoveTimeOut(SFscrollTimerId); + } + + new = (y - SFlowerY) / SFentryHeight; + if (dir->vOrigin + new >= dir->nEntries) { + return -1; + } + return new; + } + else { + if (SFbuttonPressed) { + SFcurrentListY = y; + if (!SFscrollTimerAdded) { + SFscrollTimerAdded = 1; + SFscrollTimerId = XtAppAddTimeOut(SFapp, + SFscrollTimerInterval(), + SFscrollTimer, (XtPointer) n); + } + } + + return -1; + } +} + +void +SFenterList(Widget w, int n, XEnterWindowEvent *event) +{ + int new; + + UNUSED(w); + + /* sanity */ + if (SFcurrentInvert[n] != -1) { + SFinvertEntry(n); + SFcurrentInvert[n] = -1; + } + + new = SFnewInvertEntry(n, (XMotionEvent *) event); + if (new != -1) { + SFcurrentInvert[n] = new; + SFinvertEntry(n); + } +} + +void +SFleaveList(Widget w, int n, XEvent *event) +{ + UNUSED(w); + UNUSED(event); + + if (SFcurrentInvert[n] != -1) { + SFinvertEntry(n); + SFcurrentInvert[n] = -1; + } +} + +void +SFmotionList(Widget w, int n, XMotionEvent *event) +{ + int new; + + UNUSED(w); + + new = SFnewInvertEntry(n, event); + + if (new != SFcurrentInvert[n]) { + if (SFcurrentInvert[n] != -1) { + SFinvertEntry(n); + } + SFcurrentInvert[n] = new; + if (new != -1) { + SFinvertEntry(n); + } + } +} + +void +SFvFloatSliderMovedCallback(Widget w, int n, float *fnew) +{ + int new; + + new = (*fnew) * SFdirs[SFdirPtr + n].nEntries; + + SFvSliderMovedCallback(w, n, new); +} + +void +SFvSliderMovedCallback(Widget w, int n, int new) +{ + int old; + Window win; + SFDir *dir; + + UNUSED(w); + + dir = &(SFdirs[SFdirPtr + n]); + + old = dir->vOrigin; + dir->vOrigin = new; + + if (old == new) { + return; + } + + win = XtWindow(selFileLists[n]); + + if (ABS(new - old) < SFlistSize) { + if (new > old) { + XCopyArea(SFdisplay, + win, + win, + SFscrollGC, + SFlineToTextH, + SFlowerY + (new - old) * SFentryHeight, + SFentryWidth + SFlineToTextH, + (SFlistSize - (new - old)) * SFentryHeight, + SFlineToTextH, SFlowerY); + XClearArea(SFdisplay, + win, + SFlineToTextH, + SFlowerY + (SFlistSize - (new - old)) * + SFentryHeight, + SFentryWidth + SFlineToTextH, + (new - old) * SFentryHeight, False); + SFdrawStrings(win, dir, SFlistSize - (new - old), SFlistSize - 1); + } + else { + XCopyArea(SFdisplay, + win, + win, + SFscrollGC, + SFlineToTextH, + SFlowerY, + SFentryWidth + SFlineToTextH, + (SFlistSize - (old - new)) * SFentryHeight, + SFlineToTextH, SFlowerY + (old - new) * SFentryHeight); + XClearArea(SFdisplay, + win, + SFlineToTextH, + SFlowerY, + SFentryWidth + SFlineToTextH, + (old - new) * SFentryHeight, False); + SFdrawStrings(win, dir, 0, old - new); + } + } + else { + XClearArea(SFdisplay, + win, + SFlineToTextH, + SFlowerY, + SFentryWidth + SFlineToTextH, + SFlistSize * SFentryHeight, False); + SFdrawStrings(win, dir, 0, SFlistSize - 1); + } +} + +void +SFvAreaSelectedCallback(Widget w, int n, int pnew) +{ + SFDir *dir; + int new; + + dir = &(SFdirs[SFdirPtr + n]); + + new = dir->vOrigin + ((double)pnew / SFvScrollHeight) * dir->nEntries; + + if (new > dir->nEntries - SFlistSize) { + new = dir->nEntries - SFlistSize; + } + + if (new < 0) { + new = 0; + } + + if (dir->nEntries) { + float f; + + f = ((double)new) / dir->nEntries; + + XawScrollbarSetThumb(w, f, + (double)(dir->nEntries < SFlistSize + ? dir->nEntries + : SFlistSize) + / dir->nEntries); + } + + SFvSliderMovedCallback(w, n, new); +} + +void +SFhSliderMovedCallback(Widget w, int n, float *new) +{ + SFDir *dir; + int save; + + UNUSED(w); + + dir = &(SFdirs[SFdirPtr + n]); + save = dir->hOrigin; + dir->hOrigin = (*new) * dir->nChars; + if (dir->hOrigin == save) { + return; + } + + SFdrawList(n, SF_DO_NOT_SCROLL); +} + +void +SFhAreaSelectedCallback(Widget w, int n, int pnew) +{ + SFDir *dir; + int new; + + dir = &(SFdirs[SFdirPtr + n]); + + new = dir->hOrigin + (((double)pnew) / SFhScrollWidth) * dir->nChars; + + if (new > dir->nChars - SFcharsPerEntry) { + new = dir->nChars - SFcharsPerEntry; + } + + if (new < 0) { + new = 0; + } + + if (dir->nChars) { + float f; + + f = (double)new / dir->nChars; + + XawScrollbarSetThumb(w, f, + (double)(dir->nChars < SFcharsPerEntry + ? dir->nChars + : SFcharsPerEntry) + / dir->nChars); + + SFhSliderMovedCallback(w, n, &f); + } +} + +void +SFpathSliderMovedCallback(Widget w, XtPointer client_data, float *new) +{ + SFDir *dir; + int n; + XawTextPosition pos; + int SFdirPtrSave; + + UNUSED(w); + UNUSED(client_data); + + SFdirPtrSave = SFdirPtr; + SFdirPtr = (*new) * SFdirEnd; + if (SFdirPtr == SFdirPtrSave) { + return; + } + + SFdrawLists(SF_DO_SCROLL); + + n = 2; + while (SFdirPtr + n >= SFdirEnd) { + n--; + } + + dir = &(SFdirs[SFdirPtr + n]); + + pos = dir->path - SFcurrentPath; + + if (!strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir))) { + pos -= strlen(SFstartDir); + if (pos < 0) { + pos = 0; + } + } + + XawTextSetInsertionPoint(selFileField, pos); +} + +void +SFpathAreaSelectedCallback(Widget w, XtPointer client_data, int pnew) +{ + int new; + float f; + + UNUSED(client_data); + + new = SFdirPtr + (((double)pnew) / SFpathScrollWidth) * SFdirEnd; + + if (new > SFdirEnd - 3) { + new = SFdirEnd - 3; + } + + if (new < 0) { + new = 0; + } + + f = ((double)new) / SFdirEnd; + + XawScrollbarSetThumb(w, f, + (double)(SFdirEnd < 3 ? SFdirEnd : 3) / SFdirEnd); + + SFpathSliderMovedCallback(w, (XtPointer) NULL, &f); +} + +Boolean +SFworkProc(void) +{ + SFDir *dir; + SFEntry *entry; + + for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--) { + if (!(dir->nEntries)) { + continue; + } + for (entry = &(dir->entries[dir->nEntries - 1]); + entry >= dir->entries; entry--) { + if (!(entry->statDone)) { + (void)SFstatAndCheck(dir, entry); + return False; + } + } + } + + SFworkProcAdded = 0; + + return True; +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* !defined(MOTIF) */ diff --git a/Build/source/texk/xdvik/gui/sfDraw.h b/Build/source/texk/xdvik/gui/sfDraw.h new file mode 100644 index 00000000000..e9199793df8 --- /dev/null +++ b/Build/source/texk/xdvik/gui/sfDraw.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2002-2004 the xdvik development team + * + * 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. + * + */ + +#ifndef SFDRAW_H_ +#define SFDRAW_H_ + +#include "xdvi-config.h" + +#if !defined(MOTIF) /* for xdvik */ + +#include <stdio.h> +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Xos.h> +#include <X11/Xaw/Text.h> +#include <X11/Xaw/AsciiText.h> +#include <X11/Xaw/Scrollbar.h> +#include <X11/Xaw/Cardinals.h> + +#define SF_DO_SCROLL 1 +#define SF_DO_NOT_SCROLL 0 + +extern Pixel SFfore, SFback; + +void SFinitFont(void); +void SFcreateGC(void); +void SFclearList(int n, int doScroll); +void SFdrawList(int n, int doScroll); +void SFdrawLists(int doScroll); +void SFenterList(Widget w, int n, XEnterWindowEvent *event); +void SFleaveList(Widget w, int n, XEvent *event); +void SFmotionList(Widget w, int n, XMotionEvent *event); +void SFvFloatSliderMovedCallback(Widget w, int n, float *fnew); +void SFvSliderMovedCallback(Widget w, int n, int new); +void SFvAreaSelectedCallback(Widget w, int n, int pnew); +void SFhSliderMovedCallback(Widget w, int n, float *new); +void SFhAreaSelectedCallback(Widget w, int n, int pnew); +void SFpathSliderMovedCallback(Widget w, XtPointer client_data, float *new); +void SFpathAreaSelectedCallback(Widget w, XtPointer client_data, int pnew); +Boolean SFworkProc(void); +extern int (*SFfunc)(); + +extern Widget selFileField, selFileForm, selFileHScroll, selFileHScrolls[], selFileLists[], selFileVScrolls[]; +extern Display *SFdisplay; +extern int SFcharWidth, SFcharHeight, SFcharAscent; +extern XSegment SFsegs[], SFcompletionSegs[]; +extern XawTextPosition SFtextPos; + +extern int SFupperX, SFlowerY, SFupperY; + +extern int SFtextX, SFtextYoffset; + +extern int SFentryWidth, SFentryHeight; + +extern int SFlineToTextH, SFlineToTextV; + +extern int SFbesideText, SFaboveAndBelowText; + +extern int SFcharsPerEntry; + +extern int SFlistSize; + +extern int SFcurrentInvert[]; + +extern int SFworkProcAdded; + +extern XtAppContext SFapp; + +extern int SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth; + +extern char SFtextBuffer[]; + +extern int SFbuttonPressed; + +extern XtIntervalId SFdirModTimerId; + +#endif /* !defined(MOTIF) */ + +#endif /* SFDRAW_H_ */ diff --git a/Build/source/texk/xdvik/gui/sfPath.c b/Build/source/texk/xdvik/gui/sfPath.c new file mode 100644 index 00000000000..3349919391b --- /dev/null +++ b/Build/source/texk/xdvik/gui/sfPath.c @@ -0,0 +1,908 @@ +/* + * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Software Research Associates not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Software Research Associates + * makes no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Author: Erik M. van der Poel + * Software Research Associates, Inc., Tokyo, Japan + * erik@sra.co.jp + */ + +#include "xdvi-config.h" + +#include "kpathsea/c-auto.h" +#include "kpathsea/config.h" +#include "kpathsea/c-stat.h" + +#include "xdvi.h" +#include "sfDir.h" +#include "sfDraw.h" +#include "sfPath.h" +#include "sfSelFile.h" + +#if !defined(MOTIF) /* entire file */ + +#include <stdio.h> + +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Xos.h> +#include <X11/Xaw/Text.h> +#include <X11/Xaw/AsciiText.h> +#include <X11/Xaw/Scrollbar.h> +#include <X11/Xaw/Cardinals.h> + + +#ifdef SEL_FILE_IGNORE_CASE +#include <ctype.h> +#endif /* def SEL_FILE_IGNORE_CASE */ + +#ifndef S_IXUSR +#define S_IXUSR 0100 +#endif +#ifndef S_IXGRP +#define S_IXGRP 0010 +#endif +#ifndef S_IXOTH +#define S_IXOTH 0001 +#endif + +#define S_ISXXX(m) ((m) & (S_IXUSR | S_IXGRP | S_IXOTH)) + +#include <X11/Xos.h> +#include <pwd.h> +#include <X11/Xaw/Scrollbar.h> + +typedef struct { + char *name; + char *dir; +} SFLogin; + +SFDir *SFdirs = NULL; + +int SFdirEnd; + +int SFdirPtr; + +int SFbuttonPressed = 0; + +static int SFdoNotTouchDirPtr = 0; + +static int SFdoNotTouchVorigin = 0; + +static SFDir SFrootDir, SFhomeDir; + +static SFLogin *SFlogins; + +static int SFtwiddle = 0; + +int +SFchdir(char *path) +{ + int result; + + result = 0; + + if (strcmp(path, SFcurrentDir)) { + result = chdir(path); + if (!result) { + (void)strcpy(SFcurrentDir, path); + } + } + + return result; +} + +static void +SFfree(int i) +{ + SFDir *dir; + int j; + + dir = &(SFdirs[i]); + + for (j = dir->nEntries - 1; j >= 0; j--) { + if (dir->entries[j].shown != dir->entries[j].real) { + XtFree(dir->entries[j].shown); + } + XtFree(dir->entries[j].real); + } + + XtFree((char *)dir->entries); + + XtFree(dir->dir); + + dir->dir = NULL; +} + +static void +SFstrdup(char **s1, char *s2) +{ + *s1 = strcpy(XtMalloc((unsigned)(strlen(s2) + 1)), s2); +} + +static void +SFunreadableDir(SFDir *dir) +{ + char *cannotOpen = "<cannot open> "; + + dir->entries = (SFEntry *) XtMalloc(sizeof(SFEntry)); + dir->entries[0].statDone = 1; + SFstrdup(&dir->entries[0].real, cannotOpen); + dir->entries[0].shown = dir->entries[0].real; + dir->nEntries = 1; + dir->nChars = strlen(cannotOpen); +} + +#ifdef SEL_FILE_IGNORE_CASE +static +SFstrncmp(char *p, char *q, int n) +{ + char c1, c2; + char *psave, *qsave; + int nsave; + + psave = p; + qsave = q; + nsave = n; + + c1 = *p++; + if (islower(c1)) { + c1 = toupper(c1); + } + c2 = *q++; + if (islower(c2)) { + c2 = toupper(c2); + } + + while ((--n >= 0) && (c1 == c2)) { + if (!c1) { + return strncmp(psave, qsave, nsave); + } + c1 = *p++; + if (islower(c1)) { + c1 = toupper(c1); + } + c2 = *q++; + if (islower(c2)) { + c2 = toupper(c2); + } + } + + if (n < 0) { + return strncmp(psave, qsave, nsave); + } + + return c1 - c2; +} +#endif /* def SEL_FILE_IGNORE_CASE */ + +static void +SFreplaceText(SFDir *dir, char *str) +{ + int len; + + *(dir->path) = 0; + len = strlen(str); + if (str[len - 1] == '/') { + (void)strcat(SFcurrentPath, str); + } + else { + (void)strncat(SFcurrentPath, str, len - 1); + } +/* if (strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir))) { */ +/* SFsetText(SFcurrentPath); */ +/* } */ +/* else { */ +/* SFsetText(&(SFcurrentPath[strlen(SFstartDir)])); */ +/* } */ + SFsetText(SFcurrentPath); + + SFtextChanged(); +} + +static void +SFexpand(char *str) +{ + int len; + int cmp; + char *name, *growing; + SFDir *dir; + SFEntry *entry, *max; + + len = strlen(str); + + dir = &(SFdirs[SFdirEnd - 1]); + + if (dir->beginSelection == -1) { + SFstrdup(&str, str); + SFreplaceText(dir, str); + XtFree(str); + return; + } + else if (dir->beginSelection == dir->endSelection) { + SFreplaceText(dir, dir->entries[dir->beginSelection].shown); + return; + } + + max = &(dir->entries[dir->endSelection + 1]); + + name = dir->entries[dir->beginSelection].shown; + SFstrdup(&growing, name); + + cmp = 0; + while (!cmp) { + entry = &(dir->entries[dir->beginSelection]); + while (entry < max) { + if ((cmp = strncmp(growing, entry->shown, len))) { + break; + } + entry++; + } + len++; + } + + /* + * SFreplaceText() expects filename + */ + growing[len - 2] = ' '; + + growing[len - 1] = 0; + SFreplaceText(dir, growing); + XtFree(growing); +} + +static int +SFfindFile(SFDir *dir, char *str) +{ + int i, last, max; + char *name, save; + SFEntry *entries; + int len; + int begin, end; + int result; + + len = strlen(str); + + if (str[len - 1] == ' ') { + SFexpand(str); + return 1; + } + else if (str[len - 1] == '/') { + len--; + } + + max = dir->nEntries; + + entries = dir->entries; + + i = 0; + while (i < max) { + name = entries[i].shown; + last = strlen(name) - 1; + save = name[last]; + name[last] = 0; + +#ifdef SEL_FILE_IGNORE_CASE + result = SFstrncmp(str, name, len); +#else /* def SEL_FILE_IGNORE_CASE */ + result = strncmp(str, name, len); +#endif /* def SEL_FILE_IGNORE_CASE */ + + name[last] = save; + if (result <= 0) { + break; + } + i++; + } + begin = i; + while (i < max) { + name = entries[i].shown; + last = strlen(name) - 1; + save = name[last]; + name[last] = 0; + +#ifdef SEL_FILE_IGNORE_CASE + result = SFstrncmp(str, name, len); +#else /* def SEL_FILE_IGNORE_CASE */ + result = strncmp(str, name, len); +#endif /* def SEL_FILE_IGNORE_CASE */ + + name[last] = save; + if (result) { + break; + } + i++; + } + end = i; + + if (begin != end) { + if ((dir->beginSelection != begin) || (dir->endSelection != end - 1) + ) { + dir->changed = 1; + dir->beginSelection = begin; + if (str[strlen(str) - 1] == '/') { + dir->endSelection = begin; + } + else { + dir->endSelection = end - 1; + } + } + } + else { + if (dir->beginSelection != -1) { + dir->changed = 1; + dir->beginSelection = -1; + dir->endSelection = -1; + } + } + + if (SFdoNotTouchVorigin || + ((begin > dir->vOrigin) && (end < dir->vOrigin + SFlistSize))) { + SFdoNotTouchVorigin = 0; + return 0; + } + + i = begin - 1; + if (i > max - SFlistSize) { + i = max - SFlistSize; + } + if (i < 0) { + i = 0; + } + + if (dir->vOrigin != i) { + dir->vOrigin = i; + dir->changed = 1; + } + + return 0; +} + +static void +SFunselect(void) +{ + SFDir *dir; + + dir = &(SFdirs[SFdirEnd - 1]); + if (dir->beginSelection != -1) { + dir->changed = 1; + } + dir->beginSelection = -1; + dir->endSelection = -1; +} + +static int +SFcompareLogins(const void *vp, const void *vq) +{ + const SFLogin *p = vp; + const SFLogin *q = vq; + return strcmp(p->name, q->name); +} + +static void +SFgetHomeDirs(void) +{ + struct passwd *pw; + int alloc; + int i; + SFEntry *entries = NULL; + int len; + int maxChars; + + { + alloc = 1; + i = 1; + entries = (SFEntry *) XtMalloc(sizeof(SFEntry)); + SFlogins = (SFLogin *) XtMalloc(sizeof(SFLogin)); + entries[0].real = XtMalloc(3); + (void)strcpy(entries[0].real, "~"); + entries[0].shown = entries[0].real; + entries[0].statDone = 1; + SFlogins[0].name = ""; + pw = getpwuid((int)getuid()); + SFstrdup(&SFlogins[0].dir, pw ? pw->pw_dir : "/"); + maxChars = 0; + } + + (void)setpwent(); + + while ((pw = (struct passwd *)getpwent()) && (*(pw->pw_name))) { + if (i >= alloc) { + alloc *= 2; + entries = (SFEntry *) XtRealloc( + (char *)entries, + (unsigned)(alloc * + sizeof(SFEntry))); + SFlogins = + (SFLogin *) XtRealloc((char *)SFlogins, + (unsigned)(alloc * sizeof(SFLogin)) + ); + } + len = strlen(pw->pw_name); + entries[i].real = XtMalloc((unsigned)(len + 3)); + (void)strcat(strcpy(entries[i].real, "~"), pw->pw_name); + entries[i].shown = entries[i].real; + entries[i].statDone = 1; + if (len > maxChars) { + maxChars = len; + } + SFstrdup(&SFlogins[i].name, pw->pw_name); + SFstrdup(&SFlogins[i].dir, pw->pw_dir); + i++; + } + + SFhomeDir.dir = XtMalloc(1); + SFhomeDir.dir[0] = 0; + SFhomeDir.path = SFcurrentPath; + SFhomeDir.entries = entries; + SFhomeDir.nEntries = i; + SFhomeDir.vOrigin = 0; /* :-) */ + SFhomeDir.nChars = maxChars + 2; + SFhomeDir.hOrigin = 0; + SFhomeDir.changed = 1; + SFhomeDir.beginSelection = -1; + SFhomeDir.endSelection = -1; + +#if defined(SVR4) || defined(SYSV) || defined(USG) + qsort((char *)entries, (unsigned)i, sizeof(SFEntry), SFcompareEntries); + qsort((char *)SFlogins, (unsigned)i, sizeof(SFLogin), SFcompareLogins); +#else /* defined(SVR4) || defined(SYSV) || defined(USG) */ + qsort((char *)entries, i, sizeof(SFEntry), SFcompareEntries); + qsort((char *)SFlogins, i, sizeof(SFLogin), SFcompareLogins); +#endif /* defined(SVR4) || defined(SYSV) || defined(USG) */ + + for (i--; i >= 0; i--) { + (void)strcat(entries[i].real, "/"); + } +} + +static int +SFfindHomeDir(char *begin, char *end) +{ + char save; + char *theRest; + int i; + + save = *end; + *end = 0; + + for (i = SFhomeDir.nEntries - 1; i >= 0; i--) { + if (!strcmp(SFhomeDir.entries[i].real, begin)) { + *end = save; + SFstrdup(&theRest, end); + (void)strcat(strcat(strcpy(SFcurrentPath, + SFlogins[i].dir), "/"), theRest); + XtFree(theRest); + SFsetText(SFcurrentPath); + SFtextChanged(); + return 1; + } + } + + *end = save; + + return 0; +} + +void +SFupdatePath(void) +{ + static int alloc; + static int wasTwiddle = 0; + char *begin, *end; + int i, j; + int prevChange; + int SFdirPtrSave, SFdirEndSave; + SFDir *dir; + + if (!SFdirs) { + SFdirs = (SFDir *) XtMalloc((alloc = 10) * sizeof(SFDir)); + dir = &(SFdirs[0]); + SFstrdup(&dir->dir, "/"); + (void)SFchdir("/"); + (void)SFgetDir(dir); + for (j = 1; j < alloc; j++) { + SFdirs[j].dir = NULL; + } + dir->path = SFcurrentPath + 1; + dir->vOrigin = 0; + dir->hOrigin = 0; + dir->changed = 1; + dir->beginSelection = -1; + dir->endSelection = -1; + SFhomeDir.dir = NULL; + } + + SFdirEndSave = SFdirEnd; + SFdirEnd = 1; + + SFdirPtrSave = SFdirPtr; + SFdirPtr = 0; + + begin = NULL; + + if (SFcurrentPath[0] == '~') { + if (!SFtwiddle) { + SFtwiddle = 1; + dir = &(SFdirs[0]); + SFrootDir = *dir; + if (!SFhomeDir.dir) { + SFgetHomeDirs(); + } + *dir = SFhomeDir; + dir->changed = 1; + } + end = SFcurrentPath; + SFdoNotTouchDirPtr = 1; + wasTwiddle = 1; + } + else { + if (SFtwiddle) { + SFtwiddle = 0; + dir = &(SFdirs[0]); + *dir = SFrootDir; + dir->changed = 1; + } + end = SFcurrentPath + 1; + } + + i = 0; + + prevChange = 0; + + while (*end) { + while (*end++ == '/') { + ; + } + end--; + begin = end; + while ((*end) && (*end++ != '/')) { + ; + } + if ((end - SFcurrentPath <= SFtextPos) && (*(end - 1) == '/')) { + SFdirPtr = i - 1; + if (SFdirPtr < 0) { + SFdirPtr = 0; + } + } + if (*begin) { + if (*(end - 1) == '/') { + char save = *end; + + if (SFtwiddle) { + if (SFfindHomeDir(begin, end)) { + return; + } + } + *end = 0; + i++; + SFdirEnd++; + if (i >= alloc) { + SFdirs = (SFDir *) XtRealloc( + (char *)SFdirs, + (unsigned)((alloc *= 2) * + sizeof(SFDir)) + ); + for (j = alloc / 2; j < alloc; j++) { + SFdirs[j].dir = NULL; + } + } + dir = &(SFdirs[i]); + if ((!(dir->dir)) || prevChange || strcmp(dir->dir, begin) + ) { + if (dir->dir) { + SFfree(i); + } + prevChange = 1; + SFstrdup(&dir->dir, begin); + dir->path = end; + dir->vOrigin = 0; + dir->hOrigin = 0; + dir->changed = 1; + dir->beginSelection = -1; + dir->endSelection = -1; + (void)SFfindFile(dir - 1, begin); + if (SFchdir(SFcurrentPath) || SFgetDir(dir) + ) { + SFunreadableDir(dir); + break; + } + } + *end = save; + if (!save) { + SFunselect(); + } + } + else { + if (SFfindFile(&(SFdirs[SFdirEnd - 1]), begin)) { + return; + } + } + } + else { + SFunselect(); + } + } + + if ((end == SFcurrentPath + 1) && (!SFtwiddle)) { + SFunselect(); + } + + for (i = SFdirEnd; i < alloc; i++) { + if (SFdirs[i].dir) { + SFfree(i); + } + } + + if (SFdoNotTouchDirPtr) { + if (wasTwiddle) { + wasTwiddle = 0; + SFdirPtr = SFdirEnd - 2; + if (SFdirPtr < 0) { + SFdirPtr = 0; + } + } + else { + SFdirPtr = SFdirPtrSave; + } + SFdoNotTouchDirPtr = 0; + } + + if ((SFdirPtr != SFdirPtrSave) || (SFdirEnd != SFdirEndSave)) { + XawScrollbarSetThumb(selFileHScroll, + (double)(SFdirPtr) / SFdirEnd, + (double)(SFdirEnd < 3 ? SFdirEnd : 3) / SFdirEnd); + } + + if (SFdirPtr != SFdirPtrSave) { + SFdrawLists(SF_DO_SCROLL); + } + else { + for (i = 0; i < 3; i++) { + if (SFdirPtr + i < SFdirEnd) { + if (SFdirs[SFdirPtr + i].changed) { + SFdirs[SFdirPtr + i].changed = 0; + SFdrawList(i, SF_DO_SCROLL); + } + } + else { + SFclearList(i, SF_DO_SCROLL); + } + } + } +} + +void +SFsetText(char *path) +{ + XawTextBlock text; + + text.firstPos = 0; + text.length = strlen(path); + text.ptr = path; + text.format = FMT8BIT; + + XawTextReplace(selFileField, 0, strlen(SFtextBuffer), &text); + XawTextSetInsertionPoint(selFileField, strlen(SFtextBuffer)); +} + +void +SFbuttonPressList(Widget w, int n, XButtonPressedEvent *event) +{ + UNUSED(w); + UNUSED(n); + UNUSED(event); + + SFbuttonPressed = 1; +} + +void +SFbuttonReleaseList(Widget w, int n, XButtonReleasedEvent *event) +{ + SFDir *dir; + + SFbuttonPressed = 0; + + if (SFcurrentInvert[n] != -1) { + if (n < 2) { + SFdoNotTouchDirPtr = 1; + } + SFdoNotTouchVorigin = 1; + dir = &(SFdirs[SFdirPtr + n]); + SFreplaceText(dir, + dir->entries[dir->vOrigin + SFcurrentInvert[n]].shown); + SFmotionList(w, n, (XMotionEvent *)event); + } +} + +static int +SFcheckDir(int n, SFDir *dir) +{ + struct stat statBuf; + int i; + + if ((!stat(".", &statBuf)) && (statBuf.st_mtime != dir->mtime) + ) { + + /* + * If the pointer is currently in the window that we are about + * to update, we must warp it to prevent the user from + * accidentally selecting the wrong file. + */ + if (SFcurrentInvert[n] != -1) { + XWarpPointer(SFdisplay, + None, XtWindow(selFileLists[n]), 0, 0, 0, 0, 0, 0); + } + + for (i = dir->nEntries - 1; i >= 0; i--) { + if (dir->entries[i].shown != dir->entries[i].real) { + XtFree(dir->entries[i].shown); + } + XtFree(dir->entries[i].real); + } + XtFree((char *)dir->entries); + if (SFgetDir(dir)) { + SFunreadableDir(dir); + } + if (dir->vOrigin > dir->nEntries - SFlistSize) { + dir->vOrigin = dir->nEntries - SFlistSize; + } + if (dir->vOrigin < 0) { + dir->vOrigin = 0; + } + if (dir->hOrigin > dir->nChars - SFcharsPerEntry) { + dir->hOrigin = dir->nChars - SFcharsPerEntry; + } + if (dir->hOrigin < 0) { + dir->hOrigin = 0; + } + dir->beginSelection = -1; + dir->endSelection = -1; + SFdoNotTouchVorigin = 1; + if ((dir + 1)->dir) { + (void)SFfindFile(dir, (dir + 1)->dir); + } + else { + (void)SFfindFile(dir, dir->path); + } + + if (!SFworkProcAdded) { + (void)XtAppAddWorkProc(SFapp, (XtWorkProc)SFworkProc, NULL); + SFworkProcAdded = 1; + } + + return 1; + } + + return 0; +} + +static int +SFcheckFiles(SFDir *dir) +{ + int from, to; + int result; + char old, new; + int i; + char *str; + int last; + struct stat statBuf; + + result = 0; + + from = dir->vOrigin; + to = dir->vOrigin + SFlistSize; + if (to > dir->nEntries) { + to = dir->nEntries; + } + + for (i = from; i < to; i++) { + str = dir->entries[i].real; + last = strlen(str) - 1; + old = str[last]; + str[last] = 0; + if (stat(str, &statBuf)) { + new = ' '; + } + else { + new = SFstatChar(&statBuf); + } + str[last] = new; + if (new != old) { + result = 1; + } + } + + return result; +} + +void +SFdirModTimer(XtPointer cl, XtIntervalId *id) +{ + static int n = -1; + static int f = 0; + char save; + SFDir *dir; + + UNUSED(cl); + UNUSED(id); + + fprintf(stderr, "sfdirmodtimer called!\n"); + + if ((!SFtwiddle) && (SFdirPtr < SFdirEnd)) { + n++; + if ((n > 2) || (SFdirPtr + n >= SFdirEnd)) { + n = 0; + f++; + if ((f > 2) || (SFdirPtr + f >= SFdirEnd)) { + f = 0; + } + } + dir = &(SFdirs[SFdirPtr + n]); + save = *(dir->path); + *(dir->path) = 0; + if (SFchdir(SFcurrentPath)) { + *(dir->path) = save; + + /* + * force a re-read + */ + *(dir->dir) = 0; + + SFupdatePath(); + } + else { + *(dir->path) = save; + if (SFcheckDir(n, dir) || ((f == n) && SFcheckFiles(dir)) + ) { + SFdrawList(n, SF_DO_SCROLL); + } + } + } + + SFdirModTimerId = XtAppAddTimeOut(SFapp, 1500UL, + SFdirModTimer, (XtPointer) NULL); +} + +/* Return a single character describing what kind of file STATBUF is. */ + +char +SFstatChar(struct stat *statBuf) +{ + if (S_ISDIR(statBuf->st_mode)) { + return '/'; + } + else if (S_ISREG(statBuf->st_mode)) { + return S_ISXXX(statBuf->st_mode) ? '*' : ' '; +#ifdef S_ISSOCK + } + else if (S_ISSOCK(statBuf->st_mode)) { + return '='; +#endif /* S_ISSOCK */ + } + else { + return ' '; + } +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* !defined(MOTIF) */ diff --git a/Build/source/texk/xdvik/gui/sfPath.h b/Build/source/texk/xdvik/gui/sfPath.h new file mode 100644 index 00000000000..d087c0f804c --- /dev/null +++ b/Build/source/texk/xdvik/gui/sfPath.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2004 the xdvik development team + * + * 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. + * + */ + +#ifndef SFPATH_H_ +#define SFPATH_H_ + +#include "xdvi-config.h" + +#ifndef MOTIF + +extern int SFchdir(char *path); +extern void SFupdatePath(void); +extern void SFsetText(char *path); +extern void SFbuttonPressList(Widget w, int n, XButtonPressedEvent *event); +extern void SFbuttonReleaseList(Widget w, int n, XButtonReleasedEvent *event); +extern void SFdirModTimer(XtPointer cl, XtIntervalId *id); +extern char SFstatChar(struct stat *statBuf); + +extern char SFcurrentPath[], SFstartDir[], SFcurrentDir[]; +extern SFDir *SFdirs; +extern int SFdirEnd, SFdirPtr; + +#endif /* MOTIF */ + +#endif /* SFPATH_H_ */ diff --git a/Build/source/texk/xdvik/gui/sfSelFile.c b/Build/source/texk/xdvik/gui/sfSelFile.c new file mode 100644 index 00000000000..9964c69ad37 --- /dev/null +++ b/Build/source/texk/xdvik/gui/sfSelFile.c @@ -0,0 +1,1071 @@ +/* + * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Software Research Associates not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Software Research Associates + * makes no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Author: Erik M. van der Poel + * Software Research Associates, Inc., Tokyo, Japan + * erik@sra.co.jp + */ + +#include "xdvi-config.h" + +#include "xdvi.h" +#include "dvi-init.h" /* for dviErrFlagT */ +#include "message-window.h" + +#include "kpathsea/c-stat.h" + +#include <string.h> + +#include "sfDir.h" +#include "sfPath.h" +#include "sfDraw.h" +#include "sfSelFile.h" +#include "util.h" + +#include <ctype.h> +#include "kpathsea/c-fopen.h" +#include "kpathsea/c-stat.h" + +#if !defined(MOTIF) /* entire file */ + + +#include <errno.h> + +#ifdef X_NOT_STDC_ENV +extern int errno; +#endif + +#define SEL_FILE_CANCEL -1 +#define SEL_FILE_OK 0 +#define SEL_FILE_NULL 1 +#define SEL_FILE_TEXT 2 + +/* + * Author's address: + * + * erik@sra.co.jp + * OR + * erik%sra.co.jp@uunet.uu.net + * OR + * erik%sra.co.jp@mcvax.uucp + * OR + * try junet instead of co.jp + * OR + * Erik M. van der Poel + * Software Research Associates, Inc. + * 1-1-1 Hirakawa-cho, Chiyoda-ku + * Tokyo 102 Japan. TEL +81-3-234-2692 + */ + +#include <sys/param.h> +#include <X11/cursorfont.h> +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Composite.h> +#include <X11/Shell.h> +#include <X11/Xaw/Paned.h> +#include <X11/Xaw/Text.h> +#include <X11/Xaw/AsciiText.h> +#include <X11/Xaw/Form.h> +#include <X11/Xaw/Command.h> +#include <X11/Xaw/Scrollbar.h> +#include <X11/Xaw/Label.h> +#include <X11/Xaw/Cardinals.h> + + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif /* ndef MAXPATHLEN */ + +/* global vars for communication with sfDraw.c */ +char SFstartDir[MAXPATHLEN], SFcurrentPath[MAXPATHLEN], SFcurrentDir[MAXPATHLEN]; +Widget selFileField, selFileForm, selFileHScroll, selFileHScrolls[3], selFileLists[3], selFileVScrolls[3]; +Display *SFdisplay; +Pixel SFfore, SFback; +XSegment SFsegs[2], SFcompletionSegs[2]; +XawTextPosition SFtextPos; +int SFupperX, SFlowerY, SFupperY; +int SFtextX, SFtextYoffset; +int SFentryWidth, SFentryHeight; +int SFlineToTextH = 3; +int SFlineToTextV = 3; +int SFbesideText = 3; +int SFaboveAndBelowText = 2; +int SFcharsPerEntry = 15; +int SFlistSize = 10; +int SFworkProcAdded = 0; +XtAppContext SFapp; +int SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth; +char SFtextBuffer[MAXPATHLEN]; +XtIntervalId SFdirModTimerId; +int (*SFfunc) (); + +static int SFstatus = SEL_FILE_NULL; + +static Widget selFile, selFileCancel, selFileOK, selFilePrompt; +/* For file filter. */ +static Widget selFileLabel, selFileMask, selFileHide; + +#define MASKWIDTH 16 +static char fileMask[MASKWIDTH + 2] = "*.dvi"; + +static Atom SFwmDeleteWindow; + +static char *oneLineTextEditTranslations = + "<Key>Return: redraw-display()\n" + "Ctrl<Key>M: redraw-display()\n"; + +#if !defined (HAVE_STRERROR) && !defined (strerror) +static char * +strerror(int errnum) +{ + return 0 < errnum && errnum <= sys_nerr + ? sys_errlist[errnum] : "Unknown system error"; +} +#endif /* not HAVE_STRERROR && not strerror */ + +void +raise_file_selector(void) +{ + if (selFile != 0) { + XRaiseWindow(DISP, XtWindow(selFile)); + } +} + +static void +SFexposeList(Widget w, XtPointer n, XEvent *event, Boolean *cont) +{ + UNUSED(w); + UNUSED(cont); + + if ((event->type == NoExpose) || event->xexpose.count) { + return; + } + + SFdrawList((int)n, SF_DO_NOT_SCROLL); +} + +static void +SFmodVerifyCallback(Widget w, XtPointer client_data, XEvent *event, Boolean *cont) +{ + char buf[2]; + + UNUSED(w); + UNUSED(client_data); + UNUSED(cont); + + if ((XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1) && + ((*buf) == '\r')) { + SFstatus = SEL_FILE_OK; + } + else { + SFstatus = SEL_FILE_TEXT; + } +} + +static void +SFokCallback(Widget w, XtPointer cl, XtPointer cd) +{ + UNUSED(w); + UNUSED(cl); + UNUSED(cd); + + SFstatus = SEL_FILE_OK; +} + +static XtCallbackRec SFokSelect[] = { + {SFokCallback, (XtPointer) NULL}, + {NULL, (XtPointer) NULL}, +}; + +static void +SFcancelCallback(Widget w, XtPointer cl, XtPointer cd) +{ + UNUSED(w); + UNUSED(cl); + UNUSED(cd); + + SFstatus = SEL_FILE_CANCEL; +} + +static XtCallbackRec SFcancelSelect[] = { + {SFcancelCallback, (XtPointer) NULL}, + {NULL, (XtPointer) NULL}, +}; + +static void +SFdismissAction(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + UNUSED(w); + UNUSED(params); + UNUSED(num_params); + + if (event->type == ClientMessage && (unsigned)(event->xclient.data.l[0]) != SFwmDeleteWindow) + return; + + SFstatus = SEL_FILE_CANCEL; +} + +static char *wmDeleteWindowTranslation = "<Message>WM_PROTOCOLS: SelFileDismiss()\n"; + +static XtActionsRec sf_actions[] = { + {"SelFileDismiss", SFdismissAction}, +}; + +/* Don't show files that don't get through the filter. */ + +/* return 1 if file is masked (mask does not match filename), 0 otherwise */ +static int +maskFile(const char *mask, char *filename) +{ + int c, c1; + + while ((c = *mask++)) { + if (c == '*') { + while ((c1 = *mask++)) { + if (c1 != '*') { + if (!(*filename)) + return 1; + if (c1 != '?') { + while ((filename = strchr(filename, c1))) { + if (!maskFile(mask, ++filename)) + return 0; + } + return 1; + } + else + filename++; + } + } + return 0; + } + if (c == '?') { + if (!*filename) + return 1; + } + else if (c != *filename) + return 1; + filename++; + } + return (*filename) ? 1 : 0; +} + +Boolean hideFlag = False; +static int +showEntry(char *entryReal, char **entryShown, struct stat *statBuf) +{ + UNUSED(entryShown); + + if ((hideFlag && entryReal[0] == '.') || (!S_ISDIR(statBuf->st_mode) && maskFile(fileMask, entryReal))) + return 0; + entryReal[strlen(entryReal)] = SFstatChar(statBuf); + return 1; +} + +static void +maskChanged(Widget w, XtPointer client_data, XEvent *call_data, Boolean *cont) +{ + char buf[2]; + SFDir *dir; + + UNUSED(w); + UNUSED(client_data); + UNUSED(cont); + + if ((XLookupString((XKeyPressedEvent *)call_data, buf, 2, NULL, NULL) == 1) && ((*buf) == '\r')) { + for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--) + *(dir->dir) = 0; /* force a re-read */ + SFupdatePath(); + } +} + +static void +hideFiles(Widget w, XtPointer client_data, XtPointer call_data) +{ + SFDir *dir; + SFEntry *entry; + + UNUSED(client_data); + UNUSED(call_data); + + hideFlag = !hideFlag; + if (hideFlag) { + XtVaSetValues(w, XtNlabel, "hidden", NULL); + for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--) { + if (!(dir->nEntries)) + continue; + dir->vOrigin = 0; + for (entry = &(dir->entries[dir->nEntries - 1]); + entry >= dir->entries; entry--) + entry->statDone = 0; + SFdrawLists(SF_DO_SCROLL); + } + } + else { + XtVaSetValues(w, XtNlabel, "shown", NULL); + for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--) + *(dir->dir) = 0; /* force a re-read */ + SFupdatePath(); + } +} + + +static Widget +SFcreateWidgets(Widget parent, const char *title, const char *prompt, const char *ok, const char *cancel) +{ + Cardinal i, n; + int listWidth, listHeight; + int listSpacing = 10; + int scrollThickness = 15; + int hScrollX, hScrollY; + int vScrollX, vScrollY; + Cursor xtermCursor, sbRightArrowCursor, arrowCursor; + Arg arglist[20]; + Widget paned, box; + + i = 0; + XtSetArg(arglist[i], XtNtransientFor, parent); + XtSetArg(arglist[i], XtNtitle, title); + i++; + + selFile = XtAppCreateShell("xdviSelFile", "XdviSelFile", + transientShellWidgetClass, SFdisplay, + arglist, i); + + /* Add WM_DELETE_WINDOW protocol */ + XtAppAddActions(XtWidgetToApplicationContext(selFile), + sf_actions, XtNumber(sf_actions)); + XtOverrideTranslations(selFile, + XtParseTranslationTable(wmDeleteWindowTranslation)); + + paned = XtVaCreateManagedWidget("paned", panedWidgetClass, selFile, NULL); + i = 0; + XtSetArg(arglist[i], XtNdefaultDistance, 6); + i++; + selFileForm = XtCreateManagedWidget("selFileForm", + formWidgetClass, paned, arglist, i); + + i = 0; + XtSetArg(arglist[i], XtNlabel, prompt); + i++; + XtSetArg(arglist[i], XtNresizable, True); + i++; + XtSetArg(arglist[i], XtNtop, XtChainTop); + i++; + XtSetArg(arglist[i], XtNbottom, XtChainTop); + i++; + XtSetArg(arglist[i], XtNleft, XtChainLeft); + i++; + XtSetArg(arglist[i], XtNright, XtChainLeft); + i++; + XtSetArg(arglist[i], XtNborderWidth, 0); + i++; + XtSetArg(arglist[i], XtNvertDistance, 20); + i++; + selFilePrompt = XtCreateManagedWidget("selFilePrompt", + labelWidgetClass, selFileForm, + arglist, i); + +#if 1 + i = 0; + XtSetArg(arglist[i], XtNforeground, &SFfore); + i++; + XtSetArg(arglist[i], XtNbackground, &SFback); + i++; + XtGetValues(selFilePrompt, arglist, i); +#endif + + SFinitFont(); + + SFentryWidth = SFbesideText + SFcharsPerEntry * SFcharWidth + SFbesideText; + SFentryHeight = SFaboveAndBelowText + SFcharHeight + SFaboveAndBelowText; + + listWidth = SFlineToTextH + SFentryWidth + SFlineToTextH + 1 + + scrollThickness; + listHeight = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 + + SFlineToTextV + SFlistSize * SFentryHeight + + SFlineToTextV + 1 + scrollThickness; + + SFpathScrollWidth = 3 * listWidth + 2 * listSpacing + 4; + + hScrollX = -1; + hScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 + + SFlineToTextV + SFlistSize * SFentryHeight + SFlineToTextV; + SFhScrollWidth = SFlineToTextH + SFentryWidth + SFlineToTextH; + + vScrollX = SFlineToTextH + SFentryWidth + SFlineToTextH; + vScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV; + SFvScrollHeight = SFlineToTextV + SFlistSize * SFentryHeight + + SFlineToTextV; + + SFupperX = SFlineToTextH + SFentryWidth + SFlineToTextH - 1; + SFlowerY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 + + SFlineToTextV; + SFupperY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 + + SFlineToTextV + SFlistSize * SFentryHeight - 1; + + SFtextX = SFlineToTextH + SFbesideText; + SFtextYoffset = SFlowerY + SFaboveAndBelowText + SFcharAscent; + + SFsegs[0].x1 = 0; + SFsegs[0].y1 = vScrollY; + SFsegs[0].x2 = vScrollX - 1; + SFsegs[0].y2 = vScrollY; + SFsegs[1].x1 = vScrollX; + SFsegs[1].y1 = 0; + SFsegs[1].x2 = vScrollX; + SFsegs[1].y2 = vScrollY - 1; + + SFcompletionSegs[0].x1 = SFcompletionSegs[0].x2 = SFlineToTextH; + SFcompletionSegs[1].x1 = SFcompletionSegs[1].x2 = + SFlineToTextH + SFentryWidth - 1; + + i = 0; +/* XtSetArg(arglist[i], XtNwidth, 3 * listWidth + 2 * listSpacing + 4 - w - 10); */ + XtSetArg(arglist[i], XtNwidth, 3 * listWidth + 2 * listSpacing + 4); + i++; +/* XtSetArg(arglist[i], XtNborderColor, SFfore); */ +/* i++; */ + +/* XtSetArg(arglist[i], XtNfromHoriz, selFilePrompt); */ +/* i++; */ +/* XtSetArg(arglist[i], XtNhorizDistance, 10); */ + XtSetArg(arglist[i], XtNfromVert, selFilePrompt); + i++; +/* XtSetArg(arglist[i], XtNvertDistance, 0); */ +/* i++; */ + XtSetArg(arglist[i], XtNresizable, True); + i++; + XtSetArg(arglist[i], XtNtop, XtChainTop); + i++; + XtSetArg(arglist[i], XtNbottom, XtChainTop); + i++; + XtSetArg(arglist[i], XtNleft, XtChainLeft); + i++; + XtSetArg(arglist[i], XtNright, XtChainLeft); + i++; + XtSetArg(arglist[i], XtNstring, SFtextBuffer); + i++; + XtSetArg(arglist[i], XtNlength, MAXPATHLEN); + i++; + XtSetArg(arglist[i], XtNeditType, XawtextEdit); + i++; + XtSetArg(arglist[i], XtNwrap, XawtextWrapWord); + i++; + XtSetArg(arglist[i], XtNresize, XawtextResizeHeight); + i++; + XtSetArg(arglist[i], XtNuseStringInPlace, True); + i++; + XtSetArg(arglist[i], XtNvertDistance, 5); + i++; + selFileField = XtCreateManagedWidget("selFileField", + asciiTextWidgetClass, selFileForm, + arglist, i); + + XtOverrideTranslations(selFileField, + XtParseTranslationTable + (oneLineTextEditTranslations)); +/* XtSetKeyboardFocus(selFileForm, selFileField); + need focus for selFileMask widget to set the filter */ + + i = 0; + XtSetArg(arglist[i], XtNorientation, XtorientHorizontal); + i++; + XtSetArg(arglist[i], XtNwidth, SFpathScrollWidth); + i++; + XtSetArg(arglist[i], XtNheight, scrollThickness); + i++; +/* XtSetArg(arglist[i], XtNborderColor, SFfore); */ +/* i++; */ + XtSetArg(arglist[i], XtNfromVert, selFileField); + i++; + XtSetArg(arglist[i], XtNvertDistance, 30); + i++; + XtSetArg(arglist[i], XtNtop, XtChainTop); + i++; + XtSetArg(arglist[i], XtNbottom, XtChainTop); + i++; + XtSetArg(arglist[i], XtNleft, XtChainLeft); + i++; + XtSetArg(arglist[i], XtNright, XtChainLeft); + i++; + selFileHScroll = XtCreateManagedWidget("selFileHScroll", + scrollbarWidgetClass, selFileForm, + arglist, i); + + XtAddCallback(selFileHScroll, XtNjumpProc, + (XtCallbackProc)SFpathSliderMovedCallback, (XtPointer) NULL); + XtAddCallback(selFileHScroll, XtNscrollProc, + (XtCallbackProc)SFpathAreaSelectedCallback, (XtPointer) NULL); + + i = 0; + XtSetArg(arglist[i], XtNwidth, listWidth); + i++; + XtSetArg(arglist[i], XtNheight, listHeight); + i++; +/* XtSetArg(arglist[i], XtNborderColor, SFfore); */ +/* i++; */ + XtSetArg(arglist[i], XtNfromVert, selFileHScroll); + i++; + XtSetArg(arglist[i], XtNvertDistance, 10); + i++; + XtSetArg(arglist[i], XtNtop, XtChainTop); + i++; + XtSetArg(arglist[i], XtNbottom, XtChainTop); + i++; + XtSetArg(arglist[i], XtNleft, XtChainLeft); + i++; + XtSetArg(arglist[i], XtNright, XtChainLeft); + i++; + selFileLists[0] = XtCreateManagedWidget("selFileList1", + compositeWidgetClass, selFileForm, + arglist, i); + + i = 0; + XtSetArg(arglist[i], XtNwidth, listWidth); + i++; + XtSetArg(arglist[i], XtNheight, listHeight); + i++; +/* XtSetArg(arglist[i], XtNborderColor, SFfore); */ +/* i++; */ + XtSetArg(arglist[i], XtNfromHoriz, selFileLists[0]); + i++; + XtSetArg(arglist[i], XtNfromVert, selFileHScroll); + i++; + XtSetArg(arglist[i], XtNhorizDistance, listSpacing); + i++; + XtSetArg(arglist[i], XtNvertDistance, 10); + i++; + XtSetArg(arglist[i], XtNtop, XtChainTop); + i++; + XtSetArg(arglist[i], XtNbottom, XtChainTop); + i++; + XtSetArg(arglist[i], XtNleft, XtChainLeft); + i++; + XtSetArg(arglist[i], XtNright, XtChainLeft); + i++; + selFileLists[1] = XtCreateManagedWidget("selFileList2", + compositeWidgetClass, selFileForm, + arglist, i); + + i = 0; + XtSetArg(arglist[i], XtNwidth, listWidth); + i++; + XtSetArg(arglist[i], XtNheight, listHeight); + i++; +/* XtSetArg(arglist[i], XtNborderColor, SFfore); */ +/* i++; */ + XtSetArg(arglist[i], XtNfromHoriz, selFileLists[1]); + i++; + XtSetArg(arglist[i], XtNfromVert, selFileHScroll); + i++; + XtSetArg(arglist[i], XtNhorizDistance, listSpacing); + i++; + XtSetArg(arglist[i], XtNvertDistance, 10); + i++; + XtSetArg(arglist[i], XtNtop, XtChainTop); + i++; + XtSetArg(arglist[i], XtNbottom, XtChainTop); + i++; + XtSetArg(arglist[i], XtNleft, XtChainLeft); + i++; + XtSetArg(arglist[i], XtNright, XtChainLeft); + i++; + selFileLists[2] = XtCreateManagedWidget("selFileList3", + compositeWidgetClass, selFileForm, + arglist, i); + + for (n = 0; n < 3; n++) { + + i = 0; + XtSetArg(arglist[i], XtNx, vScrollX); + i++; + XtSetArg(arglist[i], XtNy, vScrollY); + i++; + XtSetArg(arglist[i], XtNwidth, scrollThickness); + i++; + XtSetArg(arglist[i], XtNheight, SFvScrollHeight); + i++; +/* XtSetArg(arglist[i], XtNborderColor, SFfore); */ +/* i++; */ + selFileVScrolls[n] = XtCreateManagedWidget("selFileVScroll", + scrollbarWidgetClass, + selFileLists[n], arglist, i); + + XtAddCallback(selFileVScrolls[n], XtNjumpProc, + (XtCallbackProc)SFvFloatSliderMovedCallback, (XtPointer) n); + XtAddCallback(selFileVScrolls[n], XtNscrollProc, + (XtCallbackProc)SFvAreaSelectedCallback, (XtPointer) n); + + i = 0; + + XtSetArg(arglist[i], XtNorientation, XtorientHorizontal); + i++; + XtSetArg(arglist[i], XtNx, hScrollX); + i++; + XtSetArg(arglist[i], XtNy, hScrollY); + i++; + XtSetArg(arglist[i], XtNwidth, SFhScrollWidth); + i++; + XtSetArg(arglist[i], XtNheight, scrollThickness); + i++; +/* XtSetArg(arglist[i], XtNborderColor, SFfore); */ +/* i++; */ + selFileHScrolls[n] = XtCreateManagedWidget("selFileHScroll", + scrollbarWidgetClass, + selFileLists[n], arglist, i); + + XtAddCallback(selFileHScrolls[n], XtNjumpProc, + (XtCallbackProc)SFhSliderMovedCallback, (XtPointer) n); + XtAddCallback(selFileHScrolls[n], XtNscrollProc, + (XtCallbackProc)SFhAreaSelectedCallback, (XtPointer) n); + } + + /* Do the file filter stuff. */ + selFileLabel = XtVaCreateManagedWidget("selFileLabel", + labelWidgetClass, selFileForm, + XtNfromVert, selFileLists[0], + XtNvertDistance, 30, +/* XtNfromHoriz, selFileCancel, */ +/* XtNhorizDistance, 60, */ + XtNlabel, "File Mask:", + XtNborderWidth, 0, + XtNtop, XtChainTop, + XtNbottom, XtChainTop, NULL); + + selFileMask = XtVaCreateManagedWidget("selFileMask", + asciiTextWidgetClass, selFileForm, + XtNwidth, MASKWIDTH / 2 * SFcharWidth, + XtNfromVert, selFileLists[0], + XtNvertDistance, 30, + XtNfromHoriz, selFileLabel, + XtNhorizDistance, 0, + XtNtop, XtChainTop, + XtNbottom, XtChainTop, + XtNstring, fileMask, + XtNlength, MASKWIDTH, + XtNeditType, XawtextEdit, + XtNwrap, XawtextWrapNever, + XtNuseStringInPlace, True, NULL); + + for (i = 0; i < 3; i++) + XtSetKeyboardFocus(selFileLists[i], selFileField); + + XtOverrideTranslations(selFileMask, + XtParseTranslationTable + (oneLineTextEditTranslations)); + + XtAddEventHandler(selFileMask, KeyPressMask, False, + (XtEventHandler)maskChanged, (XtPointer) NULL); + + selFileLabel = XtVaCreateManagedWidget("selFileLabel", + labelWidgetClass, selFileForm, + XtNfromVert, selFileLists[0], + XtNvertDistance, 30, + XtNfromHoriz, selFileMask, + XtNhorizDistance, 40, + XtNlabel, "Dot files are:", + XtNborderWidth, 0, + XtNtop, XtChainTop, + XtNbottom, XtChainTop, NULL); + + selFileHide = XtVaCreateManagedWidget("selFileHide", + commandWidgetClass, selFileForm, +/* XtNwidth, 7 * SFcharWidth, */ + XtNfromVert, selFileLists[0], + XtNvertDistance, 30, + XtNfromHoriz, selFileLabel, + XtNhorizDistance, 2, + XtNlabel, hideFlag ? "hidden" : "shown", +/* XtNborderWidth, 1, */ + XtNtop, XtChainTop, +/* XtNjustify, XtJustifyLeft, */ + XtNbottom, XtChainTop, + NULL); + XtAddCallback(selFileHide, XtNcallback, (XtCallbackProc)hideFiles, NULL); + + box = XtVaCreateManagedWidget("box", formWidgetClass, + paned, + XtNshowGrip, False, + XtNdefaultDistance, 6, + XtNskipAdjust, True, + XtNaccelerators, G_accels_cr, + NULL); + selFileOK = XtVaCreateManagedWidget("selFileOK", commandWidgetClass, + box, + XtNlabel, ok, + XtNcallback, SFokSelect, + XtNtop, XtChainTop, + XtNbottom, XtChainBottom, + XtNleft, XtChainLeft, + XtNright, XtChainLeft, + NULL); + selFileCancel = XtVaCreateManagedWidget("selFileCancel", commandWidgetClass, + box, + XtNlabel, cancel, + XtNcallback, SFcancelSelect, +/* XtNborderColor, SFfore, */ + XtNfromHoriz, selFileOK, + XtNbottom, XtChainBottom, + XtNleft, XtChainRight, + XtNright, XtChainRight, + NULL); + + XtSetMappedWhenManaged(selFile, False); + XtRealizeWidget(selFile); + + /* Add WM_DELETE_WINDOW protocol */ + SFwmDeleteWindow = XInternAtom(SFdisplay, "WM_DELETE_WINDOW", False); + XSetWMProtocols(SFdisplay, XtWindow(selFile), &SFwmDeleteWindow, 1); + + SFcreateGC(); + + xtermCursor = XCreateFontCursor(SFdisplay, XC_xterm); + + sbRightArrowCursor = XCreateFontCursor(SFdisplay, XC_sb_right_arrow); + arrowCursor = XCreateFontCursor(SFdisplay, XC_left_ptr); + + XDefineCursor(SFdisplay, XtWindow(selFileForm), arrowCursor); + XDefineCursor(SFdisplay, XtWindow(selFileField), xtermCursor); + + for (n = 0; n < 3; n++) { + XDefineCursor(SFdisplay, XtWindow(selFileLists[n]), sbRightArrowCursor); + } + XDefineCursor(SFdisplay, XtWindow(selFileOK), arrowCursor); + XDefineCursor(SFdisplay, XtWindow(selFileCancel), arrowCursor); + + for (n = 0; n < 3; n++) { + XtAddEventHandler(selFileLists[n], ExposureMask, True, + (XtEventHandler)SFexposeList, (XtPointer) n); + XtAddEventHandler(selFileLists[n], EnterWindowMask, False, + (XtEventHandler)SFenterList, (XtPointer) n); + XtAddEventHandler(selFileLists[n], LeaveWindowMask, False, + (XtEventHandler)SFleaveList, (XtPointer) n); + XtAddEventHandler(selFileLists[n], PointerMotionMask, False, + (XtEventHandler)SFmotionList, (XtPointer) n); + XtAddEventHandler(selFileLists[n], ButtonPressMask, False, + (XtEventHandler)SFbuttonPressList, (XtPointer) n); + XtAddEventHandler(selFileLists[n], ButtonReleaseMask, False, + (XtEventHandler)SFbuttonReleaseList, (XtPointer) n); + } + + XtAddEventHandler(selFileField, KeyPressMask, False, + (XtEventHandler)SFmodVerifyCallback, (XtPointer) NULL); + + SFapp = XtWidgetToApplicationContext(selFile); + return selFile; +} + +/* position widget under the cursor */ +void +SFpositionWidget(Widget w) +{ + Arg args[3]; + Cardinal num_args; + Dimension width, height, b_width; + int x, y, max_x, max_y; + Window root, child; + int dummyx, dummyy; + unsigned int dummymask; + + XQueryPointer(XtDisplay(w), XtWindow(w), &root, &child, &x, &y, + &dummyx, &dummyy, &dummymask); + num_args = 0; + XtSetArg(args[num_args], XtNwidth, &width); + num_args++; + XtSetArg(args[num_args], XtNheight, &height); + num_args++; + XtSetArg(args[num_args], XtNborderWidth, &b_width); + num_args++; + XtGetValues(w, args, num_args); + + width += 2 * b_width; + height += 2 * b_width; + + x -= ((Position) width / 2); + if (x < 0) + x = 0; + if (x > (max_x = (Position) (XtScreen(w)->width - width))) + x = max_x; + + y -= ((Position) height / 2); + if (y < 0) + y = 0; + if (y > (max_y = (Position) (XtScreen(w)->height - height))) + y = max_y; + + num_args = 0; + XtSetArg(args[num_args], XtNx, x); + num_args++; + XtSetArg(args[num_args], XtNy, y); + num_args++; + XtSetValues(w, args, num_args); +} + + +FILE * +SFopenFile(const char *name, const char *mode, const char *prompt, const char *failed) +{ + Arg args[1]; + FILE *fp; + + UNUSED(args); + UNUSED(prompt); + UNUSED(failed); + + SFchdir(SFstartDir); + errno = 0; + if (!name || *name == 0 || (fp = XFOPEN(name, mode)) == NULL) { + XBell(DISP, 0); + return NULL; + } + return fp; +} + +void +SFtextChanged(void) +{ + if ((SFtextBuffer[0] == '/') || (SFtextBuffer[0] == '~')) { + (void)strcpy(SFcurrentPath, SFtextBuffer); + + SFtextPos = XawTextGetInsertionPoint(selFileField); + } + else { + (void)strcat(strcpy(SFcurrentPath, SFstartDir), SFtextBuffer); + + SFtextPos = XawTextGetInsertionPoint(selFileField) + strlen(SFstartDir); + } + + if (!SFworkProcAdded) { + (void)XtAppAddWorkProc(SFapp, (XtWorkProc)SFworkProc, NULL); + SFworkProcAdded = 1; + } + + SFupdatePath(); +} + +static void +SFprepareToReturn(void) +{ + SFstatus = SEL_FILE_NULL; + /* XtRemoveGrab(selFile); */ + XtUnmapWidget(selFile); + + if (SFdirModTimerId) { + XtRemoveTimeOut(SFdirModTimerId); + SFdirModTimerId = 0; + } + if (SFchdir(SFstartDir)) { + XtAppError(SFapp, "XsraSelFile: can't return to current directory"); + } +} + +void +XsraSelFile(Widget parent, struct filesel_callback *callback) +/* const char *title, const char *prompt, */ +/* const char *ok, const char *cancel, */ +/* const char *init_path, */ +/* const char *mask, */ +/* Boolean must_exist, */ +/* Widget *ret_widget */ +{ + static Boolean firstTime = True; + Widget w; + Cardinal i; + Arg arglist[20]; + XEvent event; + + if (!callback->prompt) { + callback->prompt = "Pathname:"; + } + + if (!callback->title) { + callback->title = "Xdvi: select filename"; + } + + if (!callback->ok) { + callback->ok = "OK"; + } + + if (!callback->cancel) { + callback->cancel = "Cancel"; + } + + if (firstTime) { + firstTime = False; + SFdisplay = XtDisplay(parent); + w = SFcreateWidgets(parent, callback->title, callback->prompt, callback->ok, callback->cancel); + } + else { + i = 0; + XtSetArg(arglist[i], XtNlabel, callback->prompt); + i++; + XtSetValues(selFilePrompt, arglist, i); + + i = 0; + XtSetArg(arglist[i], XtNlabel, callback->ok); + i++; + XtSetValues(selFileOK, arglist, i); + + i = 0; + XtSetArg(arglist[i], XtNlabel, callback->cancel); + i++; + XtSetValues(selFileCancel, arglist, i); + } + + SFpositionWidget(selFile); + XtMapWidget(selFile); + + { + char *cwd = xgetcwd(); + strcpy(SFstartDir, cwd); + free(cwd); + } + if (SFstartDir[0] == 0) { + XtAppError(SFapp, "XsraSelFile: can't get current directory"); + } + (void)strcat(SFstartDir, "/"); + (void)strcpy(SFcurrentDir, SFstartDir); + + if (callback->init_path) { + if (callback->init_path[0] == '/') { + (void)strcpy(SFcurrentPath, callback->init_path); + if (strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir))) { + SFsetText(SFcurrentPath); + } + else { + SFsetText(&(SFcurrentPath[strlen(SFstartDir)])); + } + } + else { + (void)strcat(strcpy(SFcurrentPath, SFstartDir), callback->init_path); + SFsetText(&(SFcurrentPath[strlen(SFstartDir)])); + } + } + else { + SFsetText(SFcurrentDir); + (void)strcpy(SFcurrentPath, SFstartDir); + } + + SFfunc = showEntry; + + SFtextChanged(); + + /* don't grab the pointer so that warning popups still work */ + /* XtAddGrab(selFile, True, True); */ + + SFdirModTimerId = XtAppAddTimeOut(SFapp, 1200UL, + SFdirModTimer, (XtPointer) NULL); + + if (strcmp(fileMask, callback->filemask) != 0) { /* if mask changed */ + SFDir *dir; + strncpy(fileMask, callback->filemask, MASKWIDTH); + XtVaSetValues(selFileMask, XtNstring, fileMask, NULL); + for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--) + *(dir->dir) = 0; /* force a re-read */ + SFupdatePath(); + } + + while (1) { + XtAppNextEvent(SFapp, &event); + switch (event.type) { + Widget w; +#if 0 /* don't do this, it may send the X server into a busy loop if the File selector + is positioned over a window that is `on top' by default */ + case Expose: + if (!raise_message_windows()) + raise_file_selector(); + break; +#endif + case KeyPress: + case ButtonPress: + /* beep if keypress was inside main window */ + w = XtWindowToWidget(DISP, event.xany.window); + while ((w != NULL) && (w != selFile)) { + /* exception: message windows */ + if (is_message_window(w)) + break; + w = XtParent(w); + } + if (w == NULL || w == globals.widgets.top_level) { + XBell(DISP, 0); + } + break; + } + + XtDispatchEvent(&event); + + switch (SFstatus) { + case SEL_FILE_TEXT: + SFstatus = SEL_FILE_NULL; + SFtextChanged(); + break; + case SEL_FILE_OK: + if (callback->must_exist) { + FILE *tmp_fp = XFOPEN(SFtextBuffer, "r"); + dviErrFlagT errflag = NO_ERROR; + if (tmp_fp == NULL) { + popup_message(selFile, + MSG_ERR, NULL, "Could not open %s: %s.\n", + SFtextBuffer, strerror(errno)); + SFstatus = SEL_FILE_NULL; + break; + } + else if (!process_preamble(tmp_fp, &errflag) + || !find_postamble(tmp_fp, &errflag) + || !read_postamble(tmp_fp, &errflag, False)) { + popup_message(selFile, + MSG_ERR, NULL, "Error opening %s:\n%s.", + SFtextBuffer, get_dvi_error(errflag)); + fclose(tmp_fp); + SFstatus = SEL_FILE_NULL; + break; + } + else { /* file is OK */ + fclose(tmp_fp); + SFprepareToReturn(); + callback->func_ptr(SFtextBuffer, callback->data); + return; +/* return xstrdup(SFtextBuffer); */ + } + } + else { + SFprepareToReturn(); + callback->func_ptr(SFtextBuffer, callback->data); + return; +/* return xstrdup(SFtextBuffer); */ + } + case SEL_FILE_CANCEL: + SFprepareToReturn(); + if (callback->exit_on_cancel) + exit(0); + return; +/* return NULL; */ + case SEL_FILE_NULL: + break; + } + } +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* !defined(MOTIF) */ diff --git a/Build/source/texk/xdvik/gui/sfSelFile.h b/Build/source/texk/xdvik/gui/sfSelFile.h new file mode 100644 index 00000000000..298d15ea45a --- /dev/null +++ b/Build/source/texk/xdvik/gui/sfSelFile.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2002-2004 the xdvik development team + * + * 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. + * + */ + +#ifndef SFSELFILE_H_ +#define SFSELFILE_H_ + +struct filesel_callback { + const char *title; + const char *prompt; + const char *ok; + const char *cancel; + const char *init_path; + const char *filemask; + Boolean must_exist; + Boolean exit_on_cancel; + void (*func_ptr)(const char *filename, void *data); + void *data; +}; + +extern void SFpositionWidget(Widget w); +extern FILE *SFopenFile(const char *name, const char *mode, const char *prompt, const char *failed); +extern void SFtextChanged(void); +extern void XsraSelFile(Widget parent, struct filesel_callback *callback); +void raise_file_selector(void); + +#endif /* SF_SEL_FILE_H_ */ diff --git a/Build/source/texk/xdvik/gui/sfinternal.h b/Build/source/texk/xdvik/gui/sfinternal.h new file mode 100644 index 00000000000..c776bfc6c97 --- /dev/null +++ b/Build/source/texk/xdvik/gui/sfinternal.h @@ -0,0 +1,59 @@ +/* + * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Software Research Associates not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Software Research Associates + * makes no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Author: Erik M. van der Poel + * Software Research Associates, Inc., Tokyo, Japan + * erik@sra.co.jp + */ + +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Xos.h> +#include <X11/Xaw/Text.h> +#include <X11/Xaw/AsciiText.h> +#undef wchar_t + +#define SEL_FILE_CANCEL -1 +#define SEL_FILE_OK 0 +#define SEL_FILE_NULL 1 +#define SEL_FILE_TEXT 2 + +#define SF_DO_SCROLL 1 +#define SF_DO_NOT_SCROLL 0 + + +extern void + SFenterList(), + SFleaveList(), + SFmotionList(), + SFbuttonPressList(), + SFbuttonReleaseList(); + +extern void + SFvSliderMovedCallback(), + SFvFloatSliderMovedCallback(), + SFhSliderMovedCallback(), + SFpathSliderMovedCallback(), + SFvAreaSelectedCallback(), + SFhAreaSelectedCallback(), + SFpathAreaSelectedCallback(); + + diff --git a/Build/source/texk/xdvik/gui/statusline.c b/Build/source/texk/xdvik/gui/statusline.c new file mode 100644 index 00000000000..16715283cf4 --- /dev/null +++ b/Build/source/texk/xdvik/gui/statusline.c @@ -0,0 +1,531 @@ +/*------------------------------------------------------------ + statusline for the xdvi(k) previewer + + written by S. Ulrich (ulrich@cis.uni-muenchen.de) 2000/02/25 + + 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 "version.h" +#include "statusline.h" +#include "xm_menu.h" +#include "x_util.h" +#include "pagehist.h" +#include "util.h" + +#include "kpathsea/c-vararg.h" +#include "my-vsnprintf.h" + +#include <ctype.h> +#include <X11/Xatom.h> +#include <X11/StringDefs.h> + +# ifdef MOTIF +# include <Xm/Label.h> +# include <Xm/Frame.h> +# include <Xm/Text.h> +# include <Xm/TextF.h> +# else +# include <X11/Xaw/Viewport.h> +# include <X11/Xaw/Label.h> +# endif + + +Widget statusline; + +static Boolean initialized = False; + +/* + * only print MAX_LEN characters to the statusline + * (it's only 1 line after all) + */ +#define MAX_LEN 512 + +/* for saving the statusline string if the statusline is + * destroyed and recreated + */ +static char g_string_savebuf[MAX_LEN + 2]; + +int global_statusline_h = 20; + +#if MOTIF +static void +handle_statusline_event(Widget w, XtPointer client_data, + XEvent *ev, Boolean *cont) +{ +/* const char *text = (const char *)client_data; */ + UNUSED(w); + UNUSED(client_data); + UNUSED(cont); + +/* fprintf(stderr, "text: |%s|; event: %p\n", text, ev); */ + /* only used to do this if page history was already active, but it's probably + nicer to be able to get the history by clicking on the statusline ... + */ + if (/* strncmp(text, "Page history:", sizeof "Page history:" - 1) == 0 && */ ev != NULL) { + XmTextPosition pos = XmTextGetCursorPosition(statusline); + char *ptr1, *ptr2; + int diff = 0; +/* fprintf(stderr, "pos: %d\n", pos); */ + if (pos == 0) { /* just display the page history */ + page_history_move(0); + return; + } + ptr1 = g_string_savebuf + pos; + ptr2 = strchr(g_string_savebuf, '['); + if (ptr2 == NULL) { /* some other string, also display the page history */ + page_history_move(0); + return; + } +/* fprintf(stderr, "ptr1: |%s|; ptr2: |%s|\n", ptr1, ptr2); */ + while (ptr1 < ptr2) { + if (*ptr1 == ' ' && *(ptr1 + 1) != '-') /* separator */ + diff--; + ptr1++; + } + + while (ptr1 > ptr2) { + if (*ptr1 == ' ' && *(ptr1 - 1) != '-') /* separator */ + diff++; + ptr1--; + } +/* fprintf(stderr, "diff: %d\n", diff); */ + page_history_move(diff); + } +} +#endif /* MOTIF */ + +/* + * Create the statusline widget. To be called at the beginning + * of the program, and when expert mode is switched off. + * + * Side effects: + * sets <global_statusline_h> to the height of the statusline in pixels. + */ + +Widget +create_statusline( +#ifdef MOTIF + Widget parent +#else + void +#endif + ) +{ +#ifndef MOTIF + Position vport_h; + Position clip_x; + Position clip_w; + static Position my_h = 0; +#endif + + /* + * FIXME: is there a better way to set the y position depending on + * the height of the widget? + * It doesn't work to change the value of XtNy *after* creating + * the widget! + */ + + if (!initialized) { +#ifndef MOTIF + /* + * determine height of statusline (depending on the font used). + * This is not changeable at runtime, so it's determined once and + * for all at program start. + */ + statusline = XtVaCreateWidget("statusline", labelWidgetClass, globals.widgets.vport_widget, + XtNlabel, (XtArgVal) "test", + NULL); + XtVaGetValues(statusline, XtNheight, &my_h, NULL); + global_statusline_h = my_h; + XtDestroyWidget(statusline); +#endif + initialized = True; + /* initialize g_string_savebuf */ + sprintf(g_string_savebuf, "This is xdvik %s", XDVI_TERSE_VERSION_INFO); + } +#ifndef MOTIF + /* determine position and width of statusline */ + XtVaGetValues(globals.widgets.clip_widget, XtNx, &clip_x, XtNwidth, &clip_w, NULL); + XtVaGetValues(globals.widgets.vport_widget, XtNheight, &vport_h, NULL); + if (vport_h - my_h <= 0) { + XDVI_FATAL((stderr, "Window height too small for statusline (minimum value: %d).", my_h)); + return NULL; + } + statusline = XtVaCreateManagedWidget("statusline", + labelWidgetClass, globals.widgets.vport_widget, + XtNlabel, (XtArgVal) g_string_savebuf, + XtNwidth, clip_w, + XtNx, clip_x - 1, /* so that left border becomes invisible */ + XtNy, vport_h - my_h, + XtNjustify, XtJustifyLeft, + /* same as for the buttons line */ + XtNborder, (XtArgVal) resource.fore_Pixel, + NULL); +#else + statusline = XtVaCreateManagedWidget("statusline", + xmTextFieldWidgetClass, parent, + XmNalignment, XmALIGNMENT_END, + XmNdepth, (XtArgVal) G_depth, + XmNbottomAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNleftOffset, 1, + XmNrightOffset, 1, + XmNbottomOffset, 1, + XmNtopOffset, 0, + XmNcursorPositionVisible, False, + XmNautoShowCursorPosition, False, + XmNmarginWidth, 4, + XmNmarginHeight, 1, + XmNeditable, False, + XmNtraversalOn, False, + XmNvalue, g_string_savebuf, + NULL); + + /* Block processing of most interactive events on this widget, except + * for button events that should navigate the page history. + */ + XtInsertEventHandler(statusline, + KeyPressMask | KeyReleaseMask | + PointerMotionMask| PointerMotionHintMask | + ButtonMotionMask | +#if !MOTIF + ButtonPressMask | ButtonReleaseMask | +#endif + FocusChangeMask, + /* ButtonPressMask | ButtonReleaseMask | */ + /* PointerMotionMask| PointerMotionHintMask | */ + /* ButtonMotionMask | */ + True, block_event_callback, + (XtPointer)0, 0); +#if MOTIF + XtInsertEventHandler(statusline, + /* suboptimal, but needs to be release not press + * since we want to query the current cursor position, + * and that may not be set yet in the press event(?). + */ + ButtonReleaseMask, + True, handle_statusline_event, + (XtPointer)g_string_savebuf, XtListTail); + +#endif /* MOTIF */ + +#endif + + return statusline; +} + + +void +toggle_statusline(void) +{ +#ifdef MOTIF + if ((resource.expert_mode & XPRT_SHOW_STATUSLINE) == 0) + XtUnmanageChild(statusline); + else + XtManageChild(statusline); + +#if defined(NEW_MENU_CREATION) || defined(MOTIF) + set_menu(&resource.expert_mode, Act_set_expert_mode, check_resource_expert); +#else + set_show_statusline_option(); +#endif +#else + static Boolean initialized = False; + static Boolean statusline_mapped = False; + + Boolean make_statusline_visible = False; + Boolean make_statusline_invisible = False; + + if (!initialized) { + statusline_mapped = (resource.expert_mode & XPRT_SHOW_STATUSLINE) != 0; + initialized = True; + } + + if ((resource.expert_mode & XPRT_SHOW_STATUSLINE) == 0) { + if (statusline_mapped) + make_statusline_invisible = True; + } + else { + if (!statusline_mapped) + make_statusline_visible = True; + } + + if (make_statusline_invisible) { + XtDestroyWidget(statusline); + statusline_mapped = False; + } + if (make_statusline_visible) { + static Dimension window_w, window_h; + + static Arg arg_wh[] = { + {XtNwidth, (XtArgVal) &window_w}, + {XtNheight, (XtArgVal) &window_h}, + }; +#ifdef MOTIF + XtGetValues(globals.widgets.main_window, arg_wh, XtNumber(arg_wh)); +#else + XtGetValues(globals.widgets.vport_widget, arg_wh, XtNumber(arg_wh)); +#endif + XtVaSetValues(globals.widgets.vport_widget, XtNresizable, (XtArgVal)True, NULL); + TRACE_GUI((stderr, "statusline: w %d, h %d", window_w, window_h)); + XtVaSetValues(globals.widgets.vport_widget, XtNwidth, (XtArgVal)window_w, XtNheight, (XtArgVal)window_h, NULL); + TRACE_GUI((stderr, "after statusline")); + create_statusline(); + statusline_mapped = True; + } +#endif /* MOTIF */ +} + + +/*------------------------------------------------------------ + * handle_statusline_resize + * + * Arguments: + * void + * + * Returns: + * void + * + * Purpose: + * Resize the statusline when the total window size changes. + * + *------------------------------------------------------------*/ + +void +handle_statusline_resize(void) +{ +#ifndef MOTIF + if ((resource.expert_mode & XPRT_SHOW_STATUSLINE) == 0) { + return; + } + + if (!statusline) + return; + + /* apparently the x,y values of a widget can only be set at creation time, so + * the following won't work: + */ + +#if 0 + /* + BROKEN Position vport_h, clip_x, clip_w; + BROKEN static Position my_h = 0; + BROKEN + BROKEN XtVaGetValues(globals.widgets.clip_widget, + BROKEN XtNx, &clip_x, + BROKEN XtNwidth, &clip_w, + BROKEN NULL); + BROKEN XtVaGetValues(globals.widgets.vport_widget, + BROKEN XtNheight, &vport_h, + BROKEN NULL); + BROKEN + BROKEN XtUnmanageChild(statusline); + BROKEN XtVaSetValues(statusline, + BROKEN XtNlabel, (XtArgVal) "", + BROKEN XtNwidth, clip_w, + BROKEN XtNx, clip_x - 1, + BROKEN XtNy, vport_h - my_h, + BROKEN XtNborderWidth, 1, + BROKEN XtNjustify, XtJustifyLeft, + BROKEN XtNborder, (XtArgVal) resource.fore_Pixel, + BROKEN NULL); + BROKEN XtManageChild(statusline); + BROKEN XFlush(DISP); + */ +#endif + + /* only this will: */ + XtDestroyWidget(statusline); + create_statusline(); +#endif +} + + + + +/* + * clear statusline by printing an empty message to it. + */ + + +static void +clear_statusline(void) +{ + if ((resource.expert_mode & XPRT_SHOW_STATUSLINE) != 0) { +# ifdef MOTIF + XmTextFieldSetString(statusline, " "); +# else + XtVaSetValues(statusline, XtNlabel, " ", NULL); +# endif + XFlush(DISP); + } + strcpy(g_string_savebuf, " "); +} + + +/* force a statusline update, no matter how busy the application is. + Use this with care (only for important messages). +*/ +void +force_statusline_update(void) +{ +#ifdef MOTIF + XmUpdateDisplay(globals.widgets.top_level); +#else + XEvent event; + XSync(DISP, False); + while (XCheckMaskEvent(DISP, ExposureMask, &event)) + XtDispatchEvent(&event); +#endif /* MOTIF */ +} + + +/* + * timeout - if > 0, timeout in seconds after which the message will + * be deleted again. If < 0, message will remain (until + * another message overprints it) + * fmt - message, a C format string + * + * If expert mode is off, print <fmt> to the statusline; else, print + * <fmt> to stdout, unless the `hushstdout' option is specified. + */ +static XtIntervalId clear_timeout_id = 0; + +static void +clear_statusline_timer_proc(XtPointer client_data, XtIntervalId *id) +{ + UNUSED(client_data); + UNUSED(id); + + if (clear_timeout_id) { + clear_statusline(); + clear_timeout_id = 0; + } +} + +static void +internal_print_statusline(statusTimerT timeout, const char *old_content, const char *fmt, va_list argp) +{ + if (!XtIsRealized(globals.widgets.top_level) || !initialized || (resource.expert_mode & XPRT_SHOW_STATUSLINE) == 0) { + if (!resource.hush_stdout && strlen(fmt) > 0) { /* check for strlen since sometimes we clear the statusline + by printing "" to it, and we don't want that on stdout */ + fprintf(stdout, "xdvi: "); + if (old_content != NULL) + (void)fputs(old_content, stdout); + (void)vfprintf(stdout, fmt, argp); + fputc('\n', stdout); + fflush(stdout); + } + } + else { + char buf[MAX_LEN + 1]; + size_t offset = 0; + + if (old_content != NULL && old_content[0] != '\0') { + offset += strlen(old_content); + strncpy(buf, old_content, MAX_LEN); + /* append separating space */ + if (strlen(old_content) < MAX_LEN - 1) { + strcat(buf, " "); + offset++; + } + } + VSNPRINTF(buf + offset, MAX_LEN - offset, fmt, argp); /* just throw away strings longer than MAX_LEN */ + buf[MAX_LEN] = '\0'; /* terminate buf */ + /* + * save current contents of statusline so that toggling the statusline + * on and off will display the same text again + */ + strcpy(g_string_savebuf, buf); +#ifdef MOTIF + XmTextFieldSetString(statusline, buf); +#else + XtVaSetValues(statusline, XtNlabel, buf, NULL); +#endif +/* fprintf(stderr, "timeout: %d, id: %ld\n", timeout, clear_timeout_id); */ + if (timeout > 0) { + timeout *= 1000; /* convert to miliseconds */ + + if (clear_timeout_id) { +/* fprintf(stderr, "clearing!\n"); */ + if (globals.debug & DBG_EVENT) + fprintf(stderr, "%s:%d: removing timeout %ld\n", __FILE__, __LINE__, clear_timeout_id); + XtRemoveTimeOut(clear_timeout_id); + } + clear_timeout_id = XtAppAddTimeOut(app, timeout, + clear_statusline_timer_proc, (XtPointer) NULL); + } + } +} + +/* + * Append the varargs-string `fmt' to the currnent contents of the statusline + * erasing it after `timeout' seconds if timeout > 0, unless the current statusline + * contents matches pattern - in that case, overwrite the contents. + */ +void +statusline_append(statusTimerT timeout, const char *pattern, const char *fmt, ...) +{ + const char *buf = NULL; + va_list argp; + + if (XtIsRealized(globals.widgets.top_level) && initialized && (resource.expert_mode & XPRT_SHOW_STATUSLINE) != 0) { + /* get current statusline contents */ +#ifdef MOTIF + XtVaGetValues(statusline, XmNvalue, &buf, NULL); +#else + XtVaGetValues(statusline, XtNlabel, &buf, NULL); +#endif + } + + while (buf != NULL && isspace((int)*buf)) /* skip spaces inserted by statusline appending */ + buf++; + va_start(argp, fmt); + + if (buf != NULL && memcmp(buf, pattern, strlen(pattern)) == 0) { + buf = NULL; + } + internal_print_statusline(timeout, buf, fmt, argp); + va_end(argp); +} + +/* + * Print the varargs-string `fmt' to the currnent contents of the statusline, + * erasing it after `timeout' seconds if timeout > 0. + */ + +void +statusline_print(statusTimerT timeout, const char *fmt, ...) +{ + va_list argp; + va_start(argp, fmt); + internal_print_statusline(timeout, NULL, fmt, argp); + va_end(argp); +} + +void +statusline_clear(void) +{ + statusline_print(STATUS_SHORT, ""); +} diff --git a/Build/source/texk/xdvik/gui/statusline.h b/Build/source/texk/xdvik/gui/statusline.h new file mode 100644 index 00000000000..5f9f5dcd10c --- /dev/null +++ b/Build/source/texk/xdvik/gui/statusline.h @@ -0,0 +1,52 @@ +#ifndef STATUSLINE_H_ +#define STATUSLINE_H_ +/* + * Copyright (c) 2001-2004 the xdvik development team + * + * 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. + * + */ + +#define STAT_BUF_LEN 512 + + +/* statusline stuff. The following function and #defines are also + * needed when compiling without statusline support. + */ +typedef enum statusTimerT_ { + STATUS_FOREVER = 0, + STATUS_SHORT = 5, + STATUS_MEDIUM = 10, + STATUS_LONG = 30 +} statusTimerT; + +extern void statusline_clear(void); +extern void statusline_print(statusTimerT timeout, const char *fmt, ...); +extern void statusline_append(statusTimerT timeout, const char *pattern, const char *fmt, ...); +#ifdef MOTIF +extern Widget create_statusline(Widget parent); +#else +extern Widget create_statusline(void); +#endif +extern void handle_statusline_resize(void); + +extern void force_statusline_update(void); +extern void toggle_statusline(void); + +#endif /* STATUSLINE_H_ */ diff --git a/Build/source/texk/xdvik/gui/topic-window.c b/Build/source/texk/xdvik/gui/topic-window.c new file mode 100644 index 00000000000..7a87a03b5c8 --- /dev/null +++ b/Build/source/texk/xdvik/gui/topic-window.c @@ -0,0 +1,650 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +/* + * `Topics' window framework used by help and preferences windows. + * Contains a list of topics on the left-hand side, and corresponding + * contents on the right-hand side. + */ + +#include "xdvi-config.h" +#include "xdvi.h" + +#if MOTIF +# include <Xm/Xm.h> +# include <Xm/Form.h> +# include <Xm/DialogS.h> +# include <Xm/PushB.h> +# include <Xm/Frame.h> +# include <Xm/PanedW.h> +# include <Xm/LabelG.h> +# include <Xm/Label.h> +# include <Xm/Protocols.h> +# include <Xm/List.h> +# include <Xm/AtomMgr.h> +#else /* MOTIF */ +# include <X11/Xaw/Paned.h> +# include <X11/Xaw/Box.h> +# include <X11/Xaw/Form.h> +# include <X11/Xaw/Cardinals.h> +# include <X11/Xaw/Command.h> +# include <X11/Xaw/List.h> +#endif /* MOTIF */ + +#include "version.h" +#include "topic-window.h" +#include "string-utils.h" +#include "util.h" +#include "x_util.h" + +#if MOTIF + +/* special formatting for headings in right window, if desired */ +const char *const TAG_NORMAL = "NORMAL"; +const char *const TAG_TOPIC_HEADING = "TOPIC_HEADING"; +const char *const TAG_TOPIC_LABEL = "TOPIC_LABEL"; + +# define SHELL_WIDGET_CLASS xmDialogShellWidgetClass +# define PANED_WIDGET_CLASS xmPanedWindowWidgetClass +# define FORM_WIDGET_CLASS xmFormWidgetClass + +# define FORM_ARGS XmNhorizontalSpacing, 10, \ + NULL +# define HELP_SHELL_ARGS XmNdeleteResponse, XmDO_NOTHING, \ + XtNmappedWhenManaged, False, \ + NULL +# define HELP_PANED_ARGS XmNsashWidth, 1, \ + XmNuserData, info, \ + XmNsashHeight, 1, \ + NULL +# define LEFT_FORM_ARGS XmNtopAttachment, XmATTACH_FORM, \ + XmNleftAttachment, XmATTACH_FORM, \ + XmNbottomAttachment, XmATTACH_FORM, \ + XmNtopOffset, 9, \ + XmNbottomOffset, 10, \ + NULL +# define RIGHT_FORM_ARGS XmNtopAttachment, XmATTACH_FORM, \ + XmNleftAttachment, XmATTACH_WIDGET, \ + XmNleftWidget, left_form, \ + XmNrightAttachment, XmATTACH_FORM, \ + XmNbottomAttachment, XmATTACH_FORM, \ + XmNbottomOffset, 10, \ + NULL +# define ACTION_AREA_ARGS NULL + +#else /* MOTIF */ + +# define SHELL_WIDGET_CLASS transientShellWidgetClass +# define PANED_WIDGET_CLASS panedWidgetClass +# define FORM_WIDGET_CLASS formWidgetClass + +# define FORM_ARGS XtNdefaultDistance, 14, \ + NULL +# define HELP_SHELL_ARGS XtNx, 60, \ + XtNy, 80, \ + XtNtransientFor, globals.widgets.top_level, \ + XtNtranslations, xlats, \ + XtNtransientFor, parent, \ + XtNallowShellResize, False, \ + NULL +# define HELP_PANED_ARGS NULL +# define LEFT_FORM_ARGS XtNtop, XtChainTop, \ + XtNbottom, XtChainBottom, \ + XtNleft, XtChainLeft, \ + XtNright, XtChainLeft, \ + XtNborderWidth, 0, \ + NULL +# define RIGHT_FORM_ARGS XtNfromHoriz, left_form, \ + XtNtop, XtChainTop, \ + XtNbottom, XtChainBottom, \ + XtNleft, XtChainLeft, \ + XtNright, XtChainRight, \ + XtNborderWidth, 0, \ + NULL +# define ACTION_AREA_ARGS XtNdefaultDistance, 6, \ + XtNshowGrip, False, \ + XtNskipAdjust, True, \ + NULL +#endif /* MOTIF */ + + +static void +ok_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *info = (struct topic_info *)client_data; + + UNUSED(w); + UNUSED(call_data); + + ASSERT(info != NULL, "No info passed to callback!"); + + if (info->ok_callback != NULL) + info->ok_callback(info); + XtPopdown(info->shell); +} + +static void +cancel_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *info = (struct topic_info *)client_data; + + UNUSED(w); + UNUSED(call_data); + + ASSERT(info != NULL, "No info passed to callback!"); + + if (info->cancel_callback != NULL) + info->cancel_callback(info); + XtPopdown(info->shell); +} + +#if MOTIF +/* + * We are overriding this since otherwise the Text widget would attempt to + * first move the invisible cursor instead of the scroll bars. + */ +/* FIXME: This gives a warning: + Warning: Actions not found: scroll-one-line-down, scroll-one-line-up +*/ +/* "<Key>osfUp: scroll-one-line-down()\n" */ +/* "<Key>osfDown: scroll-one-line-up()\n" */ +#endif + +#if MOTIF +static XmString +#else +static char * +#endif +create_label_string(const char *title, const char *subtitle) +{ +#if MOTIF + XmString label; + if (subtitle == NULL) { /* simple bold label */ + label = XmStringCreate((char *)title, (char *)TAG_TOPIC_LABEL); + } + else { /* two-part title with subheading */ + XmString s1, s2, s3, s4; + s1 = XmStringCreate((char *)title, (char *)TAG_TOPIC_HEADING); + s2 = XmStringCreate((char *)" ", (char *)TAG_NORMAL); + s3 = XmStringCreate((char *)subtitle, (char *)TAG_NORMAL); + s4 = XmStringConcat(s1, s2); + label = XmStringConcat(s4, s3); + XmStringFree(s1); + XmStringFree(s2); + XmStringFree(s3); + XmStringFree(s4); + } +#else + char *label = xstrdup(title); + if (subtitle != NULL && strlen(subtitle) > 0) { + label = xstrcat(label, " - "); + label = xstrcat(label, subtitle); + } +#endif + return label; +} + +static Widget +create_label_widget(Widget parent, const char *name, struct topic_info *info) +{ + Widget label; + +#if MOTIF + + XmString label_string = create_label_string(info->items[0].topic, info->items[0].title); + label = XtVaCreateWidget(name, xmLabelWidgetClass, parent, + XmNtopAttachment, XmATTACH_FORM, + XmNtopOffset, 14, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNlabelString, label_string, + XmNalignment, XmALIGNMENT_BEGINNING, + NULL); + XmStringFree(label_string); + +#else /* MOTIF */ + + char *label_string = create_label_string(info->items[0].topic, info->items[0].title); + label = XtVaCreateWidget(name, labelWidgetClass, parent, + XtNlabel, label_string, + XtNborderWidth, 0, + /* XtNinternalHeight, 2, */ + XtNjustify, XtJustifyLeft, + NULL); + free(label_string); + +#endif /* MOTIF */ + + return label; +} + +void +select_topic(struct topic_info *info, size_t idx) +{ +#if MOTIF + XmString label; +#else + char *label; +#endif + + /* change the heading in right window */ + label = create_label_string(info->items[idx].topic, info->items[idx].title); + XtVaSetValues(info->topic_label, +#if MOTIF + XmNlabelString, +#else + XtNlabel, +#endif + label, NULL); + +#if MOTIF + XmStringFree(label); + XmListSelectPos(info->topics_list, idx + 1, False); +#else + free(label); + XawListHighlight(info->topics_list, idx); +#endif + + if (info->curr_selected != 0) { + XtUnmanageChild(info->curr_selected); + } + else { + /* Note: doesn't matter if not managed yet */ +/* XtUnmanageChild(info->items[0].widget); */ + } + + XtManageChild(info->items[idx].widget); + info->curr_selected = info->items[idx].widget; + +#if MOTIF + XmUpdateDisplay(info->shell); +#endif +} + +static void +select_topic_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *info = (struct topic_info *)client_data; + size_t idx; + +#if MOTIF + idx = ((XmListCallbackStruct *)call_data)->item_position - 1; +#else + idx = ((XawListReturnStruct *)call_data)->list_index; +#endif + UNUSED(w); + + /* ASSERT(idx < info->items_size, "list position exceeds items_size!"); */ + + select_topic(info, idx); +} + +static Widget +create_button(Widget parent, const char *name, Boolean show_as_default, Boolean left_position) +{ + Widget button; +#if MOTIF + XmString s1 = XmStringCreateLocalized((char *)name); + button = XtVaCreateWidget(name, xmPushButtonWidgetClass, parent, + XmNlabelString, s1, + XmNdefaultButtonShadowThickness, 1, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNmarginWidth, 6, + XmNmarginHeight, 4, + XmNtopOffset, 10, + XmNbottomOffset, 10, + NULL); + if (left_position) + XtVaSetValues(button, + XmNleftAttachment, XmATTACH_FORM, + XmNleftOffset, 10, + NULL); + else + XtVaSetValues(button, + XmNrightAttachment, XmATTACH_FORM, + XmNrightOffset, 10, + NULL); + if (show_as_default) + XtVaSetValues(button, XmNshowAsDefault, True, NULL); + + XtManageChild(button); + XmStringFree(s1); +#else /* MOTIF */ + UNUSED(show_as_default); + button = XtVaCreateManagedWidget(name, commandWidgetClass, parent, + XtNtop, XtChainTop, + XtNbottom, XtChainBottom, + NULL); + if (left_position) + XtVaSetValues(button, + XtNleft, XtChainLeft, + XtNright, XtChainLeft, + NULL); + else + XtVaSetValues(button, + XtNleft, XtChainRight, + XtNright, XtChainRight, + NULL); +#endif /* MOTIF */ + return button; +} + +static void +close_topic_window(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + Widget button; + void *ptr; + struct topic_info *info; + + UNUSED(w); + UNUSED(event); + + if (*num_params < 1) { + XDVI_WARNING((stderr, "Wrong argument number (%d) in callback!", *num_params)); + return; + } + + sscanf(*params, "%p", &ptr); + info = (struct topic_info *)ptr; + + /* get a callback to close the button, in decreasing order of preference */ + if (get_widget_by_name(&button, info->shell, "Cancel", False) + || get_widget_by_name(&button, info->shell, "Close", False) + || get_widget_by_name(&button, info->shell, "OK", False)) { + XtCallCallbacks(button, +#if MOTIF + XmNactivateCallback, +#else + XtNcallback, +#endif + info); + } + else { + XDVI_WARNING((stderr, "No button found for widget %p!\n", (void *)info->shell)); + } +} + +static XtActionsRec popdown_actions[] = { +#if !MOTIF + { "WM_popdown", close_topic_window }, +#endif + { "close-topic-window", close_topic_window }, +}; + +static Widget +create_list_widget(Widget parent, const char *w_name, struct topic_info *info) +{ + Widget list; + size_t tnum; +#if MOTIF + XmString *topics = NULL; +#else + static char **topics = NULL; +#endif + + for (tnum = 0; info->items[tnum].topic != NULL; tnum++) { + /* 1 more for terminating NULL needed for Xaw */ + topics = xrealloc(topics, (tnum + 2) * sizeof *topics); +#if MOTIF + topics[tnum] = XmStringCreateLocalized(info->items[tnum].topic); +#else + topics[tnum] = xstrdup(info->items[tnum].topic); +#endif + } + +#ifndef MOTIF + /* list needs to be terminated for Xaw */ + topics[tnum] = NULL; +#endif + +#if MOTIF + { + size_t i; + Arg args[20]; + int n = 0; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNlistSpacing, 4); n++; + XtSetArg(args[n], XmNlistMarginHeight, 4); n++; + XtSetArg(args[n], XmNlistMarginWidth, 4); n++; + XtSetArg(args[n], XmNhighlightThickness, 0); n++; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; + + list = XmCreateScrolledList(parent, (char *)w_name, args, n); + +#if defined(__GNUC__) && DEVEL_MODE +#warning TODO: make up/down scroll the list, and PgUp/PgDown scroll help text +#warning TODO: add wheel mouse bindings + /* + * also fix bug with arrow keys first moving invisible cursor, then scrollbars + */ +#endif + + XmListDeleteAllItems(list); + XmListAddItems(list, topics, tnum, 0); + + for (i = 0; i < tnum; ++i) { + XmStringFree(topics[i]); + } + free(topics); + } + XtAddCallback(list, XmNbrowseSelectionCallback, select_topic_cb, info); + XmListSelectPos(list, 1, False); /* default position */ +#else /* MOTIF */ + list = XtVaCreateWidget(w_name, listWidgetClass, parent, + XtNlist, topics, + XtNdefaultColumns, 1, + XtNforceColumns, True, + XtNverticalList, True, + XtNrowSpacing, 4, + XtNheight, 429, + NULL); + XtAddCallback(list, XtNcallback, select_topic_cb, info); + XawListHighlight(list, 0); /* default position */ +#endif /* MOTIF */ + XtManageChild(list); + return list; +} + + +Widget +create_topic_window(Widget parent, + const char *window_title, + const char *widget_name, + struct topic_info *info, + void (*init_items_func)(struct topic_info *info), + /* OK button/callbacks */ + const char *ok_label, + /* Cancel button/callbacks (can be NULL) */ + const char *cancel_label) +{ + Widget topic_window, topics_list, topic_label; + Widget pane, form, left_form, right_container_form, right_form; + Widget action_area, ok_button, cancel_button; + XtTranslations xlats; + + XtAppContext app = NULL; + Atom WM_DELETE_WINDOW; + + char *translation_str; + size_t i; + +#if !MOTIF + translation_str = get_string_va("<Message>WM_PROTOCOLS: WM_popdown(%p)", info); + xlats = XtParseTranslationTable(translation_str); + free(translation_str); +#endif + + topic_window = XtVaCreatePopupShell(widget_name, SHELL_WIDGET_CLASS, + parent, + XtNtitle, window_title, + HELP_SHELL_ARGS); + +#if MOTIF + /* make the window manager destroy action pop down the window */ + WM_DELETE_WINDOW = XmInternAtom(XtDisplay(topic_window), "WM_DELETE_WINDOW", False); + XmAddWMProtocolCallback(topic_window, WM_DELETE_WINDOW, cancel_cb, info); +#else + WM_DELETE_WINDOW = XInternAtom(XtDisplay(topic_window), "WM_DELETE_WINDOW", False); +#endif + app = XtWidgetToApplicationContext(topic_window); + XtAppAddActions(app, popdown_actions, XtNumber(popdown_actions)); + + pane = XtVaCreateWidget("topic_pane", PANED_WIDGET_CLASS, topic_window, HELP_PANED_ARGS); + + form = XtVaCreateWidget("form", FORM_WIDGET_CLASS, pane, FORM_ARGS); + + /* left pane for topics selection */ + left_form = XtVaCreateWidget("left_form", FORM_WIDGET_CLASS, form, LEFT_FORM_ARGS); + + /* right form for topics display */ + right_container_form = XtVaCreateWidget("right_container_form", FORM_WIDGET_CLASS, form, RIGHT_FORM_ARGS); + + /* + Initialize the topic label and title string with dummy values - the real values + are only known inside init_items_func(). For Xaw, the size of the widget will be + adjusted below (for Motif it's always the full width of the right form, which is + what we want). + */ + /* FIXME: Motif label is still chopped off if it's longer than right form - + work around by putting longest possible lable here ... */ + /* Also, not all children will be sized correctly, e.g. the colors - work around + by specifying a rather large dummy text ... */ + info->items[0].topic = "text text text"; + info->items[0].title = "text text text text text text text text text text text text text text text text text"; + topic_label = create_label_widget(right_container_form, "topic_label", info); + info->topic_label = topic_label; + + right_form = XtVaCreateWidget("right_form", FORM_WIDGET_CLASS, + right_container_form, +#if MOTIF + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, topic_label, + XmNtopOffset, 10, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, +#else + XtNborderWidth, 0, + XtNfromVert, topic_label, + XtNvertDistance, 6, +#endif /* MOTIF */ + NULL); + info->right_form = right_form; + + /* + Call the init_items_func callback. This will populate the info->items list + with the appropriate info, and create the forms (info->items[i].widget) + which are the children of right_form (which is accessed inside init_items_func() + through info->right_form), and all their subchildren. + + It is important that every child form is already managed inside init_items_func + so that the total size of the preferences window is set to the maximum size of + the children. (The children will be unmanaged again below). All these widgets + are never destroyed, and are managed by demand via the list selection callback + (select_topic_cb). + */ + init_items_func(info); + + XtManageChild(topic_label); + + topics_list = create_list_widget(left_form, "topics_list", info); + info->topics_list = topics_list; + + translation_str = get_string_va("#override \n" + "<Key>q:close-topic-window(%p)\n" +#ifdef MOTIF + "<Key>osfCancel:close-topic-window(%p)\n" +#else + "<Key>Escape:close-topic-window(%p)\n" +#endif + "<Key>Return:close-topic-window(%p)", + info, info, info); + xlats = XtParseTranslationTable(translation_str); + free(translation_str); + translation_str = NULL; + + XtOverrideTranslations(pane, xlats); + XtOverrideTranslations(topics_list, xlats); + + XtManageChild(left_form); + + XtManageChild(right_form); + XtManageChild(right_container_form); + + /* action area at bottom */ + action_area = XtVaCreateWidget("action_area", FORM_WIDGET_CLASS, pane, ACTION_AREA_ARGS); + + ok_button = create_button(action_area, ok_label, True, True); + XtOverrideTranslations(ok_button, xlats); + + if (cancel_label != NULL) { + cancel_button = create_button(action_area, cancel_label, False, False); + adjust_width(ok_button, cancel_button); +#if MOTIF + XtAddCallback(cancel_button, XmNactivateCallback, cancel_cb, info); +#else + XtAddCallback(cancel_button, XtNcallback, cancel_cb, info); +#endif + XtOverrideTranslations(cancel_button, xlats); + } + + XtManageChild(action_area); + + XtManageChild(form); + + XtManageChild(pane); + + XtRealizeWidget(topic_window); + + /* Now unmanage all children of right_form, as described above. */ + for (i = 0; info->items[i].topic != NULL; i++) { + XtUnmanageChild(info->items[i].widget); + } + info->curr_selected = 0; + +#if MOTIF + /* enable OK button */ + XmProcessTraversal(ok_button, XmTRAVERSE_CURRENT); + XtOverrideTranslations(ok_button, + XtParseTranslationTable("<Key>Return:ArmAndActivate()\n" + "<Key>q:ArmAndActivate()")); + XtAddCallback(ok_button, XmNactivateCallback, ok_cb, info); + XmAddWMProtocolCallback(topic_window, WM_DELETE_WINDOW, cancel_cb, info); + { /* disable resizing of lower part of pane (and button) */ + Dimension h; + XtVaGetValues(ok_button, XmNheight, &h, NULL); + XtVaSetValues(action_area, + XmNpaneMaximum, h + 20, + XmNpaneMinimum, h + 20, + NULL); + } +#else + XSetWMProtocols(XtDisplay(topic_window), XtWindow(topic_window), &WM_DELETE_WINDOW, 1); + XtAddCallback(ok_button, XtNcallback, ok_cb, info); +#endif + return topic_window; +} diff --git a/Build/source/texk/xdvik/gui/topic-window.h b/Build/source/texk/xdvik/gui/topic-window.h new file mode 100644 index 00000000000..b9df8f14db7 --- /dev/null +++ b/Build/source/texk/xdvik/gui/topic-window.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +#ifndef TOPIC_WINDOW_H_ +#define TOPIC_WINDOW_H_ + +#include "xdvi-config.h" +#include "xdvi.h" +#include "version.h" + +#include <X11/Xatom.h> +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> + +struct topic_info; /* forward declaration */ + +struct topic_item { + /* topic name */ + char *topic; + /* longer title of topic, displayed as heading in the right window */ + char *title; + /* the widget (subwindow of the right window) associated with the topic */ + Widget widget; +}; + +struct topic_info { + /* the toplevel shell */ + Widget shell; + /* the topics list */ + Widget topics_list; + /* the right form, parent of the dummy form which + contains the right topic-specific window */ + Widget right_form; + /* the topic label in right window */ + Widget topic_label; + /* the currently selected widget */ + Widget curr_selected; + /* callbacks for OK button (may be NULL); it's invoked from within + the `real' widget callback and is passed a pointer to this(!) struct topic_info */ + void (*ok_callback)(XtPointer arg); + /* like ok_callback, for the Cancel button */ + void (*cancel_callback)(XtPointer arg); + /* list of topic_items */ + struct topic_item *items; + size_t items_size; + /* additional data the callback may need ... */ + void *data; +}; + +extern Widget create_topic_window(Widget parent, + const char *window_title, + const char *widget_name, + struct topic_info *info, + void (*init_items_func)(struct topic_info *info), + /* OK button label (can be NULL) */ + const char *ok_label, + /* Cancel button label (can be NULL) */ + const char *cancel_label); +extern void select_topic(struct topic_info *info, size_t idx); +#endif /* TOPIC_WINDOW_H_ */ diff --git a/Build/source/texk/xdvik/gui/xaw_bitmaps.c b/Build/source/texk/xdvik/gui/xaw_bitmaps.c new file mode 100644 index 00000000000..6b45ca60a74 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xaw_bitmaps.c @@ -0,0 +1,82 @@ +#include "xaw_bitmaps.h" + +/* + * Bitmaps for indicating checkbuttons and radiobuttons in Xaw popup windows. + * The size of these is 16x16. + */ + +/* checkbuttons: rectangular box containing a tick mark if on, empty box else */ +unsigned char button_check_on_bits[] = { + 0x00, 0x00, 0x00, 0x80, 0xfc, 0xff, 0x04, 0x60, 0x04, 0x30, 0x04, 0x38, + 0x64, 0x2c, 0xe4, 0x26, 0xc4, 0x23, 0x84, 0x21, 0x04, 0x20, 0x04, 0x20, + 0x04, 0x20, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00 +}; +unsigned char button_check_off_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, + 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, 0x04, 0x20, + 0x04, 0x20, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00 +}; + +/* radiobuttons: round and filled with dot if on, empty else */ +unsigned char button_radio_on_bits[] = { +/* 0x70, 0x00, 0x8c, 0x01, 0x02, 0x02, 0x72, 0x02, 0xf9, 0x04, 0xf9, 0x04, */ +/* 0xf9, 0x04, 0x72, 0x02, 0x02, 0x02, 0x8c, 0x01, 0x70, 0x00 */ + 0x00,0x00,0x00,0x00,0xc0,0x03,0x30,0x0c,0x08,0x10,0xc8,0x13,0xe4,0x27,0xe4, + 0x27,0xe4,0x27,0xe4,0x27,0xc8,0x13,0x08,0x10,0x30,0x0c,0xc0,0x03,0x00,0x00, + 0x00,0x00 +}; +unsigned char button_radio_off_bits[] = { +/* 0x70, 0x00, 0x8c, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x04, 0x01, 0x04, */ +/* 0x01, 0x04, 0x02, 0x02, 0x02, 0x02, 0x8c, 0x01, 0x70, 0x00 */ + 0x00,0x00,0x00,0x00,0xc0,0x03,0x30,0x0c,0x08,0x10,0x08,0x10,0x04,0x20,0x04, + 0x20,0x04,0x20,0x04,0x20,0x08,0x10,0x08,0x10,0x30,0x0c,0xc0,0x03,0x00,0x00, + 0x00,0x00 +}; + +/* + * Bitmaps for left-hand markers in Xaw pulldown menus; these only + * use the mark elements from the button markers (dot, or tick). + */ + +/* check options: similar to button_check_*_bits. */ +unsigned char menu_check_on_bits[] = { + 0x00, 0x04, 0x00, 0x06, 0x00, 0x03, 0x80, 0x01, 0xc6, 0x00, 0x6e, 0x00, + 0x3c, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned char menu_check_off_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* radio options: tick mark if on, else empty. These are different from + the `round' buttons in button_radio_* (not ideal ...) since the round + buttons are too large for menus. +*/ +unsigned char menu_radio_on_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xf8, 0x00, 0xf8, 0x00, + 0xf8, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned char menu_radio_off_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* arrow for Xaw submenus */ +unsigned char menu_arrow_bits[] = { +/* 0x00, 0x00, 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, */ +/* 0x78, 0x00, 0x38, 0x00, 0x18, 0x00, 0x08, 0x00, 0x00, 0x00 */ + + 0x00, 0x00, 0x20, 0x00, 0x60, 0x00, 0xe0, 0x00, 0xe0, 0x01, 0xe0, 0x03, + 0xe0, 0x01, 0xe0, 0x00, 0x60, 0x00, 0x20, 0x00, 0x00, 0x00 +/* 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, */ +/* 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00, 0x08, 0x00, 0x00 */ +/* 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00 */ +}; + + +/* double arrow for popup menus */ +unsigned char menu_double_arrow_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x70, 0x00, 0xf8, 0x00, + 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0xf8, 0x00, 0x70, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/Build/source/texk/xdvik/gui/xaw_bitmaps.h b/Build/source/texk/xdvik/gui/xaw_bitmaps.h new file mode 100644 index 00000000000..21daf9e2bea --- /dev/null +++ b/Build/source/texk/xdvik/gui/xaw_bitmaps.h @@ -0,0 +1,21 @@ +#define BUTTON_BITMAP_H 16 +#define BUTTON_BITMAP_W 16 +extern unsigned char button_check_on_bits[]; +extern unsigned char button_check_off_bits[]; +extern unsigned char button_radio_on_bits[]; +extern unsigned char button_radio_off_bits[]; + +#define MENU_BITMAP_H 11 +#define MENU_BITMAP_W 11 +extern unsigned char menu_check_on_bits[]; +extern unsigned char menu_check_off_bits[]; +extern unsigned char menu_radio_on_bits[]; +extern unsigned char menu_radio_off_bits[]; + +#define MENU_ARROW_H 11 +#define MENU_ARROW_W 11 +extern unsigned char menu_arrow_bits[]; + +#define MENU_DOUBLE_ARROW_H 16 +#define MENU_DOUBLE_ARROW_W 11 +extern unsigned char menu_double_arrow_bits[]; diff --git a/Build/source/texk/xdvik/gui/xaw_menu.c b/Build/source/texk/xdvik/gui/xaw_menu.c new file mode 100644 index 00000000000..ef9b387f8c1 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xaw_menu.c @@ -0,0 +1,1449 @@ +/* + * Copyright (c) 2001-2004 the xdvik development team + * + * 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. + * + */ + +/* + * Menu bar implementation for the Athena widget set. + */ +#include "xdvi-config.h" +#include "xdvi.h" + +#include "c-openmx.h" +#include "events.h" +#include "dvi-draw.h" +#include "dvi-init.h" +#include "statusline.h" +#include "pagesel.h" +#include "util.h" +#include "x_util.h" +#include "xaw_menu.h" +#include "message-window.h" +#include "my-snprintf.h" +#include "filehist.h" +#ifdef NEW_MENU_CREATION +#include "menu.h" +#endif /* NEW_MENU_CREATION */ + +#ifndef MOTIF /* entire file */ + +#include <ctype.h> + +#include <X11/Intrinsic.h> +#include <X11/Xatom.h> +#include <X11/StringDefs.h> +#include <X11/Shell.h> /* needed for def. of XtNiconX */ + +#include <X11/Xaw/Viewport.h> +#include <X11/Xaw/SimpleMenu.h> +#include <X11/Xaw/MenuButton.h> +#include <X11/Xaw/Sme.h> +#include <X11/Xaw/SmeBSB.h> +#include <X11/Xaw/SmeLine.h> +#include <X11/Xaw/Dialog.h> +#include <X11/Xaw/Text.h> +#include <X11/Xaw/Panner.h> +#include <X11/Xaw/Porthole.h> +#include <X11/Xaw/Command.h> + +#ifndef MAX +# define MAX(i, j) ( (i) > (j) ? (i) : (j) ) +#endif + + +#ifndef NEW_MENU_CREATION +#define SEP_CHAR ':' /* character separating entries in translations lines */ +#endif + +/* needed to count the callbacks */ +static int destroy_count = 0; + +/* width of button panel */ +static int my_panel_width = 0; + +/* access method for panel width */ +int +get_panel_width(void) +{ + int retval = 0; + if (resource.expert_mode & XPRT_SHOW_BUTTONS) + retval = my_panel_width; + TRACE_GUI((stderr, "get_panel_width: %d", retval)); + return retval; +} + +#ifndef NEW_MENU_CREATION +typedef enum { INVALID, /* some error */ + NONE, /* no new button created */ + MENU_SEPARATOR, MENU_BUTTON, MENU_ENTRY, COMMAND_BUTTON } buttonTypeT; + +struct button_info { + struct button_info *next; + char *label; + struct xdvi_action *action; + Widget widget; + buttonTypeT type; +}; + +static Widget panner = 0; +static struct button_info *b_head = NULL; + +#define MAX_MENU_NUM 128 +#define MAX_BUTTON_NUM 128 + +static struct menu_list_ { + char *name; + Widget button_widget; + Widget popup_widget; +} menu_list[MAX_MENU_NUM]; + +#endif /* not NEW_MENU_CREATION */ + +#ifdef NEW_MENU_CREATION +/* + ================================================================================ + Pixmaps indicating the state of menu buttons (radiobutton/checkbox + on/off, cascading menu). Inspired by menu.c, `check_bits' in the xterm source. + ================================================================================ +*/ +#include "xaw_bitmaps.h" +static Pixmap menu_check_on_bitmap; +static Pixmap menu_check_off_bitmap; +static Pixmap menu_radio_on_bitmap; +static Pixmap menu_radio_off_bitmap; +static Pixmap menu_arrow_bitmap; +#else +#include "xaw_bitmaps.h" +/* lifted from menu.c, check_bits in xterm */ +static Pixmap menu_check_bitmap; +static Pixmap menu_arrow_bitmap; +#endif /* NEW_MENU_CREATION */ + +/* + ============================================================ + Hack for pullright menus part I: data + ============================================================ +*/ + +/* There are a few custom widgets for pullright menus out there, but + * these are old and potentially buggy, so just do it manually via an + * event handler, similar to Motif tooltips. + */ +static XtIntervalId m_timeout = 0; +static Widget m_active_submenu = NULL; /* if not NULL, the currently active pullright */ +static Widget m_submenu = NULL; /* parent of the currently active pullright + (i.e. the menu label in the parent window) */ + +static void ActPopdownSubmenus(Widget w, XEvent *event, String *params, Cardinal *num_params); + +/* to safely pop down the pullright, this callback is added to its parent menu */ +static XtActionsRec menu_actions[] = { + { "popdown-submenus", ActPopdownSubmenus } +}; + +static XtAccelerators menu_accels; + +struct pullright_position_info { + Position y; + Dimension w; + Dimension h; + Dimension border_width; + Widget menu; +}; + + +#ifdef NEW_MENU_CREATION +/* + * Set all pixmaps indicating the state of the wigdet pointed to by `elems'. + */ +void +xaw_set_button_state(struct button_elems *elems, Boolean on) +{ + static Arg args[] = { + { XtNleftBitmap, (XtArgVal)0 }, + { XtNrightBitmap, (XtArgVal)0 } + }; + + if (elems->type == BT_CHECK) + args[0].value = on ? menu_check_on_bitmap : menu_check_off_bitmap; + else if (elems->type == BT_RADIO) + args[0].value = on ? menu_radio_on_bitmap : menu_radio_off_bitmap; + if (elems->submenu != NULL) + args[1].value = menu_arrow_bitmap; + + XtSetValues(elems->widget, args, XtNumber(args)); +} + +/* + * Initialize the bitmaps. + */ +void +xaw_initialize_menu_bitmaps(void) +{ + static Boolean initialized = False; + if (!initialized) { + initialized = True; + menu_check_on_bitmap + = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level), + RootWindowOfScreen(XtScreen(globals.widgets.top_level)), + (char *)menu_check_on_bits, MENU_BITMAP_W, MENU_BITMAP_H); + menu_check_off_bitmap + = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level), + RootWindowOfScreen(XtScreen(globals.widgets.top_level)), + (char *)menu_check_off_bits, MENU_BITMAP_W, MENU_BITMAP_H); + menu_radio_on_bitmap + = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level), + RootWindowOfScreen(XtScreen(globals.widgets.top_level)), + (char *)menu_radio_on_bits, MENU_BITMAP_W, MENU_BITMAP_H); + menu_radio_off_bitmap + = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level), + RootWindowOfScreen(XtScreen(globals.widgets.top_level)), + (char *)menu_radio_off_bits, MENU_BITMAP_W, MENU_BITMAP_H); + menu_arrow_bitmap + = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level), + RootWindowOfScreen(XtScreen(globals.widgets.top_level)), + (char *)menu_arrow_bits, MENU_ARROW_W, MENU_ARROW_H); + } +} + +#else + +/* toggle `checked' symbol on xaw menu w */ +void +toggle_tick(Boolean val, Widget w) +{ + static Arg args = { XtNleftBitmap, (XtArgVal) 0 }; + if (val) + args.value = (XtArgVal) menu_check_bitmap; + else + args.value = None; + XtSetValues(w, &args, 1); +} + +void +toggle_menu(int val, XtActionProc proc) +{ + struct button_info *bp; + + for (bp = b_head; bp != NULL; bp = bp->next) { + if (bp->action != NULL && bp->action->proc == proc && bp->action->param != NULL) { + TRACE_GUI((stderr, "found proc; param: |%s|", bp->action->param)); + if (strcmp(bp->action->param, "toggle") == 0) { + if (val != 0) + toggle_tick(True, bp->widget); + else + toggle_tick(False, bp->widget); + } + else if (strcmp(bp->action->param, "a") == 0) { + if (val == shrink_to_fit()) + toggle_tick(True, bp->widget); + else + toggle_tick(False, bp->widget); + } + else { + int testval = strtoul(bp->action->param, (char **)NULL, 10); + if (testval == val) { + TRACE_GUI((stderr, "enabling param |%s|", bp->action->param)); + toggle_tick(True, bp->widget); + } + else + toggle_tick(False, bp->widget); + } + } + } +} + +/* initialize `checked' symbol on xaw menu w */ +static void +initialize_tick_marks(void) +{ + int use_gs; + if (menu_check_bitmap == None) { + int check_width = 9; + int check_height = 8; + unsigned char check_bits[] = { + 0x00, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, + 0x31, 0x00, 0x1b, 0x00, 0x0e, 0x00, 0x04, 0x00 + }; + + menu_check_bitmap = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level), + RootWindowOfScreen(XtScreen(globals.widgets.top_level)), + (char *)check_bits, check_width, check_height); + } + + /* initialize tickmarks for all possible actions */ + use_gs = resource.postscript; +#ifdef PS_GS + if (!resource.useGS) + use_gs = 0; +#endif + + toggle_menu(use_gs, Act_set_ps); + toggle_menu(resource.gs_alpha, Act_set_gs_alpha); + toggle_menu(resource.keep_flag, Act_set_keep_flag); + toggle_menu(resource.pixels_per_inch / mane.shrinkfactor, Act_shrink_to_dpi); + toggle_menu(mane.shrinkfactor, Act_set_shrink_factor); + toggle_menu(resource.use_tex_pages, Act_use_tex_pages); + toggle_menu(resource.mouse_mode, Act_switch_mode); +} +#endif /* NEW_MENU_CREATION */ + +/* ================================================================================ + ================================================================================ +*/ + +static Widget line_widget, panel_widget; + +/* used for communication with the pagelist in xaw_create_pagelist */ +static int my_y_pos; + +/* access function: panel, height of buttons etc. */ +void +xaw_create_pagelist(void) +{ + Dimension height, width; + int button_width = get_panel_width() - 2 * (resource.btn_side_spacing + resource.btn_border_width); + + XtVaGetValues(globals.widgets.clip_widget, XtNheight, &height, NULL); + width = MAX(button_width, xaw_get_pagelist_size()); + height -= resource.btn_top_spacing + resource.btn_border_width + my_y_pos; + xaw_create_pagelist_widgets(height, width, my_y_pos, panel_widget); +} + + +static XtCallbackRec command_call[] = { + {handle_command, NULL}, + {NULL, NULL}, +}; + +#ifdef NEW_MENU_CREATION +void xaw_create_menu(struct button_info *items, Widget parent, int menu_depth, int *ret_width) +{ + +} + +Widget +xaw_create_menu_widgets(Widget parent) +{ + /* hack to disable the magnifier in the panel: */ + XtTranslations panel_translations = XtParseTranslationTable("#augment <ButtonPress>:"); + + line_widget = XtVaCreateWidget("line", widgetClass, parent, + XtNbackground, (XtArgVal)resource.fore_Pixel, + XtNwidth, (XtArgVal)1, + XtNfromHoriz, (XtArgVal)globals.widgets.vport_widget, + XtNborderWidth, (XtArgVal)0, + XtNtop, (XtArgVal)XtChainTop, + XtNbottom, (XtArgVal)XtChainBottom, + XtNleft, (XtArgVal)XtChainRight, + XtNright, (XtArgVal)XtChainRight, + NULL); + panel_widget = XtVaCreateWidget("panel", compositeWidgetClass, parent, + XtNborderWidth, (XtArgVal)0, + XtNfromHoriz, (XtArgVal)line_widget, + XtNtranslations, (XtArgVal)panel_translations, + XtNtop, (XtArgVal)XtChainTop, + XtNbottom, (XtArgVal)XtChainBottom, + XtNleft, (XtArgVal)XtChainRight, + XtNright, (XtArgVal)XtChainRight, + NULL); + return panel_widget; +} + +#else /* NEW_MENU_CREATION */ + + +static void +filehist_select_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + char *label, *ptr; + int idx; + + UNUSED(client_data); + UNUSED(call_data); + + XtVaGetValues(w, XtNlabel, &label, NULL); + + idx = strtol(label, &ptr, 10) - 1; + while (isspace(*ptr)) + ptr++; + TRACE_GUI((stderr, "User selected: %d, `%s'", idx, ptr)); + if (idx == 0) { + globals.ev.flags |= EV_RELOAD; + return; + } + file_history_open(ptr); +} + +static void +update_menu_labels(Widget menu) +{ + WidgetList children; + int num_children; + int i; + + static char *buf = NULL; + static size_t buf_len = 0; + size_t new_len; + + XtVaGetValues(menu, + XtNnumChildren, &num_children, + XtNchildren, &children, + NULL); + for (i = 0; i < (int)file_history_size(); i++) { + int dummy_page; + char *filename; + + if ((filename = file_history_get_elem(i, &dummy_page)) == NULL) { + XDVI_ERROR((stderr, "Error accessing element %d of file history", i)); + continue; + } + + new_len = LENGTH_OF_INT + strlen(filename) + 1; + if (new_len > buf_len) { + buf = xrealloc(buf, new_len); + buf_len = new_len; + } + + sprintf(buf, "%d %s", i + 1, filename); + XtVaSetValues(children[i], XtNlabel, buf, NULL); + TRACE_GUI((stderr, "child %d: `%s'", i, buf)); + } + + /* if history size < number of menu entries, destroy excess menu entries */ + for (; i < num_children; i++) { + XtDestroyWidget(children[i]); + } +} + +void +filehist_menu_add_entry(const char *filename) +{ + static char *buf = NULL; + static size_t buf_len = 0; + size_t new_len = LENGTH_OF_INT + strlen(filename) + 1; + + Widget menu; + /* Don't report an error here, since "filehist_pullright" is only created on-demand + when user clicks on menu, but this may be called before from the event loop. + (The menu will still contain this entry when it's created later.) */ + if (get_widget_by_name(&menu, globals.widgets.top_level, "filehist_pullright", False)) { + int num_children; + Widget w; + + if (new_len > buf_len) { + buf = xrealloc(buf, new_len); + buf_len = new_len; + } + + XtVaGetValues(menu, XtNnumChildren, &num_children, NULL); + sprintf(buf, "%d %s", num_children + 1, filename); + + w = XtVaCreateManagedWidget("_filehist", smeBSBObjectClass, menu, + XtNlabel, buf, + XtNleftMargin, 10, + NULL); + XtAddCallback(w, XtNcallback, filehist_select_cb, NULL); + update_menu_labels(menu); + } +} + +void +filehist_menu_refresh(void) +{ + Widget menu; + + /* Don't report an error here, since "filehist_pullright" is only created on-demand + when user clicks on menu, but this may be called before from the event loop. + (The menu will still contain this entry when it's created later.) */ + if (get_widget_by_name(&menu, globals.widgets.top_level, "filehist_pullright", False)) { + update_menu_labels(menu); + } +} + + +static void +filehist_insert_submenu(int idx, const char *filename, int pageno, void *data) +{ + Widget menu = (Widget)data; + Widget w; + static char *buf = NULL; + static size_t buf_len = 0; + size_t new_len = LENGTH_OF_INT + strlen(filename) + 1; + + UNUSED(pageno); + + if (new_len > buf_len) { + buf = xrealloc(buf, new_len); + buf_len = new_len; + } + + sprintf(buf, "%d %s", idx + 1, filename); + TRACE_GUI((stderr, "Creating menu `%s'", buf)); + w = XtVaCreateManagedWidget("_filehist", smeBSBObjectClass, menu, + XtNlabel, buf, + XtNleftMargin, 10, + NULL); + XtAddCallback(w, XtNcallback, filehist_select_cb, NULL); +} + + +/* + ============================================================ + Hack for pullright menus part II: callbacks and functions + ============================================================ +*/ + +/* callback to pop down the currently active pullright */ +static void +ActPopdownSubmenus(Widget w, XEvent *event, String *params, Cardinal *num_params) +{ + UNUSED(w); + UNUSED(event); + UNUSED(params); + UNUSED(num_params); + + if (m_timeout != 0) + XtRemoveTimeOut(m_timeout); + m_timeout = 0; + if (m_active_submenu != NULL) + XtPopdown(m_active_submenu); +} +/* create a parent shell for the pullright menu entries */ +static Widget +create_files_submenu(void) +{ + Widget popup = XtCreatePopupShell("filehist_pullright", simpleMenuWidgetClass, globals.widgets.top_level, + NULL, 0); + file_history_enumerate(filehist_insert_submenu, popup); + return popup; +} + +/* Acutally pop up the pullright menu */ +static void +popup_pullright(XtPointer client_data, XtIntervalId *id) +{ + int pos_x, pos_y; + Dimension w1; + Window dummy; + static Widget files_submenu = NULL; + struct pullright_position_info *info = (struct pullright_position_info *)client_data; + + UNUSED(id); + + if (files_submenu == NULL) + files_submenu = create_files_submenu(); + /* XtManageChild(files_submenu); */ + XTranslateCoordinates(DISP, XtWindow(XtParent(m_submenu)), RootWindowOfScreen(SCRN), + info->w, info->y, &pos_x, &pos_y, &dummy); + XtRealizeWidget(files_submenu); + XtVaGetValues(files_submenu, XtNwidth, &w1, NULL); + TRACE_GUI((stderr, "Popping up at %d, %d, %d, %d", pos_x, pos_y, w1, WidthOfScreen(SCRN))); + + /* if not sufficient place on the right, pop it up on the left */ + /* fprintf(stderr, "border_width: %d\n", info->border_width); */ + if (pos_x + w1 > WidthOfScreen(SCRN)) { + /* fprintf(stderr, "%d > %d!\n", pos_x + w1, WidthOfScreen(SCRN)); */ + pos_x -= (w1 + info->w + 3 * info->border_width); + /* fprintf(stderr, "new x: %d\n", pos_x); */ + } + else { + pos_x += info->border_width; + } + XtVaSetValues(files_submenu, + XtNx, pos_x, + XtNy, pos_y, + NULL); + /* use XtPopupSpringLoaded() instead of XtPopup() since it does a few things + that make the pullright behave like a proper menu, like highlighting the + current selection, setting the cursor shape etc. */ + XtPopupSpringLoaded(files_submenu); + m_active_submenu = files_submenu; +} + +/* + * This event handler (to be called by read_events(), the main event handling loop) + * creates a timeout for the pullright to pop up. +*/ +void +SubMenuHandleEvent(XtAppContext app, XEvent *event) +{ + static int flag = 0; + static struct pullright_position_info info = { -1, 0, 0, 0, NULL }; + + UNUSED(app); + + if (m_submenu == NULL) + return; + + if (event->type == EnterNotify + || event->type == MotionNotify + || event->type == LeaveNotify + || event->type == ButtonPress) { + +/* fprintf(stderr, "SubMenuHandleEvent: 0x%lx, 0x%lx\n", event->xany.window, XtWindow(m_submenu)); */ + + /* Could also loop through a list of windows here ... + We need to check for the parent of the menu item, since smeBSBObject is not + a real window, and then manually check whether the pointer is inside the menu + item. + */ + if (XtParent(m_submenu) != NULL && + event->xany.window == XtWindow(XtParent(m_submenu))) { + /* don't need to check for x coordinates since we already + know that pointer is inside the parent */ + if (info.y == -1) { /* initialize info */ + XtVaGetValues(m_submenu, + XtNy, &(info.y), + XtNwidth, &(info.w), + XtNheight, &(info.h), + NULL); + XtVaGetValues(XtParent(m_submenu), + XtNborderWidth, &(info.border_width), + NULL); + + info.menu = m_submenu; + } + if (info.y < event->xbutton.y && info.y + info.h > event->xbutton.y) { + if (flag == 0) { + /* Create a timeout of 200ms to pop up the menu, so that it doesn't + pop up always when the cursor is only moved over the pulldown menu. + I think this is about the same delay as the one used by Motif. + */ + flag = 1; + TRACE_GUI((stderr, "ENTER: %d, %d, %d; %d, %d", + info.y, info.w, info.h, event->xbutton.x, event->xbutton.y)); + m_timeout = XtAppAddTimeOut(app, 200, popup_pullright, &info); + return; + } + } + else if (flag == 1) { + flag = 0; + TRACE_GUI((stderr, "LEAVE!")); + if (m_timeout != 0) + XtRemoveTimeOut(m_timeout); + m_timeout = 0; + if (m_active_submenu != NULL) + XtPopdown(m_active_submenu); + m_active_submenu = NULL; + } + } + } +} + +static Widget +create_pulldown_entry(const char *menu_name, + Widget parent, + const char *item_name, + const char *accelerator, /* accelerator, or NULL */ + struct xdvi_action *action, + buttonTypeT *type) +{ + Widget w; + char buf[1024]; + char *m_name = xstrdup(menu_name); + m_name = xstrcat(m_name, item_name); + +#ifdef SHOW_ACCELERATORS + if (accelerator != NULL && *accelerator != '\0') + SNPRINTF(buf, 1024, "%s [%s]", item_name, accelerator); + else + strncpy(buf, item_name, 1024); +#else + UNUSED(accelerator); + strncpy(buf, item_name, 1024); +#endif + +/* fprintf(stderr, "creating pulldown for parent %p: `%s', `%s'\n", */ +/* (void *)parent, menu_name, item_name); */ + + if (strcmp(item_name, "SEP") == 0) { /* it's a separator */ + *type = MENU_SEPARATOR; + w = XtCreateManagedWidget(m_name, smeLineObjectClass, parent, NULL, 0); + } + else if (action->proc != NULL && action->proc == Act_recent_files) { /* special case: submenu with recent files */ +/* w = recent_files_submenu(m_name, parent, buf); */ +/* *type = MENU_ENTRY; */ + w = XtVaCreateManagedWidget(m_name, smeBSBObjectClass, parent, + XtNlabel, buf, + XtNleftMargin, 20, + XtNrightMargin, 16, + XtNrightBitmap, menu_arrow_bitmap, + NULL); + m_submenu = w; + /* fprintf(stderr, "setting translations for %p\n", (void *)XtParent(parent)); */ + XtOverrideTranslations(parent, menu_accels); + /* XtOverrideTranslations(XtParent(parent), menu_accels); */ + + *type = MENU_ENTRY; + } + else { /* normal menu item */ + w = XtVaCreateManagedWidget(m_name, smeBSBObjectClass, parent, + XtNlabel, buf, + XtNleftMargin, 20, + NULL); + *type = MENU_ENTRY; + XtAddCallback(w, XtNcallback, handle_command, action); + } + free(m_name); + return w; +} + +static Widget +create_pulldown_menu(const char *menu_name, + Dimension *width, + int *index, + Dimension *y_pos, + buttonTypeT *type, + int *menu_number) +{ + int i; + + /* check whether a pulldown menu with this name already exists, + and if yes, return its position in index: */ + for (i = 0; i < *menu_number; i++) { + if (menu_list[i].name != NULL && (strcmp(menu_list[i].name, menu_name)) == 0) { + *index = i; + break; + } + } + if (i == *menu_number) { /* doesn't exist, create one */ + Dimension w, h; + Widget n_button_widget, n_popup_widget; + char *button_widget_name = NULL; + + (*menu_number)++; + if (*menu_number > MAX_MENU_NUM) { + XDVI_WARNING((stderr, "Too many menus (max=%d), skipping all from \"%s\"", + MAX_MENU_NUM, menu_name)); + *type = INVALID; + return 0; + } + + /* save menu_name to list for later comparison */ + menu_list[i].name = xrealloc(menu_list[i].name, strlen(menu_name) + 1); + strcpy(menu_list[i].name, menu_name); + + button_widget_name = xstrdup(menu_name); + button_widget_name = xstrcat(button_widget_name, "B"); /* make it unique */ + + if (globals.debug & DBG_ALL) { + fprintf(stderr, "ypos: %d; h: %d; between: %d; border: %d\n", + *y_pos, + h, + resource.btn_between_spacing, + resource.btn_border_width); + } + + n_button_widget = XtVaCreateWidget(menu_list[i].name, menuButtonWidgetClass, panel_widget, + XtNmenuName, button_widget_name, + XtNx, resource.btn_side_spacing, + XtNy, *y_pos, + XtNborderWidth, resource.btn_border_width, + NULL); + n_popup_widget = XtVaCreatePopupShell(button_widget_name, simpleMenuWidgetClass, n_button_widget, + NULL); + /* Mustn't free this one?? */ +/* free(button_widget_name); */ + menu_list[i].button_widget = n_button_widget; + menu_list[i].popup_widget = n_popup_widget; + XtVaGetValues(n_button_widget, XtNwidth, &w, XtNheight, &h, NULL); + *width = w; + *index = i; + *y_pos += h + resource.btn_between_spacing + 2 * resource.btn_border_width; + *type = MENU_BUTTON; + return n_button_widget; + } + *type = NONE; + return 0; +} + +static struct button_info * +create_button_info(const char *label, struct xdvi_action *action, Widget w, buttonTypeT type) +{ + struct button_info *info = xmalloc(sizeof *info); + info->label = xstrdup(label); + info->action = action; + info->widget = w; + info->type = type; + return info; +} + +typedef enum shrinkArgT_ { NO_SHRINK_ARG, HASH, PERCENT, UNDERSCORE } shrinkArgT; + +static shrinkArgT +test_for_shrink_arg(const char *item, int *idx) +{ + const char *ptr = item; + size_t i; + for (i = 0; ptr[i] != '\0'; i++) { + if (ptr[i] == '$') { + i++; + if (ptr[i] == '#') { + *idx = i; + return HASH; + } + else if (ptr[i] == '%') { + *idx = i; + return PERCENT; + } + else if (ptr[i] == '_') { + *idx = i; + return UNDERSCORE; + } + } + } + *idx = 0; + return NO_SHRINK_ARG; +} + +static Boolean +create_shrink_button(char **label, int offset, shrinkArgT type, struct xdvi_action *action) +{ + int shrink_arg = 0; +#define TEMPSTR_LEN 128 /* enough for printing a number into */ + char tempstr[TEMPSTR_LEN]; + char *new_label = NULL; + + for (;; action = action->next) { + if (action == NULL) { + return False; + } + if (action->proc == Act_set_shrink_factor || action->proc == Act_shrink_to_dpi) { + /* NOTE: the shrinkbutton[] resource isn't implemented. It probably makes + less sense with the pulldown menu resources?? + */ + if (action->num_params > 0) { + shrink_arg = atoi(action->param); + if (action->proc == Act_shrink_to_dpi) + shrink_arg = (double)resource.pixels_per_inch / shrink_arg + 0.5; + if (shrink_arg < 1) + shrink_arg = 1; + else if (shrink_arg > 99) + shrink_arg = 99; + } + break; /* found shrink action */ + } + } + + if (type == HASH) + SNPRINTF(tempstr, TEMPSTR_LEN, "%d", shrink_arg); + else if (type == PERCENT) { + if (shrink_arg <= 15) + SNPRINTF(tempstr, TEMPSTR_LEN, "%d", (int)(100.0 / shrink_arg + .5)); + else + SNPRINTF(tempstr, TEMPSTR_LEN, "%.2f", 100.0 / shrink_arg); + } + /* NOTE: I think UNDERSCORE is related to the shrinkbutton[] resource, + which is not implemented */ + + /* return resized label */ + new_label = xmalloc(offset - 1 + strlen(tempstr) + strlen(*label + offset + 1) + 1); + memcpy(new_label, *label, offset - 1); + strcpy(new_label + offset - 1, tempstr); /* now it's null-terminated */ + strcat(new_label, *label + offset + 1); + free(*label); + *label = new_label; + +#undef TEMPSTR_LEN + return True; +} + +static Dimension +create_button(int button_number, /* number of this button */ + int *menu_num, /* number of submenus in this button */ + char **items, /* description of label and action strings */ + size_t item_count, /* number of items in description */ + Widget parent, /* parent of widget to create */ + struct button_info ***bipp, /* button info to insert new button into */ + Dimension *y_pos, /* vertical position after creating button */ + const char *c_ptr, /* entire rest of resource string, for error messages */ + size_t len) /* length of current line in resource string, for error messages*/ +{ + int menu_idx = -1, label_idx = -1, accelerator_idx = -1, action_idx = -1; +#define NAME_LEN 16 + char name[NAME_LEN]; + struct button_info *bp; + shrinkArgT shrink_arg_type; + struct xdvi_action *action; + Widget widget; + buttonTypeT b_type; +/* static int menu_num = 0; /\* count number of created menus *\/ */ + Dimension width = 0; + + ASSERT(item_count >= 2, "Too few items"); + + if (item_count == 4) { /* submenu for a button */ + menu_idx = 0; + label_idx = 1; + accelerator_idx = 2; + action_idx = 3; + } + else if (item_count == 3) { /* command button */ + label_idx = 0; + accelerator_idx = 1; + action_idx = 2; + } + else { /* separator */ + if (strcmp(items[1], "SEP") == 0) { /* separator */ + menu_idx = 0; + label_idx = 1; + } + else { + XDVI_WARNING((stderr, "Wrong number of items (%d) in translations line:\n\"%.*s\" (skipping this line).", + item_count, (int)len, c_ptr)); + return 0; + } + } + + if (action_idx != -1) { + int offset; + shrink_arg_type = test_for_shrink_arg(items[label_idx], &offset); + +/* fprintf(stderr, "compiling action: |%s|\n", items[action_idx]); */ + if ((action = compile_action(items[action_idx])) == NULL) { + XDVI_WARNING((stderr, "Invalid action \"%s\" in translations line:\n\"%.*s\" (skipping this line).", + items[action_idx], (int)len, c_ptr)); + return 0; + } + + if (shrink_arg_type != NO_SHRINK_ARG) { /* search for corresponding shrink action */ + if (!create_shrink_button(&(items[label_idx]), offset, shrink_arg_type, action)) { + XDVI_WARNING((stderr, "Non-existent shrink action \"%s\" in translations line:\n\"%.*s\"", + items[action_idx], (int)len, c_ptr)); + return 0; + } + } + } + else { + action = NULL; + } + + command_call[0].closure = (XtPointer) action; + SNPRINTF(name, NAME_LEN, "button%d", button_number); + + if (menu_idx != -1) { /* it's a pulldown menu */ + int index; + + /* create a new pulldown menu if it doesn't exist yet */ + widget = create_pulldown_menu(items[menu_idx], &width, &index, y_pos, &b_type, menu_num); + if (b_type == INVALID) + return 0; + if (b_type != NONE) { /* it didn't exist yet */ + bp = create_button_info(items[label_idx], NULL, widget, b_type); + **bipp = bp; + *bipp = &bp->next; + } + + /* create an entry for this item */ + widget = create_pulldown_entry(menu_list[index].name, + menu_list[index].popup_widget, + items[label_idx], + accelerator_idx > 0 ? items[accelerator_idx] : NULL, + action, + &b_type); + bp = create_button_info(items[label_idx], action, widget, b_type); + **bipp = bp; + *bipp = &bp->next; + } + else { /* not a pulldown menu, but a simple command button */ + command_call[0].closure = (XtPointer) action; + widget = XtVaCreateWidget(name, commandWidgetClass, parent, + XtNlabel, (XtArgVal)items[label_idx], + XtNx, resource.btn_side_spacing, + XtNy, (XtArgVal)*y_pos, + XtNborderWidth, resource.btn_border_width, + XtNcallback, (XtArgVal)command_call, + NULL); + bp = create_button_info(items[label_idx], action, widget, COMMAND_BUTTON); + **bipp = bp; + *bipp = &bp->next; + } +#undef NAME_LEN + return width; +} + +void +scroll_x_panner(int x) +{ + static int old_x = 0; + if (panner != 0 && ABS(x - old_x) > 8) { + XtVaSetValues(panner, XtNsliderX, x, NULL); + old_x = x; + } +} + +void +scroll_y_panner(int y) +{ + static int old_y = 0; + if (panner != 0 && ABS(y - old_y) > 8) { + XtVaSetValues(panner, XtNsliderY, y, NULL); + old_y = y; + } +} + +#ifdef USE_PANNER +static void +panner_cb(Widget widget, XtPointer closure, XtPointer report_ptr) +{ + XawPannerReport *report = (XawPannerReport *)report_ptr; + static int orig_x = 0, orig_y = 0; + int x = report->slider_x; + int y = report->slider_y; + static Dimension w, h; + static Arg arg_wh_clip[] = { + {XtNwidth, (XtArgVal) &w}, + {XtNheight, (XtArgVal) &h}, + }; + + UNUSED(closure); + + XtGetValues(globals.widgets.clip_widget, arg_wh_clip, XtNumber(arg_wh_clip)); + + fprintf(stderr, "w: %d, h: %d, globals.page.w: %d, globals.page.h: %d\n", + w, h, globals.page.w, globals.page.h); + XtVaSetValues(widget, + XtNsliderWidth, w, XtNsliderHeight, h, + XtNcanvasWidth, globals.page.w, XtNcanvasHeight, globals.page.h, + NULL); + + fprintf(stderr, "panner moved: %d, %d\n", report->slider_x, report->slider_y); + if (globals.widgets.x_bar != NULL) + XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer)(x - orig_x)); + if (globals.widgets.y_bar != NULL) + XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer)(y - orig_y)); + orig_x = x; + orig_y = y; +} +#endif /* USE_PANNER */ + +void +create_menu_buttons(Widget form, int *ret_panel_width) +{ + Dimension max_button_width = 0, curr_width; + Dimension y_pos; + struct button_info **bipp; + struct button_info *bp; + const char *c_ptr, *e_ptr; + int max_size; + int button_number = 0; + int menu_number = 0; /* number of menus created; passed through to create_pulldown_menu */ + + /* disable the magnifier in the panel: */ + XtTranslations panel_translations = XtParseTranslationTable("#augment <ButtonPress>:"); + + XtAppAddActions(XtWidgetToApplicationContext(form), menu_actions, XtNumber(menu_actions)); + /* add our own popdown-submenus() action to the default translations of this menu */ + menu_accels = XtParseAcceleratorTable("<BtnUp>:MenuPopdown()notify()unhighlight()popdown-submenus()"); + + line_widget = XtVaCreateWidget("line", widgetClass, form, + XtNbackground, (XtArgVal)resource.fore_Pixel, + XtNwidth, (XtArgVal) 1, + XtNfromHoriz, (XtArgVal)globals.widgets.vport_widget, + XtNborderWidth, (XtArgVal)0, + XtNtop, (XtArgVal)XtChainTop, + XtNbottom, (XtArgVal)XtChainBottom, + XtNleft, (XtArgVal)XtChainRight, + XtNright, (XtArgVal)XtChainRight, + NULL); + panel_widget = XtVaCreateWidget("panel", compositeWidgetClass, form, + XtNborderWidth, (XtArgVal)0, + XtNfromHoriz, (XtArgVal)line_widget, + XtNtranslations, (XtArgVal)panel_translations, + XtNtop, (XtArgVal)XtChainTop, + XtNbottom, (XtArgVal)XtChainBottom, + XtNleft, (XtArgVal)XtChainRight, + XtNright, (XtArgVal)XtChainRight, + NULL); + menu_arrow_bitmap + = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level), + RootWindowOfScreen(XtScreen(globals.widgets.top_level)), + (char *)menu_arrow_bits, MENU_ARROW_W, MENU_ARROW_H); + + b_head = NULL; + bipp = &b_head; + + *ret_panel_width = 0; + y_pos = resource.btn_top_spacing; + +#define FREE_LINE_ITEMS do { \ + for (curr = 0; curr < item_count; curr++) { \ + free(line_items[curr]); \ + } \ + free(line_items); \ + } while (0) + + for (c_ptr = resource.menu_translations; + c_ptr != NULL && *c_ptr != '\0'; + c_ptr = e_ptr + 1) { + e_ptr = strchr(c_ptr, '\n'); + if (e_ptr != NULL) { + char **line_items = NULL; + size_t len, curr, item_count = 0; + + if (e_ptr == c_ptr) { /* single '\n' marks additional space between buttons */ + y_pos += resource.btn_between_extra; + continue; + } + len = e_ptr - c_ptr; + + if (++button_number > MAX_BUTTON_NUM) { + XDVI_WARNING((stderr, "Too many buttons (max=%d), skipping all from:\n\"%.*s\"", + MAX_BUTTON_NUM, (int)len, c_ptr)); + break; + } + + line_items = split_line(c_ptr, SEP_CHAR, 0, len, &item_count); + if (item_count < 3 && !(item_count > 1 && strcmp(line_items[1], "SEP") == 0)) { + XDVI_WARNING((stderr, "Too few separators \"%c\" in translations line:\n\"%.*s\" (skipping this line).\n", + SEP_CHAR, (int)len, c_ptr)); + FREE_LINE_ITEMS; + continue; + } + curr_width = create_button(button_number, + &menu_number, + line_items, + item_count, + panel_widget, + &bipp, + &y_pos, + c_ptr, len); + if (curr_width == 0) { /* no button created */ + FREE_LINE_ITEMS; + continue; + } + /* adjust max_button_width to this new entry */ + if (curr_width > max_button_width) + max_button_width = curr_width; + + FREE_LINE_ITEMS; + } + } + +#undef FREE_LINE_ITEMS + + /* null-terminate button info */ + *bipp = NULL; + + max_size = xaw_get_pagelist_size(); + if (max_size > max_button_width) { + max_button_width = max_size; + } + + *ret_panel_width = max_button_width + 2 * (resource.btn_side_spacing + resource.btn_border_width); + TRACE_GUI((stderr, "panel_widget: w: %d", *ret_panel_width)); + XtVaSetValues(panel_widget, XtNwidth, (XtArgVal)*ret_panel_width, NULL); + ++(*ret_panel_width); + my_panel_width = *ret_panel_width; /* make it available */ + + for (bp = b_head; bp != NULL; bp = bp->next) { + if (bp->type == MENU_BUTTON || bp->type == COMMAND_BUTTON) { + TRACE_GUI((stderr, "bp->widget: w: %d", max_button_width)); + XtVaSetValues(bp->widget, XtNwidth, (XtArgVal)max_button_width, NULL); + XtManageChild(bp->widget); + } + } + + my_y_pos = y_pos - resource.btn_between_spacing + resource.btn_top_spacing + 2 * resource.btn_border_width; + initialize_tick_marks(); +#ifdef USE_PANNER + { + static Dimension w, h; + static Arg arg_wh_clip[] = { + {XtNwidth, (XtArgVal) &w}, + {XtNheight, (XtArgVal) &h}, + }; + XtGetValues(globals.widgets.clip_widget, arg_wh_clip, XtNumber(arg_wh_clip)); + + fprintf(stderr, "w: %d, h: %d, globals.page.w: %d, globals.page.h: %d\n", + w, h, globals.page.w, globals.page.h); + if (w == 0) + w = globals.page.w - 2; + if (h == 0) + h = globals.page.h - 2; + panner = XtVaCreateManagedWidget("panner", pannerWidgetClass, panel_widget, + XtNx, resource.btn_side_spacing, + XtNy, my_y_pos, + XtNheight, 60, + XtNwidth, max_button_width, + XtNshadowThickness, 0, + XtNsliderWidth, w, + XtNsliderHeight, h, + XtNcanvasWidth, globals.page.w, + XtNcanvasHeight, globals.page.h, + XtNsliderX, 5, + XtNsliderY, 7, + XtNinternalSpace, 0, + NULL); + } + my_y_pos += 60 + resource.btn_top_spacing + 2 * resource.btn_border_width; + XtAddCallback(panner, XtNreportCallback, panner_cb, (XtPointer)NULL); + +#endif /* USE_PANNER */ + +} +#endif /* NEW_MENU_CREATION */ + + +void +set_button_panel_height(XtArgVal h) +{ + TRACE_GUI((stderr, "line_widget: h %d", (int)h)); + XtVaSetValues(line_widget, XtNheight, h, NULL); + XtManageChild(line_widget); + + XtVaSetValues(panel_widget, XtNheight, h, NULL); + XtManageChild(panel_widget); +} + +/**************************************************************************** + * Action handling code. + */ + + +static void +handle_destroy_buttons(Widget widget, XtPointer client_data, XtPointer call_data) +{ + int panel_width; + Dimension window_w, window_h; + + UNUSED(widget); + UNUSED(client_data); + UNUSED(call_data); + + if (--destroy_count != 0) { + return; + } + XtVaSetValues(globals.widgets.vport_widget, XtNresizable, (XtArgVal)True, NULL); + XtVaGetValues(globals.widgets.form_widget, + XtNwidth, &window_w, + XtNheight, &window_h, + NULL); + + if ((resource.expert_mode & XPRT_SHOW_BUTTONS) == 0) { + /* destroy buttons */ + TRACE_GUI((stderr, "globals.widgets.vport_widget: h %d, w %d", window_w, window_h)); + XtVaSetValues(globals.widgets.vport_widget, XtNwidth, window_w, XtNheight, window_h, NULL); + } + else { + create_menu_buttons(globals.widgets.form_widget, &panel_width); + window_w -= panel_width; + TRACE_GUI((stderr, "globals.widgets.vport_widget: h %d, w %d", window_h, window_w)); + XtVaSetValues(globals.widgets.vport_widget, XtNwidth, window_w, XtNheight, window_h, NULL); + set_button_panel_height((XtArgVal) window_h); + } +} + + +static void +reconfig_window(void) { + Dimension x_top, y_top, h_top, w_top; + XWindowChanges sizeconfigure; + int sizeconfiguremask; + + /* brute-force method to bring the scrollbars back. Apparently a single XConfigureWindow() + isn't enough to get the scrollbars back, we actually need to move the window a bit, + and then move it back. */ + sizeconfiguremask = CWWidth | CWHeight; + XtVaGetValues(globals.widgets.top_level, XtNx, &x_top, XtNy, &y_top, XtNheight, &h_top, XtNwidth, &w_top, NULL); + sizeconfigure.width = w_top + 1; + sizeconfigure.height = h_top + 1; + XConfigureWindow(DISP, XtWindow(globals.widgets.top_level), sizeconfiguremask, &sizeconfigure); + sizeconfigure.width = w_top; + sizeconfigure.height = h_top; + XConfigureWindow(DISP, XtWindow(globals.widgets.top_level), sizeconfiguremask, &sizeconfigure); +} + +/* toggle scrollbars to state `visible' */ +void +toggle_scrollbars(void) +{ + Widget v_bar = XtNameToWidget(globals.widgets.vport_widget, "vertical"); + Widget h_bar = XtNameToWidget(globals.widgets.vport_widget, "horizontal"); + static Dimension bar_thick; + static Boolean v_bar_mapped = False, h_bar_mapped = False; + static Boolean initialized = False; + + Boolean make_v_bar_visible = False; + Boolean make_v_bar_invisible = False; + + Boolean make_h_bar_visible = False; + Boolean make_h_bar_invisible = False; + + if (v_bar != 0) { + int test_v = 0; + XtVaGetValues(v_bar, XtNwidth, &test_v, NULL); + if (test_v > 1) + v_bar_mapped = True; + } + if (h_bar != 0) { + int test_h = 0; + XtVaGetValues(h_bar, XtNheight, &test_h, NULL); + if (test_h > 1) + h_bar_mapped = True; + } + + if (!initialized) { + v_bar_mapped = h_bar_mapped = (resource.expert_mode & XPRT_SHOW_SCROLLBARS) != 0; + initialized = True; + if (v_bar != 0) + XtVaGetValues(v_bar, XtNwidth, &bar_thick, NULL); + else if (h_bar != 0) + XtVaGetValues(h_bar, XtNheight, &bar_thick, NULL); + else + bar_thick = 15; /* FIXME */ + } + + if ((resource.expert_mode & XPRT_SHOW_SCROLLBARS) == 0) { + if (v_bar_mapped) + make_v_bar_invisible = True; + if (h_bar_mapped) + make_h_bar_invisible = True; + } + else { + if (!v_bar_mapped) + make_v_bar_visible = True; + if (!h_bar_mapped) + make_h_bar_visible = True; + } + + if (make_h_bar_visible || make_v_bar_visible) { + if (make_h_bar_visible && h_bar != 0) { + TRACE_GUI((stderr, "h_bar: h %d", bar_thick)); + XtVaSetValues(h_bar, XtNheight, bar_thick, XtNx, bar_thick, XtNy, 0, XtNborderWidth, 1, NULL); + XtManageChild(h_bar); + h_bar_mapped = True; + } + if (make_v_bar_visible && v_bar != 0) { + TRACE_GUI((stderr, "v_bar: w %d", bar_thick)); + XtVaSetValues(v_bar, XtNwidth, bar_thick, XtNx, 0, XtNy, bar_thick, XtNborderWidth, 1, NULL); + XtManageChild(v_bar); + v_bar_mapped = True; + } + if (h_bar != 0 || v_bar != 0) { /* need to reconfigure screen */ + reconfig_window(); + } + } + else if (make_h_bar_invisible || make_v_bar_invisible) { + if (make_h_bar_invisible && h_bar != 0) { + XtUnmanageChild(h_bar); + XtVaSetValues(h_bar, XtNheight, 1, XtNx, 0, XtNy, 0, XtNborderWidth, 0, NULL); + h_bar_mapped = False; + } + if (make_v_bar_invisible && v_bar != 0) { + XtUnmanageChild(v_bar); + XtVaSetValues(v_bar, XtNwidth, 1, XtNy, 0, XtNy, 0, XtNborderWidth, 0, NULL); + v_bar_mapped = False; + } + if (h_bar != 0 || v_bar != 0) { /* need to reconfigure screen */ + reconfig_window(); + } + } + +} + +void +toggle_buttons(void) +{ + static Boolean buttons_active = False; + static Boolean initialized = False; + Dimension window_w, window_h; + + + Boolean make_buttons_visible = False; + Boolean make_buttons_invisible = False; + + int panel_width; + + if (!initialized) { + buttons_active = (resource.expert_mode & XPRT_SHOW_BUTTONS) != 0; + initialized = True; + } + + if ((resource.expert_mode & XPRT_SHOW_BUTTONS) == 0) { + if (buttons_active) + make_buttons_invisible = True; + } + else { + if (!buttons_active) + make_buttons_visible = True; + } + + if (make_buttons_visible) { + if (destroy_count != 0) { + return; + } + + create_menu_buttons(globals.widgets.form_widget, &panel_width); + XtVaGetValues(globals.widgets.form_widget, + XtNwidth, &window_w, + XtNheight, &window_h, + NULL); + XtVaSetValues(globals.widgets.vport_widget, XtNresizable, (XtArgVal)True, NULL); + window_w -= panel_width; + TRACE_GUI((stderr, "globals.widgets.vport_widget: w: %d, h: %d", window_w, window_h)); + XtVaSetValues(globals.widgets.vport_widget, XtNwidth, window_w, XtNheight, window_h, NULL); + set_button_panel_height((XtArgVal) window_h); + buttons_active = True; + } + else if (make_buttons_invisible) { + if (destroy_count != 0) { + return; + } + /* this counts the callback calls; 1 for the panel, 1 for the line */ + destroy_count = 2; + + XtAddCallback(panel_widget, XtNdestroyCallback, + handle_destroy_buttons, (XtPointer)0); + XtAddCallback(panel_widget, XtNdestroyCallback, + handle_destroy_pagelist, (XtPointer)0); + XtAddCallback(line_widget, XtNdestroyCallback, + handle_destroy_buttons, (XtPointer)0); + XtDestroyWidget(panel_widget); + XtDestroyWidget(line_widget); + buttons_active = False; +/* window_w += get_panel_width(); */ + + while (b_head != NULL) { + struct button_info *bp = b_head; + struct xdvi_action *action; + + b_head = bp->next; + free(bp->label); + /* free bp->action */ + for (action = bp->action; action != NULL;) { + struct xdvi_action *act2 = action; + action = act2->next; + if (act2->num_params > 0) { + free(act2->param); + } + free(act2); + } + free(bp); + } + } +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* ifndef MOTIF */ diff --git a/Build/source/texk/xdvik/gui/xaw_menu.h b/Build/source/texk/xdvik/gui/xaw_menu.h new file mode 100644 index 00000000000..750f79cfeb5 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xaw_menu.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2001-2004 the xdvik development team + * + * 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. + * + */ + +#ifndef XAW_MENU_H_ +#define XAW_MENU_H_ + +#ifdef NEW_MENU_CREATION +#include "menu.h" +#endif /* NEW_MENU_CREATION */ + +#ifndef MOTIF + +#ifdef NEW_MENU_CREATION +extern void xaw_set_button_state(struct button_elems *elems, Boolean on); +extern void xaw_initialize_menu_bitmaps(void); +extern Widget xaw_create_menu_widgets(Widget parent); +extern void xaw_create_menu(struct button_info *items, Widget parent, int *ret_width); +extern void xaw_set_button_state(struct button_elems *elems, Boolean on); + +extern Pixmap menu_check_bitmap; +extern Pixmap menu_uncheck_bitmap; +#else +extern void create_menu_buttons(Widget menu_bar, int *ret_panel_width); +extern void toggle_menu(int val, XtActionProc proc); +#endif /* NEW_MENU_CREATION */ + +extern void SubMenuHandleEvent(XtAppContext app, XEvent *event); +extern void filehist_menu_add_entry(const char *filename); +extern int get_panel_width(void); + +extern void toggle_tick(Boolean val, Widget w); +extern void set_button_panel_height(XtArgVal); +extern void filehist_menu_refresh(void); + +extern void xaw_create_pagelist(void); +extern void toggle_scrollbars(void); +extern void toggle_buttons(void); +extern void scroll_y_panner(int y); +extern void scroll_x_panner(int x); +#endif /* not MOTIF */ + +#endif /* XAW_MENU_H_ */ diff --git a/Build/source/texk/xdvik/gui/xicon.c b/Build/source/texk/xdvik/gui/xicon.c new file mode 100644 index 00000000000..15faa32442a --- /dev/null +++ b/Build/source/texk/xdvik/gui/xicon.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2001 Marcin Dalecki + * + * 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. + * + */ + +/* + * Implement a nice and well behaved application icon. + */ + +#include "xdvi-config.h" +#include "xdvi.h" +#include "xicon.h" +#include "util.h" +#include "xdvi-debug.h" + +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xos.h> +#include <X11/Xfuncs.h> +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Shell.h> + +#ifdef MOTIF +# include <Xm/XmStrDefs.h> +#endif + +#if defined(HAVE_X11_XPM_H) +# include <X11/xpm.h> +#elif defined(HAVE_XPM_H) +# include <xpm.h> +#elif defined(HAVE_XM_XPMP_H) +# include <Xm/XpmP.h> +#endif + +#include "xdvi.icon" +#include "pixmaps/xdvi32x32.xpm" +#include "pixmaps/xdvi16x16.xpm" +#include "pixmaps/xdvi48x48.xpm" +void +add_icon(Widget top_level, + String title_name, + String icon_name) +{ +#if !HAVE_XPM + + static Arg args[] = { + {XtNiconX, (XtArgVal) 0}, + {XtNiconY, (XtArgVal) 0}, + }; + static Pixmap icon_pm; + static Arg temp_args4 = { XtNiconPixmap, (XtArgVal) &icon_pm }; + + UNUSED(xdvi16x16_xpm); + UNUSED(xdvi32x32_xpm); + UNUSED(xdvi48x48_xpm); + + if (resource.icon_geometry != NULL) { + int junk; + + (void)XGeometry(DISP, XScreenNumberOfScreen(SCRN), + resource.icon_geometry, "", + 0, 0, 0, 0, 0, + (int *)&args[0].value, + (int *)&args[1].value, + &junk, + &junk); + XtSetValues(top_level, args, XtNumber(args)); + } + /* Set icon pixmap */ + XtGetValues(top_level, &temp_args4, 1); + if (icon_pm == (Pixmap) 0) { + temp_args4.value = + (XtArgVal)XCreateBitmapFromData(DISP, + RootWindowOfScreen(SCRN), + (const char *)xdvi_bits, + xdvi_width, + xdvi_height); + XtSetValues(top_level, &temp_args4, 1); + } +#else /* HAVE_XPM */ + + /* Use Pixmaps, looking much nicer. */ + + static Pixmap icon = 0; + static Pixmap icon_mask = 0; + static const char **pixmap_data = xdvi32x32_xpm; + XIconSize *size; + int number_sizes; + Display *dsp; + Screen *scr; + XpmAttributes attr; + + UNUSED(xdvi_bits); + /* + * get the icon size preferred by the window manager + */ + if (XGetIconSizes(XtDisplay(top_level), RootWindowOfScreen(SCRN), + &size, &number_sizes) != 0) { + if (number_sizes > 0) { + if (size->max_height >= 48 && size->max_height >= 48) + pixmap_data = xdvi48x48_xpm; + else if (size->max_height >= 32 && size->max_height >= 32) + pixmap_data = xdvi32x32_xpm; + else if (size->max_height >= 16 && size->max_height >= 16) + pixmap_data = xdvi16x16_xpm; + } + } + + dsp = XtDisplay(top_level); + scr = XtScreen(top_level); + + attr.valuemask = 0L; + attr.valuemask = XpmCloseness | XpmReturnPixels | XpmColormap | XpmDepth | XpmVisual; + attr.closeness = 65535; /* accuracy isn't crucial */ + /* use the same visual/colormap/depth as main window, else BadMatch ... */ + attr.visual = G_visual; + attr.colormap = G_colormap; + attr.depth = G_depth; + + if (!icon) { + Window rootWindow = XtWindow(globals.widgets.top_level); + ASSERT(rootWindow != 0, ""); + XpmCreatePixmapFromData(dsp, rootWindow, (char **)pixmap_data, + &icon, &icon_mask, &attr); + } + +# ifdef MOTIF + XtVaSetValues(top_level, XmNiconPixmap, icon, XmNiconMask, icon_mask, NULL); +# else + XtVaSetValues(top_level, XtNiconPixmap, icon, XtNiconMask, icon_mask, NULL); +# endif + + XpmFreeAttributes(&attr); +#endif + + { + /* code locality ... */ + static Arg args[] = { + { XtNtitle, (XtArgVal) 0 }, + { XtNiconName, (XtArgVal) 0 }, + { XtNinput, (XtArgVal) True }, + }; + + args[0].value = (XtArgVal)title_name; + args[1].value = (XtArgVal)icon_name; + XtSetValues(top_level, args, XtNumber(args)); + } +} diff --git a/Build/source/texk/xdvik/gui/xicon.h b/Build/source/texk/xdvik/gui/xicon.h new file mode 100644 index 00000000000..47991b5b092 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xicon.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2001 Marcin Dalecki + * + * 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. + * + */ + +#ifndef XICON_H_ +#define XICON_H_ + +extern void add_icon(Widget toplevel, String title_name, String icon_name); + +#endif /* XICON_H_ */ diff --git a/Build/source/texk/xdvik/gui/xlwradio.c b/Build/source/texk/xdvik/gui/xlwradio.c new file mode 100644 index 00000000000..27bb14c3eb2 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xlwradio.c @@ -0,0 +1,677 @@ +/* adapted from xlwradio.c in the XEmacs distribution. + Changes are Copyright (C) 2002-2004 the xdvik development team. + + Note SU: This widget doesn't work with Xaw3d, and I have little + inclination to make it work - the design would need to be utterly + different, e.g. the radio would need to be a diamond instead of a + circle, since 3d-shadowed circles don't look circular; colormap + issues would need to be settled, etc. And besides, I don't like Xaw3d ;) + + So instead, the caller should just do: + + myWidget = XtVaCreateManagedWidget("foo", + #ifdef XAW + radioWidgetClass, + #else + toggleWidgetClass, + #endif + ... arglist ...); + + Feel free to submit patches if you want this implemented. + + Original copyright follows: +*/ + +/* Radio Widget for XEmacs. + Copyright (C) 1999 Edward A. Falk + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Radio.c 1.1 */ + +/* + * Radio.c - Radio button widget + * + * Author: Edward A. Falk + * falk@falconer.vip.best.com + * + * Date: June 30, 1997 + * + * + * Overview: This widget is identical to the Toggle widget in behavior, + * but completely different in appearance. This widget looks like a small + * diamond-shaped button with a label to the right. + * + * To make this work, we subclass the Toggle widget to inherit its behavior + * and to inherit the label-drawing function from which Toggle is + * subclassed. We then completely replace the Expose, Set, Unset + * and Highlight member functions. + * + * The Set and Unset actions are slightly unorthodox. In Toggle's + * ClassInit function, Toggle searches the Command actions list and + * "steals" the Set and Unset functions, caching pointers to them in its + * class record. It then calls these functions from its own ToggleSet + * and Toggle actions. + * + * We, in turn, override the Set() and Unset() actions in our own ClassRec. + */ + +#include "xdvi-config.h" +#include "xdvi.h" +#include "xdvi-debug.h" +#include "util.h" +#include "xaw_bitmaps.h" /* bitmaps for radio/checkbuttons */ + +#include "xlwradioP.h" + +#ifndef MOTIF /* entire file */ + +#include <stdio.h> + +#include <X11/IntrinsicP.h> +#include <X11/StringDefs.h> +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Xos.h> +#include <X11/Xaw/Text.h> +#include <X11/Xaw/AsciiText.h> +/* #include "../src/xmu.h" */ + +#define BOX_SIZE 16 +#define PIXMAP_OFFSET 2 /* additional space between pixmap and label */ + +#ifndef UNUSED +# define UNUSED(x) ((void)(x)) +#endif + +#ifndef MAX +# define MAX(i, j) ( (i) > (j) ? (i) : (j) ) +#endif + + +#define rclass(w) ((RadioWidgetClass)((w)->core.widget_class)) + + +#ifdef _ThreeDP_h +#define swid(rw) ((rw)->threeD.shadow_width) +#else +#define swid(rw) ((rw)->core.border_width) +#endif + +#define bsize(rw) (rclass(rw)->radio_class.dsize) +#define bs(rw) (bsize(rw) + PIXMAP_OFFSET + 2*swid(rw)) + +/**************************************************************** + * + * Full class record constant + * + ****************************************************************/ + +/* The translations table from Toggle do not need to be + * overridden by Radio + */ + + +/* Member functions */ + +static void RadioInit (Widget, Widget, ArgList, Cardinal *); +static void RadioExpose (Widget, XEvent *, Region); +static void RadioResize (Widget); +static void RadioDestroy (Widget); +static void RadioClassInit (void); +static void RadioClassPartInit (WidgetClass); +static Boolean RadioSetValues (Widget, Widget, Widget, ArgList, Cardinal *); +static void DrawDiamond (Widget); +static XtGeometryResult RadioQueryGeometry (Widget, XtWidgetGeometry *, + XtWidgetGeometry *); + +/* Action procs */ + +static void RadioHighlight (Widget, XEvent *, String *, Cardinal *); +static void RadioUnhighlight (Widget, XEvent *, String *, Cardinal *); + +/* internal privates */ + +static void RadioSize (RadioWidget, Dimension *, Dimension *); + +/* The actions table from Toggle is almost perfect, but we need + * to override Highlight, Set, and Unset. + */ + +static XtActionsRec actionsList[] = +{ + {"highlight", RadioHighlight}, + {"unhighlight", RadioUnhighlight}, +}; + +#define offset(field) XtOffset(RadioWidget, radio.field) + +static XtResource resources[] = { + {XtNisRadio, XtCIsRadio, XtRBoolean, sizeof(Boolean), + offset(isRadio), XtRImmediate, (XtPointer)True }, +}; +#undef offset + +#define SuperClass ((ToggleWidgetClass)&toggleClassRec) + +RadioClassRec radioClassRec = { + { + (WidgetClass) SuperClass, /* superclass */ + "Radio", /* class_name */ + sizeof(RadioRec), /* size */ + RadioClassInit, /* class_initialize */ + RadioClassPartInit, /* class_part_initialize */ + FALSE, /* class_inited */ + RadioInit, /* initialize */ + NULL, /* initialize_hook */ + XtInheritRealize, /* realize */ + actionsList, /* actions */ + XtNumber(actionsList), /* num_actions */ + resources, /* resources */ + XtNumber(resources), /* resource_count */ + NULLQUARK, /* xrm_class */ + TRUE, /* compress_motion */ + TRUE, /* compress_exposure */ + TRUE, /* compress_enterleave */ + FALSE, /* visible_interest */ + RadioDestroy, /* destroy */ + RadioResize, /* resize */ + RadioExpose, /* expose */ + RadioSetValues, /* set_values */ + NULL, /* set_values_hook */ + XtInheritSetValuesAlmost, /* set_values_almost */ + NULL, /* get_values_hook */ + NULL, /* accept_focus */ + XtVersion, /* version */ + NULL, /* callback_private */ + XtInheritTranslations, /* tm_table */ + RadioQueryGeometry, /* query_geometry */ + XtInheritDisplayAccelerator, /* display_accelerator */ + NULL /* extension */ + }, /* CoreClass fields initialization */ + { + XtInheritChangeSensitive /* change_sensitive */ +#ifndef HAVE_OLD_XAW + , NULL +#endif + }, /* SimpleClass fields initialization */ +#ifdef _ThreeDP_h + { + XtInheritXaw3dShadowDraw /* field not used */ + }, /* ThreeDClass fields initialization */ +#endif + { + 0 /* field not used */ + }, /* LabelClass fields initialization */ + { + 0 /* field not used */ + }, /* CommandClass fields initialization */ + { + RadioSet, /* Set Procedure. */ + RadioUnset, /* Unset Procedure. */ + NULL /* extension. */ + }, /* ToggleClass fields initialization */ + { + BOX_SIZE, + DrawDiamond, /* draw procedure */ + None, /* selected radiobutton */ + None, /* unselected radiobutton */ + None, /* selected menubutton */ + None, /* unselected menubutton */ + NULL /* extension. */ + } /* RadioClass fields initialization */ +}; + + /* for public consumption */ +WidgetClass radioWidgetClass = (WidgetClass) &radioClassRec; + + + + + + +/**************************************************************** + * + * Class Methods + * + ****************************************************************/ + +static void +RadioClassInit (void) +{ + XawInitializeWidgetSet(); +} + +static void +RadioClassPartInit (WidgetClass class) +{ + RadioWidgetClass c = (RadioWidgetClass) class; + RadioWidgetClass super = (RadioWidgetClass)c->core_class.superclass; + + if( c->radio_class.drawDiamond == NULL || + c->radio_class.drawDiamond == XtInheritDrawDiamond ) + { + c->radio_class.drawDiamond = super->radio_class.drawDiamond; + } + +} + + + + +static void +RadioInit (Widget request, + Widget new, + ArgList args, + Cardinal *num_args) +{ + RadioWidget rw = (RadioWidget) new; + RadioWidget rw_req = (RadioWidget) request; + Dimension w,h; + + UNUSED(args); + UNUSED(num_args); + + /* FIXME: should pixmap initialization be here?? */ + + /* Select initial size for the widget */ + if( rw_req->core.width == 0 || rw_req->core.height == 0 ) { + RadioSize(rw, &w,&h); + if( rw_req->core.width == 0 ) + rw->core.width = w; + if( rw_req->core.height == 0 ) + rw->core.height = h; + rw->core.widget_class->core_class.resize(new); + } + /* + FIXME: access to XtWindow(rw) fails in the init method, so + I moved the bitmap creation here -- is there a better way?? + */ + /* create pixmaps */ + rclass(rw)->radio_class.sel_radio + = XCreateBitmapFromData(XtDisplay(rw), RootWindowOfScreen(XtScreen(rw)), + (char *)button_radio_on_bits, BUTTON_BITMAP_W, BUTTON_BITMAP_H); + rclass(rw)->radio_class.unsel_radio + = XCreateBitmapFromData(XtDisplay(rw), RootWindowOfScreen(XtScreen(rw)), + (char *)button_radio_off_bits, BUTTON_BITMAP_W, BUTTON_BITMAP_H); + rclass(rw)->radio_class.sel_menu + = XCreateBitmapFromData(XtDisplay(rw), RootWindowOfScreen(XtScreen(rw)), + (char *)button_check_on_bits, BUTTON_BITMAP_W, BUTTON_BITMAP_H); + rclass(rw)->radio_class.unsel_menu + = XCreateBitmapFromData(XtDisplay(rw), RootWindowOfScreen(XtScreen(rw)), + (char *)button_check_off_bits, BUTTON_BITMAP_W, BUTTON_BITMAP_H); +} + +/* Function Name: RadioDestroy + * Description: Destroy Callback for radio widget. + * Arguments: w - the radio widget that is being destroyed. + * Returns: none. + */ + +static void +RadioDestroy (Widget w) +{ + RadioWidget rw = (RadioWidget)w; + /* de-allocate bitmaps */ + XFreePixmap(XtDisplay(w), rclass(rw)->radio_class.sel_radio); + XFreePixmap(XtDisplay(w), rclass(rw)->radio_class.unsel_radio); + XFreePixmap(XtDisplay(w), rclass(rw)->radio_class.sel_menu); + XFreePixmap(XtDisplay(w), rclass(rw)->radio_class.unsel_menu); +} + + +/* React to size change from manager. Label widget will compute some internal + * stuff, but we need to override. This code requires knowledge of the + * internals of the Label widget. + */ + +static void +RadioResize (Widget w) +{ + RadioWidget rw = (RadioWidget)w; + + /* call parent resize proc */ + SuperClass->core_class.resize(w); + + /* override label offset */ + + switch( rw->label.justify ) { + case XtJustifyLeft: + rw->label.label_x += (bs(rw) + rw->label.internal_width); + break; + case XtJustifyRight: + break; + case XtJustifyCenter: + default: + rw->label.label_x += (bs(rw) + rw->label.internal_width)/2; + break; + } +} + + +/* + * Repaint the widget window. + */ + +static void +RadioExpose (Widget w, + XEvent *event, + Region region) +{ + RadioWidget rw = (RadioWidget) w; + Display *dpy = XtDisplay(w); + Window win = XtWindow(w); + GC gc; + Pixmap left_bitmap; + extern WidgetClass labelWidgetClass; + + /* Note: the Label widget examines the region to decide if anything + * needs to be drawn. I'm not sure that this is worth the effort, + * but it bears thinking on. + */ + + /* Command widget may sometimes override the label GC in order + * to draw inverse video. We don't use inverse video, so we need + * to restore the label's normal GC. + */ + rw->label.normal_GC = rw->command.normal_GC; + + + /* Let label widget draw the label. If there was an lbm_x + * field, we could let Label draw the bitmap too. But there + * isn't, so we need to temporarily remove the bitmap and + * draw it ourself later. + */ + left_bitmap = rw->label.left_bitmap; + rw->label.left_bitmap = None; + labelWidgetClass->core_class.expose(w,event,region); + rw->label.left_bitmap = left_bitmap; + + /* now manually draw the left bitmap. TODO: 3-d look, xaw-xpm */ + gc = XtIsSensitive(w) ? rw->label.normal_GC : rw->label.gray_GC; + if( left_bitmap != None && rw->label.lbm_width > 0 ) + { + /* TODO: handle pixmaps */ + XCopyPlane(dpy, left_bitmap, win, gc, + 0,0, rw->label.lbm_width, rw->label.lbm_height, + (int) rw->label.internal_width*2 + bs(rw), + (int) rw->label.internal_height + rw->label.lbm_y, + 1UL); + } + DrawDiamond(w); + /* Finally, the button itself */ + ((RadioWidgetClass)(w->core.widget_class))->radio_class.drawDiamond(w); +} + + + + +/************************************************************ + * + * Set specified arguments into widget + * + ***********************************************************/ + + +/* ARGSUSED */ +static Boolean +RadioSetValues (Widget current, + Widget request, + Widget new, + ArgList args, + Cardinal *num_args) +{ + RadioWidget oldrw = (RadioWidget) current; + RadioWidget newrw = (RadioWidget) new; + + UNUSED(request); + UNUSED(args); + UNUSED(num_args); + + /* Need to find out if the size of the widget changed. Set new size + * if it did and resize is permitted. One way to determine of the + * widget changed size would be to scan the args list. Another way + * is to compare the old and new widgets and see if any of several + * size-related fields have been changed. The Label widget chose the + * former method, but I choose the latter. + */ + + if( newrw->label.resize && + ( newrw->core.width != oldrw->core.width || + newrw->core.height != oldrw->core.height || + newrw->core.border_width != oldrw->core.border_width ) ) + { + RadioSize(newrw, &newrw->core.width, &newrw->core.height); + } + + /* The label set values routine can resize the widget. We need to + * recalculate if this is true. + */ + if (newrw->label.label_x != oldrw->label.label_x) + { + RadioResize (new); + } + return FALSE; +} + +static XtGeometryResult +RadioQueryGeometry (Widget w, + XtWidgetGeometry *intended, + XtWidgetGeometry *preferred) +{ + RadioWidget rw = (RadioWidget) w; + + preferred->request_mode = CWWidth | CWHeight; + RadioSize(rw, &preferred->width, &preferred->height); + + if ( ((intended->request_mode & (CWWidth | CWHeight)) + == (CWWidth | CWHeight)) && + intended->width == preferred->width && + intended->height == preferred->height) + return XtGeometryYes; + else if (preferred->width == w->core.width && + preferred->height == w->core.height) + return XtGeometryNo; + else + return XtGeometryAlmost; +} + + + + + +/************************************************************ + * + * Action Procedures + * + ************************************************************/ + +/* + * Draw the highlight border around the widget. The Command widget + * did this by drawing through a mask. We do it by just drawing the + * border. + */ + +static void +DrawHighlight (Widget w, + GC gc) +{ + RadioWidget rw = (RadioWidget)w; + XRectangle rects[4]; + Dimension ht = rw->command.highlight_thickness; + + if( ht <= 0 || + ht > rw->core.width/2 || + ht > rw->core.height/2 ) + return; + + if( ! XtIsRealized(w) ) + return; + + rects[0].x = 0; rects[0].y = 0; + rects[0].width = rw->core.width; rects[0].height = ht; + rects[1].x = 0; rects[1].y = rw->core.height - ht; + rects[1].width = rw->core.width; rects[1].height = ht; + rects[2].x = 0; rects[2].y = ht; + rects[2].width = ht; rects[2].height = rw->core.height - ht*2; + rects[3].x = rw->core.width - ht; rects[3].y = ht; + rects[3].width = ht; rects[3].height = rw->core.height - ht*2; + XFillRectangles( XtDisplay(w), XtWindow(w), gc, rects, 4); +} + +static void +RadioHighlight (Widget w, + XEvent *event, + String *params, + Cardinal *num_params) +{ + RadioWidget rw = (RadioWidget)w; + UNUSED(event); + UNUSED(params); + UNUSED(num_params); + + DrawHighlight(w, rw->command.normal_GC); +} + + +static void +RadioUnhighlight (Widget w, + XEvent *event, + String *params, + Cardinal *num_params) +{ + RadioWidget rw = (RadioWidget)w; + UNUSED(event); + UNUSED(params); + UNUSED(num_params); + + DrawHighlight(w, rw->command.inverse_GC); +} + + +void +RadioSet (Widget w, + XEvent *event, + String *params, + Cardinal *num_params) +{ + RadioWidget rw = (RadioWidget)w; + RadioWidgetClass class = (RadioWidgetClass) w->core.widget_class; + + UNUSED(event); + UNUSED(params); + UNUSED(num_params); + + if( rw->command.set ) + return; + + rw->command.set = TRUE; + if( XtIsRealized(w) ) + class->radio_class.drawDiamond(w); +} + + +void +RadioUnset (Widget w, + XEvent *event, + String *params, + Cardinal *num_params) +{ + RadioWidget rw = (RadioWidget)w; + RadioWidgetClass class = (RadioWidgetClass) w->core.widget_class; + + UNUSED(event); + UNUSED(params); + UNUSED(num_params); + + if( ! rw->command.set ) + return; + + rw->command.set = FALSE; + if( XtIsRealized(w) ) + class->radio_class.drawDiamond(w); +} + + + + +/************************************************************ + * + * Internal Procedures + * + ************************************************************/ + + +/* Size of widget. Width is size of box plus width of border around + * box plus width of label plus three margins plus the size of the left + * bitmap, if any. Height is max(box,bitmap,label) plus two margins. + */ + +static void +RadioSize (RadioWidget rw, + Dimension *w, + Dimension *h) +{ + *w = rw->label.label_width + bs(rw) + LEFT_OFFSET(rw) + + 3 * rw->label.internal_width; + *h = MAX( rw->label.label_height, bs(rw) ) + + 2 * rw->label.internal_width; +} + + +static void +DrawDiamond(Widget w) +{ + RadioWidget rw = (RadioWidget) w; + Display *dpy = XtDisplay(w); + Window win = XtWindow(w); + + Position x = rw->label.internal_width; + Position y = rw->core.height/2; + GC gc = XtIsSensitive(w) ? rw->command.normal_GC : rw->label.gray_GC; + Pixmap selected, unselected; + + if (rw->radio.isRadio) { /* it's a radio button */ + selected = rclass(rw)->radio_class.sel_radio; + unselected = rclass(rw)->radio_class.unsel_radio; + } + else { + selected = rclass(rw)->radio_class.sel_menu; + unselected = rclass(rw)->radio_class.unsel_menu; + } + + ASSERT(selected != None, ""); + ASSERT(unselected != None, ""); + + if(rw->command.set) { + XCopyPlane(dpy, selected, win, gc, + 0, 0, BUTTON_BITMAP_H, BUTTON_BITMAP_W, + x, y - BUTTON_BITMAP_H / 2, 1L); + } + else { + XCopyPlane(dpy, unselected, win, gc, + 0, 0, BUTTON_BITMAP_H, BUTTON_BITMAP_W, + x, y - BUTTON_BITMAP_H / 2, 1L); + } +} + + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* MOTIF */ diff --git a/Build/source/texk/xdvik/gui/xlwradio.h b/Build/source/texk/xdvik/gui/xlwradio.h new file mode 100644 index 00000000000..728c97fcbee --- /dev/null +++ b/Build/source/texk/xdvik/gui/xlwradio.h @@ -0,0 +1,119 @@ +/* adapted from xlwradio.c in the XEmacs distribution. + Changes are Copyright (C) 2002-2004 the xdvik development team + Original copyright follows: +*/ + +/* Radio Widget for XEmacs. + Copyright (C) 1999 Edward A. Falk + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Radio.h 1.1 */ + +/* + * Radio.h - Radio button widget + * + * Author: Edward A. Falk + * falk@falconer.vip.best.com + * + * Date: June 30, 1997 + */ + +#ifndef _XawRadio_h +#define _XawRadio_h + +#include "xdvi-config.h" + +#ifndef MOTIF /* entire file */ + +/*********************************************************************** + * + * Radio Widget + * + * The Radio widget is identical to the Toggle widget in behavior but + * not in appearance. The Radio widget looks like a small diamond + * shaped button to the left of the label. + * (SU: changed this to use a bitmap instead.) + * + ***********************************************************************/ + +#include <X11/IntrinsicP.h> +#include <X11/StringDefs.h> +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Xos.h> +#include <X11/Xaw/Text.h> +#include <X11/Xaw/AsciiText.h> +#include <X11/Xaw/Toggle.h> + +/* Resources: + + Name Class RepType Default Value + ---- ----- ------- ------------- + radioGroup RadioGroup Widget NULL + radioData RadioData Pointer (XPointer) Widget + isRadio IsRadio Booelan True + state State Boolean Off + background Background Pixel XtDefaultBackground + bitmap Pixmap Pixmap None + border BorderColor Pixel XtDefaultForeground + borderWidth BorderWidth Dimension 1 + callback Callback Pointer NULL + cursor Cursor Cursor None + destroyCallback Callback Pointer NULL + font Font XFontStructx* XtDefaultFont + foreground Foreground Pixel XtDefaultForeground + height Height Dimension text height + highlightThickness Thickness Dimension 2 + insensitiveBorder sensitive Pixmap Gray + internalHeight Height Dimension 2 + internalWidth Width Dimension 4 + justify Justify XtJustify XtJustifyCenter + label Label String NULL + mappedWhenManaged MappedWhenManaged Boolean True + resize Resize Boolean True + sensitive Sensitive Boolean True + width Width Dimension text width + x Position Position 0 + y Position Position 0 + +*/ + +/* + * These should be in StringDefs.h but aren't so we will define + * them here if they are needed. + */ + + +extern WidgetClass radioWidgetClass; + +typedef struct _RadioClassRec *RadioWidgetClass; +typedef struct _RadioRec *RadioWidget; + +#define XtNisRadio "isRadio" +#define XtCIsRadio "IsRadio" + +/************************************************************ + * + * Public Functions + * + ************************************************************/ + +#endif /* MOTIF */ + +#endif /* _XawRadio_h */ diff --git a/Build/source/texk/xdvik/gui/xlwradioP.h b/Build/source/texk/xdvik/gui/xlwradioP.h new file mode 100644 index 00000000000..5ce6f654d7f --- /dev/null +++ b/Build/source/texk/xdvik/gui/xlwradioP.h @@ -0,0 +1,140 @@ +/* adapted from xlwradio.c in the XEmacs distribution. + Changes are Copyright (C) 2002-2004 the xdvik development team + Original copyright follows: +*/ + +/* Radio Widget for XEmacs. + Copyright (C) 1999 Edward A. Falk + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* + * RadioP.h - Private definitions for Radio widget + * + * Author: Edward A. Falk + * falk@falconer.vip.best.com + * + * Date: June 30, 1997 + * + */ + +#ifndef _XawRadioP_h +#define _XawRadioP_h + +#include "xdvi-config.h" + +#include "xlwradio.h" + +#ifndef MOTIF + +#include <X11/IntrinsicP.h> +#include <X11/StringDefs.h> +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Xos.h> +#include <X11/Xaw/Text.h> +#include <X11/Xaw/AsciiText.h> + +#include <X11/Xaw/ToggleP.h> + +/*********************************************************************** + * + * Radio Widget Private Data + * + ***********************************************************************/ + +typedef void (*XawDiamondProc) (Widget); + +void RadioSet (Widget w, + XEvent *event, + String *params, /* unused */ + Cardinal *num_params); /* unused */ + +void RadioUnset (Widget w, + XEvent *event, + String *params, /* unused */ + Cardinal *num_params); /* unused */ + +/************************************ + * + * Class structure + * + ***********************************/ + +/* New fields for the Radio widget class record */ +typedef struct _RadioClass { + Dimension dsize; /* diamond size */ + XawDiamondProc drawDiamond; + /* pixmaps for the button */ + Pixmap sel_radio; /* selected state */ + Pixmap unsel_radio; /* unselected state */ + Pixmap sel_menu; /* selected state */ + Pixmap unsel_menu; /* unselected state */ + /* TODO: 3-d and xaw-xpm features? */ + XtPointer extension; +} RadioClassPart; + +#define XtInheritDrawDiamond ((XawDiamondProc)_XtInherit) + +/* Full class record declaration */ +typedef struct _RadioClassRec { + CoreClassPart core_class; + SimpleClassPart simple_class; +#ifdef _ThreeDP_h + ThreeDClassPart threeD_class; +#endif + LabelClassPart label_class; + CommandClassPart command_class; + ToggleClassPart toggle_class; + RadioClassPart radio_class; +} RadioClassRec; + +extern RadioClassRec radioClassRec; + +/*************************************** + * + * Instance (widget) structure + * + **************************************/ + +/* New fields for the Radio widget record */ +typedef struct { + /* resources */ + Boolean isRadio; /* radio if True, checkbox else */ + /* TODO: 3-d and xaw-xpm features? */ + + /* private data */ + XtPointer extension; +} RadioPart; + + /* Full widget declaration */ +typedef struct _RadioRec { + CorePart core; + SimplePart simple; +#ifdef _ThreeDP_h + ThreeDPart threeD; +#endif + LabelPart label; + CommandPart command; + TogglePart toggle; + RadioPart radio; +} RadioRec; + +#endif /* MOTIF */ + +#endif /* _XawRadioP_h */ diff --git a/Build/source/texk/xdvik/gui/xm_colorsel.c b/Build/source/texk/xdvik/gui/xm_colorsel.c new file mode 100644 index 00000000000..9b91e2c76fd --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_colorsel.c @@ -0,0 +1,664 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +/* + * Color selector window showing a range of colors, and sliders to customize them. + * Heavily influenced by color_slide.c from ch. 13 of O'Reilly's Motif Programming Manual 1.2. + */ + +#include "xdvi-config.h" +#include "xdvi.h" + +#include "Tip.h" +#include "x_util.h" +#include "xm_colorsel.h" +#include "xm_prefsP.h" +#include "xm_prefs.h" +#include "events.h" +#include "dvi-draw.h" +#include "util.h" +#include "topic-window.h" +#include "search-internal.h" + +#ifdef MOTIF /* entire file */ + +#include <X11/Xatom.h> + +#include <Xm/Xm.h> +#include <Xm/Protocols.h> +#include <Xm/DialogS.h> +#include <Xm/LabelG.h> +#include <Xm/PushB.h> +#include <Xm/Form.h> +#include <Xm/PanedW.h> +#include <Xm/DrawnB.h> +#include <Xm/RowColumn.h> +#include <Xm/Scale.h> +#include <Xm/SelectioB.h> +# include <Xm/AtomMgr.h> + +#include <Xm/MwmUtil.h> + +#define ROWS 7 +#define COLS 10 + +/* for passing around information - no global data here ;-) */ +struct color_info { + XColor new_color; + Widget tooltip; + Widget new_sample; + Widget r_scale; + Widget g_scale; + Widget b_scale; + struct color_button_info *button_info; +}; + +/* + * These are the colors of the GTK/Mozilla color picker + * (see e.g. http://lxr.mozilla.org/mozilla/source/toolkit/content/widgets/colorpicker.xml) + * which has the colors more or less sorted by columns. + * It's terminated by NULL just in case the table gets out of sync with + * the row/column number. + */ +static const char *colors[] = { + /* column 1 */ + "#FFFFFF", "#CCCCCC", "#C0C0C0", "#999999", "#666666", "#333333", "#000000", + /* column 2 */ + "#FFCCCC", "#FF6666", "#FF0000", "#CC0000", "#990000", "#660000", "#330000", + /* column 3 */ + "#FFCC99", "#FF9966", "#FF9900", "#FF6600", "#CC6600", "#993300", "#663300", + /* column 4 */ + "#FFFF99", "#FFFF66", "#FFCC66", "#FFCC33", "#CC9933", "#996633", "#663333", + /* column 5 */ + "#FFFFCC", "#FFFF33", "#FFFF00", "#FFCC00", "#999900", "#666600", "#333300", + /* column 6 */ + "#99FF99", "#66FF99", "#33FF33", "#33CC00", "#009900", "#006600", "#003300", + /* column 7 */ + "#99FFFF", "#33FFFF", "#66CCCC", "#00CCCC", "#339999", "#336666", "#003333", + /* column 8 */ + "#CCFFFF", "#66FFFF", "#33CCFF", "#3366FF", "#3333FF", "#000099", "#000066", + /* column 9 */ + "#CCCCFF", "#9999FF", "#6666CC", "#6633FF", "#6600CC", "#333399", "#330099", + /* column 10 */ + "#FFCCFF", "#FF99FF", "#CC66CC", "#CC33CC", "#993399", "#663366", "#330033", + /* safety */ + NULL +}; + +#define MOTIF 1 + +static void +popdown_cb(Widget widget, XtPointer client_data, XtPointer call_data) +{ + Widget dialog; + struct prefs_choice *prefs = (struct prefs_choice *)client_data; + + UNUSED(call_data); + + ASSERT(prefs != NULL, "user_data in popdown_cb musn't be NULL!"); + if (get_widget_by_name(&dialog, widget, Xdvi_COLOR_DIALOG_NAME, True)) { +/* fprintf(stderr, "popdown!\n"); */ + remove_from_deplist(prefs, dialog); + XtUnmanageChild(dialog); + } +} + +static void +popdown_apply_cb(Widget widget, XtPointer client_data, XtPointer call_data) +{ + struct color_info *cinfo = NULL; + static XmDrawnButtonCallbackStruct cbs; + struct prefs_choice *prefs = (struct prefs_choice *)client_data; + + UNUSED(call_data); + ASSERT(prefs != NULL, "struct prefs_choice * in popdown_apply_cb mustn't be NULL!"); + + /* To update button to new color, set its button_info color + and call the button's expose callback */ + XtVaGetValues(widget, XmNuserData, &cinfo, NULL); + ASSERT(cinfo != NULL, "XmNuserData in popdown_apply_cb musn't be NULL!"); + cinfo->button_info->pixel = cinfo->new_color.pixel; + + cbs.reason = XmCR_EXPOSE; + XtCallCallbacks(cinfo->button_info->w, XmNexposeCallback, &cbs); + + remove_from_deplist(prefs, widget); + + /* Store the current setting - + FIXME: better way of converting color or pixel to string? pixel_to_str() in x_util.c is broken! */ + store_preference(&(prefs->db), cinfo->button_info->resource_name, + "#%.2x%.2x%.2x", + cinfo->new_color.red >> 8, + cinfo->new_color.green >> 8, + cinfo->new_color.blue >> 8); + + /* Update the display to use the new foreground/background. + * I don't really understand the entire color interface, but + * apparently the following works, and nothing else I tried did ... */ + if (strcmp(cinfo->button_info->resource_name, "foreground") == 0) { + XGCValues values; + + fg_initial.r = cinfo->new_color.red; + fg_initial.g = cinfo->new_color.green; + fg_initial.b = cinfo->new_color.blue; + + values.foreground = resource.fore_Pixel = resource.rule_pixel = cinfo->new_color.pixel; + XChangeGC(DISP, globals.gc.ruler, GCForeground, &values); + + scanned_page = scanned_page_color = scanned_page_reset; + globals.ev.flags |= EV_NEWPAGE; + + } + else if (strcmp(cinfo->button_info->resource_name, "background") == 0) { + bg_initial.r = cinfo->new_color.red; + bg_initial.g = cinfo->new_color.green; + bg_initial.b = cinfo->new_color.blue; + + resource.back_Pixel = cinfo->new_color.pixel; + scanned_page = scanned_page_color = scanned_page_reset; + globals.ev.flags |= EV_RELOAD; /* EV_NEWPAGE not sufficient with color code ... */ + } + else if (strcmp(cinfo->button_info->resource_name, "highlight") == 0) { + XGCValues values; + values.foreground = resource.hl_Pixel = cinfo->new_color.pixel; + XChangeGC(DISP, globals.gc.high, GCForeground, &values); + /* hack to update match GC: fake change in inverted property, redraw + so that GC is cleared, then change inverted property back */ + resource.match_highlight_inverted = !resource.match_highlight_inverted; + search_draw_inverted_regions(); + resource.match_highlight_inverted = !resource.match_highlight_inverted; + /* end of hack to update match GC */ + globals.ev.flags |= EV_NEWPAGE; /* force redraw */ + } + + XtUnmanageChild(widget); +} + +static void +popdown_cancel_cb(Widget widget, XtPointer client_data, XtPointer call_data) +{ + struct prefs_choice *prefs = (struct prefs_choice *)client_data; + +/* fprintf(stderr, "popdown cancel; call_data: %p!\n", call_data); */ + if (call_data != NULL) { +/* fprintf(stderr, "removing from deplist: %p!!!\n", prefs); */ + remove_from_deplist(prefs, widget); + } + + XtUnmanageChild(widget); +/* XtPopdown(XtParent(widget)); */ +} + +static void +revert_color_cb(Widget widget, XtPointer client_data, XtPointer call_data) +{ + struct color_info *cinfo = (struct color_info *)client_data; + Pixel pix; + unsigned r, g, b; + + UNUSED(call_data); + + /* get color of old_sample button */ + XtVaGetValues(widget, XmNbackground, &pix, NULL); + /* fprintf(stderr, "Color: 0x%.6lx\n", pix); */ + + /* use this instead of XtVaSetValues on XmNbackground so that + the foreground color gets also changed appropriately. */ +#if XmVersion >= 1002 + XmChangeColor(cinfo->new_sample, pix); +#else + XtVaSetValues(cinfo->new_sample, XmNbackground, pix, NULL); +#endif + + /* update sliders */ + XFreeColors(DISP, G_colormap, &(cinfo->new_color.pixel), 1, 0); + pixel_to_color(pix, &(cinfo->new_color), DISP, G_colormap); + + r = cinfo->new_color.red >> 8; + g = cinfo->new_color.green >> 8; + b = cinfo->new_color.blue >> 8; + +/* fprintf(stderr, "Pixel: 0x%.6lx\n", cinfo->new_color.pixel); */ + + XtVaSetValues(cinfo->r_scale, XmNvalue, r, NULL); + XtVaSetValues(cinfo->g_scale, XmNvalue, g, NULL); + XtVaSetValues(cinfo->b_scale, XmNvalue, b, NULL); +} + +static void +show_color_cb(Widget widget, XtPointer client_data, XtPointer call_data) +{ + struct color_info *cinfo = (struct color_info *)client_data; + Pixel pix; + unsigned r, g, b; + + UNUSED(call_data); + XtVaGetValues(widget, XmNbackground, &pix, NULL); +/* fprintf(stderr, "Color: 0x%.6lx\n", pix); */ + + /* use this instead of XtVaSetValues on XmNbackground so that + the foreground color gets also changed appropriately. */ +#if XmVersion >= 1002 + XmChangeColor(cinfo->new_sample, pix); +#else + XtVaSetValues(cinfo->new_sample, XmNbackground, pix, NULL); +#endif + + /* update sliders */ + XFreeColors(DISP, G_colormap, &(cinfo->new_color.pixel), 1, 0); + pixel_to_color(pix, &(cinfo->new_color), DISP, G_colormap); + + r = cinfo->new_color.red >> 8; + g = cinfo->new_color.green >> 8; + b = cinfo->new_color.blue >> 8; + +/* fprintf(stderr, "Pixel: 0x%.6lx\n", cinfo->new_color.pixel); */ + + XtVaSetValues(cinfo->r_scale, XmNvalue, r, NULL); + XtVaSetValues(cinfo->g_scale, XmNvalue, g, NULL); + XtVaSetValues(cinfo->b_scale, XmNvalue, b, NULL); +} + +static void +slider_cb(Widget widget, XtPointer client_data, XtPointer call_data) +{ + struct color_info *cinfo = (struct color_info *)client_data; + XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *)call_data; + +/* fprintf(stderr, "Pixel1 : 0x%.6lx\n", cinfo->new_color.pixel); */ + /* reuse cinfo->new_color */ + XFreeColors(DISP, G_colormap, &(cinfo->new_color.pixel), 1, 0); + + if (widget == cinfo->r_scale) { + cinfo->new_color.red = cbs->value << 8; /* << 8 == * 65535 */ + } + else if (widget == cinfo->g_scale) { + cinfo->new_color.green = cbs->value << 8; + } + else if (widget == cinfo->b_scale) { + cinfo->new_color.blue = cbs->value << 8; + } + + if (!XAllocColor(DISP, G_colormap, &(cinfo->new_color))) { + XDVI_ERROR((stderr, "Couldn't XAllocColor\n")); + return; + } +/* fprintf(stderr, "Pixel: 0x%.6lx\n", cinfo->new_color.pixel); */ + +#if XmVersion >= 1002 + /* if avaliable, use this so that the foreground color also + gets updated appropriately: */ + XmChangeColor(cinfo->new_sample, cinfo->new_color.pixel); +#else + XtVaSetValues(cinfo->new_sample, XmNbackground, cinfo->new_color.pixel, NULL); +#endif +} + +static void +h_init_sliders(struct color_info *cinfo) +{ + unsigned r, g, b; + + r = cinfo->new_color.red >> 8; + g = cinfo->new_color.green >> 8; + b = cinfo->new_color.blue >> 8; + + XtVaSetValues(cinfo->r_scale, XmNvalue, r, NULL); + XtVaSetValues(cinfo->g_scale, XmNvalue, g, NULL); + XtVaSetValues(cinfo->b_scale, XmNvalue, b, NULL); +} + +static Widget +h_create_color_matrix(Widget parent, Widget top, struct color_info *cinfo) +{ + int i, j; + const char **color_ptr = colors; + + Widget matrix = + XtVaCreateManagedWidget("color_matrix", xmRowColumnWidgetClass, + parent, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, top, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNpacking, XmPACK_COLUMN, + XmNnumColumns, COLS, + XmNorientation, XmVERTICAL, + XmNbackground, BlackPixelOfScreen(XtScreen(parent)), + XmNspacing, 1, + XmNmarginWidth, 1, + XmNmarginHeight, 1, + NULL); + + for (i = 0; i < ROWS; i++) { + for (j = 0; j < COLS; j++) { + Pixel pix = BlackPixelOfScreen(XtScreen(parent)); + Widget button; + if (*color_ptr == NULL) /* safety */ + break; + str_to_pixel(top, *color_ptr++, &pix); + /* fprintf(stderr, "creating button %d, %d - %s\n", i, j, k % 2 ? "b" : "w"); */ + button = XtVaCreateManagedWidget("c_button", xmDrawnButtonWidgetClass, + matrix, + XmNbackground, pix, + XmNheight, 19, + XmNwidth, 19, + XmNmarginWidth, 0, + XmNmarginHeight, 0, + XmNhighlightThickness, 0, + XmNshadowThickness, 0, + NULL); + XtAddCallback(button, XmNactivateCallback, show_color_cb, cinfo); + } + } + return matrix; +} + +static void +h_create_sliders(Widget parent, Widget top, struct color_info *cinfo) +{ + XmString str; + Widget r_label, g_label, b_label; + Widget r_scale, g_scale, b_scale; + Dimension curr, max; + + Arg scale_args[8]; + Arg label_args[8]; + int scale_n = 0; + + XtSetArg(scale_args[scale_n], XmNmaximum, 255); scale_n++; + /* True is an older setting of XmNshowValue that should also work with 2.x */ + XtSetArg(scale_args[scale_n], XmNshowValue, True); scale_n++; + XtSetArg(scale_args[scale_n], XmNorientation, XmHORIZONTAL); scale_n++; +#if XmVersion >= 2000 + XtSetArg(scale_args[scale_n], XmNshowArrows, XmEACH_SIDE); scale_n++; + /* XmVersion < 2000 only had `True' which was the default anyway */ +#endif + + str = XmStringCreateLocalized("Red"); + XtSetArg(label_args[0], XmNlabelString, str); + r_label = XtCreateManagedWidget("r_label", xmLabelGadgetClass, parent, label_args, 1); + XmStringFree(str); + + r_scale = XtCreateWidget("r_scale", xmScaleWidgetClass, parent, scale_args, scale_n); + cinfo->r_scale = r_scale; + XtAddCallback(r_scale, XmNdragCallback, slider_cb, cinfo); + XtAddCallback(r_scale, XmNvalueChangedCallback, slider_cb, cinfo); + + str = XmStringCreateLocalized("Green"); + XtSetArg(label_args[0], XmNlabelString, str); + g_label = XtCreateManagedWidget("g_label", xmLabelGadgetClass, parent, label_args, 1); + XmStringFree(str); + + g_scale = XtCreateWidget("g_scale", xmScaleWidgetClass, parent, scale_args, scale_n); + cinfo->g_scale = g_scale; + XtAddCallback(g_scale, XmNdragCallback, slider_cb, cinfo); + XtAddCallback(g_scale, XmNvalueChangedCallback, slider_cb, cinfo); + + str = XmStringCreateLocalized("Blue"); + XtSetArg(label_args[0], XmNlabelString, str); + b_label = XtCreateManagedWidget("b_label", xmLabelGadgetClass, parent, label_args, 1); + XmStringFree(str); + + b_scale = XtCreateWidget("b_scale", xmScaleWidgetClass, parent, scale_args, scale_n); + cinfo->b_scale = b_scale; + XtAddCallback(b_scale, XmNdragCallback, slider_cb, cinfo); + XtAddCallback(b_scale, XmNvalueChangedCallback, slider_cb, cinfo); + + /* get max width of labels, and adjust scale size to what's left */ + XtVaGetValues(r_label, XmNwidth, &curr, NULL); + XtVaGetValues(g_label, XmNwidth, &max, NULL); + if (curr > max) + max = curr; + XtVaGetValues(b_label, XmNwidth, &curr, NULL); + if (curr > max) + max = curr; + /* we know the width of the color selector is about 200 pixels (10 fields each 20 wide), + so we use that as max length */ + curr = 200 - max - 2; /* some additional offset */ + + /* fprintf(stderr, "width of slider: %d\n", curr); */ + /* fprintf(stderr, "width of string: %d\n", max); */ + + /* attach widgets; needs to be done after creating them since r_label + attachment references g_label etc. */ + XtVaSetValues(r_label, + XmNleftAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, g_label, + XmNbottomOffset, 20, /* needed to make label visible */ + NULL); + XtVaSetValues(r_scale, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, top, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, g_label, + XmNbottomOffset, 20, /* needed to make label visible */ + XmNscaleWidth, curr, + NULL); + XtVaSetValues(g_label, + XmNleftAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, b_label, + XmNbottomOffset, 20, /* needed to make label visible */ + NULL); + XtVaSetValues(g_scale, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, b_label, + XmNbottomOffset, 20, /* needed to make label visible */ + XmNscaleWidth, curr, + NULL); + XtVaSetValues(b_label, + XmNleftAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNbottomOffset, 15, + NULL); + XtVaSetValues(b_scale, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNbottomOffset, 15, + XmNscaleWidth, curr, + NULL); + + XtManageChild(r_scale); + XtManageChild(g_scale); + XtManageChild(b_scale); + + h_init_sliders(cinfo); + +} + +static Widget +h_create_shell(Widget parent, struct color_info *cinfo) +{ + Widget shell, dialog, form, button_row, color_matrix; + Widget old_sample, new_sample; + Widget tip_shell; + Atom WM_DELETE_WINDOW; + Arg args[32]; + int n; + + XmString str; + + Pixel old_pixel = cinfo->button_info->pixel; + /* hack to disable resizing of the shell, since this messes up the color matrix badly */ + int decoration = MWM_DECOR_ALL | MWM_DECOR_RESIZEH | MWM_DECOR_MENU | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE; + int functions = MWM_FUNC_ALL | MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE; + + struct color_button_info *binfo = cinfo->button_info; + struct topic_info *tinfo = binfo->tinfo; + struct prefs_choice *prefs = (struct prefs_choice *)tinfo->data; +/* struct prefs_choice *prefs; */ + + n = 0; + XtSetArg(args[n], XmNuserData, cinfo); n++; + +/* XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); n++; */ +/* XtSetArg(args[n], XmNnoResize, True); n++; */ +/* XtSetArg(args[n], XmNmwmFunctions, functions); n++; */ +/* XtSetArg(args[n], XmNmwmDecorations, decoration); n++; */ + XtSetArg(args[n], XtNtitle, "Xdvik: Select Color"); n++; + + dialog = XmCreatePromptDialog(parent, Xdvi_COLOR_DIALOG_NAME, args, n); + /* unmanage stuff to get a `scratch' widget */ + XtUnmanageChild(XtNameToWidget(dialog, "Help")); + XtUnmanageChild(XtNameToWidget(dialog, "Text")); + XtUnmanageChild(XtNameToWidget(dialog, "Selection")); + + shell = XtParent(dialog); + + XtVaSetValues(shell, + XmNdeleteResponse, XmDO_NOTHING, + XmNnoResize, True, + XmNmwmFunctions, functions, + XmNmwmDecorations, decoration, + NULL); + + /* cinfo->new_color is a allocated/freed by request; initialize it with old_pixel */ + XAllocColor(DISP, G_colormap, &(cinfo->new_color)); + cinfo->new_color.flags = DoRed | DoGreen | DoBlue; + + XFreeColors(DISP, G_colormap, &(cinfo->new_color.pixel), 1, 0); + pixel_to_color(old_pixel, &(cinfo->new_color), DISP, G_colormap); + + + tip_shell = XtVaCreatePopupShell("tipShell", tipWidgetClass, + parent, XtNwidth, 1, XtNheight, 1, + NULL); + cinfo->tooltip = tip_shell; + + form = XtVaCreateWidget("form", xmFormWidgetClass, + dialog, + XmNhorizontalSpacing, 10, + XmNverticalSpacing, 10, + XmNallowResize, False, + NULL); + + button_row = XtVaCreateManagedWidget("button_row", xmRowColumnWidgetClass, + form, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNorientation, XmHORIZONTAL, + XmNpacking, XmPACK_COLUMN, + XmNspacing, 8, + XmNmarginWidth, 1, + XmNmarginHeight, 1, + NULL); + + str = XmStringCreateLocalized("Old"); + old_sample = XtVaCreateManagedWidget(Xdvi_COLOR_DIALOG_OLD_SAMPLE_NAME, xmPushButtonWidgetClass, + button_row, + XmNbackground, old_pixel, + XmNlabelString, str, + XmNhighlightThickness, 0, + XmNshadowThickness, 1, + NULL); + XtAddCallback(old_sample, XmNactivateCallback, revert_color_cb, cinfo); + XmStringFree(str); + str = XmStringCreateLocalized("New"); + new_sample = XtVaCreateManagedWidget("new_sample", xmPushButtonWidgetClass, + button_row, + XmNbackground, cinfo->new_color.pixel, + XmNlabelString, str, + XmNhighlightThickness, 0, + XmNshadowThickness, 1, + NULL); + XmStringFree(str); + XtInsertEventHandler(new_sample, + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask| PointerMotionHintMask | + ButtonMotionMask | FocusChangeMask, + True, block_event_callback, + (XtPointer)0, 0); + cinfo->new_sample = new_sample; + + color_matrix = h_create_color_matrix(form, button_row, cinfo); + + h_create_sliders(form, color_matrix, cinfo); + + XtManageChild(form); + + XtAddCallback(dialog, XmNokCallback, popdown_apply_cb, prefs); + XtAddCallback(dialog, XmNcancelCallback, popdown_cancel_cb, prefs); + + XtManageChild(dialog); + + add_to_deplist(prefs, dialog); + +/* h_create_commands(shell, color_globals.widgets.paned, "OK", "Cancel", cinfo); */ + +/* XtManageChild(color_globals.widgets.paned); */ + +/* XtRealizeWidget(shell); */ + + TipAddWidget(tip_shell, old_sample, "Click to revert to old color"); + /* TipAddWidget(tip_shell, color_matrix, "Select color"); */ + + WM_DELETE_WINDOW = XmInternAtom(DISP, "WM_DELETE_WINDOW", False); + XmAddWMProtocolCallback(shell, WM_DELETE_WINDOW, popdown_cb, prefs); + + return dialog; +} + +void +popup_color_dialog(Widget parent, struct color_button_info *button_info) +{ + static Widget shell; + static Boolean first_time = True; + /* Must be static so that we can pass its address around */ + static struct color_info cinfo; + struct topic_info *tinfo = button_info->tinfo; + struct prefs_choice *prefs = (struct prefs_choice *)tinfo->data; + + cinfo.button_info = button_info; + + if (first_time) { + shell = h_create_shell(parent, &cinfo); + first_time = False; + } + else { + /* Already created, but we may need to change the colors of old_sample + and new_sample. This is done by changing the value of old_sample and + invoking its activate callback, which will `revert' new_sample to + the same color. */ + Widget w; + if (get_widget_by_name(&w, shell, Xdvi_COLOR_DIALOG_OLD_SAMPLE_NAME, True)) { +#if XmVersion >= 1002 + XmChangeColor(w, cinfo.button_info->pixel); +#else + XtVaSetValues(w, XmNbackground, cinfo.button_info->pixel, NULL); +#endif + XtCallCallbacks(w, XmNactivateCallback, &cinfo); + } + add_to_deplist(prefs, shell); + XtManageChild(shell); + } +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* MOTIF */ diff --git a/Build/source/texk/xdvik/gui/xm_colorsel.h b/Build/source/texk/xdvik/gui/xm_colorsel.h new file mode 100644 index 00000000000..bb4bd38b94a --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_colorsel.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +#ifndef XM_COLORSEL_H_ +#define XM_COLORSEL_H_ + +#ifdef MOTIF + +struct prefs_choice; +struct topic_info; + +struct color_button_info { + Widget w; /* current button */ + Pixel pixel; /* label color of this button */ + const char *resource_name; /* resource name to save this color to */ + struct topic_info *tinfo; +}; + +void popup_color_dialog(Widget parent, struct color_button_info *cinfo); + +#endif /* MOTIF */ + +#endif /* XM_COLORSEL_H_ */ diff --git a/Build/source/texk/xdvik/gui/xm_filesel.c b/Build/source/texk/xdvik/gui/xm_filesel.c new file mode 100644 index 00000000000..e5475f0bbe5 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_filesel.c @@ -0,0 +1,195 @@ +/* + * 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. + * + */ + +/* + * Implementation of the File selection dialogue for the Motif GUI. + */ + +#include "xdvi-config.h" +#include "xdvi.h" +#include "sfSelFile.h" + +#include "dvi.h" +#include "string-utils.h" +#include "util.h" +#include "events.h" +#include "message-window.h" +#include "dvi-init.h" /* for dviErrFlagT */ + +#if defined(MOTIF) /* entire file */ + +#include <Xm/FileSB.h> + +static Widget dialog = NULL; +static char *browse_fname = NULL; + +/* + * Process callback from Dialog cancel actions. + */ +static void +cancel_callback(Widget w, + XtPointer client_data, + XtPointer call_data) +{ + struct filesel_callback *callback = (struct filesel_callback *)client_data; + + UNUSED(w); + UNUSED(call_data); +#if 0 + /* DON'T reset browse_fname, so that user gets the current + value as default next time when he cancels now */ + if (browse_fname != NULL) + { + XtFree(browse_fname); + browse_fname = NULL; + } +#endif + + XtUnmanageChild(dialog); + if (callback->exit_on_cancel) { + exit(0); + } +} + +/* + * Process callback from Dialog actions. + */ + +static void +accept_callback(Widget w, + XtPointer client_data, + XtPointer call_data) +{ + XmFileSelectionBoxCallbackStruct *fcb; + struct filesel_callback *callback; + + UNUSED(w); + + ASSERT(client_data != NULL, "struct filesel_callback pointer expected in client data"); + callback = (struct filesel_callback *)client_data; + + if (browse_fname != NULL) { + XtFree(browse_fname); + browse_fname = NULL; + } + fcb = (XmFileSelectionBoxCallbackStruct *)call_data; + + /* get the filename from the file selection box */ + XmStringGetLtoR(fcb->value, G_charset, &browse_fname); + + if (callback->must_exist) { + FILE *tmp_fp = XFOPEN(browse_fname, "r"); + dviErrFlagT errflag = NO_ERROR; + if (tmp_fp == NULL) { + popup_message(XtParent(dialog), + MSG_ERR, NULL, "Could not open %s: %s.\n", + browse_fname, strerror(errno)); + /* leave file selection box open */ + return; + } + else if (!process_preamble(tmp_fp, &errflag) + || !find_postamble(tmp_fp, &errflag) + || !read_postamble(tmp_fp, &errflag, True)) { + popup_message(XtParent(dialog), + MSG_ERR, NULL, "Error opening %s:\n%s.", + browse_fname, get_dvi_error(errflag)); + fclose(tmp_fp); + /* leave file selection box open */ + return; + } + else { /* file is OK */ + fclose(tmp_fp); + } + } + + /* success; close dialog, and call our callback */ + XtUnmanageChild(dialog); + callback->func_ptr(browse_fname, callback->data); +} + +void raise_file_selector(void) +{ + /* dummy */ + return; +} + +void +XsraSelFile(Widget parent, struct filesel_callback *callback) +{ +#define ARG_CNT 4 + XmString filemask = NULL; + XmString directory = NULL; + Arg args[ARG_CNT]; + int i = 0; + + if (dialog == NULL) { + dialog = XmCreateFileSelectionDialog(parent, "file", NULL, 0); + /* Set ret_dialog to the DialogShell parent, not the command widget + returned by the Motif routines. We need the DialogShell, otherwise + Motif will crash if ret_dialog is used as parent for popup_message() + windows!!! + */ + XtVaSetValues(XtParent(dialog), XmNtitle, callback->title, NULL); + + /* my_must_exist = must_exist; */ + /* XtAddCallback(dialog, XmNokCallback, accept_callback, (XtPointer)&my_must_exist); */ + XtAddCallback(dialog, XmNokCallback, accept_callback, (XtPointer)callback); + XtAddCallback(dialog, XmNcancelCallback, cancel_callback, (XtPointer)callback); + /* We have no help in this window, so hide help button */ + XtUnmanageChild(XmFileSelectionBoxGetChild(dialog, (unsigned char)XmDIALOG_HELP_BUTTON)); + } + else if (XtIsManaged(dialog)) { + XBell(DISP, 10); + XRaiseWindow(DISP, XtWindow(dialog)); + return; + } + + /* only show files matching our mask */ + filemask = XmStringCreateLtoR((char *)callback->filemask, G_charset); + XtSetArg(args[i], XmNpattern, filemask); i++; + + /* set directory to last directory used */ + if (browse_fname != NULL) { + char *path = xstrdup(browse_fname); + char *ptr = strrchr(path, '/'); + if (ptr != NULL) + *ptr = '\0'; + directory = XmStringCreateLtoR(path, G_charset); + XtSetArg(args[i], XmNdirectory, directory); i++; + free(path); + } + + ASSERT(i < ARG_CNT, "args list too short"); + XtSetValues(dialog, args, (Cardinal)i); + + free(filemask); + free(directory); + + XtManageChild(dialog); +#undef ARG_CNT +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* defined(MOTIF) */ diff --git a/Build/source/texk/xdvik/gui/xm_menu.c b/Build/source/texk/xdvik/gui/xm_menu.c new file mode 100644 index 00000000000..51a2bedbd53 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_menu.c @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2001 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 "xm_menu.h" +#include "xm_toolbar.h" +#include "my-snprintf.h" +#include "util.h" +#include "x_util.h" +#include "version.h" +#include "statusline.h" +#include "pagehist.h" +#include "dvi-init.h" +#include "filehist.h" +#include "c-openmx.h" +#include "message-window.h" + +#ifdef MOTIF /* needed for `make depend' */ + +#include <Xm/RowColumn.h> +#include <Xm/Frame.h> +#include <Xm/PushB.h> +#include <Xm/PushBG.h> +#include <Xm/CascadeBG.h> +#include <Xm/SeparatoG.h> +#include <Xm/ToggleB.h> +#include <Xm/ToggleBG.h> +#include <Xm/Separator.h> + + +/* + * There's an apparent bug in Motif related to the interaction between + * the menubar menus and the magnifier. + * + * If you click on a menu on the menubar and then click on the drawing + * widget to pop up a magnifier, the keyboard and pointer are still + * grabbed, leading to a weird situation in which the magnifier stays + * around even after you release the pointer. The ungrab_serial + * counter works around this bug by ignoring such pointer events. + * + * This bug occurs in Motif 1.2 and OpenMotif 2.2.2 (at least). It + * does not occur in Lesstif. + */ +static unsigned long ungrab_event_num = 0; + +void +popdown_callback(Widget w, XtPointer client_data, XtPointer call_data) +{ + UNUSED(w); + UNUSED(client_data); + /* Lesstif gives call_data == NULL */ + if (call_data != NULL && *((XtGrabKind *) call_data) != XtGrabNone) { + ungrab_event_num = LastKnownRequestProcessed(DISP); + } +} + +Boolean +pulldown_menu_active(unsigned long event_num) +{ + return event_num < ungrab_event_num; +} + + +void +toggle_menubar(void) +{ + if ((resource.expert_mode & XPRT_SHOW_MENUBAR) == 0) + XtUnmanageChild(globals.widgets.menu_bar); + else + XtManageChild(globals.widgets.menu_bar); +} + +static void +update_menu_labels(Widget menu) +{ + WidgetList children; + int num_children; + int i; + + static char *buf = NULL; + static size_t buf_len = 0; + + XtVaGetValues(menu, + XmNnumChildren, &num_children, + XmNchildren, &children, + NULL); + +/* for (i = 0; i < num_children; i++) { */ + for (i = 0; i < (int)file_history_size(); i++) { + int dummy_page; + char *filename; + size_t new_len; + XmString str; + + if ((filename = file_history_get_elem(i, &dummy_page)) == NULL) { + XDVI_ERROR((stderr, "Error accessing element %d of file history", i)); + continue; + } + + new_len = LENGTH_OF_INT + strlen(filename) + 1; + if (new_len > buf_len) { + buf = xrealloc(buf, new_len); + buf_len = new_len; + } + + sprintf(buf, "%d %s", i + 1, filename); + str = XmStringCreateLocalized(buf); + XtVaSetValues(children[i], + XmNlabelString, str, + NULL); + if (i + 1 < 10) { + XtVaSetValues(children[i], XmNmnemonic, buf[0], NULL); + } + XmStringFree(str); + } + + /* if history size < number of menu entries, destroy excess menu entries */ + for (; i < num_children; i++) { + XtDestroyWidget(children[i]); + } +} + +static void +filehist_select_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + Widget menu; + XmString label; + char *label_ptr; + int pageno; + char *fname; + int idx; + +/* UNUSED(w); */ + UNUSED(call_data); + UNUSED(client_data); + + XtVaGetValues(w, + XmNuserData, &menu, + XmNlabelString, &label, + NULL); + + XmStringGetLtoR(label, G_charset, &label_ptr); + idx = strtol(label_ptr, (char **)NULL, 10) - 1; + XtFree(label_ptr); + + ASSERT(menu != NULL, "XmNuserData in filehist_select_cb musn't be NULL!"); + + if (idx == 0) { /* user re-selected current file: reload */ + globals.ev.flags |= EV_RELOAD; + return; + } + if ((fname = file_history_get_elem(idx, &pageno)) == NULL) { + statusline_print(STATUS_MEDIUM, "Error accessing file %d of history", idx); + return; + } + + file_history_open(fname); +} + +void +filehist_menu_refresh(void) +{ + Widget menu; + if (get_widget_by_name(&menu, globals.widgets.menu_bar, Xdvi_FILEHIST_MENU, True)) { + update_menu_labels(menu); + } +} + +void +filehist_menu_add_entry(const char *filename) +{ + Widget menu; + + static char *buf = NULL; + static size_t buf_len = 0; + size_t new_len = LENGTH_OF_INT + strlen(filename) + 1; + + if (get_widget_by_name(&menu, globals.widgets.menu_bar, Xdvi_FILEHIST_MENU, True)) { + int num_children; + Widget w; + + if (new_len > buf_len) { + buf = xrealloc(buf, new_len); + buf_len = new_len; + } + + XtVaGetValues(menu, XmNnumChildren, &num_children, NULL); + + sprintf(buf, "%d %s", num_children + 1, filename); + + w = XtVaCreateManagedWidget(buf, xmPushButtonGadgetClass, menu, + XmNuserData, menu, + NULL); + if (num_children + 1 < 10) { + XtVaSetValues(w, XmNmnemonic, buf[0], NULL); + } + XtAddCallback(w, XmNactivateCallback, filehist_select_cb, (XtPointer)(num_children + 1)); + + update_menu_labels(menu); + + } +} + +static void +filehist_submenu(int idx, const char *filename, int pageno, void *data) +{ + Widget menu = (Widget)data; + Widget w; + static char *buf = NULL; + static size_t buf_len = 0; + size_t new_len = LENGTH_OF_INT + strlen(filename) + 1; + + if (new_len > buf_len) { + buf = xrealloc(buf, new_len); + buf_len = new_len; + } + + UNUSED(pageno); + + sprintf(buf, "%d %s", idx + 1, filename); + TRACE_GUI((stderr, "Creating menu `%s'", buf)); + w = XtVaCreateManagedWidget(buf, xmPushButtonGadgetClass, menu, +/* XmNmnemonic, buf[0], */ + XmNuserData, menu, + NULL); + if (idx + 1 < 10) { + XtVaSetValues(w, XmNmnemonic, buf[0], NULL); + } + XtAddCallback(w, XmNactivateCallback, filehist_select_cb, (XtPointer)idx); +} + + + +static Widget +recent_files_submenu(Widget parent, char *title, char mnemonic) +{ + Widget menu = 0, cascade = 0; + XmString str; + + menu = XmCreatePulldownMenu(parent, Xdvi_FILEHIST_MENU, NULL, 0); + str = XmStringCreateLocalized(title); + cascade = XtVaCreateManagedWidget(title, xmCascadeButtonGadgetClass, parent, + XmNsubMenuId, menu, + XmNlabelString, str, + XmNmnemonic, mnemonic, + NULL); + XmStringFree(str); + + file_history_enumerate(filehist_submenu, menu); + return cascade; +} + +Widget +xm_create_menu(Widget parent, char *title, char mnemonic, struct button_info *item) +{ + Widget menu = 0, cascade = 0, w = 0; + size_t i; + XmString str; + + menu = XmCreatePulldownMenu(parent, "_pulldown", NULL, 0); + str = XmStringCreateLocalized(title); + cascade = XtVaCreateManagedWidget(title, xmCascadeButtonGadgetClass, parent, + XmNsubMenuId, menu, + XmNlabelString, str, + XmNmnemonic, mnemonic, + NULL); + XmStringFree(str); + + /* add the menu items */ + for (i = 0; item != NULL && i < item->size; i++) { + item->elems[i].widget = 0; + /* if there's a subitem, call this function recursively */ + if (item->elems[i].submenu != NULL) { + w = xm_create_menu(menu, item->elems[i].title, + item->elems[i].mnemonic, item->elems[i].submenu); + /* + workaround for pointer grabbing bug; add additional callback to all menus + (they have the same parent) + */ + XtAddCallback(XtParent(menu), XtNpopdownCallback, + popdown_callback, (XtPointer) 0); + } + else if (item->elems[i].action != NULL && item->elems[i].action->proc == Act_recent_files) { + /* special case: submenu with recent files */ + w = recent_files_submenu(menu, item->elems[i].title, item->elems[i].mnemonic); + XtAddCallback(XtParent(menu), XtNpopdownCallback, + popdown_callback, (XtPointer) 0); + } + else { + switch(item->elems[i].type) { + case BT_PUSH: + w = XtVaCreateManagedWidget(item->elems[i].title, xmPushButtonGadgetClass, menu, + NULL); + break; + case BT_RADIO: + w = XtVaCreateManagedWidget(item->elems[i].title, xmToggleButtonGadgetClass, menu, + XmNindicatorType, XmONE_OF_MANY, + NULL); + break; + case BT_CHECK: + w = XtVaCreateManagedWidget(item->elems[i].title, xmToggleButtonGadgetClass, menu, + XmNindicatorType, XmN_OF_MANY, + NULL); + break; + case BT_SEP: + w = XtVaCreateManagedWidget(item->elems[i].title, xmSeparatorGadgetClass, menu, + NULL); + break; + default: + XDVI_WARNING((stderr, "unrecognized button type %d in menu %s (skipping this item).\n", + item->elems[i].type, item->elems[i].title)); + break; + } + item->elems[i].widget = w; + } + + if (item->elems[i].mnemonic != 0) + XtVaSetValues(w, XmNmnemonic, item->elems[i].mnemonic, NULL); + + if (item->elems[i].accelerator != NULL) { + str = XmStringCreateLocalized(item->elems[i].accelerator); + XtVaSetValues(w, XmNacceleratorText, str, NULL); + XmStringFree(str); + } + + if (item->elems[i].action != NULL) { + String cb_type; + if (XmIsToggleButton(w) || XmIsToggleButtonGadget(w)) + cb_type = XmNvalueChangedCallback; + else + cb_type = XmNactivateCallback; + XtAddCallback(w, cb_type, handle_command, item->elems[i].action); + } + } + return cascade; +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* MOTIF */ + diff --git a/Build/source/texk/xdvik/gui/xm_menu.h b/Build/source/texk/xdvik/gui/xm_menu.h new file mode 100644 index 00000000000..28dab3a384e --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_menu.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2001 Marcin Dalecki + * Copyright (c) 2002-2004 the xdvik development team + * + * 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. + * + */ + +#ifndef XM_MENU_H_ +#define XM_MENU_H_ + +#include "menu.h" + +#ifdef MOTIF + +#define Xdvi_FILEHIST_MENU "filehist_menu" + +extern Widget xm_create_menu(Widget parent, char *title, char mnemonic, + struct button_info *item); + +extern Boolean pulldown_menu_active(unsigned long event_num); +extern void toggle_menubar(void); +extern void popdown_callback(Widget w, XtPointer client_data, XtPointer call_data); +extern void filehist_menu_add_entry(const char *filename); +extern void filehist_menu_refresh(void); + +#endif + +#endif /* XM_MENU_H_ */ diff --git a/Build/source/texk/xdvik/gui/xm_prefs.c b/Build/source/texk/xdvik/gui/xm_prefs.c new file mode 100644 index 00000000000..388ef07ac12 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_prefs.c @@ -0,0 +1,590 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +/* + * Preferences dialog for xdvik. + */ + +#include "xdvi-config.h" +#include "xdvi.h" + +#include "x_util.h" +#include "xm_colorsel.h" +#include "topic-window.h" +#include "message-window.h" +#include "util.h" +#include "events.h" + +#include "xm_prefsP.h" +#include "xm_prefs.h" + +#include "xm_prefs_appearance.h" +#include "xm_prefs_fonts.h" +#include "xm_prefs_helpers.h" +#include "xm_prefs_page.h" +#include "xm_prefs_scroll.h" + +/* for reverting preferences, we need access to more prototypes ... */ +#include "dvi-draw.h" +#include "search-internal.h" +#include "statusline.h" +#include "xm_toolbar.h" +#include "xm_menu.h" +#include "pagesel.h" + +#ifdef MOTIF /* entire file */ + +#include <X11/Xatom.h> + +#include <Xm/Xm.h> +#include <Xm/Protocols.h> +#include <Xm/DialogS.h> +#include <Xm/Form.h> + +#define SCROLLING_DONE 0 + +#define NUM_PREFS_TOPICS 16 /* should be ample ... */ + +/* hmm, should maybe move these into xm_prefsP.c ... */ +void +h_attach_below(Widget x, Widget y) { + if (y == NULL) { + /* no top widget, attach to form - could just pass NULL as XmNtopWidget, but lesstif warns in that case ... */ + XtVaSetValues(x, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + NULL); + } + else { + XtVaSetValues(x, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, y, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + NULL); + } +} + + +static void +initialize_items(struct topic_info *info) +{ + int i = 0; + + info->items[i].widget = prefs_appearance(info); + info->items[i].topic = xstrdup("Appearance"); + info->items[i].title = xstrdup("Change the appearance of xdvi"); + + i++; + info->items[i].widget = prefs_fonts_and_colors(info); + info->items[i].topic = xstrdup("Fonts and Colors"); + info->items[i].title = xstrdup("Display of fonts and colors in DVI files"); + + i++; + info->items[i].widget = prefs_paper(info); + info->items[i].topic = xstrdup("Page Size"); + info->items[i].title = xstrdup("Customize the default window and page size"); + +#if SCROLLING_DONE /* not finished yet */ + i++; + info->items[i].widget = prefs_scrolling(info); + info->items[i].topic = xstrdup("Scrolling"); + info->items[i].title = xstrdup("Customize the scrolling behaviour when switching pages"); +#endif + + i++; + info->items[i].widget = prefs_helpers(info); +#ifdef LESSTIF_VERSION + /* compensate for width computation bug by adding extra whitespace at end */ + info->items[i].topic = xstrdup("Helper Applications "); +#else + info->items[i].topic = xstrdup("Helper Applications"); +#endif + info->items[i].title = xstrdup("External programs used by xdvi"); + + /* terminate */ + i++; + info->items[i].widget = 0; + info->items[i].topic = info->items[i].title = NULL; + + ASSERT(i < NUM_PREFS_TOPICS, "NUM_PREFS_TOPICS too small!"); +} + +void +remove_from_deplist(struct prefs_choice *prefs, Widget w) +{ + size_t i; + Boolean found = False; + + /* locate this widget */ + for (i = 0; i < prefs->depwin_cnt; i++) { + if (prefs->depwin[i] == w) { + found = True; + break; + } + } + + if (found) { + /* move later widgets down */ + for (; i < prefs->depwin_cnt - 1; i++) { + prefs->depwin[i] = prefs->depwin[i + 1]; + } + prefs->depwin_cnt--; + } + +#if 0 + fprintf(stderr, "deplist after removal:\n"); + for (i = 0; i < prefs->depwin_cnt; i++) { + fprintf(stderr, "%d: %p\n", i, prefs->depwin[i]); + } +#endif +} + +static void +update_button(Widget button, Pixel pix) +{ + struct color_button_info *cinfo; + static XmDrawnButtonCallbackStruct cbs; + + XtVaGetValues(button, XmNuserData, &cinfo, NULL); + cinfo->pixel = pix; + cbs.reason = XmCR_EXPOSE; + XtCallCallbacks(button, XmNexposeCallback, &cbs); +} + +static void +update_button_by_name(Pixel pix, const char *name) +{ + static Widget pref_shell = 0; + Widget button; + + if (pref_shell == 0 + && !get_widget_by_name(&pref_shell, globals.widgets.top_level, Xdvi_PREFS_DIALOG_NAME, True)) + return; + + if (get_widget_by_name(&button, pref_shell, name, True)) { + update_button(button, pix); + } +} + +static Boolean +revert_colors(Pixel fg, Pixel bg, Pixel hl) +{ + XColor color_data[2]; + Boolean need_redraw = False; + Widget button; + Pixel visited, unvisited; + + color_data[0].pixel = resource.fore_Pixel; + color_data[1].pixel = resource.back_Pixel; + + str_to_pixel(globals.widgets.top_level, resource.visited_link_color, &visited); + str_to_pixel(globals.widgets.top_level, resource.link_color, &unvisited); + + XQueryColors(DISP, G_colormap, color_data, 2); + + if (fg != resource.fore_Pixel) { + XGCValues values; + + fg_initial.r = color_data[0].red; + fg_initial.g = color_data[0].green; + fg_initial.b = color_data[0].blue; + + values.foreground = resource.rule_pixel = resource.fore_Pixel; + XChangeGC(DISP, globals.gc.ruler, GCForeground, &values); + + scanned_page = scanned_page_color = scanned_page_reset; + + update_button_by_name(resource.fore_Pixel, Xdvi_FG_COLOR_BTN); + + need_redraw = True; + } + if (bg != resource.back_Pixel) { + bg_initial.r = color_data[1].red; + bg_initial.g = color_data[1].green; + bg_initial.b = color_data[1].blue; + + scanned_page = scanned_page_color = scanned_page_reset; + + update_button_by_name(resource.back_Pixel, Xdvi_BG_COLOR_BTN); + + need_redraw = True; + } + if (hl != resource.hl_Pixel) { + XGCValues values; + values.foreground = resource.hl_Pixel; + XChangeGC(DISP, globals.gc.high, GCForeground, &values); + /* hack to update match GC: fake change in inverted property, redraw + so that GC is cleared, then change inverted property back */ + resource.match_highlight_inverted = !resource.match_highlight_inverted; + search_draw_inverted_regions(); + resource.match_highlight_inverted = !resource.match_highlight_inverted; + + update_button_by_name(resource.hl_Pixel, Xdvi_HL_COLOR_BTN); + + need_redraw = True; + } + /* NOTE: pixels for hyperlinks are not stored anywhere; just update always: */ + if (get_widget_by_name(&button, globals.widgets.top_level, Xdvi_VISITED_LINKS_BTN, True)) { + update_button(button, visited); + h_update_hyperlinks(button, visited); /* this already triggers a redraw */ + } + if (get_widget_by_name(&button, globals.widgets.top_level, Xdvi_UNVISITED_LINKS_BTN, True)) { + update_button(button, unvisited); + h_update_hyperlinks(button, unvisited); /* this already triggers a redraw */ + } + return need_redraw; +} + +static void +revert_resources(void) +{ + Pixel curr_fg = resource.fore_Pixel; + Pixel curr_bg = resource.back_Pixel; + Pixel curr_hl = resource.hl_Pixel; + Boolean need_redraw = False; + + /* save some old values */ + int save_shrink = resource.shrinkfactor; + + reload_app_resources(); + + /* revert from saved values */ + resource.use_color = globals.curr_use_color; + resource.gamma = globals.curr_gamma; + resource.paper = globals.curr_paper; + resource.shrinkfactor = save_shrink; + do_set_shrinkfactor(resource.shrinkfactor, True); + + update_preferences_darkness(); + + /* revert expert mode */ + if (resource.expert) + resource.expert_mode = XPRT_SHOW_NONE; + update_expert_mode(); + + toggle_statusline(); +#ifndef MOTIF + if (!BROKEN_RECONFIG) + toggle_scrollbars(); +#else + toggle_scrollbars(); +#endif + +#ifdef MOTIF + toggle_pagelist(); + toggle_toolbar(); + toggle_menubar(); +#else + toggle_buttons(); +#endif + + /* reset tooltips_wait_period, similar to TipAddWidget() in Tip.c */ + resource.tooltips_wait_period = resource.tooltips_wait_period_bak; + if (resource.tooltips_wait_period < 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; + } + + update_preferences_expert(); + update_preferences_tooltips(); + update_preferences_search(); + + update_preferences_color(); + update_preferences_hyperlinks(); + + update_preferences_windowsize(); + update_preferences_shrink(); + update_preferences_paper(); + + update_preferences_helpers(); + + need_redraw |= revert_colors(curr_fg, curr_bg, curr_hl); + +/* if (need_redraw) */ + /* just reload it always, there's too many exceptions ... + redraw isn't always sufficient if file has colors. */ + globals.ev.flags |= EV_RELOAD; + +} + +/* add widget w to list of dependent windows */ +void +add_to_deplist(struct prefs_choice *prefs, Widget w) +{ +#if 0 + size_t i; +#endif + prefs->depwin = xrealloc(prefs->depwin, + (prefs->depwin_cnt + 1) * sizeof *(prefs->depwin)); + prefs->depwin[prefs->depwin_cnt] = w; + prefs->depwin_cnt++; + +#if 0 + fprintf(stderr, "deplist after adding:\n"); + for (i = 0; i < prefs->depwin_cnt; i++) { + fprintf(stderr, "%lu: %p\n", (unsigned long)i, prefs->depwin[i]); + } +#endif +} + +static void +reread_prefs_cb(Window w) +{ + if (w != XtWindow(globals.widgets.top_level)) { + XChangeProperty(DISP, w, + atom_reread_prefs(), atom_reread_prefs(), 8, + PropModeReplace, + /* dummy values, since all the other instance needs to do is + reread the ~/.xdvirc.tmp file */ + (unsigned char *)"Y", 1); + } +} + +static void +apply_prefs_cb(XtPointer arg) +{ + struct topic_info *info = (struct topic_info *)arg; + struct prefs_choice *prefs = (struct prefs_choice *)info->data; + size_t i; + Widget colorsel; + + if (get_widget_by_name(&colorsel, globals.widgets.top_level, Xdvi_COLOR_DIALOG_NAME, False)) { + XtPopdown(XtParent(colorsel)); + } + + /* pop down dependent windows */ + TRACE_GUI((stderr, "window count: %lu\n", (unsigned long)prefs->depwin_cnt)); + for (i = 0; i < prefs->depwin_cnt; i++) { + TRACE_GUI((stderr, "popping down %lu: %p", (unsigned long)i, (void *)(prefs->depwin[i]))); + if (XtIsRealized(prefs->depwin[i])) { + XtCallCallbacks(prefs->depwin[i], XmNcancelCallback, NULL); + XSync(DISP, True); /* wait for server to catch up */ + if (XtIsRealized(prefs->depwin[i])) { + TRACE_GUI((stderr, "calling XmNokCallback of %lu: %p", (unsigned long)i, (void *)(prefs->depwin[i]))); + XtCallCallbacks(prefs->depwin[i], XmNokCallback, NULL); + } + } + } + free(prefs->depwin); + prefs->depwin = NULL; + prefs->depwin_cnt = 0; + + if (prefs->db == NULL) /* callback invoked multiple times? */ + return; + + merge_into_user_db(prefs->db); /* this destroys prefs->db */ + prefs->db = NULL; + + /* remember some current values */ + free(globals.curr_paper); + if (resource.paper != NULL) + globals.curr_paper = xstrdup(resource.paper); + + free(globals.curr_editor); + if (resource.editor != NULL) + globals.curr_editor = xstrdup(resource.editor); + + free(globals.curr_browser); + if (resource.browser != NULL) + globals.curr_browser = xstrdup(resource.browser); + +/* fprintf(stderr, "set curr_browser to: |%s|\n", globals.curr_browser); */ +/* fprintf(stderr, "set curr_editor to: |%s|\n", globals.curr_editor); */ + + if (get_xdvi_window_id(False, NULL) && save_user_preferences(False)) { + /* if other instances of xdvi are running, make them reread the + changed preferences by writing them to ~/.xdvirc.tmp and having + them read that file; otherwise they would overwrite the file if + user quits them after the current instance. + */ + get_xdvi_window_id(False, reread_prefs_cb); + } +} + +static void +revert_prefs_cb(XtPointer arg) +{ + struct topic_info *info = (struct topic_info *)arg; + struct prefs_choice *prefs = (struct prefs_choice *)info->data; + size_t i; + + /* pop down dependent windows */ + TRACE_GUI((stderr, "window count: %lu", (unsigned long)prefs->depwin_cnt)); + for (i = 0; i < prefs->depwin_cnt; i++) { + TRACE_GUI((stderr, "popping down %lu: %p", (unsigned long)i, (void *)(prefs->depwin[i]))); + if (XtIsRealized(prefs->depwin[i])) { + XtCallCallbacks(prefs->depwin[i], XmNcancelCallback, NULL); + XSync(DISP, True); /* wait for server to catch up */ + if (XtIsRealized(prefs->depwin[i])) { + TRACE_GUI((stderr, "calling XmNokCallback of %lu: %p", (unsigned long)i, (void *)(prefs->depwin[i]))); + XtCallCallbacks(prefs->depwin[i], XmNokCallback, NULL); + } + } + } + free(prefs->depwin); + prefs->depwin = NULL; + prefs->depwin_cnt = 0; + + if (prefs->db == NULL) { /* callback invoked multiple times, or prefs not changed */ + return; + } + + revert_resources(); + + XrmDestroyDatabase(prefs->db); + prefs->db = NULL; +} + +static void +save_prefs_exit(XtPointer arg) +{ + struct topic_info *info = (struct topic_info *)arg; + apply_prefs_cb(info); + XtPopdown(info->shell); + XSync(DISP, False); + xdvi_exit(EXIT_SUCCESS); +} + +static void +no_save_prefs_exit(XtPointer arg) +{ + struct topic_info *info = (struct topic_info *)arg; + revert_prefs_cb(info); + XtPopdown(info->shell); + XSync(DISP, False); + xdvi_exit(EXIT_SUCCESS); +} + +static void +close_prefs_exit(Widget w, XtPointer arg) +{ + struct topic_info *info = (struct topic_info *)arg; + struct prefs_choice *prefs = (struct prefs_choice *)info->data; +/* Widget dialog; */ +/* if (get_widget_by_name(&dialog, w, Xdvi_MESSAGE_DIALOG_NAME, True)) { */ +/* fprintf(stderr, "!!!!!!!!! removing window from deplist!\n"); */ +/* remove_from_deplist(prefs, dialog); */ +/* } */ + + remove_from_deplist(prefs, w); + +} + +Boolean +preferences_changed(void) +{ + Widget prefs_shell = 0, topic_pane = 0; + + if (get_widget_by_name(&prefs_shell, globals.widgets.top_level, "preferences_window", False) + && get_widget_by_name(&topic_pane, prefs_shell, "topic_pane", False)) { + struct topic_info *info = NULL; + struct prefs_choice *prefs = NULL; + + XtVaGetValues(topic_pane, XmNuserData, &info, NULL); + if (info == NULL) { + return False; + } + prefs = (struct prefs_choice *)info->data; + if (prefs->db != NULL) { /* prefs changed */ + + Widget popup = choice3_dialog(prefs_shell, + MSG_QUESTION, NULL, +#ifndef MOTIF + NULL, +#endif + close_prefs_exit, info, /* pre_callbacks */ + "Save and Exit", save_prefs_exit, info, + "Exit, don't Save", no_save_prefs_exit, info, + "Cancel", NULL, NULL, + "Preferences have been changed, but not saved yet. " + "Save them now?"); + add_to_deplist(prefs, popup); + return True; + } + } + + return False; +} + +void +popup_preferences_dialog(Widget parent, int arg) +{ + static Widget preferences_shell = 0; + static struct topic_info info; + static struct topic_item items[NUM_PREFS_TOPICS]; + static struct prefs_choice *prefs = NULL; + + if (preferences_shell == 0) { /* called 1st time; create widget */ + info.ok_callback = apply_prefs_cb; + info.cancel_callback = revert_prefs_cb; + info.items = items; +/* info.items_size = NUM_PREFS_TOPICS; */ + + prefs = xmalloc(sizeof *prefs); + prefs->depwin_cnt = 0; + prefs->depwin = NULL; +/* prefs->orig = orig_prefs; */ + /* apply_prefs_cb/revert_prefs_cb are responsible for copying + the changed preferences into the current preferences as + appropriate, and free()ing prefs.changed */ +/* prefs->changed = xmalloc(sizeof *(prefs->changed)); */ +/* copy_resources(orig_prefs, prefs->changed); */ + prefs->db = NULL; /* XrmGetStringDatabase(""); */ + info.data = prefs; + + preferences_shell = create_topic_window(parent, + "xdvik: Preferences", + Xdvi_PREFS_DIALOG_NAME, + &info, + initialize_items, + "OK", "Cancel"); + info.shell = preferences_shell; + center_window(preferences_shell, parent); + select_topic(&info, 0); + } + + if (arg >= 0) + select_topic(&info, arg); + + XtPopup(preferences_shell, XtGrabNone); + + if (resource.no_init_file) { + popup_message(preferences_shell, + MSG_WARN, + NULL, + "You specified the resource `noInitFile' or the `-q' command-line option. " + "Any preferences that you set in this dialog will be lost when you exit xdvi."); + } +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* MOTIF */ diff --git a/Build/source/texk/xdvik/gui/xm_prefs.h b/Build/source/texk/xdvik/gui/xm_prefs.h new file mode 100644 index 00000000000..0fb4acee4b8 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_prefs.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +#ifndef XM_PREFS_H_ +#define XM_PREFS_H_ + +#ifdef MOTIF + +#include "xm_prefsP.h" + +extern void popup_preferences_dialog(Widget parent, int arg); +extern void copy_resources(const struct x_resources *source, struct x_resources *target); +extern void remove_from_deplist(struct prefs_choice *prefs, Widget w); +extern void add_to_deplist(struct prefs_choice *prefs, Widget w); +extern Boolean preferences_changed(void); + +#endif + +#endif /* XM_PREFS_H_ */ diff --git a/Build/source/texk/xdvik/gui/xm_prefsP.h b/Build/source/texk/xdvik/gui/xm_prefsP.h new file mode 100644 index 00000000000..3ec65ef9607 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_prefsP.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +/* + * `Private' common header for preferences dialogs. + */ + +#ifndef XM_PREFS_P_H_ +#define XM_PREFS_P_H_ + +#include "xdvi.h" +#include "xdvi-config.h" + +#ifdef MOTIF +/* + * For consistency when also using the names for XtNameToWidget() in callbacks + */ +/* widget names */ +#define Xdvi_PREFS_DIALOG_NAME "preferences_window" +#define Xdvi_COLOR_DIALOG_NAME "color_dialog" +#define Xdvi_COLOR_DIALOG_OLD_SAMPLE_NAME "old_sample" +#define Xdvi_BROWSER_MENU_NAME "browser_menu" +#define Xdvi_BROWSER_COMBO_NAME "browser_combo" +#define Xdvi_EDITOR_MENU_NAME "editor_menu" +#define Xdvi_EDITOR_COMBO_NAME "editor_combo" +#define Xdvi_EDITOR_POPUP_NAME "editor_prompt" +#define Xdvi_EDITOR_POPUP "editor_prompt_popup" +#define Xdvi_BROWSER_POPUP_NAME "browser_prompt" +#define Xdvi_BROWSER_POPUP "browser_prompt_popup" +#define Xdvi_TIPS_STATUSLINE "tips_statusline" +#define Xdvi_TIPS_POPUPS "tips_popups" +#define Xdvi_TIPS_DELAY_TEXT "tips_delay_text" +#define Xdvi_TIPS_DELAY_LABEL1 "tips_delay_label1" +#define Xdvi_TIPS_DELAY_LABEL2 "tips_delay_label2" +#define Xdvi_HOME_POSITION_X_OFF_TEXT "x_off_text" +#define Xdvi_HOME_POSITION_Y_OFF_TEXT "y_off_text" +#define Xdvi_HOME_POSITION_UNITS_MENU "units_menu" +#define Xdvi_HOME_POSITION_UNITS_PULLDOWN "units_pulldown" +#define Xdvi_DARKNESS_SPINBOX "darkness_spinbox" +#define Xdvi_DARKNESS_TEXT "darkness_text" +#define Xdvi_SHRINK_SPINBOX "shrink_spinbox" +#define Xdvi_SHRINK_TEXT "shrink_text" +#define Xdvi_PAPER_CASCADE "papersize_option" +#define Xdvi_PAPER_MENU "papersize_menu" + +/* widget labels that are also used as widget names */ +#define Xdvi_TB_BUTTONS_FLAT_STR "Flat" +#define Xdvi_TB_BUTTONS_RAISED_STR "Raised" +#define Xdvi_GUI_STATUSLINE_STR "Show Statusline" +#define Xdvi_GUI_TOOLBAR_STR "Show Toolbar" +#define Xdvi_GUI_PAGELIST_STR "Show Pagelist" +#define Xdvi_GUI_SCROLLBARS_STR "Show Scrollbars" +#define Xdvi_MATCH_INVERTED_STR "Inverted" +#define Xdvi_MATCH_BOXED_STR "Boxed with Highlight Color" +#define Xdvi_FG_COLOR_STR "Text:" +#define Xdvi_FG_COLOR_BTN "fg_button" +#define Xdvi_BG_COLOR_STR "Background:" +#define Xdvi_BG_COLOR_BTN "bg_button" +#define Xdvi_HL_COLOR_BTN "hl_button" +#define Xdvi_VISITED_LINKS_STR "Visited Links:" +#define Xdvi_VISITED_LINKS_BTN "visited_links_button" +#define Xdvi_UNVISITED_LINKS_STR "Unvisited Links:" +#define Xdvi_UNVISITED_LINKS_BTN "unvisited_links_button" +#define Xdvi_DOCUMENT_COLORS_STR "Use Document Colors" +#define Xdvi_LINKS_UNDERLINED_STR "Underlined" +#define Xdvi_ADD_COMMAND_STR "Other ..." +#define Xdvi_SCROLL_KEEP_STR "Keep current position" +#define Xdvi_SCROLL_UNKEEP_STR "Scroll back to home position of page" +#define Xdvi_HOME_POSITION_STR "Home position at:" +#define Xdvi_HOME_POSITION_X_STR "x" +#define Xdvi_HOME_POSITION_Y_STR "y" +#define Xdvi_SCROLL_CURRENT_STR "Use current position as home position" +#define Xdvi_REMEMBER_WINDOWSIZE_STR "Remember current window size" +#define Xdvi_PAPER_PORTRAIT_STR "Portrait" +#define Xdvi_PAPER_LANDSCAPE_STR "Landscape" +#define Xdvi_APPLY_STR "Apply" + +#define Xdvi_PREFS_BROWSER_DEFAULTS \ + "mozilla -remote 'openURL(%s,new-window)'\n" \ + "netscape -raise -remote 'openURL(%s,new-window)'\n" \ + "xterm -e lynx %s\n" \ + Xdvi_ADD_COMMAND_STR +#define Xdvi_PREFS_EDITOR_DEFAULTS \ + "gnuclient -q +%l %f\n" \ + "emacsclient --no-wait +%l %f\n" \ + "gvim --servername xdvi --remote +%l %f\n" \ + "nc -noask +%l %f\n" \ + "xterm -e vi +%l %f\n" \ + Xdvi_ADD_COMMAND_STR + +struct prefs_choice { + XrmDatabase db; +/* struct x_resources *orig; */ +/* struct x_resources *changed; */ + Widget *depwin; + size_t depwin_cnt; +}; + +extern void h_attach_below(Widget x, Widget y); +extern void h_update_hyperlinks(Widget w, Pixel pix); /* implemented in xm_prefs_fonts.c */ + +#endif /* MOTIF */ + +#endif /* XM_PREFS_P_H_ */ + diff --git a/Build/source/texk/xdvik/gui/xm_prefs_appearance.c b/Build/source/texk/xdvik/gui/xm_prefs_appearance.c new file mode 100644 index 00000000000..b7f966059d5 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_prefs_appearance.c @@ -0,0 +1,540 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +/* + * Panel 1 (Apperance) for xdvik preferences dialog. + */ + +#include "xdvi-config.h" +#include "xdvi.h" + +#include "x_util.h" +#include "my-snprintf.h" +#include "xm_colorsel.h" +#include "topic-window.h" +#include "message-window.h" +#include "util.h" +#include "events.h" + +#include "xm_toolbar.h" +#include "xm_menu.h" +#include "statusline.h" +#include "pagesel.h" + +#include "xm_prefsP.h" +#include "xm_prefs_appearance.h" + +#ifdef MOTIF /* entire file */ + +#include <X11/Xatom.h> + +#include <Xm/Xm.h> +#include <Xm/Protocols.h> +#include <Xm/DialogS.h> +#include <Xm/LabelG.h> +#include <Xm/PushB.h> +#include <Xm/Form.h> +#include <Xm/Frame.h> +#include <Xm/PanedW.h> +#include <Xm/DrawnB.h> +#include <Xm/RowColumn.h> +#include <Xm/Scale.h> +#include <Xm/ToggleBG.h> +#include <Xm/Text.h> +#include <Xm/TextF.h> + +/* + * Handy defaults + */ +static Arg one_of_many = { XmNindicatorType, XmONE_OF_MANY }; +static Arg n_of_many = { XmNindicatorType, XmN_OF_MANY }; +static Arg frame_title = { XmNchildType, XmFRAME_TITLE_CHILD }; +static Arg two_cols[] = { + { XmNpacking, XmPACK_TIGHT }, + { XmNnumColumns, 2 }, + { XmNorientation, XmHORIZONTAL } +}; +static Arg two_cols_fixed[] = { + { XmNpacking, XmPACK_COLUMN }, + { XmNnumColumns, 2 } +}; + +static void +match_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *info = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)(info->data); + Widget inverted, boxed; + + UNUSED(w); + UNUSED(call_data); + + resource.match_highlight_inverted = !resource.match_highlight_inverted; + + /* force a redraw so that a current match is updated if there was one */ + globals.ev.flags |= EV_NEWPAGE; + + store_preference(&(prefs->db), "matchInverted", "%s", + resource.match_highlight_inverted ? "True" : "False"); + + if (get_widget_by_name(&inverted, info->shell, Xdvi_MATCH_INVERTED_STR, True) + && get_widget_by_name(&boxed, info->shell, Xdvi_MATCH_BOXED_STR, True)) { + + XmToggleButtonGadgetSetState(inverted, resource.match_highlight_inverted, False); + XmToggleButtonGadgetSetState(boxed, !resource.match_highlight_inverted, False); + } +} + +static void +tooltips_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *info = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)(info->data); + const char *w_name = XtName(w); + + UNUSED(call_data); + + if (strcmp(w_name, Xdvi_TIPS_STATUSLINE) == 0) { + resource.tooltips_in_statusline = !resource.tooltips_in_statusline; + store_preference(&(prefs->db), "tooltipsInStatusline", "%s", + resource.tooltips_in_statusline ? "True" : "False"); + } + else if (strcmp(w_name, Xdvi_TIPS_POPUPS) == 0) { + Widget label1, text, label2; + + resource.show_tooltips = !resource.show_tooltips; + + if (!resource.show_tooltips && resource.tooltips_wait_period >= 0) { + if (resource.tooltips_wait_period == 0) + resource.tooltips_wait_period = -1; + else + resource.tooltips_wait_period = -resource.tooltips_wait_period; + } + else if (resource.show_tooltips && resource.tooltips_wait_period < 0) { + resource.tooltips_wait_period = -resource.tooltips_wait_period; + } + store_preference(&(prefs->db), "showTooltips", "%s", resource.show_tooltips ? "True" : "False"); + + if (get_widget_by_name(&label1, info->shell, Xdvi_TIPS_DELAY_LABEL1, True) + && get_widget_by_name(&text, info->shell, Xdvi_TIPS_DELAY_TEXT, True) + && get_widget_by_name(&label2, info->shell, Xdvi_TIPS_DELAY_LABEL2, True)) { + + XtSetSensitive(label1, resource.show_tooltips); + XtSetSensitive(text, resource.show_tooltips); + XtSetSensitive(label2, resource.show_tooltips); + } + } + else if (strcmp(w_name, Xdvi_TIPS_DELAY_TEXT) == 0) { + char *buf = XmTextFieldGetString(w); + int val = strtol(buf, (char **)NULL, 10); + XtFree((char *)buf); + TRACE_GUI((stderr, "tooltips_cb wait period2: %d\n", val)); + + resource.tooltips_wait_period = val; + store_preference(&(prefs->db), "tipShell.waitPeriod", "%d", val); + + } + else { + XDVI_WARNING((stderr, "unexpected widget name `%s' in tooltips_cb", XtName(w))); + } + +} + +#if 0 +static void +toolbar_buttons_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *info = (struct topic_info *)client_data; + struct x_resources *res = (struct x_resources *)(info->data); + Boolean is_flat = False, is_raised = False; + + Widget flat_b, raised_b; + + UNUSED(call_data); + + if (!get_widget_by_name(&flat_b, XtParent(w), TB_BUTTONS_FLAT_STR, True) || + !get_widget_by_name(&raised_b, XtParent(w), TB_BUTTONS_RAISED_STR, True)) + return; + + if (w == flat_b) { + is_flat = True; + } + else if (w == raised_b) { + is_raised = True; + } + else { + unexpected_widget_in_callback(w, "toolbar_buttons_cb()"); + return; + } + + res->toolbar_buttons_raised = is_raised ? True : False; + + XmToggleButtonGadgetSetState(raised_b, is_raised, False); + XmToggleButtonGadgetSetState(flat_b, is_flat, False); +} +#endif /* 0 */ + +void update_preferences_expert(void) +{ + Widget shell; + Widget statusline_b, toolbar_b, pagelist_b, scrollbars_b; + + /* it's not an error if the prefs dialog doesn't exist yet */ + if (get_widget_by_name(&shell, globals.widgets.top_level, Xdvi_PREFS_DIALOG_NAME, False) + && get_widget_by_name(&statusline_b, shell, Xdvi_GUI_STATUSLINE_STR, True) + && get_widget_by_name(&toolbar_b, shell, Xdvi_GUI_TOOLBAR_STR, True) + && get_widget_by_name(&pagelist_b, shell, Xdvi_GUI_PAGELIST_STR, True) + && get_widget_by_name(&scrollbars_b, shell, Xdvi_GUI_SCROLLBARS_STR, True)) { + + XmToggleButtonGadgetSetState(statusline_b, resource.expert_mode & XPRT_SHOW_STATUSLINE ? True : False, False); + XmToggleButtonGadgetSetState(toolbar_b, resource.expert_mode & XPRT_SHOW_TOOLBAR ? True : False, False); + XmToggleButtonGadgetSetState(pagelist_b, resource.expert_mode & XPRT_SHOW_PAGELIST ? True : False, False); + XmToggleButtonGadgetSetState(scrollbars_b, resource.expert_mode & XPRT_SHOW_SCROLLBARS ? True : False, False); + } +} + +void update_preferences_tooltips(void) +{ + Widget shell; + Widget statusline_b, popup_b, label1, text, label2; + + if (resource.toolbar_unusable) + return; + + if (get_widget_by_name(&shell, globals.widgets.top_level, Xdvi_PREFS_DIALOG_NAME, True) + && get_widget_by_name(&statusline_b, shell, Xdvi_TIPS_STATUSLINE, True) + && get_widget_by_name(&popup_b, shell, Xdvi_TIPS_POPUPS, True) + && get_widget_by_name(&label1, shell, Xdvi_TIPS_DELAY_LABEL1, True) + && get_widget_by_name(&text, shell, Xdvi_TIPS_DELAY_TEXT, True) + && get_widget_by_name(&label2, shell, Xdvi_TIPS_DELAY_LABEL2, True)) { + + char buf[LENGTH_OF_INT]; + + SNPRINTF(buf, LENGTH_OF_INT, "%d", ABS(resource.tooltips_wait_period)); + XtVaSetValues(text, XmNvalue, buf, NULL); + + XmToggleButtonGadgetSetState(statusline_b, resource.tooltips_in_statusline, False); + XmToggleButtonGadgetSetState(popup_b, resource.show_tooltips, False); + + XtSetSensitive(label1, resource.show_tooltips); + XtSetSensitive(text, resource.show_tooltips); + XtSetSensitive(label2, resource.show_tooltips); + } +} + +void update_preferences_search(void) +{ + Widget shell; + Widget inverted, boxed; + + if (get_widget_by_name(&shell, globals.widgets.top_level, Xdvi_PREFS_DIALOG_NAME, True) + && get_widget_by_name(&inverted, shell, Xdvi_MATCH_INVERTED_STR, True) + && get_widget_by_name(&boxed, shell, Xdvi_MATCH_BOXED_STR, True)) { + + XmToggleButtonGadgetSetState(inverted, resource.match_highlight_inverted, False); + XmToggleButtonGadgetSetState(boxed, !resource.match_highlight_inverted, False); + } +} + +static void +gui_buttons_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *info = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)(info->data); + char *name = XtName(w); + + UNUSED(call_data); + + if (strcmp(name, Xdvi_GUI_STATUSLINE_STR) == 0) { + resource.expert_mode ^= XPRT_SHOW_STATUSLINE; + toggle_statusline(); + update_expert_mode(); + } + else if (strcmp(name, Xdvi_GUI_TOOLBAR_STR) == 0) { + resource.expert_mode ^= XPRT_SHOW_TOOLBAR; + toggle_toolbar(); + update_expert_mode(); + } + else if (strcmp(name, Xdvi_GUI_PAGELIST_STR) == 0) { + resource.expert_mode ^= XPRT_SHOW_PAGELIST; + toggle_pagelist(); + update_expert_mode(); + } + else if (strcmp(name, Xdvi_GUI_SCROLLBARS_STR) == 0) { +#if defined(LESSTIF_VERSION) + static Boolean warned_about_lesstif = False; +#endif + resource.expert_mode ^= XPRT_SHOW_SCROLLBARS; + toggle_scrollbars(); + update_expert_mode(); +#if defined(LESSTIF_VERSION) + if (!warned_about_lesstif) { + warned_about_lesstif = True; + popup_message(globals.widgets.top_level, + MSG_INFO, + NULL, + "This version has been compiled with LessTif; " + "toggling the scrollbars won't work with LessTif."); + } +#endif + } + else { + popup_message(globals.widgets.top_level, + MSG_ERR, + REPORT_XDVI_BUG_TEMPLATE, + "Unexpected label in gui buttons: |%s|!\n", name); + } + store_preference(&(prefs->db), "expertMode", "%d", resource.expert_mode); +} + + +#if 0 +static void +toolbar_buttons_init(struct topic_info *info, Widget raised, Widget flat) +{ + Boolean is_flat = False, is_raised = False; + struct x_resources *res = (struct x_resources *)(info->data); + + if (res->toolbar_buttons_raised) + is_raised = True; + + XmToggleButtonGadgetSetState(raised, is_raised, False); + XmToggleButtonGadgetSetState(flat, is_flat, False); + + XtAddCallback(flat, XmNvalueChangedCallback, toolbar_buttons_cb, (XtPointer)info); + XtAddCallback(raised, XmNvalueChangedCallback, toolbar_buttons_cb, (XtPointer)info); +} +#endif /* 0 */ + + +Widget +prefs_appearance(struct topic_info *info) +{ + Widget form; + Widget gui_frame, gui_label, gui_rowcol, + gui_statusline, gui_toolbar, gui_pagelist, gui_scrollbars; + Widget tips_frame, tips_label, tips_form, tips_statusline; + Widget tips_popups, tips_delay_text, tips_delay_label1, tips_delay_label2; + Widget match_frame, match_label, match_rowcol, match_inverted, match_boxed; +#if 0 + Widget tb_buttons_frame, tb_buttons_label, tb_buttons_rowcol, + tb_buttons_flat, tb_buttons_raised; +#endif + XmString str; + char buf[LENGTH_OF_INT]; + Arg args[10]; + int n; + + form = XmCreateForm(info->right_form, "appearance_form", NULL, 0); + h_attach_below(form, NULL); + + n = 0; + XtSetArg(args[n], XmNmarginWidth, 8); n++; + XtSetArg(args[n], XmNmarginHeight, 4); n++; + gui_frame = XmCreateFrame(form, "gui_frame", args, n); + h_attach_below(gui_frame, NULL); + + gui_label = XmCreateLabelGadget(gui_frame, "Window Configuration", &frame_title, 1); + XtManageChild(gui_label); + + gui_rowcol = XmCreateRowColumn(gui_frame, "gui_rowcol", two_cols_fixed, XtNumber(two_cols_fixed)); + XtManageChild(gui_rowcol); + + gui_statusline = XmCreateToggleButtonGadget(gui_rowcol, Xdvi_GUI_STATUSLINE_STR, &n_of_many, 1); + XmToggleButtonGadgetSetState(gui_statusline, resource.expert_mode & XPRT_SHOW_STATUSLINE ? True : False, False); + XtManageChild(gui_statusline); + + gui_toolbar = XmCreateToggleButtonGadget(gui_rowcol, Xdvi_GUI_TOOLBAR_STR, &n_of_many, 1); + XmToggleButtonGadgetSetState(gui_toolbar, resource.expert_mode & XPRT_SHOW_TOOLBAR ? True : False, False); + XtManageChild(gui_toolbar); + + gui_pagelist = XmCreateToggleButtonGadget(gui_rowcol, Xdvi_GUI_PAGELIST_STR, &n_of_many, 1); + XmToggleButtonGadgetSetState(gui_pagelist, resource.expert_mode & XPRT_SHOW_PAGELIST ? True : False, False); + XtManageChild(gui_pagelist); + + gui_scrollbars = XmCreateToggleButtonGadget(gui_rowcol, Xdvi_GUI_SCROLLBARS_STR, &n_of_many, 1); + XmToggleButtonGadgetSetState(gui_scrollbars, resource.expert_mode & XPRT_SHOW_SCROLLBARS ? True : False, False); + XtManageChild(gui_scrollbars); + + XtAddCallback(gui_statusline, XmNvalueChangedCallback, gui_buttons_cb, (XtPointer)info); + XtAddCallback(gui_toolbar, XmNvalueChangedCallback, gui_buttons_cb, (XtPointer)info); + XtAddCallback(gui_pagelist, XmNvalueChangedCallback, gui_buttons_cb, (XtPointer)info); + XtAddCallback(gui_scrollbars, XmNvalueChangedCallback, gui_buttons_cb, (XtPointer)info); + + n = 0; + XtSetArg(args[n], XmNmarginWidth, 8); n++; + XtSetArg(args[n], XmNmarginHeight, 4); n++; + XtSetArg(args[n], XmNtopOffset, 10); n++; + tips_frame = XmCreateFrame(form, "tips_frame", args, n); + h_attach_below(tips_frame, gui_frame); + + tips_label = XmCreateLabelGadget(tips_frame, "Show Tooltips", &frame_title, 1); + XtManageChild(tips_label); + + tips_form = XmCreateForm(tips_frame, "tips_form", NULL, 0); + + str = XmStringCreateLocalized("As Text in Statusline"); + tips_statusline = XtVaCreateManagedWidget(Xdvi_TIPS_STATUSLINE, + xmToggleButtonGadgetClass, tips_form, + XmNlabelString, str, + XmNindicatorType, XmN_OF_MANY, + XmNset, True, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + NULL); + XmStringFree(str); + + str = XmStringCreateLocalized("As Popups"); + tips_popups = XtVaCreateManagedWidget(Xdvi_TIPS_POPUPS, + xmToggleButtonGadgetClass, tips_form, + XmNlabelString, str, + XmNindicatorType, XmN_OF_MANY, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, tips_statusline, + XmNleftAttachment, XmATTACH_FORM, + NULL); + XmStringFree(str); + + str = XmStringCreateLocalized("with"); + tips_delay_label1 = XtVaCreateManagedWidget(Xdvi_TIPS_DELAY_LABEL1, + xmLabelGadgetClass, tips_form, + XmNlabelString, tips_form, + XmNlabelString, str, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, tips_statusline, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, tips_popups, + XmNleftOffset, 0, /* no spacing to prev text */ + NULL); + XmStringFree(str); + + SNPRINTF(buf, LENGTH_OF_INT, "%d", ABS(resource.tooltips_wait_period)); + + tips_delay_text = XtVaCreateManagedWidget(Xdvi_TIPS_DELAY_TEXT, + xmTextFieldWidgetClass, tips_form, + XmNcolumns, 4, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, tips_statusline, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, tips_delay_label1, + XmNvalue, buf, + NULL); + + str = XmStringCreateLocalized("milliseconds delay"); + tips_delay_label2 = XtVaCreateManagedWidget(Xdvi_TIPS_DELAY_LABEL2, + xmLabelGadgetClass, tips_form, + XmNlabelString, str, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, tips_statusline, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, tips_delay_text, + NULL); + + adjust_heights(tips_popups, tips_delay_label1, tips_delay_text, tips_delay_label2, NULL); + + XmToggleButtonGadgetSetState(tips_statusline, resource.tooltips_in_statusline, False); + XmToggleButtonGadgetSetState(tips_popups, resource.show_tooltips, False); + + if (resource.toolbar_unusable) { + XtSetSensitive(tips_frame, False); + } + else { + XtSetSensitive(tips_delay_label1, resource.show_tooltips); + XtSetSensitive(tips_delay_text, resource.show_tooltips); + XtSetSensitive(tips_delay_label2, resource.show_tooltips); + + XtAddCallback(tips_statusline, XmNvalueChangedCallback, tooltips_cb, (XtPointer)info); + XtAddCallback(tips_popups, XmNvalueChangedCallback, tooltips_cb, (XtPointer)info); + XtAddCallback(tips_delay_text, XmNvalueChangedCallback, tooltips_cb, (XtPointer)info); + } + +#if 0 + tb_buttons_frame = XmCreateFrame(form, "tb_buttons_frame", &v_off, 1); + h_attach_below(tb_buttons_frame, tips_frame); + + tb_buttons_label = XmCreateLabelGadget(tb_buttons_frame, "Toolbar Buttons:", &frame_title, 1); + XtManageChild(tb_buttons_label); + + tb_buttons_rowcol = XmCreateRowColumn(tb_buttons_frame, "tb_buttons_rowcol", two_cols, XtNumber(two_cols)); + XtManageChild(tb_buttons_rowcol); + + tb_buttons_raised = XmCreateToggleButtonGadget(tb_buttons_rowcol, "Raised", &one_of_many, 1); + XtManageChild(tb_buttons_raised); + + tb_buttons_flat = XmCreateToggleButtonGadget(tb_buttons_rowcol, "Flat", &one_of_many, 1); + XtManageChild(tb_buttons_flat); + toolbar_buttons_init((XtPointer)info, tb_buttons_raised, tb_buttons_flat); +#endif /* 0 */ + + n = 0; + XtSetArg(args[n], XmNmarginWidth, 8); n++; + XtSetArg(args[n], XmNmarginHeight, 4); n++; + XtSetArg(args[n], XmNtopOffset, 10); n++; + match_frame = XmCreateFrame(form, "match_frame", args, n); + XtVaSetValues(match_frame, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, tips_frame, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + + match_label = XmCreateLabelGadget(match_frame, "String Matches are shown:", &frame_title, 1); + XtManageChild(match_label); + + match_rowcol = XmCreateRowColumn(match_frame, "tb_buttons_rowcol", two_cols, XtNumber(two_cols)); + XtManageChild(match_rowcol); + + match_inverted = XmCreateToggleButtonGadget(match_rowcol, Xdvi_MATCH_INVERTED_STR, &one_of_many, 1); + XtManageChild(match_inverted); + + match_boxed = XmCreateToggleButtonGadget(match_rowcol, Xdvi_MATCH_BOXED_STR, &one_of_many, 1); + XtManageChild(match_boxed); + + XmToggleButtonGadgetSetState(match_inverted, resource.match_highlight_inverted, False); + XmToggleButtonGadgetSetState(match_boxed, !resource.match_highlight_inverted, False); + + XtAddCallback(match_inverted, XmNvalueChangedCallback, match_cb, (XtPointer)info); + XtAddCallback(match_boxed, XmNvalueChangedCallback, match_cb, (XtPointer)info); + + /* manage children (order shouldn't matter, since children are already managed, but ...) */ + XtManageChild(gui_frame); + + XtManageChild(tips_form); + XtManageChild(tips_frame); + +#if 0 + XtManageChild(tb_buttons_frame); +#endif + + XtManageChild(match_frame); + + XtManageChild(form); + + return form; +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* MOTIF */ diff --git a/Build/source/texk/xdvik/gui/xm_prefs_appearance.h b/Build/source/texk/xdvik/gui/xm_prefs_appearance.h new file mode 100644 index 00000000000..1c94a184d3c --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_prefs_appearance.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +#ifndef XM_PREFS_APPEARANCE_ +#define XM_PREFS_APPEARANCE_ + +#ifdef MOTIF + +struct topic_info; /* forward declaration */ +extern Widget prefs_appearance(struct topic_info *info); +extern void update_preferences_expert(void); +extern void update_preferences_tooltips(void); +extern void update_preferences_search(void); + +#endif /* MOTIF */ + +#endif /* XM_PREFS_APPEARANCE_ */ diff --git a/Build/source/texk/xdvik/gui/xm_prefs_fonts.c b/Build/source/texk/xdvik/gui/xm_prefs_fonts.c new file mode 100644 index 00000000000..bf25734bc50 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_prefs_fonts.c @@ -0,0 +1,696 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +/* + * Panel 2 (Fonts and colors) for xdvik preferences dialog. + */ + +#include "xdvi-config.h" +#include "xdvi.h" + +#include "x_util.h" +#include "xm_colorsel.h" +#include "topic-window.h" +#include "util.h" +#include "events.h" +#include "hypertex.h" +#include "my-snprintf.h" + +#include "xm_prefsP.h" +#include "xm_prefs_fonts.h" + +#ifdef MOTIF /* entire file */ + +#include <X11/Xatom.h> + +#include <Xm/Xm.h> +#include <Xm/Protocols.h> +#include <Xm/DialogS.h> +#include <Xm/LabelG.h> +#include <Xm/PushB.h> +#include <Xm/Form.h> +#include <Xm/Frame.h> +#include <Xm/PanedW.h> +#include <Xm/DrawnB.h> +#include <Xm/RowColumn.h> +#include <Xm/Scale.h> +#include <Xm/ToggleBG.h> +#include <Xm/Text.h> +#include <Xm/TextF.h> + +#if XmVersion >= 2000 && !defined(LESSTIF_VERSION) +/* spinbox only available with Motif 2.0; the lesstif version (as of 0.93) looks like shit */ +#include <Xm/SpinB.h> +#define USE_SPINBOX 1 +#else +#define USE_SPINBOX 0 +#endif + +/* + * Handy defaults + */ +static Arg v_off = { XmNtopOffset, 10 }; +static Arg n_of_many = { XmNindicatorType, XmN_OF_MANY }; +static Arg frame_title = { XmNchildType, XmFRAME_TITLE_CHILD }; + + +static void +underline_toggle_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *tinfo = (struct topic_info *)client_data; + struct prefs_choice *prefs; + Boolean is_set; + + UNUSED(call_data); + + ASSERT(tinfo != NULL, "struct topic_info * in underline_toggle_cb mustn't be NULL!"); + prefs = (struct prefs_choice *)tinfo->data; + + XtVaGetValues(w, XmNset, &is_set, NULL); + if (is_set) + resource.link_style |= 1; + else + resource.link_style &= ~1; + + store_preference(&(prefs->db), "linkStyle", "%d", resource.link_style); + + /* update display */ + globals.ev.flags |= EV_NEWPAGE; + XFlush(DISP); +} + +static void +colorspecial_toggle_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *tinfo = (struct topic_info *)client_data; + struct prefs_choice *prefs; + + UNUSED(w); +/* UNUSED(client_data); */ + UNUSED(call_data); + + ASSERT(tinfo != NULL, "struct topic_info * in colorspecial_toggle_cb mustn't be NULL!"); + prefs = (struct prefs_choice *)tinfo->data; + + do_toggle_color(False); + store_preference(&(prefs->db), "color", "%s", resource.use_color ? "True" : "False"); +} + +static void +darkness_text_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + char *text; + int val; + struct topic_info *info = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)(info->data); + +#if USE_SPINBOX + /* synchronize internal spinbox value. Apparently this loses the insertion position, + so we need to save it and set it again. */ + XmTextPosition pos = XmTextFieldGetInsertionPosition(w); + + UNUSED(call_data); + text = XmTextFieldGetString(w); + val = strtol(text, (char **)NULL, 10); + + if (val != 0) { + XtVaSetValues(w, XmNposition, val, NULL); + XmTextFieldSetInsertionPosition(w, pos); + } +#else + Widget text_field; + UNUSED(call_data); + + XtVaGetValues(w, XmNuserData, &text_field, NULL); + XtVaGetValues(text_field, XmNvalue, &text, NULL); + val = strtol(text, (char **)NULL, 10); +#endif + if (XtIsRealized(w)) { + do_set_density(val / 100.0, True, False); + store_preference(&(prefs->db), "gamma", "%f", val / 100.0); + } + XtFree(text); +} + + +#if 0 /* currently unused */ +static void +darkness_spinbox_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + int val; + struct topic_info *info = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)(info->data); + UNUSED(client_data); + UNUSED(call_data); + + XtVaGetValues(w, XmNposition, &val, NULL); + if (XtIsRealized(w)) { + do_set_density(val / 100.0, True, False); + /* fprintf(stderr, "storing pref: %d\n", val); */ + store_preference(&(prefs->db), "gamma", "%f", val / 100.0); + } + +} +#endif /* currently unused */ + + +void +h_update_hyperlinks(Widget w, Pixel pix) +{ + const char *name = XtName(w); + int type = 0; + + if (strcmp(name, Xdvi_UNVISITED_LINKS_BTN) == 0) { + type = 1; + } + else if (strcmp(name, Xdvi_VISITED_LINKS_BTN) == 0) { + type = 2; + } + + /* fprintf(stderr, "WIDGET: |%s|; type: %d\n", name, type); */ + + if (type != 0) { + GC new_gc = set_or_make_gc(NULL, GXcopy, pix, resource.back_Pixel); + GC old_gc; + double r, g, b; + double factor = 65535.0; + XColor color; + + pixel_to_color(pix, &color, DISP, G_colormap); + r = color.red / factor; + g = color.green / factor; + b = color.blue / factor; + + if (type == 1) { + old_gc = globals.gc.linkcolor; + sprintf(g_link_color_rgb, "push rgb %.2f %.2f %.2f", r, g, b); + } + else { + old_gc = globals.gc.visited_linkcolor; + sprintf(g_visited_link_color_rgb, "push rgb %.2f %.2f %.2f", r, g, b); + } + + if (type == 1) { + globals.gc.linkcolor = new_gc; + XFreeGC(XtDisplay(w), old_gc); + } + else { + globals.gc.visited_linkcolor = new_gc; + XFreeGC(XtDisplay(w), old_gc); + } + /* update display */ + globals.ev.flags |= EV_NEWPAGE; + XFlush(DISP); + } +} + +static void +redraw_or_push_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + XmDrawnButtonCallbackStruct *cbs = (XmDrawnButtonCallbackStruct *)call_data; + static GC border_gc = 0; + struct color_button_info *cinfo; + Pixel pix = (Pixel)client_data; + + if (cbs == NULL) { + XDVI_WARNING((stderr, "cinfo mustn't be NULL in redraw_or_push_cb!\n")); + return; + } + + /* this can also be called via XtCallCallbacks, without call_data */ + if (cbs->reason == XmCR_EXPOSE) { + /* redraw button label */ + Dimension high_thick, st, wd, ht; + Dimension inner_w, inner_h, off_x, off_y; + GC label_gc; + XGCValues values; + + XtVaGetValues(w, + XmNhighlightThickness, &high_thick, + XmNshadowThickness, &st, + XmNwidth, &wd, + XmNheight, &ht, + XmNuserData, &cinfo, + NULL); + + values.function = GXcopy; + + if (border_gc == 0) { + /* create the static GC for drawing the border - Note: never free'd! */ + values.foreground = BlackPixelOfScreen(XtScreen(w)); + /* values.line_width = 2; */ + /* note: never free'd! */ + border_gc = XCreateGC(XtDisplay(w), XtWindow(w), + GCFunction | GCForeground /* | GCLineWidth */, &values); + } + values.foreground = cinfo->pixel; + label_gc = XCreateGC(XtDisplay(w), XtWindow(w), + GCFunction | GCForeground, &values); + + off_x = high_thick + st + 4; + inner_w = wd - 2 * off_x; + off_y = high_thick + st + 4; + inner_h = ht - 2 * off_y; +#if 0 + fprintf(stderr, "shadow: %d, high: %d\n", st, high_thick); + fprintf(stderr, "width: %d, height: %d\n", wd, ht); + fprintf(stderr, "inner w: %d, inner h: %d\n", inner_w, inner_h); +#endif + + XFillRectangle(XtDisplay(w), XtWindow(w), label_gc, + off_x, off_y, inner_w, inner_h); + XDrawRectangle(XtDisplay(w), XtWindow(w), border_gc, + off_x - 1, off_y - 1, inner_w + 1, inner_h + 1); + XFreeGC(XtDisplay(w), label_gc); + + if (pix != cinfo->pixel) { + h_update_hyperlinks(w, cinfo->pixel); + } + } + else if (cbs->reason == XmCR_ACTIVATE) { + /* pushed, open color dialog */ + XtVaGetValues(w, XmNuserData, &cinfo, NULL); + if (cinfo != NULL) { + cinfo->w = w; + popup_color_dialog(globals.widgets.top_level, cinfo); + } + else { + XDVI_WARNING((stderr, "cinfo mustn't be NULL in redraw_or_push_cb!\n")); + return; + } + } + /* else, XmCR_RESIZE - nothing to do */ +} + +static Widget +h_create_colorsample(Widget parent, Widget left, + const char *name, + const char *resource_name, + Pixel pix, + struct topic_info *info) +{ + struct color_button_info *cinfo = xmalloc(sizeof *cinfo); /* note: never free()d */ + Widget button; + + UNUSED(left); + + cinfo->pixel = pix; + cinfo->tinfo = info; + cinfo->resource_name = xstrdup(resource_name); /* note: never free()d */ + + button = XtVaCreateManagedWidget(name, xmDrawnButtonWidgetClass, parent, + XtNwidth, 42, + XtNheight, 28, + XmNuserData, cinfo, + /* we want the normal button look here to + make it obvious that users can press it */ + XmNshadowType, XmSHADOW_OUT, + XmNpushButtonEnabled, True, + NULL); + XtAddCallback(button, XmNactivateCallback, redraw_or_push_cb, (XtPointer)pix); + XtAddCallback(button, XmNexposeCallback, redraw_or_push_cb, (XtPointer)pix); + XtAddCallback(button, XmNresizeCallback, redraw_or_push_cb, (XtPointer)pix); + + return button; +} + + +static Widget +h_create_fonts_frame(Widget top, struct topic_info *info) +{ + Widget darkness_form, darkness_label; + + UNUSED(info); + + darkness_form = XmCreateForm(top, "darkness_form", NULL, 0); + XtVaSetValues(darkness_form, + XmNverticalSpacing, 10, + XmNhorizontalSpacing, 10, + NULL); + + + darkness_label = XmCreateLabelGadget(darkness_form, "Font Darkness: ", NULL, 0); + XtVaSetValues(darkness_label, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + XtManageChild(darkness_label); + { + Widget darkness_text; +#if USE_SPINBOX + Widget darkness_spinbox = XmCreateSpinBox(darkness_form, Xdvi_DARKNESS_SPINBOX, NULL, 0); + darkness_text = XmCreateTextField(darkness_spinbox, Xdvi_DARKNESS_TEXT, NULL, 0); + XtVaSetValues(darkness_spinbox, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, darkness_label, + XmNinitialDelay, 200, + XmNrepeatDelay, 50, + NULL); + XtVaSetValues(darkness_text, + XmNspinBoxChildType, XmNUMERIC, + XmNminimumValue, 0, + XmNmaximumValue, 1000, + XmNeditable, True, + XmNcolumns, 4, + XmNincrementValue, 1, + XmNwrap, False, /* too confusing */ + XmNposition, (int)(resource.gamma * 100.0 + 0.5), + NULL); + XtAddCallback(darkness_text, XmNactivateCallback, darkness_text_cb, (XtPointer)info); + XtAddCallback(darkness_text, XmNvalueChangedCallback, darkness_text_cb, (XtPointer)info); + + adjust_heights(darkness_spinbox, darkness_text, darkness_label, NULL); + + XtManageChild(darkness_text); + XtManageChild(darkness_spinbox); +#else + char buf[LENGTH_OF_INT]; + Widget darkness_button; + darkness_text = XmCreateTextField(darkness_form, Xdvi_DARKNESS_TEXT, NULL, 0); + SNPRINTF(buf, LENGTH_OF_INT, "%d", (int)(resource.gamma * 100.0 + 0.5)); + XtVaSetValues(darkness_text, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, darkness_label, + XmNcolumns, 4, + XmNvalue, buf, + XmNuserData, darkness_text, + NULL); + XtOverrideTranslations(darkness_text, XtParseTranslationTable("<Key>Return:activate()")); + XtAddCallback(darkness_text, XmNactivateCallback, darkness_text_cb, (XtPointer)info); + + darkness_button = XmCreatePushButton(darkness_form, Xdvi_APPLY_STR, NULL, 0); + XtVaSetValues(darkness_button, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, darkness_text, + XmNuserData, darkness_text, + NULL); + XtAddCallback(darkness_button, XmNactivateCallback, darkness_text_cb, (XtPointer)info); + + adjust_heights(darkness_label, darkness_text, darkness_button, NULL); + XtManageChild(darkness_text); + XtManageChild(darkness_button); +#endif + } + +/* XtManageChild(darkness_form); */ + + return darkness_form; +} + +static Widget +h_create_colors_form(Widget top, struct topic_info *info) +{ + Widget text_bg_frame, text_bg_label, text_bg_form; + Widget links_frame, links_label, links_form; + Widget fg_label, fg_button; + Widget bg_label, bg_button; + Widget unvisited_label, unvisited_button; + Widget visited_label, visited_button; + Widget underline_toggle; + Widget colorspecials_toggle; + + Widget form = XmCreateForm(top, "colors_form", NULL, 0); + Pixel visited_link_pix, link_pix; + + str_to_pixel(top, resource.visited_link_color, &visited_link_pix); + str_to_pixel(top, resource.link_color, &link_pix); + + text_bg_frame = XmCreateFrame(form, "text_bg_frame", NULL, 0); + XtVaSetValues(text_bg_frame, + XmNmarginWidth, 10, + XmNmarginHeight, 10, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_POSITION, + XmNrightPosition, 50, + XmNrightOffset, 10, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + + text_bg_label = XmCreateLabelGadget(text_bg_frame, "Text Color", &frame_title, 1); + XtManageChild(text_bg_label); + + text_bg_form = XmCreateForm(text_bg_frame, "fg_form", NULL, 0); + XtVaSetValues(text_bg_form, XmNverticalSpacing, 2, NULL); + + fg_label = XmCreateLabelGadget(text_bg_form, Xdvi_FG_COLOR_STR, NULL, 0); + fg_button = h_create_colorsample(text_bg_form, fg_label, + Xdvi_FG_COLOR_BTN, "foreground", + resource.fore_Pixel, info); + XtVaSetValues(fg_label, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_FORM, + NULL); + XtVaSetValues(fg_button, + XmNrightAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_NONE, + NULL); + adjust_heights(fg_label, fg_button, NULL); + XtManageChild(fg_label); + XtManageChild(fg_button); + + bg_label = XmCreateLabelGadget(text_bg_form, Xdvi_BG_COLOR_STR, NULL, 0); + bg_button = h_create_colorsample(text_bg_form, bg_label, + Xdvi_BG_COLOR_BTN, "background", + resource.back_Pixel, info); + XtVaSetValues(bg_label, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, fg_label, + NULL); + XtVaSetValues(bg_button, + XmNrightAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_NONE, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, fg_label, + NULL); + adjust_heights(bg_label, bg_button, NULL); + XtManageChild(bg_label); + XtManageChild(bg_button); + + colorspecials_toggle = XmCreateToggleButtonGadget(text_bg_form, Xdvi_DOCUMENT_COLORS_STR, &n_of_many, 1); + XtVaSetValues(colorspecials_toggle, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, bg_label, + NULL); + XtManageChild(colorspecials_toggle); + XmToggleButtonGadgetSetState(colorspecials_toggle, resource.use_color, False); + XtAddCallback(colorspecials_toggle, XmNvalueChangedCallback, colorspecial_toggle_cb, (XtPointer)info); + + links_frame = XmCreateFrame(form, "links_frame", NULL, 0); + XtVaSetValues(links_frame, + XmNmarginWidth, 10, + XmNmarginHeight, 10, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, text_bg_frame, + XmNleftOffset, 10, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + + links_label = XmCreateLabelGadget(links_frame, "Hyperlinks", &frame_title, 1); + XtManageChild(links_label); + + links_form = XmCreateForm(links_frame, "links_form", NULL, 0); + XtVaSetValues(links_form, + XmNverticalSpacing, 2, + NULL); + + unvisited_label = XmCreateLabelGadget(links_form, Xdvi_UNVISITED_LINKS_STR, NULL, 0); + unvisited_button = h_create_colorsample(links_form, unvisited_label, + Xdvi_UNVISITED_LINKS_BTN, "linkColor", + link_pix, info); + XtVaSetValues(unvisited_label, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_FORM, + NULL); + XtVaSetValues(unvisited_button, + XmNrightAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_NONE, + XmNtopAttachment, XmATTACH_FORM, + NULL); + adjust_heights(unvisited_label, unvisited_button, NULL); + XtManageChild(unvisited_label); + XtManageChild(unvisited_button); + + visited_label = XmCreateLabelGadget(links_form, Xdvi_VISITED_LINKS_STR, NULL, 0); + visited_button = h_create_colorsample(links_form, visited_label, + Xdvi_VISITED_LINKS_BTN, "visitedLinkColor", + visited_link_pix, info); + XtVaSetValues(visited_label, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, unvisited_label, + NULL); + XtVaSetValues(visited_button, + XmNrightAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_NONE, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, unvisited_label, + NULL); + adjust_heights(visited_label, visited_button, NULL); + XtManageChild(visited_label); + XtManageChild(visited_button); + + underline_toggle = XmCreateToggleButtonGadget(links_form, Xdvi_LINKS_UNDERLINED_STR, &n_of_many, 1); + XtVaSetValues(underline_toggle, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, visited_label, + NULL); + XtManageChild(underline_toggle); + +/* if (res->link_style & 1) */ + if (resource.link_style & 1) + XmToggleButtonGadgetSetState(underline_toggle, True, False); + else + XmToggleButtonGadgetSetState(underline_toggle, False, False); + + XtAddCallback(underline_toggle, XmNvalueChangedCallback, underline_toggle_cb, (XtPointer)info); + + XtManageChild(links_frame); + XtManageChild(text_bg_frame); + + XtManageChild(text_bg_form); + XtManageChild(links_form); + + return form; +} + +void update_preferences_color(void) +{ + Widget shell, button; + + /* it's not an error if the prefs dialog doesn't exist yet */ + if (get_widget_by_name(&shell, globals.widgets.top_level, Xdvi_PREFS_DIALOG_NAME, False) + && get_widget_by_name(&button, shell, Xdvi_DOCUMENT_COLORS_STR, True)) { + XmToggleButtonGadgetSetState(button, resource.use_color, False); + } +} + +void update_preferences_hyperlinks(void) +{ + Widget shell, button; + + /* it's not an error if the prefs dialog doesn't exist yet */ + if (get_widget_by_name(&shell, globals.widgets.top_level, Xdvi_PREFS_DIALOG_NAME, False) + && get_widget_by_name(&button, shell, Xdvi_LINKS_UNDERLINED_STR, True)) { + XmToggleButtonGadgetSetState(button, resource.link_style & 1 ? True : False, False); + } +} + +void update_preferences_darkness(void) +{ + Widget shell, text; + + if (get_widget_by_name(&shell, globals.widgets.top_level, Xdvi_PREFS_DIALOG_NAME, False) + && get_widget_by_name(&text, shell, Xdvi_DARKNESS_TEXT, True)) { + + char buf[LENGTH_OF_INT]; + SNPRINTF(buf, LENGTH_OF_INT, "%d", (int)(resource.gamma * 100.0 + 0.5)); +#if USE_SPINBOX + XmTextFieldSetString(text, buf); + XtVaSetValues(text, XmNposition, (int)(resource.gamma * 100.0 + 0.5), NULL); +#else + XtVaSetValues(text, XmNvalue, buf, NULL); +#endif + } +} + +Widget +prefs_fonts_and_colors(struct topic_info *info) +{ + Widget form; + Widget colors_form; + Widget other_colors_frame, fonts_frame; + + form = XmCreateForm(info->right_form, "fonts_colors_form", NULL, 0); + h_attach_below(form, NULL); + + colors_form = h_create_colors_form(form, info); + h_attach_below(colors_form, NULL); + XtManageChild(colors_form); + + { /* other colors - currently only highlight color */ + Widget other_colors_form, other_colors_label; + Widget hl_color_text, hl_color_button; + + other_colors_frame = XmCreateFrame(form, "other_colors_frame", &v_off, 1); + XtVaSetValues(other_colors_frame, + XmNmarginWidth, 10, + NULL); + h_attach_below(other_colors_frame, colors_form); + + other_colors_label = XmCreateLabelGadget(other_colors_frame, "Highlight Color", &frame_title, 1); + other_colors_form = XmCreateForm(other_colors_frame, "other_colors_form", NULL, 0); + XtVaSetValues(other_colors_form, XmNverticalSpacing, 2, NULL); + + hl_color_text = XmCreateLabelGadget(other_colors_form, +#if defined(LESSTIF_VERSION) /* stupid LessTif doesn't wrap Labels at '\n' */ + "Color of page border, rulers and bounding boxes.", +#else + "Color used for page border, rulers in `ruler mode', and\n" + "bounding boxes for forward search and EPS images.", +#endif + NULL, 0); + hl_color_button = h_create_colorsample(other_colors_form, hl_color_text, + Xdvi_HL_COLOR_BTN, "highlight", + resource.hl_Pixel, info); + XtVaSetValues(hl_color_text, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNbottomOffset, 10, + XmNalignment, XmALIGNMENT_BEGINNING, + NULL); + XtVaSetValues(hl_color_button, + XmNtopAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + NULL); + + XtManageChild(hl_color_text); + XtManageChild(hl_color_button); + XtManageChild(other_colors_form); + XtManageChild(other_colors_label); + XtManageChild(other_colors_frame); + } + + fonts_frame = h_create_fonts_frame(form, info); + h_attach_below(fonts_frame, other_colors_frame); + XtManageChild(fonts_frame); + + XtManageChild(form); + + return form; +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* MOTIF */ diff --git a/Build/source/texk/xdvik/gui/xm_prefs_fonts.h b/Build/source/texk/xdvik/gui/xm_prefs_fonts.h new file mode 100644 index 00000000000..b87984cafb2 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_prefs_fonts.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +#ifndef XM_PREFS_FONTS_ +#define XM_PREFS_FONTS_ + +#ifdef MOTIF + +struct topic_info; /* forward declaration */ +extern Widget prefs_fonts_and_colors(struct topic_info *info); +extern void update_preferences_color(void); +extern void update_preferences_hyperlinks(void); +extern void update_preferences_darkness(void); + +#endif /* MOTIF */ + +#endif /* XM_PREFS_FONTS_ */ diff --git a/Build/source/texk/xdvik/gui/xm_prefs_helpers.c b/Build/source/texk/xdvik/gui/xm_prefs_helpers.c new file mode 100644 index 00000000000..493e061827f --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_prefs_helpers.c @@ -0,0 +1,946 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +/* + * Panel 3 (Helper Applications) for xdvik preferences dialog. + */ + +#include "xdvi-config.h" +#include "xdvi.h" + +#include "x_util.h" +#include "xm_colorsel.h" +#include "topic-window.h" +#include "util.h" +#include "string-utils.h" +#include "message-window.h" +#include "events.h" +#include "xm_menu.h" + +#include "xm_prefsP.h" +#include "xm_prefs.h" +#include "xm_prefs_helpers.h" + +#ifdef MOTIF /* entire file */ + +#include <X11/Xatom.h> + +#include <Xm/Xm.h> +#include <Xm/Protocols.h> +#include <Xm/DialogS.h> +#include <Xm/LabelG.h> +#include <Xm/PushB.h> +#include <Xm/Form.h> +#include <Xm/Frame.h> +#include <Xm/PanedW.h> +#include <Xm/DrawnB.h> +#include <Xm/RowColumn.h> +#include <Xm/Scale.h> +#include <Xm/ToggleBG.h> +#include <Xm/PushBG.h> +#include <Xm/Text.h> +#include <Xm/TextF.h> +#include <Xm/FileSB.h> +#include <Xm/SelectioB.h> + +struct choice_dialog_info { + struct topic_info *tinfo; + /* struct prefs_choice *prefs; */ + Widget combo_box; + Widget message_popup; +}; + +static char **m_browser_list = NULL; +static char **m_editor_list = NULL; + +/* "mozilla -remote 'openURL(%s,new-window)'", */ +/* "netscape -raise -remote 'openURL(%s,new-window)'", */ +/* "xterm -e lynx %s", */ +/* Xdvi_ADD_COMMAND_STR, */ +/* NULL /\* terminate *\/ */ +/* }; */ + +/* static const char *editor_list[] = { */ +/* "gnuclient -q +%l %f", */ +/* "emacsclient --no-wait +%l %f", */ +/* "gvim --servername xdvi --remote +%l %f", */ +/* "nc -noask +%l %f", */ +/* "xterm -e vi +%l %f", */ +/* Xdvi_ADD_COMMAND_STR, */ +/* NULL /\* terminate *\/ */ +/* }; */ + +/* combo box only available with Motif 2.0; + * the lesstif version is incomplete + */ +#if XmVersion >= 2000 && !defined(LESSTIF_VERSION) +#include <Xm/ComboBox.h> +#include <Xm/List.h> +#define USE_COMBOBOX 1 +#else +#include <Xm/CascadeBG.h> +#define USE_COMBOBOX 0 +#endif + +static void select_browser_cb(Widget w, XtPointer client_data, XtPointer call_data); +static void select_editor_cb(Widget w, XtPointer client_data, XtPointer call_data); + +/* + * Helper functions + */ +static char * +flatten_list(char **list, const char *sep) +{ + char *result = xstrdup(""); + int i; + + for (i = 0; list[i] != NULL; i++) { + result = xstrcat(result, list[i]); + result = xstrcat(result, sep); + } + return result; +} + +static char ** +add_to_list(char **list, const char *str) +{ + int i, k; + /* reallocate with larger capacity */ + for (i = 0; list[i] != NULL; i++) { ; } + i++; + list = xrealloc(list, (i + 1) * sizeof *list); + + /* shift old contents down */ + for (k = i; k > 0; k--) { + list[k] = list[k - 1]; + } + /* add new element at beginning */ + list[0] = xstrdup(str); + + return list; +} + + +/* + * User clicked on `Help' in browser text input prompt + */ +static void +help_browser_dialog_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + Widget popup = popup_message(XtParent(w), + MSG_HELP, + NULL, + "The browser is used by xdvi to browse remote documents. " + "Please enter the name of the browser (i.e. the executable program) " + "you want to use. The browser command may optionally contain a string " + "`%%s' which is replaced by the current URL. If no `%%s' is present, " + "the URL argument is simply appended to the command."); + UNUSED(client_data); + UNUSED(call_data); + + TRACE_GUI((stderr, "setting user data to %p", (void *)popup)); + XtVaSetValues(w, XmNuserData, popup, NULL); +} + +/* + * User clicked on `Help' in editor text input prompt + */ +static void +help_editor_dialog_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + Widget popup = popup_message(XtParent(w), + MSG_HELP, + NULL, + "The editor is used by reverse search to open the TeX source for a DVI file " + "(see `Help' -> `Source Specials' for details)." + "Please enter the name of the editor executable you want to use. " + "The editor command may optionally contain two format strings: " + "`%%l' is replaced by the line number in the TeX file, and `%%f' by the file name. " + "(If the format strings are not present, they are appended implicitly.)"); + UNUSED(client_data); + UNUSED(call_data); + + TRACE_GUI((stderr, "setting user data to %p", (void *)popup)); + XtVaSetValues(w, XmNuserData, popup, NULL); +} + +/* + * User cancelled browser or editor text input prompt + */ +static void +destroy_dialog_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ +/* Widget browser_combo = (Widget)client_data; */ + struct choice_dialog_info *info = (struct choice_dialog_info *)client_data; + Widget browser_combo = NULL; + Widget help_popup; +#if USE_COMBOBOX + Widget browser_list_w; +#endif + UNUSED(call_data); + + XtVaGetValues(w, XmNuserData, &help_popup, NULL); + TRACE_GUI((stderr, "GOT help_popup: %p", (void *)help_popup)); + + if (info != NULL) + browser_combo = info->combo_box; + + if (browser_combo != NULL) { +#if USE_COMBOBOX + /* Need to unselect the `Other...' entry from the list; to do this, + we select the index from XmNuserData if it's >= 0, or the first item. */ + int idx; + XtVaGetValues(browser_combo, + XmNlist, &browser_list_w, + XmNuserData, &idx, + NULL); + if (browser_list_w == 0) + XDVI_ERROR((stderr, "couldn't get list component of combo box!\n")); + else { + if (idx < 0) + idx = 0; + XmListSelectPos(browser_list_w, idx + 1, True); + } +#else + Widget parent; + if ((parent = XtParent(w)) != NULL) { + Widget rowcol, child; + if (strcmp(XtName(parent), Xdvi_BROWSER_POPUP) == 0) { + if (get_widget_by_name(&rowcol, globals.widgets.top_level, Xdvi_BROWSER_COMBO_NAME, True)) { + XtVaGetValues(rowcol, XmNuserData, &child, NULL); + XtVaSetValues(rowcol, XmNmenuHistory, child, NULL); + } + } + else if (strcmp(XtName(parent), Xdvi_EDITOR_POPUP) == 0) { + if (get_widget_by_name(&rowcol, globals.widgets.top_level, Xdvi_EDITOR_COMBO_NAME, True)) { + XtVaGetValues(rowcol, XmNuserData, &child, NULL); + XtVaSetValues(rowcol, XmNmenuHistory, child, NULL); + } + } + else { + XDVI_WARNING((stderr, "unexpected widget name `%s' in destroy_dialog_cb", XtName(parent))); + } + } +#endif + } + + /* + * Also popdown dependent help window if it exists before destroying the widget; + * otherwise, Motif may even crash in _XmIsFastSubclass() when the help window is moved! + * + * Since `help_popup' is the top-level xmDialogShellWidget, we need to get its + * xmMessageBoxWidget child (by name ...). Don't report an error if it doesn't + * exist in case the help window has already been closed. + */ + if (help_popup != NULL) { + static Widget message; + if (get_widget_by_name(&message, help_popup, Xdvi_MESSAGE_DIALOG_NAME, False)) + XtCallCallbacks(message, XmNokCallback, NULL); + } + + if (info != NULL) { + struct prefs_choice *prefs = (struct prefs_choice *)info->tinfo->data; + remove_from_deplist(prefs, w); + free(info); + } + + /* destroy the parent of this dialog (the shell) */ + XtDestroyWidget(XtParent(w)); +} + +static void +h_get_input_wrapper(const char *listbox_name, + Widget w, XtPointer client_data, XtPointer call_data) +{ +/* Widget combobox = (Widget)client_data; */ + struct choice_dialog_info *info = (struct choice_dialog_info *)client_data; + struct topic_info *tinfo = info->tinfo; + struct prefs_choice *prefs = (struct prefs_choice *)tinfo->data; + Widget combobox, child; + /* following need to be allocated since we want to set resource.xyz to it */ + static char *browser_choice= NULL, *editor_choice = NULL; +#if !USE_COMBOBOX + XtCallbackProc select_cb = NULL; +#endif + XmSelectionBoxCallbackStruct *cbs = (XmSelectionBoxCallbackStruct *)call_data; + static char *ptr = NULL; + char *tmp_list; + + if (call_data == NULL) /* widget already being destroyed? */ + return; + + ASSERT(info != NULL, "client_data in h_get_input_wrapper mustn't be NULL!"); + combobox = info->combo_box; + child = info->message_popup; + XtVaGetValues(w, XmNuserData, &child, NULL); + TRACE_GUI((stderr, "GOT child: %p", (void *)child)); + + if (ptr) + XtFree((XtPointer)ptr); + ptr = NULL; + XmStringGetLtoR(cbs->value, G_charset, &ptr); + if (strlen(ptr) == 0 || is_spaces_only(ptr)) { + popup_message(XtParent(w), + MSG_ERR, + NULL, + "Empty input string"); + return; + } + + if (strcmp(listbox_name, Xdvi_BROWSER_COMBO_NAME) == 0) { +#if 0 + int i; +#endif + + free(browser_choice); + browser_choice = xstrdup(ptr); + resource.browser = browser_choice; + +#if 0 + for (i = 0; m_browser_list[i] != NULL; i++) { + fprintf(stderr, "list %d: |%s|\n", i, m_browser_list[i]); + } +#endif + m_browser_list = add_to_list(m_browser_list, ptr); +#if 0 + for (i = 0; m_browser_list[i] != NULL; i++) { + fprintf(stderr, "NEW list %d: |%s|\n", i, m_browser_list[i]); + } +#endif + tmp_list = flatten_list(m_browser_list, "\n"); +#if 0 + fprintf(stderr, "TMP LIST: |%s|\n", tmp_list); +#endif /* 0 */ + store_preference(&(prefs->db), "prefsBrowserList", "%s", tmp_list); + free(tmp_list); + + store_preference(&(prefs->db), "wwwBrowser", "%s", ptr); +#if !USE_COMBOBOX + select_cb = select_browser_cb; +#endif + } + else if (strcmp(listbox_name, Xdvi_EDITOR_COMBO_NAME) == 0) { + free(editor_choice); + editor_choice = xstrdup(ptr); + resource.editor = editor_choice; + + m_editor_list = add_to_list(m_editor_list, ptr); + tmp_list = flatten_list(m_editor_list, "\n"); + store_preference(&(prefs->db), "prefsEditorList", "%s", tmp_list); + free(tmp_list); + + store_preference(&(prefs->db), "editor", "%s", ptr); +#if !USE_COMBOBOX + select_cb = select_editor_cb; +#endif + } + else + XDVI_WARNING((stderr, "Unexpected name in h_get_input_wrapper: `%s'", listbox_name)); + +#if USE_COMBOBOX + UNUSED(listbox_name); + /* add user input to the combo box list, and make it current */ + XmComboBoxAddItem(combobox, cbs->value, 1, True); + XmComboBoxSelectItem(combobox, cbs->value); +#else + { + Widget new_menu, parent, grandparent, rowcol; + if ((parent = XtParent(w)) != NULL && (grandparent = XtParent(parent)) != NULL) { + /* add new item to front of list ... */ + new_menu = XtVaCreateManagedWidget(ptr, xmPushButtonGadgetClass, grandparent, + XmNpositionIndex, 0, + XmNuserData, tinfo, + NULL); + if (select_cb != NULL) { + XtAddCallback(new_menu, XmNactivateCallback, select_cb, grandparent); + } + /* ... and make it current! */ + if (get_widget_by_name(&rowcol, globals.widgets.top_level, listbox_name, True)) { + XtVaSetValues(rowcol, XmNmenuHistory, new_menu, NULL); + } + } + } +#endif + + /* as above: popdown help window */ + if (child != NULL) { + Widget message; + if (get_widget_by_name(&message, child, Xdvi_MESSAGE_DIALOG_NAME, False)) + XtCallCallbacks(message, XmNokCallback, NULL); + } + + remove_from_deplist(prefs, w); + free(info); + + /* destroy the parent of this dialog (the shell) */ + XtDestroyWidget(XtParent(w)); +} + +static void +h_selector(const char *prompt_name, + const char *title_str, const char *label_str, + XtCallbackProc ok_cb, + XtCallbackProc destroy_cb, + XtCallbackProc help_cb, + Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *tinfo = NULL; + struct prefs_choice *prefs = NULL; + struct choice_dialog_info *info = NULL; + char *choice; + /* following need to be allocated since we want to set resource.xyz to it */ + static char *browser_choice= NULL, *editor_choice = NULL; + +#if USE_COMBOBOX + XmComboBoxCallbackStruct *cb; +#else + XmString str; + UNUSED(client_data); + UNUSED(call_data); +#endif + +#if USE_COMBOBOX + tinfo = (struct topic_info *)client_data; + prefs = (struct prefs_choice *)tinfo->data; +#endif + info = xmalloc(sizeof *info); +#if USE_COMBOBOX + cb = (XmComboBoxCallbackStruct *)call_data; + + if (cb->event == NULL) /* only browsing, no selection */ + return; + + choice = (char *)XmStringUnparse(cb->item_or_text, XmFONTLIST_DEFAULT_TAG, + XmCHARSET_TEXT, XmCHARSET_TEXT, + NULL, 0, + XmOUTPUT_ALL); +#else + XtVaGetValues(w, XmNlabelString, &str, NULL); + XmStringGetLtoR(str, G_charset, &choice); + + XtVaGetValues(w, XmNuserData, &tinfo, NULL); + ASSERT(tinfo != NULL, "XmNuserData in callback musn't be NULL!"); + prefs = (struct prefs_choice *)tinfo->data; +#endif + + if (strcmp(choice, Xdvi_ADD_COMMAND_STR) == 0) { + Widget prompt_widget; + Arg args[8]; + int n = 0; + XmString title = XmStringCreateLocalized((char *)title_str); + XmString label = XmStringCreateLocalized((char *)label_str); + + XtSetArg(args[n], XmNselectionLabelString, label); n++; + XtSetArg(args[n], XmNautoUnmanage, False); n++; + XtSetArg(args[n], XmNdialogTitle, title); n++; + XtSetArg(args[n], XmNuserData, NULL); n++; + prompt_widget = XmCreatePromptDialog( +#if USE_COMBOBOX + w, +#else + XtParent(w), +#endif + (char *)prompt_name, args, n); +/* XmStringFree(label); */ + + add_to_deplist(prefs, prompt_widget); + + info->tinfo = tinfo; + info->combo_box = w; + info->message_popup = NULL; + + /* Note: w is the browser_combo widget */ + XtAddCallback(prompt_widget, XmNokCallback, ok_cb, (XtPointer)info); + XtAddCallback(prompt_widget, XmNcancelCallback, destroy_cb, (XtPointer)info); + XtAddCallback(prompt_widget, XmNhelpCallback, help_cb, (XtPointer)info); + + XtManageChild(prompt_widget); + } + else { /* normal item */ +#if USE_COMBOBOX + int i; +#endif + + if (strcmp(prompt_name, Xdvi_BROWSER_POPUP_NAME) == 0) { +#if !USE_COMBOBOX + Widget rowcol; + if (get_widget_by_name(&rowcol, globals.widgets.top_level, Xdvi_BROWSER_COMBO_NAME, True)) { + XtVaSetValues(rowcol, XmNuserData, w, NULL); + } +#endif + free(browser_choice); + browser_choice = xstrdup(choice); + resource.browser = browser_choice; + store_preference(&(prefs->db), "wwwBrowser", "%s", browser_choice); + } + else if (strcmp(prompt_name, Xdvi_EDITOR_POPUP_NAME) == 0) { +#if !USE_COMBOBOX + Widget rowcol; + if (get_widget_by_name(&rowcol, globals.widgets.top_level, Xdvi_EDITOR_COMBO_NAME, True)) { + XtVaSetValues(rowcol, XmNuserData, w, NULL); + } +#endif + free(editor_choice); + editor_choice = xstrdup(choice); + resource.editor = editor_choice; + store_preference(&(prefs->db), "editor", "%s", editor_choice); + } + else + XDVI_ERROR((stderr, "Unknown category `%s' in h_selector()!", prompt_name)); + + +#if USE_COMBOBOX + /* update the currently selected value */ + XtVaGetValues(w, XmNselectedPosition, &i, NULL); + XtVaSetValues(w, XmNuserData, i, NULL); +#endif + } +#if USE_COMBOBOX + XtFree(choice); +#endif +} + +/* + * User OK'ed browser text input prompt + */ +static void +get_browser_text_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + if (XtIsRealized(w)) { + h_get_input_wrapper(Xdvi_BROWSER_COMBO_NAME, w, client_data, call_data); + } +} + + +/* + * User OK'ed editor text input prompt + */ +static void +get_editor_text_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + if (XtIsRealized(w)) { + h_get_input_wrapper(Xdvi_EDITOR_COMBO_NAME, w, client_data, call_data); + } +} + + +/* + * User selected an item from browser combo box pulldown list + */ +static void +select_browser_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + h_selector(Xdvi_BROWSER_POPUP_NAME, + "Xdvi: Add Browser Command", + "Browser Command (optional `%s' is replaced by URL): ", + get_browser_text_cb, destroy_dialog_cb, help_browser_dialog_cb, + w, client_data, call_data); +} + +/* + * User selected an item from editor combo box pulldown list + */ +static void +select_editor_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + UNUSED(client_data); + h_selector(Xdvi_EDITOR_POPUP_NAME, + "Xdvi: Add Editor Command", + "Editor Command (optional: `%l' = line number, `%f' = file name): ", + get_editor_text_cb, destroy_dialog_cb, help_editor_dialog_cb, + w, client_data, call_data); +} + +void +update_preferences_helpers(void) +{ + Widget shell, browser_cascade, editor_cascade; + + if (get_widget_by_name(&shell, globals.widgets.top_level, Xdvi_PREFS_DIALOG_NAME, False) + && get_widget_by_name(&browser_cascade, shell, Xdvi_BROWSER_COMBO_NAME, True) + && get_widget_by_name(&editor_cascade, shell, Xdvi_EDITOR_COMBO_NAME, True)) { + +#if USE_COMBOBOX + + Widget browser_list_w, editor_list_w; + XmString str; + int i; + Boolean found = False; + + XtVaGetValues(browser_cascade, XmNlist, &browser_list_w, NULL); + XtVaGetValues(editor_cascade, XmNlist, &editor_list_w, NULL); + + for (i = 0; m_browser_list[i] != NULL; i++) { + if (globals.curr_browser != NULL) { /* if this is set, ignore browser setting */ + if (strcmp(globals.curr_browser, m_browser_list[i]) == 0) { + found = True; + break; + } + } + else if (resource.browser != NULL) { + if (strcmp(resource.browser, m_browser_list[i]) == 0) { + found = True; + break; + } + } + } + if (!found) + i = 0; + str = XmStringCreateLtoR((char *)m_browser_list[i], "UNMARKED"); + XmComboBoxSelectItem(browser_cascade, str); + XmStringFree(str); + + for (i = 0; m_editor_list[i] != NULL; i++) { + if (globals.curr_editor != NULL) { /* if this is set, ignore editor setting */ + if (strcmp(globals.curr_editor, m_editor_list[i]) == 0) { + found = True; + break; + } + } + else if (resource.editor != NULL) { + if (strcmp(resource.editor, m_editor_list[i]) == 0) { + found = True; + break; + } + } + } + if (!found) + i = 0; + str = XmStringCreateLtoR((char *)m_editor_list[i], "UNMARKED"); + XmComboBoxSelectItem(editor_cascade, str); + XmStringFree(str); + /* XmListSetPos(editor_list_w, i); */ + /* fprintf(stderr, "setting editor list index %d, %s\n", */ + /* i, editor_list[i-1]); */ + + + /* if (XmListGetSelectedPos(browser_list_w, &browser_items, &browser_cnt) */ + /* && XmListGetSelectedPos(editor_list_w, &editor_items, &editor_cnt)) { */ + /* if (browser_cnt > 0 && editor_cnt > 0) { */ + /* int browser_idx = browser_items[0] - 1; */ + /* int editor_idx = editor_items[0] - 1; */ + /* fprintf(stderr, "selected: %s, %s\n", */ + /* browser_list[browser_idx], */ + /* editor_list[editor_idx]); */ + /* } */ + /* else { */ + /* XDVI_WARNING((stderr, "Shouldn't happen: No items selected in browser list?")); */ + /* return; */ + /* } */ + /* XtFree((XtPointer)browser_items); */ + /* XtFree((XtPointer)editor_items); */ + /* } */ +#else + Widget browser_menu, editor_menu; + if (get_widget_by_name(&browser_menu, shell, Xdvi_BROWSER_MENU_NAME, True) + && get_widget_by_name(&editor_menu, shell, Xdvi_EDITOR_MENU_NAME, True)) { + int i, num_buttons; + WidgetList buttons; + + XtVaGetValues(browser_menu, + XmNnumChildren, &num_buttons, + XmNchildren, &buttons, + NULL); + for (i = 0; i < num_buttons; i++) { + XmString str; + char *ptr; + + XtVaGetValues(buttons[i], XmNlabelString, &str, NULL); + XmStringGetLtoR(str, G_charset, &ptr); +/* fprintf(stderr, "kid %d: %s\n", i, ptr); */ + if (globals.curr_browser != NULL) { /* if this is set, ignore browser setting */ + if (strcmp(globals.curr_browser, ptr) == 0) { + break; + } + } + else if (resource.browser != NULL) { + if (strcmp(resource.browser, ptr) == 0) { + break; + } + } + } + if (i >= num_buttons) /* not found */ + i = 0; + XtVaSetValues(browser_cascade, XmNmenuHistory, buttons[i], NULL); + + /* same for editor */ + XtVaGetValues(editor_menu, + XmNnumChildren, &num_buttons, + XmNchildren, &buttons, NULL); + for (i = 0; i < num_buttons; i++) { + XmString str; + char *ptr; + + XtVaGetValues(buttons[i], XmNlabelString, &str, NULL); + XmStringGetLtoR(str, G_charset, &ptr); +/* fprintf(stderr, "kid %d: %s\n", i, ptr); */ + if (globals.curr_editor != NULL) { /* if this is set, ignore editor setting */ + if (strcmp(globals.curr_editor, ptr) == 0) { + break; + } + } + else if (resource.editor != NULL) { + if (strcmp(resource.editor, ptr) == 0) { + break; + } + } + } + if (i >= num_buttons) /* not found */ + i = 0; + XtVaSetValues(editor_cascade, XmNmenuHistory, buttons[i], NULL); + + } +#endif + /* for browser/editor, try: + + Widget menu; + int num_buttons; + WidgetList buttons; + + XtVaGetValues( simple_option_widget, XmNsubMenuId, &menu, NULL); + + XtVaGetValues( menu, XmNnumChildren, &num_buttons, + XmNchildren, &buttons, NULL ) ; + + */ + } +} + +static Widget +h_create_command(const char *name, + const char *menu_name, + const char *label, + const char *curr_value, + Widget parent, Widget top, + char **command_list, + XtCallbackProc select_cb, + struct topic_info *tinfo) +{ + const char *ptr = NULL; + size_t i; + +#if USE_COMBOBOX + XmStringTable str_list; + Widget text_label, combo_box; + size_t k, num; + int curr_index = -1; +#else + Widget menu, cascade, item; + XmString str; + Arg args[8]; + int n; +#endif + + /* check if we need to add the default resource name */ + ptr = curr_value; + + /* don't add it if it's already in our list */ + for (i = 0; ptr != NULL && command_list[i] != NULL; i++) { + if (strcmp(command_list[i], ptr) == 0) { + ptr = NULL; + } + } +#if USE_COMBOBOX + UNUSED(menu_name); + text_label = XmCreateLabelGadget(parent, (char *)label, NULL, 0); + XtVaSetValues(text_label, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, top, + XmNleftAttachment, XmATTACH_FORM, +/* XmNrightAttachment, XmATTACH_FORM, */ + NULL); + + /* count elements in command_list */ + for (num = 0; command_list[num] != NULL; num++) { ; } + + if (ptr != NULL) + num++; + + str_list = (XmStringTable)XtMalloc(num * sizeof(XmString *)); + + i = 0; + if (ptr != NULL) + str_list[i++] = XmStringCreateLtoR((char *)ptr, "UNMARKED"); + + for (k = 0; i < num; i++, k++) { + if (curr_value != NULL && strcmp(command_list[k], curr_value) == 0) { + curr_index = i; + } + if (strcmp(command_list[k], Xdvi_ADD_COMMAND_STR) == 0) + str_list[i] = XmStringCreateLtoR((char *)command_list[k], "MARKED"); + else + str_list[i] = XmStringCreateLtoR((char *)command_list[k], "UNMARKED"); + } + combo_box = XtVaCreateWidget(name, xmComboBoxWidgetClass, + parent, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, top, /* if top == NULL, this used XmATTACH_FORM */ + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, text_label, + XmNrightAttachment, XmATTACH_FORM, + XmNcomboBoxType, XmDROP_DOWN_LIST, + XmNitems, str_list, + XmNitemCount, num, + XmNuserData, curr_index, + NULL); + if (top != NULL) /* FIXME: This assumes we only have 2 items ... */ + XtVaSetValues(combo_box, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + + /* make resource setting current value */ + if (curr_index >= 0) { + XmComboBoxSelectItem(combo_box, str_list[curr_index]); + } + + for (i = 0; i < num; i++) + XmStringFree(str_list[i]); + XtFree((XtPointer)str_list); + + XtAddCallback(combo_box, XmNselectionCallback, select_cb, (XtPointer)tinfo); + + /* + * workaround for pointer grabbing bug (see xm_menu.c). We need to use + * the popdownCallback of the internal `GrabShell' child of the combo box. + */ + { + Widget grab_shell; + if (get_widget_by_name(&grab_shell, combo_box, "GrabShell", True)) + XtAddCallback(grab_shell, XtNpopdownCallback, popdown_callback, NULL); + } + adjust_heights(text_label, combo_box, NULL); + + XtManageChild(text_label); + XtManageChild(combo_box); + + return combo_box; +#else + menu = XmCreatePulldownMenu(parent, (char *)menu_name, NULL, 0); +/* XtVaSetValues(menu, */ +/* XmNtopAttachment, XmATTACH_WIDGET, */ +/* XmNtopWidget, top, */ +/* XmNleftAttachment, XmATTACH_FORM, */ +/* XmNrightAttachment, XmATTACH_FORM, */ +/* NULL); */ + + str = XmStringCreateLocalized((char *)label); + n = 0; + XtSetArg(args[n], XmNsubMenuId, menu); n++; + XtSetArg(args[n], XmNlabelString, str); n++; + XtSetArg(args[n], XmNuserData, NULL); n++; + cascade = XmCreateOptionMenu(parent, (char *)name, args, n); + if (top) + XtVaSetValues(cascade, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, top, + XmNleftAttachment, XmATTACH_FORM, + NULL); + else + XtVaSetValues(cascade, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + NULL); + XmStringFree(str); + + if (ptr != NULL) { + item = XtVaCreateManagedWidget(ptr, xmPushButtonGadgetClass, menu, + XmNuserData, tinfo, + NULL); + XtAddCallback(item, XmNactivateCallback, select_cb, menu); + } + + for (i = 0; command_list[i] != NULL; i++) { + item = XtVaCreateManagedWidget(command_list[i], xmPushButtonGadgetClass, menu, + XmNuserData, tinfo, + NULL); + XtAddCallback(item, XmNactivateCallback, select_cb, menu); + } + XtManageChild(cascade); + return cascade; +#endif +} + +Widget +prefs_helpers(struct topic_info *tinfo) +{ +/* struct prefs_choice *prefs = (struct prefs_choice *)info->data; */ + Widget form, /* frame, */ form1; + Widget browser_command, editor_command; + + form = XmCreateForm(tinfo->right_form, "helpers_form", NULL, 0); + +/* frame = XmCreateFrame(form, "commands_frame", NULL, 0); */ + + form1 = XtVaCreateWidget("commands_form", xmFormWidgetClass, + form, + XmNverticalSpacing, 10, + XmNhorizontalSpacing, 0, + NULL); + + if (m_browser_list == NULL) + m_browser_list = get_separated_list(resource.prefs_browser_list, "\n", False); + + if (m_editor_list == NULL) + m_editor_list = get_separated_list(resource.prefs_editor_list, "\n", False); + + browser_command = h_create_command(Xdvi_BROWSER_COMBO_NAME, + Xdvi_BROWSER_MENU_NAME, + "Web Browser: ", + resource.browser, + form1, NULL, + m_browser_list, + select_browser_cb, tinfo); + + editor_command = h_create_command(Xdvi_EDITOR_COMBO_NAME, + Xdvi_EDITOR_MENU_NAME, + "Editor for Source Specials: ", + resource.editor, + form1, browser_command, + m_editor_list, + select_editor_cb, tinfo); + +/* #if PS_GS */ +/* Widget b2 = XmCreateLabelGadget(rowcol, "[x] Use Ghostscript to interpret PS specials", NULL, 0); */ + + /* TODO: don't need this??? Similar: ps2pdf, dvips conversion?? */ + /* Widget b3 = XmCreateLabelGadget(rowcol, "Path to Ghostscript: ____________", NULL, 0); */ + +/* #endif */ +/* Widget b4 = XmCreateLabelGadget(rowcol, "Editor for source specials: <pulldown> [Other ...]", NULL, 0); */ +/* XtManageChild(b1); */ +/* XtManageChild(b2); */ +/* XtManageChild(b3); */ +/* XtManageChild(b4); */ +/* XtManageChild(rowcol); */ + + XtManageChild(form1); +/* XtManageChild(frame); */ + + return form; +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* MOTIF */ + diff --git a/Build/source/texk/xdvik/gui/xm_prefs_helpers.h b/Build/source/texk/xdvik/gui/xm_prefs_helpers.h new file mode 100644 index 00000000000..07b851186b1 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_prefs_helpers.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +#ifndef XM_PREFS_HELPERS_ +#define XM_PREFS_HELPERS_ + +#ifdef MOTIF + +struct topic_info; /* forward declaration */ +extern Widget prefs_helpers(struct topic_info *info); +extern void update_preferences_helpers(void); + +#endif /* MOTIF */ + +#endif /* XM_PREFS_HELPERS_ */ diff --git a/Build/source/texk/xdvik/gui/xm_prefs_page.c b/Build/source/texk/xdvik/gui/xm_prefs_page.c new file mode 100644 index 00000000000..b40f1f63132 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_prefs_page.c @@ -0,0 +1,695 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +/* + * Panel 4 (Paper size) for xdvik preferences dialog. + */ + +#include "xdvi-config.h" +#include "xdvi.h" + +#include "x_util.h" +#include "xm_colorsel.h" +#include "topic-window.h" +#include "util.h" +#include "events.h" +#include "statusline.h" + +#include "xm_prefsP.h" +#include "xm_prefs_page.h" +#include "my-snprintf.h" + +#ifdef MOTIF /* entire file */ + +#include <X11/Xatom.h> + +#include <Xm/Xm.h> +#include <Xm/Protocols.h> +#include <Xm/DialogS.h> +#include <Xm/LabelG.h> +#include <Xm/PushB.h> +#include <Xm/PushBG.h> +#include <Xm/Form.h> +#include <Xm/Frame.h> +#include <Xm/PanedW.h> +#include <Xm/DrawnB.h> +#include <Xm/RowColumn.h> +#include <Xm/Scale.h> +#include <Xm/ToggleBG.h> +#include <Xm/Text.h> +#include <Xm/TextF.h> +#include <Xm/CascadeBG.h> + +#if XmVersion >= 2000 && !defined(LESSTIF_VERSION) +/* spinbox only available with Motif 2.0; the lesstif version (as of 0.93) looks like shit */ +#include <Xm/SpinB.h> +#define USE_SPINBOX 1 +#else +#define USE_SPINBOX 0 +#endif + +/* + * Handy defaults + */ +static Arg one_of_many = { XmNindicatorType, XmONE_OF_MANY }; +static Arg n_of_many = { XmNindicatorType, XmN_OF_MANY }; +static Arg frame_title = { XmNchildType, XmFRAME_TITLE_CHILD }; +static Arg one_col[] = { + { XmNpacking, XmPACK_TIGHT }, + { XmNnumColumns, 1 }, + { XmNorientation, XmVERTICAL } +}; + + +/* this is a small selection from the paper types in xdvi.c + * (only the more frequenlty used ones) + */ +static struct paper_info { + const char *format; + const char *resource_format; + const char *size; + const char *landscape_size; +} papersizes[] = { + { "US (Letter)", "us", "8.5x11in", "11x8.5in" }, + { "Tabloid", "tabloid", "11x17in", "17x11in" }, /* dvips compatibility */ + { "Legal", "legal", "8.5x14in", "14x8.5in" }, + { "A0", "a0", "841x1189mm", "1189x841mm" }, + { "A1", "a1", "594x841mm", "841x594mm" }, + { "A2", "a2", "420x594mm", "594x420mm" }, + { "A3", "a3", "297x420mm", "420x297mm" }, + { "A4", "a4", "210x297mm", "297x210mm" }, + { "A5", "a5", "148x210mm", "210x148mm" }, + { "B5", "b5", "176x250mm", "250x176mm" }, + { "B6", "b6", "125x176mm", "176x125mm" }, + { "B7", "b7", "88x125mm", "125x88mm" }, + { NULL, NULL, NULL, NULL } /* terminate */ +}; + +void update_preferences_windowsize(void) +{ + Widget shell, button; + + /* it's not an error if the prefs dialog doesn't exist yet */ + if (get_widget_by_name(&shell, globals.widgets.top_level, Xdvi_PREFS_DIALOG_NAME, False) + && get_widget_by_name(&button, shell, Xdvi_REMEMBER_WINDOWSIZE_STR, True)) { + XmToggleButtonGadgetSetState(button, resource.remember_windowsize, False); + } +} + +void update_preferences_shrink(void) +{ + Widget shell, text; + + if (get_widget_by_name(&shell, globals.widgets.top_level, Xdvi_PREFS_DIALOG_NAME, False) + && get_widget_by_name(&text, shell, Xdvi_SHRINK_TEXT, True)) { + char buf[LENGTH_OF_INT]; + SNPRINTF(buf, LENGTH_OF_INT, "%d", resource.shrinkfactor); +#if USE_SPINBOX + XmTextFieldSetString(text, buf); + XtVaSetValues(text, XmNposition, resource.shrinkfactor, NULL); +#else + XtVaSetValues(text, XmNvalue, buf, NULL); +#endif + } +} + +static void +landscape_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *tinfo = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)tinfo->data; + Widget portrait_b, landscape_b; + Boolean landscape = False, is_set; + static char *paper_option = NULL; + Boolean match = False; + int i; + + UNUSED(call_data); + + for (i = 0; papersizes[i].format != NULL; i++) { + /* ignore final `r' when comparing */ + if (strncmp(resource.paper, + papersizes[i].resource_format, + strlen(papersizes[i].resource_format)) == 0) { + const char *ptr; + match = True; + if (strcmp(resource.paper, "letter") == 0) /* special case */ + ptr = "us"; + else + ptr = papersizes[i].resource_format; + free(paper_option); + paper_option = xstrdup(ptr); + break; + } + } + + if (!match) { + XDVI_ERROR((stderr, "paper resource `%s' not found in list!\n", resource.paper)); + return; + } + + if (get_widget_by_name(&portrait_b, tinfo->shell, Xdvi_PAPER_PORTRAIT_STR, True) + && get_widget_by_name(&landscape_b, tinfo->shell, Xdvi_PAPER_LANDSCAPE_STR, True)) { + + if (w == portrait_b) { + XtVaGetValues(portrait_b, XmNset, &is_set, NULL); + landscape = !is_set; + } + else { + XtVaGetValues(landscape_b, XmNset, &is_set, NULL); + landscape = is_set; + } + +/* fprintf(stderr, "++++++++ CALLBACK: setting portrait to %s, landscape to %s\n", */ +/* landscape ? "False" : "True", */ +/* landscape ? "True" : "False"); */ + + XmToggleButtonGadgetSetState(portrait_b, !landscape, False); + XmToggleButtonGadgetSetState(landscape_b, landscape, False); + } + + if (landscape) { + paper_option = xstrcat(paper_option, "r"); + } + + resource.paper = paper_option; + store_preference(&(prefs->db), "paper", "%s", paper_option); +} + +static void +select_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *tinfo = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)tinfo->data; + + XmString str; + char *choice; + int i; + static char *curr_choice= NULL; + Boolean landscape = False; + + UNUSED(call_data); + + XtVaGetValues(w, XmNlabelString, &str, NULL); + XmStringGetLtoR(str, G_charset, &choice); + + if (strlen(resource.paper) > 0 && + strcmp(resource.paper, "letter") != 0 && + resource.paper[strlen(resource.paper) - 1] == 'r') + landscape = True; + + free(curr_choice); + curr_choice = NULL; + for (i = 0; papersizes[i].format != NULL; i++) { + if (strcmp(choice, papersizes[i].format) == 0) { + curr_choice = xstrdup(papersizes[i].resource_format); + } + } + if (curr_choice == NULL) { + XDVI_ERROR((stderr, "choice`%s' not found in list!\n", choice)); + return; + } + + if (landscape) { + curr_choice = xstrcat(curr_choice, "r"); + } + + resource.paper = curr_choice; + store_preference(&(prefs->db), "paper", "%s", curr_choice); +} + +void +update_preferences_paper(void) +{ + Widget shell, portrait_button, landscape_button, sizes_menu, sizes_cascade; + Boolean landscape = False; + + /* it's not an error if the prefs dialog doesn't exist yet */ + if (get_widget_by_name(&shell, globals.widgets.top_level, Xdvi_PREFS_DIALOG_NAME, False) + && get_widget_by_name(&portrait_button, shell, Xdvi_PAPER_PORTRAIT_STR, True) + && get_widget_by_name(&landscape_button, shell, Xdvi_PAPER_LANDSCAPE_STR, True) + && get_widget_by_name(&sizes_menu, shell, Xdvi_PAPER_MENU, True) + && get_widget_by_name(&sizes_cascade, shell, Xdvi_PAPER_CASCADE, True)) { + + int i; + Widget sizes_button; + + if (strlen(resource.paper) > 0 && + strcmp(resource.paper, "letter") != 0 && + resource.paper[strlen(resource.paper) - 1] == 'r') { + + landscape = True; + } + +#if 0 + XtVaGetValues(portrait_button, XmNset, &test1, NULL); + XtVaGetValues(landscape_button, XmNset, &test2, NULL); + + fprintf(stderr, "+++++++ old state of portrait: %d, landscape: %d\n", + test1, test2); + + fprintf(stderr, "+++++++ setting portrait to %s, landscape to %s\n", + landscape ? "False" : "True", + landscape ? "True" : "False"); +#endif /* 0 */ + + /* + Apparently there's a bug in Motif (OpenMotif 2.1) in that the button + is not properly updated in all cases. + + To reproduce: + + Open Preferences dialog, change `landscape' option, click + `OK'. Now open the dialog again, change the option again, + but click `Cancel'. This correctly reverts the visible + state of the buttons, but when opening the dialog again, + the button's internal state is not consistent with the + visual appearance: when clicking on the unset button, its + ValueChanged callback is not invoked, so nothing happens; + but after clicking on another(!) button in the preferences + dialog, it works again. + + As a workaround, we destroy the buttons and re-create them from scratch - yuck! + */ + { + Widget form = XtParent(portrait_button); + Widget cascade; + struct topic_info *info; + + XtVaGetValues(portrait_button, + XmNleftWidget, &cascade, + XmNuserData, &info, + NULL); + XtDestroyWidget(portrait_button); + XtDestroyWidget(landscape_button); + + portrait_button = XmCreateToggleButtonGadget(form, Xdvi_PAPER_PORTRAIT_STR, &one_of_many, 1); + XmToggleButtonGadgetSetState(portrait_button, !landscape, False); + XtVaSetValues(portrait_button, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, cascade, + XmNleftOffset, 10, + XmNuserData, (XtPointer)info, + NULL); + landscape_button = XmCreateToggleButtonGadget(form, Xdvi_PAPER_LANDSCAPE_STR, &one_of_many, 1); + XmToggleButtonGadgetSetState(landscape_button, landscape, False); + XtVaSetValues(landscape_button, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, portrait_button, + XmNleftOffset, 10, + XmNuserData, (XtPointer)info, + NULL); + XtManageChild(portrait_button); + XtManageChild(landscape_button); + XtAddCallback(portrait_button, XmNvalueChangedCallback, landscape_cb, (XtPointer)info); + XtAddCallback(landscape_button, XmNvalueChangedCallback, landscape_cb, (XtPointer)info); + } +#if 0 + XtVaGetValues(portrait_button, XmNset, &test1, NULL); + XtVaGetValues(landscape_button, XmNset, &test2, NULL); + + fprintf(stderr, "+++++++ new state of portrait: %d, landscape: %d\n", + test1, test2); +#endif /* 0 */ + + for (i = 0; papersizes[i].format != NULL; i++) { + if (strncmp(resource.paper, + papersizes[i].resource_format, + strlen(papersizes[i].resource_format)) == 0 + && get_widget_by_name(&sizes_button, sizes_menu, papersizes[i].format, True)) { + + XtVaSetValues(sizes_cascade, XmNmenuHistory, sizes_button, NULL); + } + } + } +} + +#if 0 /* currently unused */ +static void +shrinkfactor_spinbox_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + int val; +/* static Boolean first_time = True; */ + struct topic_info *info = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)(info->data); + UNUSED(call_data); + + XtVaGetValues(w, XmNposition, &val, NULL); + + /* return if value not changed yet; else the first invocation of the + window may actually reset the shrink factor to the default. + */ +/* if (val != resource.shrinkfactor) { */ +/* first_time = False; */ +/* } */ + + if (XtIsRealized(w)) { + /* don't set the resource.shrinkfactor here so that we can revert it from there if needed */ + do_set_shrinkfactor(val, False); + store_preference(&(prefs->db), "shrinkFactor", "%d", val); + } +} +#endif /* currently unused */ + +static void +set_shrinkfactor_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + char *text; + int val; + struct topic_info *info = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)(info->data); + +#if USE_SPINBOX + XmTextPosition pos; + UNUSED(call_data); + text = XmTextFieldGetString(w); + val = strtol(text, (char **)NULL, 10); +#else + Widget text_field; + UNUSED(call_data); + XtVaGetValues(w, XmNuserData, &text_field, NULL); + ASSERT(text_field != 0, "Expected text field in XmNuserData, set_shrinkfactor_cb()"); + XtVaGetValues(text_field, XmNvalue, &text, NULL); + val = strtol(text, (char **)NULL, 10); +/* fprintf(stderr, "spinbox value: |%s|\n", text); */ +#endif + + if (!XtIsRealized(w)) + return; + + /* verify values in case of direct text input */ + if (val > 100 || val <= 0) { + if (val > 100) + statusline_print(STATUS_MEDIUM, "Shrink factor larger than maximum 100"); + else if (val <= 0) + statusline_print(STATUS_MEDIUM, "Shrink factor smaller than minimum 1"); + return; + } +#if USE_SPINBOX + /* synchronize internal spinbox value */ + pos = XmTextFieldGetInsertionPosition(w); + XtVaSetValues(w, XmNposition, val, NULL); + XmTextFieldSetInsertionPosition(w, pos); +#endif + /* don't set the resource.shrinkfactor here so that we can revert it from there if needed */ + do_set_shrinkfactor(val, False); + store_preference(&(prefs->db), "shrinkFactor", "%d", val); + + XtFree(text); +} + +static Widget +h_create_shrink_frame(Widget top, struct topic_info *info) +{ + Widget shrink_form, shrink_label; + + UNUSED(info); + + shrink_form = XmCreateForm(top, "shrink_form", NULL, 0); + XtVaSetValues(shrink_form, +/* XmNverticalSpacing, 10, */ +/* XmNhorizontalSpacing, 10, */ + NULL); + + + shrink_label = XmCreateLabelGadget(shrink_form, "Default Shrink Factor: ", NULL, 0); + XtVaSetValues(shrink_label, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + /* TODO XmNleftOffset, 10, */ + NULL); + XtManageChild(shrink_label); + { + Widget shrink_text; +#if USE_SPINBOX + Widget shrink_spinbox = + XtVaCreateManagedWidget(Xdvi_SHRINK_SPINBOX, xmSpinBoxWidgetClass, shrink_form, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, shrink_label, + XmNinitialDelay, 200, + XmNrepeatDelay, 50, + NULL); + shrink_text = + XtVaCreateManagedWidget(Xdvi_SHRINK_TEXT, xmTextFieldWidgetClass, shrink_spinbox, + XmNspinBoxChildType, XmNUMERIC, + XmNminimumValue, 1, + XmNmaximumValue, 100, + XmNeditable, True, + XmNcolumns, 3, + XmNincrementValue, 1, + XmNwrap, False, /* too confusing */ + /* NOTE: use resource.shrinkfactor here, not current setting, + otherwise the current setting will sneak into ~/.xdvirc */ + XmNposition, (int)(resource.shrinkfactor), + NULL); + + XtAddCallback(shrink_text, XmNactivateCallback, set_shrinkfactor_cb, (XtPointer)info); + XtAddCallback(shrink_text, XmNvalueChangedCallback, set_shrinkfactor_cb, (XtPointer)info); + + adjust_heights(shrink_spinbox, shrink_text, shrink_label, NULL); + +#else + char buf[LENGTH_OF_INT]; + Widget shrink_button; + shrink_text = XmCreateTextField(shrink_form, Xdvi_SHRINK_TEXT, NULL, 0); + SNPRINTF(buf, LENGTH_OF_INT, "%d", resource.shrinkfactor); + XtVaSetValues(shrink_text, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, shrink_label, + XmNcolumns, 4, + XmNvalue, buf, + XmNuserData, shrink_text, + NULL); + XtOverrideTranslations(shrink_text, XtParseTranslationTable("<Key>Return:activate()")); + XtAddCallback(shrink_text, XmNactivateCallback, set_shrinkfactor_cb, (XtPointer)info); + + shrink_button = XmCreatePushButton(shrink_form, Xdvi_APPLY_STR, NULL, 0); + XtVaSetValues(shrink_button, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftOffset, 10, + XmNleftWidget, shrink_text, + XmNuserData, shrink_text, + NULL); + XtAddCallback(shrink_button, XmNactivateCallback, set_shrinkfactor_cb, (XtPointer)info); + + adjust_heights(shrink_label, shrink_text, shrink_button, NULL); + XtManageChild(shrink_text); + XtManageChild(shrink_button); +#endif + } + +/* XtManageChild(shrink_form); */ + + return shrink_form; +} + +static void +remember_windowsize_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *info = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)(info->data); + + UNUSED(w); + UNUSED(call_data); + + resource.remember_windowsize = !resource.remember_windowsize; + + store_preference(&(prefs->db), "rememberWindowSize", "%s", resource.remember_windowsize ? "True" : "False"); +} + + + +static Widget +h_create_papersize_form(Widget parent, struct topic_info *info) +{ + Widget form, menu, cascade, portrait_option, landscape_option; + XmString str; + Arg args[8]; + int n; + size_t i; + Boolean landscape = False; + + form = XmCreateForm(parent, "paper_form", NULL, 0); + + menu = XmCreatePulldownMenu(form, Xdvi_PAPER_MENU, NULL, 0); + + if (strcmp(resource.paper, "letter") != 0 && /* exceptions: these have no landscape format */ + strcmp(resource.paper, "ledger") != 0 && + strlen(resource.paper) > 0 && + resource.paper[strlen(resource.paper) - 1] == 'r') { /* landscape format */ + landscape = True; + } + + str = XmStringCreateLocalized("Paper Size:"); + + n = 0; + XtSetArg(args[n], XmNsubMenuId, menu); n++; + XtSetArg(args[n], XmNlabelString, str); n++; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; + + cascade = XmCreateOptionMenu(form, Xdvi_PAPER_CASCADE, args, n); + XmStringFree(str); + + portrait_option = XmCreateToggleButtonGadget(form, Xdvi_PAPER_PORTRAIT_STR, &one_of_many, 1); + XmToggleButtonGadgetSetState(portrait_option, !landscape, False); + XtVaSetValues(portrait_option, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, cascade, + XmNleftOffset, 10, + XmNuserData, (XtPointer)info, + NULL); + + landscape_option = XmCreateToggleButtonGadget(form, Xdvi_PAPER_LANDSCAPE_STR, &one_of_many, 1); + XmToggleButtonGadgetSetState(landscape_option, landscape, False); + XtVaSetValues(landscape_option, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, portrait_option, + XmNleftOffset, 10, + XmNuserData, (XtPointer)info, + NULL); +/* adjust_heights(landscape_option, menu, cascade, NULL); */ + XtAddCallback(portrait_option, XmNvalueChangedCallback, landscape_cb, (XtPointer)info); + XtAddCallback(landscape_option, XmNvalueChangedCallback, landscape_cb, (XtPointer)info); +/* XtAddCallback(portrait_option, XmNarmCallback, test_cb, (XtPointer)info); */ +/* XtAddCallback(landscape_option, XmNarmCallback, test_cb, (XtPointer)info); */ + + XtManageChild(portrait_option); + XtManageChild(landscape_option); + + for (i = 0; papersizes[i].format != NULL; i++) { + Widget w = XtVaCreateManagedWidget(papersizes[i].format, xmPushButtonGadgetClass, menu, + XmNuserData, landscape_option, + NULL); +/* fprintf(stderr, "Created button: %p for %s\n", w, papersizes[i].format); */ + + /* select default value */ + if (strncmp(resource.paper, + papersizes[i].resource_format, + strlen(papersizes[i].resource_format)) == 0) { + XtVaSetValues(cascade, XmNmenuHistory, w, NULL); + } + XtAddCallback(w, XmNactivateCallback, select_cb, (XtPointer)info); + } + XtManageChild(cascade); + + return form; +} + +Widget +prefs_paper(struct topic_info *info) +{ + Widget form; + Widget geometry_frame, geometry_label, geometry_rowcol, shrink_frame, geometry_button; + Widget paper_frame, paper_label; + Arg args[10]; + int n = 0; + Widget rowcol; +#if !defined(LESSTIF_VERSION) + Widget paper_text; +#endif + Widget paper_size_form; + + form = XtVaCreateWidget("form", xmFormWidgetClass, + info->right_form, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + + n = 0; + XtSetArg(args[n], XmNmarginWidth, 8); n++; + XtSetArg(args[n], XmNmarginHeight, 4); n++; + geometry_frame = XmCreateFrame(form, "geometry_frame", args, n); + h_attach_below(geometry_frame, NULL); + + geometry_label = XmCreateLabelGadget(geometry_frame, "Window Size and Shrink Factor", &frame_title, 1); + XtManageChild(geometry_label); + + geometry_rowcol = XmCreateRowColumn(geometry_frame, "geometry_rowcol", one_col, XtNumber(one_col)); + + geometry_button = XmCreateToggleButtonGadget(geometry_rowcol, Xdvi_REMEMBER_WINDOWSIZE_STR, &n_of_many, 1); + XmToggleButtonGadgetSetState(geometry_button, resource.remember_windowsize, False); + XtAddCallback(geometry_button, XmNvalueChangedCallback, remember_windowsize_cb, (XtPointer)info); + XtManageChild(geometry_button); + + shrink_frame = h_create_shrink_frame(geometry_rowcol, info); + XtManageChild(shrink_frame); + + XtManageChild(geometry_rowcol); + + n = 0; + XtSetArg(args[n], XmNmarginWidth, 8); n++; + XtSetArg(args[n], XmNmarginHeight, 4); n++; + XtSetArg(args[n], XmNtopOffset, 10); n++; + paper_frame = XmCreateFrame(form, "paper_frame", args, n); + h_attach_below(paper_frame, geometry_frame); + + paper_label = XmCreateLabelGadget(paper_frame, "Default Paper Size", &frame_title, 1); + XtManageChild(paper_label); + + rowcol = XmCreateRowColumn(paper_frame, "papersize_rowcol", NULL, 0); + +#if !defined(LESSTIF_VERSION) /* stupid LessTif doesn't wrap Labels at '\n' */ + paper_text = XmCreateLabelGadget(rowcol, + "Used if the DVI file does not specify the paper size, e.g. via\n" + "\\usepackage[dvips]{geometry}. This setting will only take effect\n" + "after restarting xdvi.", NULL, 0); + XtVaSetValues(paper_text, + XmNalignment, XmALIGNMENT_BEGINNING, + NULL); + XtManageChild(paper_text); +#endif + + paper_size_form = h_create_papersize_form(rowcol, info); + XtManageChild(paper_size_form); + + XtManageChild(rowcol); + + XtManageChild(geometry_frame); + XtManageChild(paper_frame); + + XtManageChild(form); + + return form; +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* MOTIF */ + diff --git a/Build/source/texk/xdvik/gui/xm_prefs_page.h b/Build/source/texk/xdvik/gui/xm_prefs_page.h new file mode 100644 index 00000000000..e333f831507 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_prefs_page.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +#ifndef XM_PREFS_PAGE_ +#define XM_PREFS_PAGE_ + +#ifdef MOTIF + +struct topic_info; /* forward declaration */ +extern Widget prefs_paper(struct topic_info *info); +extern void update_preferences_windowsize(void); +extern void restore_preferences_shrink(void); +extern void update_preferences_shrink(void); +extern void update_preferences_paper(void); + +#endif /* MOTIF */ + +#endif /* XM_PREFS_PAGE_ */ diff --git a/Build/source/texk/xdvik/gui/xm_prefs_scroll.c b/Build/source/texk/xdvik/gui/xm_prefs_scroll.c new file mode 100644 index 00000000000..fda08dd33dd --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_prefs_scroll.c @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +/* + * Panel 5 (Scrolling behaviour) for xdvik preferences dialog. + */ + +#include "xdvi-config.h" +#include "xdvi.h" + +#include "x_util.h" +#include "xm_colorsel.h" +#include "topic-window.h" +#include "util.h" +#include "events.h" +#include "dvi-init.h" +#include "statusline.h" + +#include "xm_prefsP.h" +#include "xm_prefs_scroll.h" +#include "my-snprintf.h" + +#ifdef MOTIF /* entire file */ + +#include <ctype.h> + +#include <X11/Xatom.h> + +#include <Xm/Xm.h> +#include <Xm/Protocols.h> +#include <Xm/DialogS.h> +#include <Xm/LabelG.h> +#include <Xm/PushB.h> +#include <Xm/PushBG.h> +#include <Xm/Form.h> +#include <Xm/Frame.h> +#include <Xm/PanedW.h> +#include <Xm/DrawnB.h> +#include <Xm/RowColumn.h> +#include <Xm/Scale.h> +#include <Xm/ToggleBG.h> +#include <Xm/Text.h> +#include <Xm/TextF.h> +#include <Xm/CascadeBG.h> + +/* + * Handy defaults + */ +static Arg one_of_many = { XmNindicatorType, XmONE_OF_MANY }; + +static void +select_unit_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *info = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)(info->data); + + Widget pulldown = XtParent(w); + Widget form = XtParent(XtParent(pulldown)); + Widget text_x, text_y; + + const char *w_name = XtName(w); + + UNUSED(call_data); + + fprintf(stderr, "setting name: %s\n", w_name); + XtVaSetValues(pulldown, XmNuserData, (XtPointer)w_name, NULL); + + if (get_widget_by_name(&text_x, form, Xdvi_HOME_POSITION_X_OFF_TEXT, True) + && get_widget_by_name(&text_y, form, Xdvi_HOME_POSITION_Y_OFF_TEXT, True)) { + const char *buf_x = XmTextFieldGetString(text_x); + const char *buf_y = XmTextFieldGetString(text_y); + + int val_x = strtol(buf_x, (char **)NULL, 10); + int val_y = strtol(buf_y, (char **)NULL, 10); + + static char x_off[LENGTH_OF_INT + 16]; + static char y_off[LENGTH_OF_INT + 16]; + + SNPRINTF(x_off, LENGTH_OF_INT + 16, "%s%s", buf_x, w_name); + SNPRINTF(y_off, LENGTH_OF_INT + 16, "%s%s", buf_y, w_name); + + resource.sidemargin = x_off; + resource.topmargin = y_off; + + resource.sidemargin_int = val_x; + resource.topmargin_int = val_y; + + store_preference(&(prefs->db), "sideMargin", "%s", x_off); + store_preference(&(prefs->db), "topMargin", "%s", y_off); + + globals.ev.flags |= EV_NEWPAGE; + XFlush(DISP); + + XtFree((char *)buf_x); + XtFree((char *)buf_y); + } +} + +static void +set_offset_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *info = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)(info->data); + + Widget form = XtParent(w); + Widget pulldown; + + UNUSED(call_data); + + if (get_widget_by_name(&pulldown, form, Xdvi_HOME_POSITION_UNITS_PULLDOWN, True)) { + char *ptr = NULL; + const char *w_name = XtName(w); + + XtVaGetValues(pulldown, XmNuserData, &ptr, NULL); + + if (ptr == NULL) { + XDVI_WARNING((stderr, "XmNuserData for %s is NULL in set_offset_cb()\n", + Xdvi_HOME_POSITION_UNITS_PULLDOWN)); + return; + } + fprintf(stderr, "UNIT: |%s|\n", ptr); + + if (strcmp(w_name, Xdvi_HOME_POSITION_X_OFF_TEXT) == 0) { + static char x_off[LENGTH_OF_INT + 16]; + const char *buf = XmTextFieldGetString(w); + int val = strtol(buf, (char **)NULL, 10); + + SNPRINTF(x_off, LENGTH_OF_INT + 16, "%s%s", buf, ptr); + + XtFree((char *)buf); + + resource.sidemargin = x_off; + resource.sidemargin_int = val; + store_preference(&(prefs->db), "sideMargin", "%s", x_off); + + goto_page(current_page, resource.keep_flag ? NULL : home, True); + } + else if (strcmp(w_name, Xdvi_HOME_POSITION_Y_OFF_TEXT) == 0) { + static char y_off[LENGTH_OF_INT + 16]; + const char *buf = XmTextFieldGetString(w); + int val = strtol(buf, (char **)NULL, 10); + + SNPRINTF(y_off, LENGTH_OF_INT + 16, "%s%s", buf, ptr); + + XtFree((char *)buf); + + resource.topmargin = y_off; + resource.topmargin_int = val; + store_preference(&(prefs->db), "topMargin", "%s", y_off); + + goto_page(current_page, resource.keep_flag ? NULL : home, True); + } + else { + XDVI_WARNING((stderr, "unexpected widget name `%s' in set_offset_cb()", w_name)); + } + } +} + +static void +home_position_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *info = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)(info->data); + + Widget parent = XtParent(XtParent(w)); + Widget toggle_home, toggle_curr, label_x, label_y, text_x, text_y, units_menu; + + UNUSED(call_data); + + resource.use_current_offset = !resource.use_current_offset; + + if (get_widget_by_name(&toggle_home, parent, Xdvi_HOME_POSITION_STR, True) + && get_widget_by_name(&toggle_curr, parent, Xdvi_SCROLL_CURRENT_STR, True) + && get_widget_by_name(&label_x, parent, Xdvi_HOME_POSITION_X_STR, True) + && get_widget_by_name(&label_y, parent, Xdvi_HOME_POSITION_Y_STR, True) + && get_widget_by_name(&text_x, parent, Xdvi_HOME_POSITION_X_OFF_TEXT, True) + && get_widget_by_name(&text_y, parent, Xdvi_HOME_POSITION_Y_OFF_TEXT, True) + && get_widget_by_name(&units_menu, parent, Xdvi_HOME_POSITION_UNITS_MENU, True)) { + + XmToggleButtonGadgetSetState(toggle_home, !resource.use_current_offset, False); + XmToggleButtonGadgetSetState(toggle_curr, resource.use_current_offset, False); + + XtSetSensitive(label_x, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(label_y, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(text_x, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(text_y, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(units_menu, !resource.keep_flag && !resource.use_current_offset); + } + + store_preference(&(prefs->db), "useCurrentOffset", "%s", resource.use_current_offset ? "True" : "False"); +} + +static void +keep_cb(Widget w, XtPointer client_data, XtPointer call_data) +{ + struct topic_info *info = (struct topic_info *)client_data; + struct prefs_choice *prefs = (struct prefs_choice *)(info->data); + + Widget parent, keep, unkeep, toggle_home, toggle_curr, label_x, label_y, text_x, text_y, units_menu; + + UNUSED(call_data); + + resource.keep_flag = !resource.keep_flag; + + parent = XtParent(w); + + if (get_widget_by_name(&keep, parent, Xdvi_SCROLL_KEEP_STR, True) + && get_widget_by_name(&unkeep, parent, Xdvi_SCROLL_UNKEEP_STR, True) + && get_widget_by_name(&toggle_home, parent, Xdvi_HOME_POSITION_STR, True) + && get_widget_by_name(&toggle_curr, parent, Xdvi_SCROLL_CURRENT_STR, True) + && get_widget_by_name(&label_x, parent, Xdvi_HOME_POSITION_X_STR, True) + && get_widget_by_name(&label_y, parent, Xdvi_HOME_POSITION_Y_STR, True) + && get_widget_by_name(&text_x, parent, Xdvi_HOME_POSITION_X_OFF_TEXT, True) + && get_widget_by_name(&text_y, parent, Xdvi_HOME_POSITION_Y_OFF_TEXT, True) + && get_widget_by_name(&units_menu, parent, Xdvi_HOME_POSITION_UNITS_MENU, True)) { + + XmToggleButtonGadgetSetState(keep, resource.keep_flag, False); + XmToggleButtonGadgetSetState(unkeep, !resource.keep_flag, False); + + XtSetSensitive(label_x, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(label_y, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(text_x, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(text_y, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(units_menu, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(toggle_home, !resource.keep_flag); + XtSetSensitive(toggle_curr, !resource.keep_flag); + } + + store_preference(&(prefs->db), "keepPosition", "%s", resource.keep_flag ? "True" : "False"); + +} + +static Widget +h_create_scrolling(Widget parent, struct topic_info *info) +{ + Widget form, form1, form2, size_option, curr_option; + Widget x_off_label, x_off_text; + Widget y_off_label, y_off_text; + Widget off_units_menu, cascade, entry1, entry2, entry3; + + Widget scroll_keep, scroll_unkeep; + + char x_buf[LENGTH_OF_INT + 16]; + char y_buf[LENGTH_OF_INT + 16]; + int n; + Arg args[8]; + + fprintf(stderr, "sidemargin: %s, topmargin: %s\n", resource.sidemargin, resource.topmargin); + + if (resource.sidemargin != NULL) { + char *ptr; + strcpy(x_buf, resource.sidemargin); + ptr = x_buf; + if (*ptr == '-') + ptr++; + while (isdigit((int)*ptr)) + ptr++; + *ptr = '\0'; + } + else + strcpy(x_buf, "0"); + + if (resource.topmargin != NULL) { + char *ptr; + strcpy(y_buf, resource.topmargin); + ptr = y_buf; + if (*ptr == '-') + ptr++; + while (isdigit((int)*ptr)) + ptr++; + *ptr = '\0'; + } + else + strcpy(y_buf, "0"); + + form = XmCreateForm(parent, "offsets_form", NULL, 0); + + scroll_keep = XmCreateToggleButtonGadget(form, Xdvi_SCROLL_KEEP_STR, &one_of_many, 1); + XtVaSetValues(scroll_keep, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + NULL); + scroll_unkeep = XmCreateToggleButtonGadget(form, Xdvi_SCROLL_UNKEEP_STR, &one_of_many, 1); + XtVaSetValues(scroll_unkeep, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, scroll_keep, + XmNleftAttachment, XmATTACH_FORM, + NULL); + + XmToggleButtonGadgetSetState(scroll_keep, resource.keep_flag, False); + XmToggleButtonGadgetSetState(scroll_unkeep, !resource.keep_flag, False); + + XtAddCallback(scroll_keep, XmNvalueChangedCallback, keep_cb, (XtPointer)info); + XtAddCallback(scroll_unkeep, XmNvalueChangedCallback, keep_cb, (XtPointer)info); + + XtManageChild(scroll_keep); + XtManageChild(scroll_unkeep); + + form1 = XmCreateForm(form, "offsets_form1", NULL, 0); + XtVaSetValues(form1, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, scroll_unkeep, + XmNleftAttachment, XmATTACH_FORM, + XmNleftOffset, 20, + NULL); + + size_option = XmCreateToggleButtonGadget(form1, Xdvi_HOME_POSITION_STR, &one_of_many, 1); + XtVaSetValues(size_option, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + x_off_label = XmCreateLabelGadget(form1, Xdvi_HOME_POSITION_X_STR, NULL, 0); + XtVaSetValues(x_off_label, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, size_option, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + x_off_text = XtVaCreateManagedWidget(Xdvi_HOME_POSITION_X_OFF_TEXT, + xmTextFieldWidgetClass, form1, + XmNcolumns, 4, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, x_off_label, + XmNbottomAttachment, XmATTACH_FORM, + XmNvalue, x_buf, +/* XmNvalue, buf, */ + NULL); + y_off_label = XmCreateLabelGadget(form1, Xdvi_HOME_POSITION_Y_STR, NULL, 0); + XtVaSetValues(y_off_label, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, x_off_text, + XmNleftOffset, 10, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + y_off_text = XtVaCreateManagedWidget(Xdvi_HOME_POSITION_Y_OFF_TEXT, + xmTextFieldWidgetClass, form1, + XmNcolumns, 4, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, y_off_label, + XmNbottomAttachment, XmATTACH_FORM, + XmNvalue, y_buf, + NULL); + n = 0; + XtSetArg(args[n], XmNuserData, (XtPointer)"in"); n++; + off_units_menu = XmCreatePulldownMenu(form1, Xdvi_HOME_POSITION_UNITS_PULLDOWN, args, n); + + n = 0; + XtSetArg(args[n], XmNsubMenuId, off_units_menu); n++; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; + XtSetArg(args[n], XmNleftWidget, y_off_text); n++; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; + cascade = XmCreateOptionMenu(form1, Xdvi_HOME_POSITION_UNITS_MENU, args, n); + entry1 = XtVaCreateManagedWidget("in", xmPushButtonGadgetClass, off_units_menu, + NULL); + XtAddCallback(entry1, XmNactivateCallback, select_unit_cb, info); + entry2 = XtVaCreateManagedWidget("cm", xmPushButtonGadgetClass, off_units_menu, + NULL); + XtAddCallback(entry2, XmNactivateCallback, select_unit_cb, info); + entry3 = XtVaCreateManagedWidget("Pixel", xmPushButtonGadgetClass, off_units_menu, + NULL); + XtAddCallback(entry3, XmNactivateCallback, select_unit_cb, info); + + XtAddCallback(x_off_text, XmNvalueChangedCallback, set_offset_cb, (XtPointer)info); + XtAddCallback(y_off_text, XmNvalueChangedCallback, set_offset_cb, (XtPointer)info); + + XtManageChild(size_option); + XtManageChild(x_off_label); + XtManageChild(x_off_text); + XtManageChild(y_off_label); + XtManageChild(y_off_text); + XtManageChild(cascade); + + form2 = XmCreateForm(form, "offsets_form2", NULL, 0); + XtVaSetValues(form2, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, form1, + XmNleftAttachment, XmATTACH_FORM, + XmNleftOffset, 20, + NULL); + + curr_option = XmCreateToggleButtonGadget(form2, Xdvi_SCROLL_CURRENT_STR, &one_of_many, 1); + XtVaSetValues(curr_option, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + XtManageChild(curr_option); + + XtSetSensitive(x_off_label, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(x_off_text, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(y_off_label, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(y_off_text, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(cascade, !resource.keep_flag && !resource.use_current_offset); + XtSetSensitive(size_option, !resource.keep_flag); + XtSetSensitive(curr_option, !resource.keep_flag); + + XmToggleButtonGadgetSetState(size_option, !resource.use_current_offset, False); + XmToggleButtonGadgetSetState(curr_option, resource.use_current_offset, False); + + XtAddCallback(size_option, XmNvalueChangedCallback, home_position_cb, (XtPointer)info); + XtAddCallback(curr_option, XmNvalueChangedCallback, home_position_cb, (XtPointer)info); + + XtManageChild(form1); + XtManageChild(form2); + + return form; +} + +Widget +prefs_scrolling(struct topic_info *info) +{ + Widget form; + Widget scrolling_form, scrolling_frame; + + form = XtVaCreateWidget("form", xmFormWidgetClass, + info->right_form, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + + scrolling_frame = XmCreateFrame(form, "scrolling_frame", NULL, 0); + h_attach_below(scrolling_frame, NULL); + + scrolling_form = h_create_scrolling(scrolling_frame, info); + XtVaSetValues(scrolling_form, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, scrolling_frame, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + + XtManageChild(scrolling_form); + XtManageChild(scrolling_frame); + + return form; +} + +#else +/* silence `empty compilation unit' warnings */ +static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); } +#endif /* MOTIF */ + diff --git a/Build/source/texk/xdvik/gui/xm_prefs_scroll.h b/Build/source/texk/xdvik/gui/xm_prefs_scroll.h new file mode 100644 index 00000000000..838a55afdfd --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_prefs_scroll.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2004 Stefan Ulrich + * + * 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. + * + */ + +#ifndef XM_PREFS_SCROLL_ +#define XM_PREFS_SCROLL_ + +#ifdef MOTIF + +struct topic_info; /* forward declaration */ +extern Widget prefs_scrolling(struct topic_info *info); + +#endif /* MOTIF */ + +#endif /* XM_PREFS_SCROLL_ */ diff --git a/Build/source/texk/xdvik/gui/xm_toolbar.c b/Build/source/texk/xdvik/gui/xm_toolbar.c new file mode 100644 index 00000000000..c1bc76286a1 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_toolbar.c @@ -0,0 +1,860 @@ +/* + * Copyright (c) 2001 Marcin Dalecki + * Copyright (c) 2002-2004 the xdvik development team + * + * 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. + * + */ + +/* + * Tool bar implementation for the Motif widget set. + */ + +#include "xdvi-config.h" +#include "xdvi.h" + +#include "pagesel.h" +#include "help-window.h" +#include "message-window.h" +#include "kpathsea/tex-file.h" +#include "kpathsea/expand.h" +#include "statusline.h" +#include "dvi-init.h" +#include "events.h" +#include "dvi-draw.h" +#include "xm_menu.h" +#include "xm_toolbar.h" +#include "util.h" +#include "x_util.h" +#include "Tip.h" + +#include <stdlib.h> + +#include "xdvi-config.h" +#include "c-openmx.h" + +/* default toolbar pixmap file */ +#include "pixmaps/toolbar.xpm" + +#ifdef MOTIF +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Shell.h> + +#include <Xm/Xm.h> +#include <Xm/RowColumn.h> +#include <Xm/Frame.h> +#include <Xm/PushB.h> +#include <Xm/Form.h> +#include <Xm/Separator.h> +#include <Xm/CascadeB.h> +#include <Xm/MainW.h> +#include <Xm/DialogS.h> +#include <Xm/MessageB.h> +#include <Xm/Label.h> + +#if defined(HAVE_X11_XPM_H) +# include <X11/xpm.h> +#elif defined(HAVE_XPM_H) +# include <xpm.h> +#elif defined(HAVE_XM_XPMP_H) +# include <Xm/XpmP.h> +#endif + +#include <sys/stat.h> + +/* + * The following needs to be keept in sync with the actual pixmap sizes in + * tools.xpm. When editing the pixmaps, take care that the symbolic names + * are not messed up (e.g. it works with "pixmap" from ftp.x11.org, but + * not with xv). + */ + +#define PIXMAP_WIDTH 18 +#define PIXMAP_HEIGHT 18 +/* #define PIXMAP_COUNT 13 */ + + +static Widget tool_bar_frame; + +#define SEP_CHAR ':' /* character separating entries in translations lines */ + +void +toggle_scrollbars(void) +{ + Widget x_bar = XtNameToWidget(globals.widgets.main_window, "HorScrollBar"); + Widget y_bar = XtNameToWidget(globals.widgets.main_window, "VertScrollBar"); + + if ((resource.expert_mode & XPRT_SHOW_SCROLLBARS) == 0) { + XtUnmanageChild(x_bar); + XtUnmanageChild(y_bar); + } + else { + XtManageChild(x_bar); + XtManageChild(y_bar); + } + + set_menu(&resource.expert_mode, Act_set_expert_mode, check_resource_expert); +} + +void +toggle_toolbar(void) +{ +#if !HAVE_XPM + statusline_print(STATUS_LONG, + "Compiled without XPM support; no toolbar available."); +#else + if (resource.toolbar_unusable) { + statusline_print(STATUS_LONG, + "Toolbar pixmap file not found; toolbar is disabled."); + return; + } + + if ((resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0) + XtUnmanageChild(tool_bar_frame); + else + XtManageChild(tool_bar_frame); + + set_menu(&resource.expert_mode, Act_set_expert_mode, check_resource_expert); + +#endif /* not HAVE_XPM */ +} + +typedef enum { TB_BUTTON, TB_SEPARATOR } toolbarButtonT; + +/* global array of button infos */ +static struct toolbar_button_info { + Widget button; + toolbarButtonT type; + char *tip; +} *toolbar_buttons = NULL; + +#if HAVE_XPM /* remainder of file: toolbar is disabled without XPM support */ + +/* to save current values to */ +static Pixel m_background = 0, m_top_shadow_color = 0, m_bottom_shadow_color = 0; + +/* to save resource value of XmNshadowThickness before overriding it if + toolbar_buttons_raised is false; it will be used to raise the button on enter: +*/ +static int m_shadow_thickness; +extern Boolean get_int_arg(String *param, Cardinal *num_params, int *res); + +/* indexes for named colors */ +enum { + BACKGROUND, + FOREGROUND, + BOTTOM_SHADOW, + TOP_SHADOW, + HIGHLIGHT +}; + +/* save info about buttons that need to change state */ +static struct state_buttons_info { + Widget back_button; /* insensitive when current page is last page */ + Widget zoom_in_button; /* insensitive when shrinkfactor is 1 */ + Widget forward_button; /* insensitive when current page is first page */ + Widget hyperref_back_button; /* insensitive when at end of href history */ + Widget hyperref_forward_button; /* insensitive when at begin of href history */ +} m_button_info; + +static XtCallbackRec command_call[] = { + { handle_command, NULL }, + { NULL, NULL }, +}; + +static void +set_button_sensitivity(Widget w, Boolean sensitive) +{ + if (w == NULL) { /* if button hadn't been initialized properly */ + return; + } + XtVaSetValues(w, XmNsensitive, sensitive, NULL); + /* also remove `raised' property on mouse over */ + if (!sensitive && !resource.toolbar_buttons_raised) { + Dimension x, y, wd, ht; + Pixel foreground; + static GC backgroundGC = NULL; + if (!XtIsRealized(w)) + return; + XtVaGetValues(w, + XmNx, &x, + XmNy, &y, + XmNwidth, &wd, + XmNheight, &ht, + NULL); + if (backgroundGC == NULL) { + XtVaGetValues(w, XmNforeground, &foreground, NULL); + backgroundGC = set_or_make_gc(NULL, GXcopy, m_background, foreground); + } + XtVaSetValues(w, + XmNtopShadowColor, m_background, + XmNbottomShadowColor, m_background, + NULL); +#if 0 + fprintf(stderr, "drawing on %d,%d\n", x, y); + XFillRectangle(XtDisplay(w), XtWindow(w), globals.gc.high, /* backgroundGC, */ + x, y, wd, ht); +#endif + } + XSync(DISP, False); +} + +void +tb_check_navigation_sensitivity(int pageno) +{ + if (!XtIsRealized(globals.widgets.top_level) || (resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0) + return; + + set_button_sensitivity(m_button_info.forward_button, pageno == total_pages - 1 ? False : True); + set_button_sensitivity(m_button_info.back_button, pageno == 0 ? False : True); +} + +void +tb_set_htex_back_sensitivity(Boolean sensitive) +{ + if (!XtIsRealized(globals.widgets.top_level) || (resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0) + return; + set_button_sensitivity(m_button_info.hyperref_back_button, sensitive); +} + +void +tb_set_htex_forward_sensitivity(Boolean sensitive) +{ + if (!XtIsRealized(globals.widgets.top_level) || (resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0) + return; + + set_button_sensitivity(m_button_info.hyperref_forward_button, sensitive); +} + +void +tb_set_pagehist_back_sensitivity(Boolean sensitive) +{ + if (!XtIsRealized(globals.widgets.top_level) || (resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0) + return; + set_button_sensitivity(m_button_info.hyperref_back_button, sensitive); +} + +void +tb_set_pagehist_forward_sensitivity(Boolean sensitive) +{ + if (!XtIsRealized(globals.widgets.top_level) || (resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0) + return; + + set_button_sensitivity(m_button_info.hyperref_forward_button, sensitive); +} + +void +tb_set_zoom_sensitivity(Boolean sensitive) +{ + if (!XtIsRealized(globals.widgets.top_level) || (resource.expert_mode & XPRT_SHOW_TOOLBAR) == 0) + return; + + set_button_sensitivity(m_button_info.zoom_in_button, sensitive); +} + +#if 0 +static void +search_callback(Widget w, + XtPointer client_data, + XmAnyCallbackStruct *call_data) +{ + UNUSED(w); + UNUSED(client_data); + UNUSED(call_data); + + popup_message(globals.widgets.top_level, + MSG_ERR, NULL, "Sorry, not yet implemented"); +} +#endif /* 0 */ + + + +/* + * If successful, this extracts a square pixmap from resource.toolbar_pixmap_file + * and returns True. + * In this case, the pixmap `sen' will contain the actual + * pixmap and `insen' will contain a drawn pixmap for the insensitive state of + * the button. We are emulating a shaded state derived from the monochrome 'm' + * color attributes of the XPM. + * + * If unsuccessful, it returns False, and the Pixmap pointers are undefined. + */ + +static Boolean +create_pixmap(Widget parent, int iconidx, Pixmap *sen, Pixmap *insen) +{ + static Boolean first_time = True; + static Window rootWindow; + static XpmColorSymbol color[5] = { + {"none", "none", 0}, + {"iconColor1", NULL, 0}, + {"bottomShadowColor", NULL, 0}, + {"topShadowColor", NULL, 0}, + {"selectColor", NULL, 0} + }; + static int screenNum; + static Pixmap tools_map; + static Pixmap tools_mask; + static Pixmap shade_map; + static Pixmap shade_mask; + + int status = 0; + XpmAttributes attr; + Pixmap map; + Pixmap mask; + static String pixmap_file_path = NULL; /* note: never free()d */ + + ASSERT(iconidx >= 0, "index must be positive"); + + if (first_time) { + /* FIXME: We use a dummy window here to get the correct depth/visual for the + pixmap creation (cant use globals.widgets.top_level since it's not realized + yet and realizing it now would result in wrong dimensions) ... */ + Widget dummy = XtVaAppCreateShell("xdvi", "Xdvi", + transientShellWidgetClass, DISP, + XtNdepth, G_depth, + XtNvisual, G_visual, + XtNcolormap, G_colormap, + XtNmappedWhenManaged, False, + NULL); + XtRealizeWidget(dummy); /* note: doesn't pop it up */ + rootWindow = XtWindow(dummy); + screenNum = DefaultScreen(XtDisplay(globals.widgets.top_level)); + ASSERT(rootWindow != 0, ""); + XtVaGetValues(parent, + XmNbackground, &color[BACKGROUND].pixel, + XmNforeground, &color[FOREGROUND].pixel, + XmNbottomShadowColor, &color[BOTTOM_SHADOW].pixel, + XmNtopShadowColor, &color[TOP_SHADOW].pixel, + XmNhighlightColor, &color[HIGHLIGHT].pixel, + NULL); + /* try to locate the XPM file with the toolbar pixmaps */ + pixmap_file_path = XtResolvePathname(DISP, + "pixmaps", + resource.toolbar_pixmap_file, + (String)NULL, /* suffix */ + (String)NULL, /* use default path */ + (Substitution)NULL, /* substitutions */ + 0, /* number of substitutions */ + (XtFilePredicate)NULL); /* return True iff file exists */ + + TRACE_GUI((stderr, "pixmap file search via XtResolvePathname: %s => %s", + resource.toolbar_pixmap_file, pixmap_file_path ? (char*)pixmap_file_path : "<NULL>")); + if (pixmap_file_path == NULL) { + pixmap_file_path = (String)kpse_find_file(resource.toolbar_pixmap_file, + kpse_program_text_format, + 0); + TRACE_GUI((stderr, + "pixmap file search via kpse_find_file: %s => %s", + resource.toolbar_pixmap_file, + pixmap_file_path ? (char*)pixmap_file_path : "<NULL>")); + if (pixmap_file_path == NULL) { + TRACE_GUI((stderr, "No installed toolbar pixmap found, using built-in pixmap.")); + } + } + } + + /* Setup the color subsititution table */ + attr.valuemask = XpmColorSymbols | XpmCloseness | XpmColormap | XpmDepth | XpmVisual; + attr.closeness = 65535; /* accuracy isn't crucial */ + attr.colorsymbols = color; + attr.numsymbols = XtNumber(color); + attr.visual = G_visual; + attr.colormap = G_colormap; + attr.depth = G_depth; + + /* Create the "sensitive" pixmap */ + if (!tools_map) { + if (pixmap_file_path != NULL) { + status = XpmReadFileToPixmap(XtDisplay(globals.widgets.top_level), rootWindow, + pixmap_file_path, &tools_map, &tools_mask, &attr); + } + else { + status = XpmCreatePixmapFromData(XtDisplay(globals.widgets.top_level), rootWindow, + (char **)toolbar_xpm, &tools_map, &tools_mask, &attr); + } + } + else + status = XpmSuccess; + + map = tools_map; + mask = tools_mask; + + if (status == XpmSuccess) { + static Pixmap tmp_mask; + static GC gc; + + if (first_time) { + tmp_mask = XCreatePixmap(XtDisplay(globals.widgets.top_level), rootWindow, PIXMAP_WIDTH, PIXMAP_HEIGHT, 1); + gc = XCreateGC(XtDisplay(globals.widgets.top_level), tmp_mask, 0, NULL); + } + XCopyArea(XtDisplay(globals.widgets.top_level), + mask, tmp_mask, gc, iconidx * PIXMAP_WIDTH, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT, 0, 0); + + mask = tmp_mask; + } + else { /* something went wrong */ + popup_message(globals.widgets.top_level, + MSG_ERR, + "Something's wrong with your XPM file - " + "try to load it into an image editor and fix the problem.", + "Xpm error: %s - switching toolbar off.", + XpmGetErrorString(status)); + sen = insen = NULL; + resource.expert_mode ^= XPRT_SHOW_TOOLBAR; + return False; + } + + XpmFreeAttributes(&attr); + + if (map != 0) { + static GC back_gc, bots_gc; + + if (first_time) { + XGCValues gcvalues; + + gcvalues.foreground = color[BACKGROUND].pixel; + back_gc = XCreateGC(XtDisplay(globals.widgets.top_level), rootWindow, GCForeground, &gcvalues); + + gcvalues.foreground = color[BOTTOM_SHADOW].pixel; + bots_gc = XCreateGC(XtDisplay(globals.widgets.top_level), rootWindow, GCForeground, &gcvalues); + } + + /* Need to create new Pixmaps with the mask applied. */ + XSetClipMask(XtDisplay(globals.widgets.top_level), bots_gc, mask); + + /* Create the "sensitive" pixmap. */ + *sen = XCreatePixmap(XtDisplay(globals.widgets.top_level), rootWindow, PIXMAP_WIDTH, PIXMAP_HEIGHT, + G_depth); + XFillRectangle(XtDisplay(globals.widgets.top_level), *sen, back_gc, 0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT); + if (iconidx != -1) + XCopyArea(XtDisplay(globals.widgets.top_level), map, *sen, bots_gc, + iconidx * PIXMAP_WIDTH, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT, 0, 0); + else + XCopyArea(XtDisplay(globals.widgets.top_level), map, *sen, bots_gc, + 0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT, 0, 0); + + if (iconidx == -1) + XFreePixmap(XtDisplay(globals.widgets.top_level), map); + + /* Create the "insensitive" pixmap. */ + if (insen != NULL) { + Pixmap map; + Pixmap mask; + + attr.valuemask = XpmColorSymbols | XpmCloseness | XpmColorKey | XpmColormap | XpmDepth | XpmVisual; + attr.closeness = 65535; /* accuracy isn't crucial */ + attr.colorsymbols = color; + attr.numsymbols = XtNumber(color); + attr.color_key = XPM_MONO; + attr.visual = G_visual; + attr.colormap = G_colormap; + attr.depth = G_depth; + + + if (!shade_map) { + if (pixmap_file_path != NULL) { + status = XpmReadFileToPixmap(XtDisplay(globals.widgets.top_level), rootWindow, + pixmap_file_path, &shade_map, &shade_mask, &attr); + } + else { + status = XpmCreatePixmapFromData(XtDisplay(globals.widgets.top_level), rootWindow, + (char **)toolbar_xpm, &shade_map, &shade_mask, &attr); + } + } + else + status = XpmSuccess; + + map = shade_map; + mask = shade_mask; + + if (status == XpmSuccess) { + static Pixmap tmp_mask; + static GC gc; + + if (first_time) { + tmp_mask = XCreatePixmap(XtDisplay(globals.widgets.top_level), rootWindow, + PIXMAP_WIDTH, PIXMAP_HEIGHT, 1); + gc = XCreateGC(XtDisplay(globals.widgets.top_level), tmp_mask, 0, NULL); + } + + XCopyArea(XtDisplay(globals.widgets.top_level), mask, tmp_mask, gc, + iconidx * PIXMAP_WIDTH, 0, + PIXMAP_WIDTH, PIXMAP_HEIGHT, + 0, 0); + + mask = tmp_mask; + } + else { /* something went wrong */ + popup_message(globals.widgets.top_level, + MSG_ERR, + "Something's wrong with your XPM file - " + "try to load it into an image editor and fix the problem.", + "Xpm error: %s - switching toolbar off.", + XpmGetErrorString(status)); + sen = insen = NULL; + resource.expert_mode ^= XPRT_SHOW_TOOLBAR; + return False; + } + + if (mask != 0) { + static GC tops_gc; + + if (first_time) { + XGCValues gcvalues; + + gcvalues.foreground = color[TOP_SHADOW].pixel; + tops_gc = XCreateGC(XtDisplay(globals.widgets.top_level), rootWindow, GCForeground, &gcvalues); + } + + /* Need to create new Pixmaps with the mask applied. */ + XSetClipMask(XtDisplay(globals.widgets.top_level), bots_gc, mask); + XSetClipMask(XtDisplay(globals.widgets.top_level), tops_gc, mask); + XSetClipOrigin(XtDisplay(globals.widgets.top_level), tops_gc, 1, 1); + + *insen = XCreatePixmap(XtDisplay(globals.widgets.top_level), rootWindow, PIXMAP_WIDTH, PIXMAP_HEIGHT, + G_depth); + + XFillRectangle(XtDisplay(globals.widgets.top_level), *insen, back_gc, 0, 0, + PIXMAP_WIDTH, PIXMAP_HEIGHT); + XFillRectangle(XtDisplay(globals.widgets.top_level), *insen, tops_gc, 1, 1, + PIXMAP_WIDTH - 1, PIXMAP_HEIGHT - 1); + XFillRectangle(XtDisplay(globals.widgets.top_level), *insen, bots_gc, 0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT); + + if (iconidx == -1) + XFreePixmap(XtDisplay(globals.widgets.top_level), map); + } + + XpmFreeAttributes(&attr); + } + } + + first_time = False; + return True; +} + +/* + * If successful, this returns True, and sets *button to a button with + * a square pixmap on it (which is cut out form `index' position in + * resource.toolbar_pixmap_file). If unsuccessful, it returns false, + * and *button is undefined. + */ +static Boolean +create_toolbar_button(Widget parent, Widget *button, + const Pixmap *image_sens, const Pixmap *image_insens) +{ + Boolean sensitive = True; /* dummy */ + *button = XtVaCreateManagedWidget("button", xmPushButtonWidgetClass, parent, + XmNhighlightOnEnter, True, + XmNlabelPixmap, *image_sens, + XmNlabelInsensitivePixmap, *image_insens, + XmNsensitive, sensitive, + XmNlabelType, XmPIXMAP, + NULL); + XtVaGetValues(*button, XmNshadowThickness, &m_shadow_thickness, NULL); + if (!resource.toolbar_buttons_raised) { + if (m_background == 0) { /* initialize values */ + XtVaGetValues(*button, + /* should we rather get background of parent widget? */ + XmNbackground, &m_background, + XmNtopShadowColor, &m_top_shadow_color, + XmNbottomShadowColor, &m_bottom_shadow_color, + NULL); + } + /* remove shadows, setting them later when mouse enters button */ + XtVaSetValues(*button, + XmNtopShadowColor, m_background, + XmNbottomShadowColor, m_background, + NULL); + } + return True; +} + +static void +create_toolbar_separator(Widget parent, Widget *separator, int width) +{ + *separator = XtVaCreateManagedWidget("toolbarSeparator", + xmSeparatorWidgetClass, parent, + XmNseparatorType, XmNO_LINE, + XmNminWidth, width, + XmNwidth, width, + XmNorientation, XmVERTICAL, + XmNleftAttachment, XmATTACH_WIDGET, + XmNrightAttachment, XmATTACH_WIDGET, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); +} + +/* taken from shadow_trick in WlToolBar.c, mgv-3.1.5: + Get a raised behaviour for armed buttons, by explicitly setting + a shadow on entry and removing it on leave. +*/ +static void +enter_leave(Widget w, XtPointer closure, XEvent *event, Boolean *cont) +{ + char *tooltip = (char *)closure; + XCrossingEvent *ev = (XCrossingEvent *)event; + static Boolean entered = False; /* to skip double leaves */ + + UNUSED(cont); + + if (ev->type == EnterNotify) { + if (!resource.toolbar_buttons_raised) { + /* draw shadows */ + XtVaSetValues(w, + XmNtopShadowColor, m_top_shadow_color, + XmNbottomShadowColor, m_bottom_shadow_color, + NULL); + } + entered = True; + if (resource.tooltips_in_statusline) { + statusline_print(STATUS_SHORT, tooltip); + } + } + else if (ev->type == LeaveNotify && !resource.toolbar_buttons_raised) { + /* remove shadows */ + if (!entered) + return; + + XtVaSetValues(w, + XmNtopShadowColor, m_background, + XmNbottomShadowColor, m_background, + NULL); + entered = False; + } +} + +/* + save info about special buttons in m_button_info + (see definition of that for details) +*/ +static void +button_info_save(struct xdvi_action *action, Widget w) +{ + if (action->proc == Act_back_page && strcmp(action->param, "1") == 0) { + m_button_info.back_button = w; + } + else if (action->proc == Act_forward_page && strcmp(action->param, "1") == 0) { + m_button_info.forward_button = w; + } + else if (action->proc == Act_pagehistory_back) { + set_button_sensitivity(w, False); + m_button_info.hyperref_back_button = w; + } + else if (action->proc == Act_pagehistory_forward) { + set_button_sensitivity(w, False); + m_button_info.hyperref_forward_button = w; + } + else if (action->proc == Act_set_shrink_factor && action->param[0] == '+') { + m_button_info.zoom_in_button = w; + } +} + +#endif /* HAVE_XPM */ + +/* + * Create a toolbar with buttons, return toolbar widget. + */ +Widget +create_toolbar(Widget parent, Widget menu_bar) +{ +#if HAVE_XPM + size_t alloc_len = 0, n; + size_t alloc_step = 16; + const char *c_ptr, *e_ptr; +#endif /* HAVE_XPM */ + Widget tool_bar; + resource.toolbar_unusable = False; + + tool_bar_frame = XtVaCreateWidget("toolBarFrame", + xmFrameWidgetClass, parent, + XmNshadowType, XmSHADOW_OUT, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, menu_bar, + NULL); + + tool_bar = XtVaCreateManagedWidget("toolBar", + xmRowColumnWidgetClass, tool_bar_frame, + XmNchildType, XmFRAME_WORKAREA_CHILD, + XmNrowColumnType, XmWORK_AREA, + XmNorientation, XmHORIZONTAL, + XmNtraversalOn, False, + XmNisHomogeneous, False, + XmNpacking, XmPACK_TIGHT, + XmNspacing, 0, /* override to use SEPARATOR(n) instead */ + XmNadjustLast, True, + NULL); + +#if HAVE_XPM + /* parse toolbar_translations, create the widgets and assign the actions */ + for (n = 0, c_ptr = resource.toolbar_translations; + c_ptr != NULL && *c_ptr != '\0'; + c_ptr = e_ptr, n++) { + char **line_items = NULL; + int extra_space; + size_t len, curr, item_count = 0; + + if ((e_ptr = strchr(c_ptr, '\n')) == NULL + /* ... and in case last line doesn't end with \n ... */ + && (e_ptr = strchr(c_ptr, '\0')) == NULL) { + break; + } + + if (e_ptr == c_ptr) { + XDVI_WARNING((stderr, "Skipping empty line in toolbarTranslations resource.")); + e_ptr++; + continue; + } + len = e_ptr - c_ptr; + TRACE_GUI((stderr, "LEN %lu: |%.*s|", (unsigned long)len, (int)len, c_ptr)); + + line_items = split_line(c_ptr, SEP_CHAR, 0, len, &item_count); + + if (globals.debug & DBG_GUI) { + int k; + for (k = 0; line_items[k] != NULL; k++) { + fprintf(stderr, "ITEM %d of %lu: |%s|\n", k, (unsigned long)item_count, line_items[k]); + } + } + while (alloc_len <= n + 1) { + alloc_len += alloc_step; + toolbar_buttons = xrealloc(toolbar_buttons, alloc_len * sizeof *toolbar_buttons); + } + + if (item_count == 1 && sscanf(line_items[0], "SPACER(%d)", &extra_space) == 1) { + TRACE_GUI((stderr, "creating spacer of witdh %d at %lu", extra_space, (unsigned long)n)); + create_toolbar_separator(tool_bar, &(toolbar_buttons[n].button), extra_space); + toolbar_buttons[n].type = TB_SEPARATOR; + } + else if (item_count == 4) { + Pixmap sens, insens; + int idx = strtoul(line_items[0], (char **)NULL, 10); + struct xdvi_action *action; + + TRACE_GUI((stderr, "creating pixmap at %d", idx)); + if (!create_pixmap(tool_bar, idx, &sens, &insens)) { + free(toolbar_buttons); + toolbar_buttons = NULL; + break; + } + TRACE_GUI((stderr, "creating button %ld", (unsigned long)n)); + if (!create_toolbar_button(tool_bar, &(toolbar_buttons[n].button), &sens, &insens)) { + free(toolbar_buttons); + toolbar_buttons = NULL; + break; + } + toolbar_buttons[n].type = TB_BUTTON; + + if ((action = compile_action(line_items[3])) != NULL) { + char *long_tooltip = xstrdup(line_items[1]); + toolbar_buttons[n].tip = xstrdup(line_items[2]); + /* char *short_tooltip = xstrdup(line_items[2]); */ + command_call[0].closure = (XtPointer) action; + + /* + eventually save this widget in list of `special' buttons + that need to toggle between sensitive/insensitive + */ + button_info_save(action, toolbar_buttons[n].button); + + XtVaSetValues(toolbar_buttons[n].button, XmNactivateCallback, (XtArgVal)command_call, NULL); + XtAddEventHandler(toolbar_buttons[n].button, + EnterWindowMask | LeaveWindowMask, + False, + enter_leave, + long_tooltip); + } + else { + XDVI_WARNING((stderr, "Invalid action \"%s\" in toolbarTranslations resource:\n\"%.*s\"", + line_items[3], (int)len, c_ptr)); + } + } + else { + XDVI_WARNING((stderr, "Skipping malformed line \"%.*s\" in toolbarTranslations resource " + "(%lu instead of 4 items).", + (int)len, c_ptr, (unsigned long)item_count)); + toolbar_buttons[n].button = 0; + } + + for (curr = 0; curr < item_count; curr++) { + free(line_items[curr]); + } + free(line_items); + line_items = NULL; + + if (*e_ptr != '\0') + e_ptr++; + } +#else + if ((resource.expert_mode & XPRT_SHOW_SCROLLBARS) != 0) { + XDVI_WARNING((stderr, "This version has been compiled without XPM support. " + "Disabling the toolbar, which needs XPM.")); + } +#endif /* HAVE_XPM */ + + if (toolbar_buttons == NULL) { + resource.toolbar_unusable = True; + resource.expert_mode ^= XPRT_SHOW_TOOLBAR; + } + else { +#if HAVE_XPM + toolbar_buttons[n].button = 0; /* terminate info */ +#endif + } + return tool_bar; +} + +/* + * Add the tips to the toolbar. This has to be done after the toolbar has been + * realized. + */ +void +create_tips(Widget toplevel) +{ +#if HAVE_XPM + Widget tip_shell; + + int i; + + if (resource.toolbar_unusable) /* don't create tips in this case */ + return; + + tip_shell = XtVaCreatePopupShell("tipShell", tipWidgetClass, + toplevel, XtNwidth, 1, XtNheight, 1, + NULL); + + for (i = 0; toolbar_buttons[i].button != 0; i++) { + if (toolbar_buttons[i].type == TB_BUTTON) { + TipAddWidget(tip_shell, toolbar_buttons[i].button, toolbar_buttons[i].tip); + } + } +#else + UNUSED(toplevel); +#endif /* HAVE_XPM */ +} + + +#else /* MOTIF */ +/* silence "empty compilation unit" and "`toolbar_xpm' defined but not used" warnings */ +static void bar(void); static void foo() { UNUSED(toolbar_xpm); bar(); } static void bar(void) { foo(); } +#endif /* MOTIF */ diff --git a/Build/source/texk/xdvik/gui/xm_toolbar.h b/Build/source/texk/xdvik/gui/xm_toolbar.h new file mode 100644 index 00000000000..7afd1012d59 --- /dev/null +++ b/Build/source/texk/xdvik/gui/xm_toolbar.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2001 Marcin Dalecki + * Copyright (c) 2002-2004 the xdvik development team + * + * 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. + + */ + +#ifndef XM_TOOLBAR_H_ +#define XM_TOOLBAR_H_ + +#ifdef MOTIF + +extern Widget create_toolbar(Widget parent, Widget menu_bar); + +extern Boolean toolbar_visible, scrollbars_visible, pagelist_visible; +extern void create_tips(Widget parent); + +#if HAVE_XPM +extern void tb_check_navigation_sensitivity(int pageno); +extern void tb_check_navigation_sensitivity(int pageno); +extern void tb_set_htex_forward_sensitivity(Boolean sensitive); +extern void tb_set_htex_back_sensitivity(Boolean sensitive); +extern void tb_set_pagehist_forward_sensitivity(Boolean sensitive); +extern void tb_set_pagehist_back_sensitivity(Boolean sensitive); +extern void tb_set_zoom_sensitivity(Boolean sensitive); +#endif /* HAVE_XPM */ + +extern void toggle_toolbar(void); +extern void toggle_scrollbars(void); + +#endif /* MOTIF */ + +#endif /* XM_TOOLBAR_H_ */ |