1 /*
2  * Copyright (c) 1990-2004  Paul Vojta and others
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
18  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * NOTE: xdvi is based on prior work, as noted in the modification history in
23  * xdvi.c.
24  *
25  */
26 
27 /*
28  * Implementation of the magnifier window.
29  */
30 
31 #include <math.h>
32 
33 #include "xdvi-config.h"
34 /* one of the following should define OPEN_MAX: */
35 #include <limits.h>
36 #include "c-openmx.h"
37 
38 #include "xdvi.h"
39 
40 #ifdef MOTIF
41 #include <Xm/Xm.h>
42 #endif
43 
44 #include "events.h"
45 #include "dvi-draw.h"
46 #include "dvi-init.h"
47 #include "statusline.h"
48 #include "hypertex.h"
49 #include "mag.h"
50 #include "xm_toolbar.h"
51 #include "xm_menu.h" /* for get_last_ungrab() */
52 #include "util.h"
53 #include "pagesel.h"
54 
55 #if HAVE_XKB_BELL_EXT
56 # include <X11/XKBlib.h>
57 # define XdviBell(display, window, percent)	\
58 	 XkbBell(display, window, percent, (Atom) None)
59 #else
60 # define XdviBell(display, window, percent)	XBell(display, percent)
61 #endif
62 
63 /* to measure distance of pointer from ruler in ruler mode */
64 static int g_ruler_pos_x = 0, g_ruler_pos_y = 0;
65 
66 struct WindowRec magnifier = { (Window) 0, 1, 0, 0, 0, 0, MAXDIM, 0, MAXDIM, 0 };
67 
68 /*
69  * Mechanism to keep track of the magnifier window.  The problems are,
70  *
71  * - if the button is released while the window is being drawn, this could
72  *   cause an X error if we continue drawing in it after it is destroyed, and
73  *
74  * - creating and destroying the window too quickly confuses the window
75  *   manager, which is avoided by waiting for an expose event before destroying
76  *   it.
77  */
78 
79 short magnifier_stat;	/* 1 = wait for expose, -1 = destroy upon expose */
80 
81 static Position main_x;
82 static Position main_y;
83 
84 static Position mag_x = 0;
85 static Position mag_y = 0;
86 
87 /* Under Motif the following two remain always constant */
88 static int mag_conv_x = 0;
89 static int mag_conv_y = 0;
90 
91 static Position new_mag_x = 0;
92 static Position new_mag_y = 0;
93 
94 
95 /* default magnifier dimensions */
96 static struct mg_size_rec {
97     int w;
98     int h;
99 } mg_size[] = {
100     {200, 150}, {400, 250}, {700, 500}, {1000, 800}, {1200, 1200}
101 };
102 
get_magglass_items(void)103 size_t get_magglass_items(void) {
104     return XtNumber(mg_size);
105 }
106 
get_magglass_width(int idx)107 int get_magglass_width(int idx) {
108     return mg_size[idx].w;
109 }
110 
get_magglass_height(int idx)111 int get_magglass_height(int idx) {
112     return mg_size[idx].h;
113 }
114 
set_magglass_widht(int idx,int w)115 void set_magglass_widht(int idx, int w) {
116     mg_size[idx].w = w;
117 }
118 
set_magglass_height(int idx,int h)119 void set_magglass_height(int idx, int h) {
120     mg_size[idx].h = h;
121 }
122 
123 
124 static void
can_exposures(struct WindowRec * windowrec)125 can_exposures(struct WindowRec *windowrec)
126 {
127     windowrec->min_x = windowrec->min_y = MAXDIM;
128     windowrec->max_x = windowrec->max_y = 0;
129 }
130 
131 static void
mag_motion(XEvent * event)132 mag_motion(XEvent * event)
133 {
134     MYTRACE((stderr, "mag_motion!\n"));
135     new_mag_x = event->xmotion.x + mag_conv_x;
136     main_x = event->xmotion.x_root - new_mag_x;
137     new_mag_y = event->xmotion.y + mag_conv_y;
138     main_y = event->xmotion.y_root - new_mag_y;
139 
140     if (new_mag_x != mag_x || new_mag_y != mag_y)
141 	globals.ev.flags |= EV_MAG_MOVE;
142     else
143 	globals.ev.flags &= ~EV_MAG_MOVE;
144 }
145 
146 void
mag_release(XEvent * event)147 mag_release(XEvent * event)
148 {
149     UNUSED(event);
150     if (magnifier.win != (Window) 0) {
151 	if (magnifier_stat) {
152 	    magnifier_stat = -1;	/* destroy upon expose */
153 	}
154 	else {
155 	    XDestroyWindow(DISP, magnifier.win);
156 	    if (drawing_mag) {
157 		globals.ev.flags |= EV_MAG_GONE;
158 	    }
159 	    magnifier.win = (Window) 0;
160 	    mouse_motion = mouse_release = null_mouse;
161 	    globals.ev.flags &= ~EV_MAG_MOVE;
162 	    globals.cursor.flags &= ~CURSOR_MAG;
163 	    globals.ev.flags |= EV_CURSOR;
164 	    can_exposures(&magnifier);
165 	    /*
166 	      Workaround for bug #703304:
167 	      For obscure reasons, XFree 3.3.5 (and apparently also Solaris 8)
168 	      doesn't generate an expose event after the magnifier has beed
169 	      destroyed. But only a redraw() event caused by expose would reset
170 	      currwin.win back to mane.win, which is needed e.g. for getting the
171 	      hyperlink info updated (otherwise, the mouse will not become active
172 	      over a hyperlink).
173 
174 	      Forcing a redraw with
175 	      redraw(&mane);
176 	      may cause a `BadDrawable' X error with color material (e.g. from #702288),
177 	      or a `draw_part: shouldn't happen: POST encountered' error. Neither do
178 	      the following work:
179 	      globals.ev.flags |= EV_EXPOSE;		(doesn't fix the bug)
180 	      draw_page();			(same effect as redraw(&mane))
181 	      globals.ev.flags |= EV_NEWPAGE;	(works, but is crude and causes flicker)
182 
183 	      So I decided to use expose() for the time being, which
184 	      sets mane.min_x to the current x point (which doesn't happen
185 	      with EV_EXPOSE; this causes the test for `mane.min_x < MAXDIM'
186 	      to fail in events.c; look for `see comment in mag.c').
187 	    */
188 	    /*  	    fprintf(stderr, "========triggering expose!\n"); */
189 	    expose(&mane, event->xbutton.x_root, event->xbutton.y_root, 10, 10);
190 	}
191     }
192 }
193 
194 static int
tick_scale(int k)195 tick_scale(int k)
196 {
197     if (k == 0)
198 	return 3;
199     else if ((k % 1000) == 0)
200 	return 7;
201     else if ((k % 500) == 0)
202 	return 6;
203     else if ((k % 100) == 0)
204 	return 5;
205     else if ((k % 50) == 0)
206 	return 4;
207     else if ((k % 10) == 0)
208 	return 3;
209     else if ((k % 5) == 0)
210 	return 2;
211     else
212 	return 1;
213 }
214 
215 static void
draw_ticks(unsigned int width,unsigned int height,GC ourGC)216 draw_ticks(unsigned int width, unsigned int height, GC ourGC)
217 {
218     int k;				/* tick counter */
219     double old_pixels_per_tick;
220     double pixels_per_tick;
221     int scale;
222     int tick_offset;			/* offset along axes */
223     int x;				/* coordinates of top-left popup */
224     int y;				/* window corner */
225     double xx;				/* coordinates of tick */
226     double yy;				/* coordinates of tick */
227     static char *last_tick_units = "";	/* memory of last tick units */
228 
229     if (resource.tick_length <= 0)	/* user doesn't want tick marks */
230 	return;
231 
232     x = 0;	/* the pop-up window always has origin (0,0)  */
233     y = 0;
234 
235     /* We need to clear the existing window to remove old rulers.  I think
236        that this could be avoided if draw_ticks() could be invoked earlier.
237        The expose argument in XClearArea() must be True to force redrawing
238        of the text inside the popup window. Also, it would be better to draw
239        the rulers before painting the text, so that rulers would not
240        overwrite the text, but I haven't figured out yet how to arrange
241        that. */
242 
243     XClearArea(DISP, magnifier.win, x, y, width, height, True);
244 
245     /* The global resource.pixels_per_inch tells us how to find the ruler
246        scale.  For example, 300dpi corresponds to these TeX units:
247 
248        1 TeX point (pt)     =   4.151      pixels
249        1 big point (bp)     =   4.167      pixels
250        1 pica (pc)          =  49.813      pixels
251        1 cicero (cc)                =  53.501      pixels
252        1 didot point (dd)   =   4.442      pixels
253        1 millimeter (mm)    =  11.811      pixels
254        1 centimeter (cm)    = 118.110      pixels
255        1 inch (in)          = 300.000      pixels
256        1 scaled point (sp)  =   0.00006334 pixels
257 
258        The user can select the units via a resource (e.g. XDvi*tickUnits: bp),
259        or a command-line option (e.g. -xrm '*tickUnits: cm').  The length of
260        the ticks can be controlled by a resource (e.g. XDvi*tickLength: 10), or
261        a command-line option (e.g. -xrm '*tickLength: 10000').  Zero, or negative,
262        tick length completely suppresses rulers. */
263 
264     pixels_per_tick = (double)resource.pixels_per_inch;
265     if (strcmp(resource.tick_units, "pt") == 0)
266 	pixels_per_tick /= 72.27;
267     else if (strcmp(resource.tick_units, "bp") == 0)
268 	pixels_per_tick /= 72.0;
269     else if (strcmp(resource.tick_units, "in") == 0)
270 	/* NO-OP */ ;
271     else if (strcmp(resource.tick_units, "cm") == 0)
272 	pixels_per_tick /= 2.54;
273     else if (strcmp(resource.tick_units, "mm") == 0)
274 	pixels_per_tick /= 25.4;
275     else if (strcmp(resource.tick_units, "dd") == 0)
276 	pixels_per_tick *= (1238.0 / 1157.0) / 72.27;
277     else if (strcmp(resource.tick_units, "cc") == 0)
278 	pixels_per_tick *= 12.0 * (1238.0 / 1157.0) / 72.27;
279     else if (strcmp(resource.tick_units, "pc") == 0)
280 	pixels_per_tick *= 12.0 / 72.27;
281     else if (strcmp(resource.tick_units, "sp") == 0)
282 	pixels_per_tick /= (65536.0 * 72.27);
283     else if (strcmp(resource.tick_units, "px") == 0)
284 	pixels_per_tick = 10;
285     else {
286 	XDVI_WARNING((stderr, "Unrecognized tickUnits [%s]: defaulting to TeX points [pt]",
287 		      resource.tick_units));
288 	resource.tick_units = "pt";
289 	pixels_per_tick /= 72.27;
290     }
291 
292     /* To permit accurate measurement in the popup window, we can reasonably
293      * place tick marks about 3 to 10 pixels apart, so we scale the computed
294      * pixels_per_tick by a power of ten to bring it into that range.
295      */
296 
297     old_pixels_per_tick = pixels_per_tick;	/* remember the original scale */
298     while (pixels_per_tick < 3.0)
299 	pixels_per_tick *= 10.0;
300     while (pixels_per_tick > 30.0)
301 	pixels_per_tick /= 10.0;
302 
303     /* tell user what the ruler scale is, but only when it changes */
304     if (strcmp(last_tick_units, resource.tick_units) != 0) {
305 	if (old_pixels_per_tick != pixels_per_tick)
306 	    printf("Ruler tick interval adjusted to represent %.2f%s\n",
307 		   pixels_per_tick / old_pixels_per_tick, resource.tick_units);
308 	else if (globals.debug & DBG_EVENT)
309 	    printf("Ruler tick interval represents 1%s\n", resource.tick_units);
310     }
311 
312     /* In order to make the ruler as accurate as possible, given the coarse
313      * screen resolution, we compute tick positions in floating-point
314      * arithmetic, then round to nearest integer values.
315      */
316 
317     /* draw vertical ticks on top and bottom */
318     for (k = 0, xx = 0.0; xx < (double)width; k++, xx += pixels_per_tick) {
319 	tick_offset = (int)(0.5 + xx);	/* round to nearest pixel */
320 	scale = tick_scale(k);
321 	XDrawLine(DISP, magnifier.win, ourGC,
322 		  x + tick_offset, y, x + tick_offset, y + scale * resource.tick_length);
323 	XDrawLine(DISP, magnifier.win, ourGC,
324 		  x + tick_offset, y + height,
325 		  x + tick_offset, y + height - scale * resource.tick_length);
326     }
327 
328     /* draw horizontal ticks on left and right */
329     for (k = 0, yy = 0.0; yy < (double)height; k++, yy += pixels_per_tick) {
330 	tick_offset = (int)(0.5 + yy);	/* round to nearest pixel */
331 	scale = tick_scale(k);
332 	XDrawLine(DISP, magnifier.win, ourGC,
333 		  x, y + tick_offset, x + scale * resource.tick_length, y + tick_offset);
334 	XDrawLine(DISP, magnifier.win, ourGC,
335 		  x + width, y + tick_offset,
336 		  x + width - scale * resource.tick_length, y + tick_offset);
337     }
338 
339     last_tick_units = resource.tick_units;
340 
341     XFlush(DISP);	/* bring window up-to-date */
342 }
343 
344 static void
compute_mag_pos(int * xp,int * yp)345 compute_mag_pos(int *xp, int *yp)
346 {
347     int t;
348 
349     t = mag_x + main_x - magnifier.width / 2;
350     if (t > WidthOfScreen(SCRN) - (int)magnifier.width - 2 * MAGBORD)
351 	t = WidthOfScreen(SCRN) - (int)magnifier.width - 2 * MAGBORD;
352     if (t < 0)
353 	t = 0;
354     *xp = t;
355     t = mag_y + main_y - magnifier.height / 2;
356     if (t > HeightOfScreen(SCRN) - (int)magnifier.height - 2 * MAGBORD)
357 	t = HeightOfScreen(SCRN) - (int)magnifier.height - 2 * MAGBORD;
358     if (t < 0)
359 	t = 0;
360     *yp = t;
361 }
362 
363 static void
scroll_window(struct WindowRec * windowrec,int x0,int y0)364 scroll_window(struct WindowRec *windowrec, int x0, int y0)
365 {
366     int x, y;
367     int x2 = 0, y2 = 0;
368     int ww, hh;
369 
370     x = x0 - windowrec->base_x;
371     y = y0 - windowrec->base_y;
372     ww = windowrec->width - x;
373     hh = windowrec->height - y;
374     windowrec->base_x = x0;
375     windowrec->base_y = y0;
376     if (currwin.win == windowrec->win) {
377 	currwin.base_x = x0;
378 	currwin.base_y = y0;
379     }
380     windowrec->min_x -= x;
381     if (windowrec->min_x < 0)
382 	windowrec->min_x = 0;
383     windowrec->max_x -= x;
384     if ((unsigned int)windowrec->max_x > windowrec->width)
385 	windowrec->max_x = windowrec->width;
386     windowrec->min_y -= y;
387     if (windowrec->min_y < 0)
388 	windowrec->min_y = 0;
389     windowrec->max_y -= y;
390     if ((unsigned int)windowrec->max_y > windowrec->height)
391 	windowrec->max_y = windowrec->height;
392     if (x < 0) {
393 	x2 = -x;
394 	x = 0;
395 	ww = windowrec->width - x2;
396     }
397     if (y < 0) {
398 	y2 = -y;
399 	y = 0;
400 	hh = windowrec->height - y2;
401     }
402     if (ww <= 0 || hh <= 0) {
403 	XClearWindow(DISP, windowrec->win);
404 	windowrec->min_x = windowrec->min_y = 0;
405 	windowrec->max_x = windowrec->width;
406 	windowrec->max_y = windowrec->height;
407     }
408     else {
409 	XCopyArea(DISP, windowrec->win, windowrec->win, globals.gc.copy,
410 		  x, y, (unsigned int)ww, (unsigned int)hh, x2, y2);
411 	if (x > 0)
412 	    clearexpose(windowrec, ww, 0, (unsigned int)x, windowrec->height);
413 	if (x2 > 0)
414 	    clearexpose(windowrec, 0, 0, (unsigned int)x2, windowrec->height);
415 	if (y > 0)
416 	    clearexpose(windowrec, 0, hh, windowrec->width, (unsigned int)y);
417 	if (y2 > 0)
418 	    clearexpose(windowrec, 0, 0, windowrec->width, (unsigned int)y2);
419     }
420 }
421 
422 static void
do_movemag(int x,int y)423 do_movemag(int x, int y)
424 {
425     int xx, yy;
426 
427     mag_x = x;
428     mag_y = y;
429     if (mag_x == new_mag_x && mag_y == new_mag_y)
430 	globals.ev.flags &= ~EV_MAG_MOVE;
431     compute_mag_pos(&xx, &yy);
432     XMoveWindow(DISP, magnifier.win, xx, yy);
433     scroll_window(&magnifier,
434 		  (x + mane_base_x) * mane.shrinkfactor - (int)magnifier.width / 2,
435 		  (y + mane_base_y) * mane.shrinkfactor - (int)magnifier.height / 2);
436     draw_ticks(magnifier.width, magnifier.height, globals.gc.ruler);
437 }
438 
439 extern jmp_buf next_env;
440 
441 void
show_distance_from_ruler(XEvent * event,Boolean to_stdout)442 show_distance_from_ruler(XEvent *event, Boolean to_stdout)
443 {
444     int loc_x, loc_y;
445     int precision = 2;
446     double factor;
447 
448     if (event == NULL) /* when option is toggled */
449 	return;
450 
451     loc_x = event->xbutton.x;
452     loc_y = event->xbutton.y;
453     if (event->xbutton.window != mane.win) {
454 	Window ww;
455 	(void)XTranslateCoordinates(DISP,
456 				    RootWindowOfScreen(SCRN), mane.win,
457 				    event->xbutton.x_root,
458 				    event->xbutton.y_root,
459 				    &loc_x,
460 				    &loc_y,
461 				    &ww);	/* throw away last argument */
462     }
463 
464     /* map everything below 0 to the origin */
465     if (loc_x < 0)
466 	loc_x = 0;
467     if (loc_y < 0)
468 	loc_y = 0;
469 
470     if (strcmp(resource.tick_units, "pt") == 0) {
471 	factor = 72.27 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
472     }
473     else if (strcmp(resource.tick_units, "bp") == 0) {
474 	factor = 72.0 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
475     }
476     else if (strcmp(resource.tick_units, "in") == 0) {
477 	factor = currwin.shrinkfactor / (double)resource.pixels_per_inch;
478     }
479     else if (strcmp(resource.tick_units, "cm") == 0) {
480 	factor = 2.54 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
481 	precision = 3;
482     }
483     else if (strcmp(resource.tick_units, "mm") == 0) {
484 	factor = 25.4 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
485     }
486     else if (strcmp(resource.tick_units, "dd") == 0) {
487 	factor = 72.27 / (1238.0 / 1157.0) * currwin.shrinkfactor / (double)resource.pixels_per_inch;
488     }
489     else if (strcmp(resource.tick_units, "cc") == 0) {
490 	factor = 72.27 / (12.0 * (1238.0 / 1157.0)) * currwin.shrinkfactor / (double)resource.pixels_per_inch;
491     }
492     else if (strcmp(resource.tick_units, "pc") == 0) {
493 	factor = 72.27 / 12.0 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
494     }
495     else if (strcmp(resource.tick_units, "sp") == 0) {
496 	factor = 65536.0 * 72.27 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
497 	precision = 1;
498     }
499     else if (strcmp(resource.tick_units, "px") == 0) { /* pixel units */
500 	factor = 1;
501     }
502     else {
503 	XDVI_WARNING((stderr, "Unrecognized tickUnits [%s]: defaulting to TeX points [pt]",
504 		      resource.tick_units));
505 	resource.tick_units = "pt";
506 	factor = 72.27 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
507     }
508 
509     if (mouse_release != null_mouse) {
510 	if (to_stdout) {
511 	    XDVI_INFO((stdout, "Ruler/Point: %d,%d, dx: %.*f %s, dy: %.*f %s, dr: %.*f %s",
512 		       loc_x, loc_y,
513 		       precision, 0.000, resource.tick_units,
514 		       precision, 0.000, resource.tick_units,
515 		       precision, 0.000, resource.tick_units));
516 	}
517 	else {
518 	    statusline_info(STATUS_FOREVER,
519 			     "Ruler/Point: %d,%d, dx: %.*f %s, dy: %.*f %s, dt: %.*f %s",
520 			     loc_x, loc_y,
521 			     precision, 0.000, resource.tick_units,
522 			     precision, 0.000, resource.tick_units,
523 			     precision, 0.000, resource.tick_units);
524 	}
525     }
526     else {
527 	int d_x = loc_x - g_ruler_pos_x;
528 	int d_y = loc_y - g_ruler_pos_y;
529 	double d_z = sqrt((double)d_x * d_x + (double)d_y * d_y);
530 	double unit_x = (double)d_x * factor;
531 	double unit_y = (double)d_y * factor;
532 	double unit_z = d_z * factor;
533 	if (to_stdout) {
534 	    XDVI_INFO((stdout, "Ruler: %d,%d, Point: %d,%d, dx: %.*f %s, dy: %.*f %s, dr: %.*f %s",
535 		       g_ruler_pos_x, g_ruler_pos_y, loc_x, loc_y,
536 		       precision, unit_x, resource.tick_units,
537 		       precision, unit_y, resource.tick_units,
538 		       precision, unit_z, resource.tick_units));
539 	}
540 	else {
541 	    statusline_info(STATUS_FOREVER,
542 			     "Ruler: %d,%d, Point: %d,%d, dx: %.*f %s, dy: %.*f %s, dr: %.*f %s",
543 			     g_ruler_pos_x, g_ruler_pos_y, loc_x, loc_y,
544 			     precision, unit_x, resource.tick_units,
545 			     precision, unit_y, resource.tick_units,
546 			     precision, unit_z, resource.tick_units);
547 	}
548     }
549 }
550 
551 void
move_magnifier(void)552 move_magnifier(void)
553 {
554     if (magnifier.win == (Window) 0)
555 	globals.ev.flags &= ~EV_MAG_MOVE;
556     else if (abs(new_mag_x - mag_x) > 2 * abs(new_mag_y - mag_y))
557 	do_movemag(new_mag_x, mag_y);
558     else if (abs(new_mag_y - mag_y) > 2 * abs(new_mag_x - mag_x))
559 	do_movemag(mag_x, new_mag_y);
560     else
561 	do_movemag(new_mag_x, new_mag_y);
562 }
563 
564 
565 void
clear_ruler(void)566 clear_ruler(void)
567 {
568     /* maybe we should do this only for mouse-1? */
569     clearexpose(&mane, 0, g_ruler_pos_y,
570 		ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 2, 1);
571     clearexpose(&mane, g_ruler_pos_x, 0,
572 		1, ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 2);
573 }
574 
575 void
show_ruler(XEvent * event)576 show_ruler(XEvent *event)
577 {
578     if (mouse_release == null_mouse) {
579 	if (mouse_release != null_mouse && mouse_release != drag_ruler_release)
580 	    return;
581 	if (mouse_release == null_mouse) {
582 	    mouse_motion = drag_ruler_motion;
583 	    mouse_release = drag_ruler_release;
584 	}
585 	drag_ruler_motion(event);
586     }
587 }
588 
589 static void
draw_ruler(int x,int y)590 draw_ruler(int x, int y)
591 {
592     /* don't draw if outside page region (will be clipped automatically by Motif,
593        but not by Xaw, where draw widget is entire window) */
594     if (x > (int)ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 1
595 	|| y > (int)ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 1)
596 	return;
597 
598     XFillRectangle(DISP, mane.win, globals.gc.high,
599 		   0, y,
600 		   ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 2, 1);
601     XFillRectangle(DISP, mane.win, globals.gc.high,
602 		   x, 0,
603 		   1, ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 2);
604 }
605 
606 
607 /* snap ruler back to origing (0,0) */
608 void
ruler_snap_origin(XEvent * event)609 ruler_snap_origin(XEvent *event)
610 {
611     clear_ruler();
612     g_ruler_pos_x = g_ruler_pos_y = 0;
613     draw_ruler(g_ruler_pos_x, g_ruler_pos_y);
614     /* deactivate mouse dragging */
615     mouse_motion = mouse_release = null_mouse;
616     show_distance_from_ruler(event, False);
617 }
618 
619 void
redraw_ruler(void)620 redraw_ruler(void)
621 {
622     draw_ruler(g_ruler_pos_x, g_ruler_pos_y);
623 }
624 
magnifier_move(String params,XEvent * event)625 void magnifier_move(String params, XEvent *event)
626 {
627     int x, y;
628     XSetWindowAttributes attr;
629 #ifndef MOTIF
630     Window throwaway;
631 #endif
632     const char *p = params;
633 
634     if (*p == '*') {
635 	int n = atoi(p + 1) - 1;
636 
637 	if (n < 0 || n >= (int)get_magglass_items() || get_magglass_width(n) <= 0) {
638 	    XdviBell(DISP, event->xany.window, 0);
639 	    return;
640 	}
641 	magnifier.width = get_magglass_width(n);
642 	magnifier.height = get_magglass_height(n);
643     }
644     else {
645 	magnifier.width = magnifier.height = atoi(p);
646 	p = strchr(p, 'x');
647 	if (p != NULL) {
648 	    magnifier.height = atoi(p + 1);
649 	    if (magnifier.height == 0)
650 		magnifier.width = 0;
651 	}
652 	if (magnifier.width == 0) {
653 	    XdviBell(DISP, event->xany.window, 0);
654 	    return;
655 	}
656     }
657 #ifndef MOTIF
658     XTranslateCoordinates(DISP, event->xbutton.window, mane.win,
659 			  0, 0, &mag_conv_x, &mag_conv_y, &throwaway);
660 #endif
661 
662     mag_x = event->xbutton.x + mag_conv_x;
663     mag_y = event->xbutton.y + mag_conv_y;
664     main_x = event->xbutton.x_root - mag_x;
665     main_y = event->xbutton.y_root - mag_y;
666     compute_mag_pos(&x, &y);
667     magnifier.base_x = (mag_x + mane_base_x) * mane.shrinkfactor - magnifier.width / 2;
668     magnifier.base_y = (mag_y + mane_base_y) * mane.shrinkfactor - magnifier.height / 2;
669     attr.save_under = True;
670     attr.border_pixel = resource.rule_pixel;
671 #if COLOR
672     attr.background_pixel = bg_current->pixel;
673 #else
674     attr.background_pixel = resource.back_Pixel;
675 #endif
676     attr.override_redirect = True;
677 #ifdef GREY
678     attr.colormap = G_colormap;
679 #endif
680     magnifier.win = XCreateWindow(DISP, RootWindowOfScreen(SCRN),
681 				  x, y, magnifier.width, magnifier.height, MAGBORD,
682 				  G_depth, InputOutput, G_visual,
683 				  CWSaveUnder | CWBorderPixel | CWBackPixel |
684 #ifdef GREY
685 				  CWColormap |
686 #endif
687 				  CWOverrideRedirect, &attr);
688     XSelectInput(DISP, magnifier.win, ExposureMask);
689     XMapWindow(DISP, magnifier.win);
690 
691     /*
692      * This call will draw the point rulers when the magnifier first pops up,
693      * if the XDvi*delayRulers resource is false.  Some users may prefer rulers
694      * to remain invisible until the magnifier is moved, so the default is
695      * true.  Rulers can be suppressed entirely by setting the XDvi*tickLength
696      * resource to zero or negative.
697      */
698 
699     if (!resource.delay_rulers)
700 	draw_ticks(magnifier.width, magnifier.height, globals.gc.ruler);
701 
702     globals.cursor.flags |= CURSOR_MAG;
703     globals.ev.flags |= EV_CURSOR;
704 
705     magnifier_stat = 1;	/* waiting for exposure */
706     mouse_motion = mag_motion;
707     mouse_release = mag_release;
708 }
709 
710 void
drag_ruler_motion(XEvent * event)711 drag_ruler_motion(XEvent *event)
712 {
713     int loc_x, loc_y;
714     if (event == NULL) { /* toggled via menu */
715 	/* hack to avoid redrawing ruler at last g_* positions when mode is
716 	   toggled on via menu, then off via keystroke */
717 	g_ruler_pos_x = g_ruler_pos_y = 0;
718 	return;
719     }
720 
721     loc_x = event->xbutton.x;
722     loc_y = event->xbutton.y;
723 
724     if (event->xbutton.window != mane.win) {
725 	Window dummy;
726 	(void)XTranslateCoordinates(DISP,
727 				    RootWindowOfScreen(SCRN), mane.win,
728 				    event->xbutton.x_root,
729 				    event->xbutton.y_root,
730 				    &loc_x,
731 				    &loc_y,
732 				    &dummy);
733     }
734 
735     /* map everything below 0 to the origin */
736     if (loc_x < 0)
737 	loc_x = 0;
738     if (loc_y < 0)
739 	loc_y = 0;
740 
741     clear_ruler();
742     draw_ruler(loc_x, loc_y);
743     g_ruler_pos_x = loc_x;
744     g_ruler_pos_y = loc_y;
745 }
746 
747 void
drag_ruler_release(XEvent * event)748 drag_ruler_release(XEvent *event)
749 {
750     UNUSED(event);
751     mouse_motion = mouse_release = null_mouse;
752 }
753 
754 /* XtActionsRec mag_actions[] = { */
755 /*     {"magnifier", Act_magnifier}, */
756 /*     {"do-href", Act_href}, */
757 /*     {"do-href-newwindow", Act_href_newwindow}, */
758 /*     {"switch-magnifier-units", Act_switch_magnifier_units}, */
759 /* }; */
760 
761 /*
762  * This isn't creating the actual magnifier. It is created lazily on demand
763  * if one of the corresponding actions is taken.  Therefore we are here
764  * just adding the record of actions related to magnifier handling to the
765  * application.
766  */
767 
768 /* void */
769 /* create_magnifier(void) */
770 /* { */
771 /*     XtAppAddActions(globals.app, mag_actions, XtNumber(mag_actions)); */
772 /* } */
773