summaryrefslogtreecommitdiff
path: root/Build/source/texk/xdvik/gui/mag.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/xdvik/gui/mag.c')
-rw-r--r--Build/source/texk/xdvik/gui/mag.c861
1 files changed, 861 insertions, 0 deletions
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));
+}