Back to index

tetex-bin  3.0
mag.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 1990-2004  Paul Vojta and others
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy
00005  * of this software and associated documentation files (the "Software"), to
00006  * deal in the Software without restriction, including without limitation the
00007  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
00008  * sell copies of the Software, and to permit persons to whom the Software is
00009  * furnished to do so, subject to the following conditions:
00010  *
00011  * The above copyright notice and this permission notice shall be included in
00012  * all copies or substantial portions of the Software.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00017  * PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
00018  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00020  * OTHER DEALINGS IN THE SOFTWARE.
00021  *
00022  * NOTE: xdvi is based on prior work, as noted in the modification history in
00023  * xdvi.c.
00024  *
00025  */
00026 
00027 /*
00028  * Implementation of the magnifier window.
00029  */
00030 
00031 #include <math.h>
00032 
00033 #include "xdvi-config.h"
00034 /* one of the following should define OPEN_MAX: */
00035 #include <limits.h>
00036 #include "c-openmx.h"
00037 #include "xdvi.h"
00038 
00039 #ifdef MOTIF
00040 #include <Xm/Xm.h>
00041 #endif
00042 
00043 #include "events.h"
00044 #include "dvi-draw.h"
00045 #include "dvi-init.h"
00046 #include "statusline.h"
00047 #include "hypertex.h"
00048 #include "mag.h"
00049 #include "xm_toolbar.h"
00050 #include "xm_menu.h" /* for get_last_ungrab() */
00051 #include "util.h"
00052 #include "pagesel.h"
00053 
00054 /* to measure distance of pointer from ruler in ruler mode */
00055 static int g_ruler_pos_x = 0, g_ruler_pos_y = 0;
00056 
00057 struct WindowRec magnifier = { (Window) 0, 1, 0, 0, 0, 0, MAXDIM, 0, MAXDIM, 0 };
00058 
00059 /*
00060  * Mechanism to keep track of the magnifier window.  The problems are,
00061  *
00062  * - if the button is released while the window is being drawn, this could
00063  *   cause an X error if we continue drawing in it after it is destroyed, and
00064  *
00065  * - creating and destroying the window too quickly confuses the window
00066  *   manager, which is avoided by waiting for an expose event before destroying
00067  *   it.
00068  */
00069 
00070 short magnifier_stat;       /* 1 = wait for expose, -1 = destroy upon expose */
00071 
00072 static Position main_x;
00073 static Position main_y;
00074 
00075 static Position mag_x = 0;
00076 static Position mag_y = 0;
00077 
00078 /* Under Motif the following two remain always constant */
00079 static int mag_conv_x = 0;
00080 static int mag_conv_y = 0;
00081 
00082 static Position new_mag_x = 0;
00083 static Position new_mag_y = 0;
00084 
00085 static void
00086 can_exposures(struct WindowRec *windowrec)
00087 {
00088     windowrec->min_x = windowrec->min_y = MAXDIM;
00089     windowrec->max_x = windowrec->max_y = 0;
00090 }
00091 
00092 static void
00093 mag_motion(XEvent * event)
00094 {
00095     MYTRACE((stderr, "mag_motion!\n"));
00096     new_mag_x = event->xmotion.x + mag_conv_x;
00097     main_x = event->xmotion.x_root - new_mag_x;
00098     new_mag_y = event->xmotion.y + mag_conv_y;
00099     main_y = event->xmotion.y_root - new_mag_y;
00100 
00101     if (new_mag_x != mag_x || new_mag_y != mag_y)
00102        globals.ev.flags |= EV_MAG_MOVE;
00103     else
00104        globals.ev.flags &= ~EV_MAG_MOVE;
00105 }
00106 
00107 void
00108 mag_release(XEvent * event)
00109 {
00110     UNUSED(event);
00111     if (magnifier.win != (Window) 0) {
00112        if (magnifier_stat) {
00113            magnifier_stat = -1;    /* destroy upon expose */
00114        }
00115        else {
00116            XDestroyWindow(DISP, magnifier.win);
00117            if (drawing_mag) {
00118               globals.ev.flags |= EV_MAG_GONE;
00119            }
00120            magnifier.win = (Window) 0;
00121            mouse_motion = mouse_release = null_mouse;
00122            globals.ev.flags &= ~EV_MAG_MOVE;
00123            globals.cursor.flags &= ~CURSOR_MAG;
00124            globals.ev.flags |= EV_CURSOR;
00125            can_exposures(&magnifier);
00126            /*
00127              Workaround for bug #703304:
00128              For obscure reasons, XFree 3.3.5 (and apparently also Solaris 8)
00129              doesn't generate an expose event after the magnifier has beed
00130              destroyed. But only a redraw() event caused by expose would reset
00131              currwin.win back to mane.win, which is needed e.g. for getting the
00132              hyperlink info updated (otherwise, the mouse will not become active
00133              over a hyperlink).
00134 
00135              Forcing a redraw with
00136                 redraw(&mane);
00137              may cause a `BadDrawable' X error with color material (e.g. from #702288),
00138              or a `draw_part: shouldn't happen: POST encountered' error. Neither do
00139              the following work:
00140                 globals.ev.flags |= EV_EXPOSE;          (doesn't fix the bug)
00141                draw_page();               (same effect as redraw(&mane))
00142                globals.ev.flags |= EV_NEWPAGE;   (works, but is crude and causes flicker)
00143                
00144              So I decided to use expose() for the time being, which 
00145              sets mane.min_x to the current x point (which doesn't happen
00146              with EV_EXPOSE; this causes the test for `mane.min_x < MAXDIM'
00147              to fail in events.c; look for `see comment in mag.c').
00148            */
00149 /*         fprintf(stderr, "========triggering expose!\n"); */
00150            expose(&mane, event->xbutton.x_root, event->xbutton.y_root, 10, 10);
00151        }
00152     }
00153 }
00154 
00155 static int
00156 tick_scale(int k)
00157 {
00158     if (k == 0)
00159        return 3;
00160     else if ((k % 1000) == 0)
00161        return 7;
00162     else if ((k % 500) == 0)
00163        return 6;
00164     else if ((k % 100) == 0)
00165        return 5;
00166     else if ((k % 50) == 0)
00167        return 4;
00168     else if ((k % 10) == 0)
00169        return 3;
00170     else if ((k % 5) == 0)
00171        return 2;
00172     else
00173        return 1;
00174 }
00175 
00176 static void
00177 draw_ticks(unsigned int width, unsigned int height, GC ourGC)
00178 {
00179     int k;                         /* tick counter */
00180     double old_pixels_per_tick;
00181     double pixels_per_tick;
00182     int scale;
00183     int tick_offset;               /* offset along axes */
00184     int x;                         /* coordinates of top-left popup */
00185     int y;                         /* window corner */
00186     double xx;                            /* coordinates of tick */
00187     double yy;                            /* coordinates of tick */
00188     static char *last_tick_units = "";    /* memory of last tick units */
00189 
00190     if (resource.tick_length <= 0) /* user doesn't want tick marks */
00191        return;
00192 
00193     x = 0;    /* the pop-up window always has origin (0,0)  */
00194     y = 0;
00195 
00196     /* We need to clear the existing window to remove old rulers.  I think
00197        that this could be avoided if draw_ticks() could be invoked earlier.
00198        The expose argument in XClearArea() must be True to force redrawing
00199        of the text inside the popup window. Also, it would be better to draw
00200        the rulers before painting the text, so that rulers would not
00201        overwrite the text, but I haven't figured out yet how to arrange
00202        that. */
00203 
00204     XClearArea(DISP, magnifier.win, x, y, width, height, True);
00205 
00206     /* The global resource.pixels_per_inch tells us how to find the ruler
00207        scale.  For example, 300dpi corresponds to these TeX units:
00208 
00209        1 TeX point (pt)     =   4.151      pixels
00210        1 big point (bp)     =   4.167      pixels
00211        1 pica (pc)          =  49.813      pixels
00212        1 cicero (cc)                =  53.501      pixels
00213        1 didot point (dd)   =   4.442      pixels
00214        1 millimeter (mm)    =  11.811      pixels
00215        1 centimeter (cm)    = 118.110      pixels
00216        1 inch (in)          = 300.000      pixels
00217        1 scaled point (sp)  =   0.00006334 pixels
00218 
00219        The user can select the units via a resource (e.g. XDvi*tickUnits: bp),
00220        or a command-line option (e.g. -xrm '*tickUnits: cm').  The length of
00221        the ticks can be controlled by a resource (e.g. XDvi*tickLength: 10), or
00222        a command-line option (e.g. -xrm '*tickLength: 10000').  If the tick
00223        length exceeds the popup window size, then a graph-paper grid is drawn
00224        over the whole window.  Zero, or negative, tick length completely
00225        suppresses rulers. */
00226 
00227     pixels_per_tick = (double)resource.pixels_per_inch;
00228     if (strcmp(resource.tick_units, "pt") == 0)
00229        pixels_per_tick /= 72.27;
00230     else if (strcmp(resource.tick_units, "bp") == 0)
00231        pixels_per_tick /= 72.0;
00232     else if (strcmp(resource.tick_units, "in") == 0)
00233        /* NO-OP */ ;
00234     else if (strcmp(resource.tick_units, "cm") == 0)
00235        pixels_per_tick /= 2.54;
00236     else if (strcmp(resource.tick_units, "mm") == 0)
00237        pixels_per_tick /= 25.4;
00238     else if (strcmp(resource.tick_units, "dd") == 0)
00239        pixels_per_tick *= (1238.0 / 1157.0) / 72.27;
00240     else if (strcmp(resource.tick_units, "cc") == 0)
00241        pixels_per_tick *= 12.0 * (1238.0 / 1157.0) / 72.27;
00242     else if (strcmp(resource.tick_units, "pc") == 0)
00243        pixels_per_tick *= 12.0 / 72.27;
00244     else if (strcmp(resource.tick_units, "sp") == 0)
00245        pixels_per_tick /= (65536.0 * 72.27);
00246     else if (strcmp(resource.tick_units, "px") == 0)
00247        pixels_per_tick = 10;
00248     else {
00249        XDVI_WARNING((stderr, "Unrecognized tickUnits [%s]: defaulting to TeX points [pt]",
00250                     resource.tick_units));
00251        resource.tick_units = "pt";
00252        pixels_per_tick /= 72.27;
00253     }
00254 
00255     /* To permit accurate measurement in the popup window, we can reasonably
00256      * place tick marks about 3 to 10 pixels apart, so we scale the computed
00257      * pixels_per_tick by a power of ten to bring it into that range.
00258      */
00259 
00260     old_pixels_per_tick = pixels_per_tick;       /* remember the original scale */
00261     while (pixels_per_tick < 3.0)
00262        pixels_per_tick *= 10.0;
00263     while (pixels_per_tick > 30.0)
00264        pixels_per_tick /= 10.0;
00265 
00266     /* tell user what the ruler scale is, but only when it changes */
00267     if (strcmp(last_tick_units, resource.tick_units) != 0) {
00268        if (old_pixels_per_tick != pixels_per_tick)
00269            printf("Ruler tick interval adjusted to represent %.2f%s\n",
00270                  pixels_per_tick / old_pixels_per_tick, resource.tick_units);
00271        else if (globals.debug & DBG_EVENT)
00272            printf("Ruler tick interval represents 1%s\n", resource.tick_units);
00273     }
00274 
00275     /* In order to make the ruler as accurate as possible, given the coarse
00276      * screen resolution, we compute tick positions in floating-point
00277      * arithmetic, then round to nearest integer values.
00278      */
00279 
00280     /* draw vertical ticks on top and bottom */
00281     for (k = 0, xx = 0.0; xx < (double)width; k++, xx += pixels_per_tick) {
00282        tick_offset = (int)(0.5 + xx);     /* round to nearest pixel */
00283        scale = tick_scale(k);
00284        XDrawLine(DISP, magnifier.win, ourGC,
00285                 x + tick_offset, y, x + tick_offset, y + scale * resource.tick_length);
00286        XDrawLine(DISP, magnifier.win, ourGC,
00287                 x + tick_offset, y + height,
00288                 x + tick_offset, y + height - scale * resource.tick_length);
00289     }
00290 
00291     /* draw horizontal ticks on left and right */
00292     for (k = 0, yy = 0.0; yy < (double)height; k++, yy += pixels_per_tick) {
00293        tick_offset = (int)(0.5 + yy);     /* round to nearest pixel */
00294        scale = tick_scale(k);
00295        XDrawLine(DISP, magnifier.win, ourGC,
00296                 x, y + tick_offset, x + scale * resource.tick_length, y + tick_offset);
00297        XDrawLine(DISP, magnifier.win, ourGC,
00298                 x + width, y + tick_offset,
00299                 x + width - scale * resource.tick_length, y + tick_offset);
00300     }
00301 
00302     last_tick_units = resource.tick_units;
00303 
00304     XFlush(DISP);    /* bring window up-to-date */
00305 }
00306 
00307 static void
00308 compute_mag_pos(int *xp, int *yp)
00309 {
00310     int t;
00311 
00312     t = mag_x + main_x - magnifier.width / 2;
00313     if (t > WidthOfScreen(SCRN) - (int)magnifier.width - 2 * MAGBORD)
00314        t = WidthOfScreen(SCRN) - (int)magnifier.width - 2 * MAGBORD;
00315     if (t < 0)
00316        t = 0;
00317     *xp = t;
00318     t = mag_y + main_y - magnifier.height / 2;
00319     if (t > HeightOfScreen(SCRN) - (int)magnifier.height - 2 * MAGBORD)
00320        t = HeightOfScreen(SCRN) - (int)magnifier.height - 2 * MAGBORD;
00321     if (t < 0)
00322        t = 0;
00323     *yp = t;
00324 }
00325 
00326 static void
00327 scroll_window(struct WindowRec *windowrec, int x0, int y0)
00328 {
00329     int x, y;
00330     int x2 = 0, y2 = 0;
00331     int ww, hh;
00332 
00333     x = x0 - windowrec->base_x;
00334     y = y0 - windowrec->base_y;
00335     ww = windowrec->width - x;
00336     hh = windowrec->height - y;
00337     windowrec->base_x = x0;
00338     windowrec->base_y = y0;
00339     if (currwin.win == windowrec->win) {
00340        currwin.base_x = x0;
00341        currwin.base_y = y0;
00342     }
00343     windowrec->min_x -= x;
00344     if (windowrec->min_x < 0)
00345        windowrec->min_x = 0;
00346     windowrec->max_x -= x;
00347     if ((unsigned int)windowrec->max_x > windowrec->width)
00348        windowrec->max_x = windowrec->width;
00349     windowrec->min_y -= y;
00350     if (windowrec->min_y < 0)
00351        windowrec->min_y = 0;
00352     windowrec->max_y -= y;
00353     if ((unsigned int)windowrec->max_y > windowrec->height)
00354        windowrec->max_y = windowrec->height;
00355     if (x < 0) {
00356        x2 = -x;
00357        x = 0;
00358        ww = windowrec->width - x2;
00359     }
00360     if (y < 0) {
00361        y2 = -y;
00362        y = 0;
00363        hh = windowrec->height - y2;
00364     }
00365     if (ww <= 0 || hh <= 0) {
00366        XClearWindow(DISP, windowrec->win);
00367        windowrec->min_x = windowrec->min_y = 0;
00368        windowrec->max_x = windowrec->width;
00369        windowrec->max_y = windowrec->height;
00370     }
00371     else {
00372        XCopyArea(DISP, windowrec->win, windowrec->win, globals.gc.copy,
00373                 x, y, (unsigned int)ww, (unsigned int)hh, x2, y2);
00374        if (x > 0)
00375            clearexpose(windowrec, ww, 0, (unsigned int)x, windowrec->height);
00376        if (x2 > 0)
00377            clearexpose(windowrec, 0, 0, (unsigned int)x2, windowrec->height);
00378        if (y > 0)
00379            clearexpose(windowrec, 0, hh, windowrec->width, (unsigned int)y);
00380        if (y2 > 0)
00381            clearexpose(windowrec, 0, 0, windowrec->width, (unsigned int)y2);
00382     }
00383 }
00384 
00385 static void
00386 do_movemag(int x, int y)
00387 {
00388     int xx, yy;
00389 
00390     mag_x = x;
00391     mag_y = y;
00392     if (mag_x == new_mag_x && mag_y == new_mag_y)
00393        globals.ev.flags &= ~EV_MAG_MOVE;
00394     compute_mag_pos(&xx, &yy);
00395     XMoveWindow(DISP, magnifier.win, xx, yy);
00396     scroll_window(&magnifier,
00397                (x + mane_base_x) * mane.shrinkfactor - (int)magnifier.width / 2,
00398                (y + mane_base_y) * mane.shrinkfactor - (int)magnifier.height / 2);
00399     draw_ticks(magnifier.width, magnifier.height, globals.gc.ruler);
00400 }
00401 
00402 extern jmp_buf next_env;
00403 
00404 void
00405 show_distance_from_ruler(XEvent *event, Boolean to_stdout)
00406 {
00407     int loc_x, loc_y;
00408     int precision = 2;
00409     double factor;
00410 
00411     if (event == NULL) /* when option is toggled */
00412        return;
00413 
00414     loc_x = event->xbutton.x;
00415     loc_y = event->xbutton.y;
00416     if (event->xbutton.window != mane.win) {
00417        Window ww;
00418        (void)XTranslateCoordinates(DISP,
00419                                 RootWindowOfScreen(SCRN), mane.win,
00420                                 event->xbutton.x_root,
00421                                 event->xbutton.y_root,
00422                                 &loc_x,
00423                                 &loc_y,
00424                                 &ww);     /* throw away last argument */
00425     }
00426 
00427     /* map everything below 0 to the origin */
00428     if (loc_x < 0)
00429        loc_x = 0;
00430     if (loc_y < 0)
00431        loc_y = 0;
00432 
00433     if (strcmp(resource.tick_units, "pt") == 0) {
00434        factor = 72.27 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
00435     }
00436     else if (strcmp(resource.tick_units, "bp") == 0) {
00437        factor = 72.0 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
00438     }
00439     else if (strcmp(resource.tick_units, "in") == 0) {
00440        factor = currwin.shrinkfactor / (double)resource.pixels_per_inch;
00441     }
00442     else if (strcmp(resource.tick_units, "cm") == 0) {
00443        factor = 2.54 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
00444        precision = 3;
00445     }
00446     else if (strcmp(resource.tick_units, "mm") == 0) {
00447        factor = 25.4 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
00448     }
00449     else if (strcmp(resource.tick_units, "dd") == 0) {
00450        factor = 72.27 / (1238.0 / 1157.0) * currwin.shrinkfactor / (double)resource.pixels_per_inch;
00451     }
00452     else if (strcmp(resource.tick_units, "cc") == 0) {
00453        factor = 72.27 / (12.0 * (1238.0 / 1157.0)) * currwin.shrinkfactor / (double)resource.pixels_per_inch;
00454     }
00455     else if (strcmp(resource.tick_units, "pc") == 0) {
00456        factor = 72.27 / 12.0 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
00457     }
00458     else if (strcmp(resource.tick_units, "sp") == 0) {
00459        factor = 65536.0 * 72.27 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
00460        precision = 1;
00461     }
00462     else if (strcmp(resource.tick_units, "px") == 0) { /* pixel units */
00463        factor = 1;
00464     }
00465     else {
00466        XDVI_WARNING((stderr, "Unrecognized tickUnits [%s]: defaulting to TeX points [pt]",
00467                     resource.tick_units));
00468        resource.tick_units = "pt";
00469        factor = 72.27 * currwin.shrinkfactor / (double)resource.pixels_per_inch;
00470     }
00471     
00472     if (mouse_release != null_mouse) {
00473        if (to_stdout) {
00474            XDVI_INFO((stdout, "Ruler/Point: %d,%d, dx: %.*f %s, dy: %.*f %s, dr: %.*f %s",
00475                      loc_x, loc_y,
00476                      precision, 0.000, resource.tick_units,
00477                      precision, 0.000, resource.tick_units,
00478                      precision, 0.000, resource.tick_units));
00479        }
00480        else {
00481            statusline_print(STATUS_FOREVER,
00482                           "Ruler/Point: %d,%d, dx: %.*f %s, dy: %.*f %s, dt: %.*f %s",
00483                           loc_x, loc_y,
00484                           precision, 0.000, resource.tick_units,
00485                           precision, 0.000, resource.tick_units,
00486                           precision, 0.000, resource.tick_units);
00487        }
00488     }
00489     else {
00490        int d_x = loc_x - g_ruler_pos_x;
00491        int d_y = loc_y - g_ruler_pos_y;
00492        double d_z = sqrt((double)d_x * d_x + (double)d_y * d_y);
00493        double unit_x = (double)d_x * factor;
00494        double unit_y = (double)d_y * factor;
00495        double unit_z = d_z * factor;
00496        if (to_stdout) {
00497            XDVI_INFO((stdout, "Ruler: %d,%d, Point: %d,%d, dx: %.*f %s, dy: %.*f %s, dr: %.*f %s",
00498                      g_ruler_pos_x, g_ruler_pos_y, loc_x, loc_y,
00499                      precision, unit_x, resource.tick_units,
00500                      precision, unit_y, resource.tick_units,
00501                      precision, unit_z, resource.tick_units));
00502        }
00503        else {
00504            statusline_print(STATUS_FOREVER,
00505                           "Ruler: %d,%d, Point: %d,%d, dx: %.*f %s, dy: %.*f %s, dr: %.*f %s",
00506                           g_ruler_pos_x, g_ruler_pos_y, loc_x, loc_y,
00507                           precision, unit_x, resource.tick_units,
00508                           precision, unit_y, resource.tick_units,
00509                           precision, unit_z, resource.tick_units);
00510        }
00511     }
00512 }
00513 
00514 void
00515 move_magnifier(void)
00516 {
00517     if (magnifier.win == (Window) 0)
00518        globals.ev.flags &= ~EV_MAG_MOVE;
00519     else if (abs(new_mag_x - mag_x) > 2 * abs(new_mag_y - mag_y))
00520        do_movemag(new_mag_x, mag_y);
00521     else if (abs(new_mag_y - mag_y) > 2 * abs(new_mag_x - mag_x))
00522        do_movemag(mag_x, new_mag_y);
00523     else
00524        do_movemag(new_mag_x, new_mag_y);
00525 }
00526 
00527 static void
00528 Act_href(Widget w, XEvent *event,
00529         String *params, Cardinal *num_params)
00530 {
00531     int x, y;
00532     Window dummy;
00533     
00534     UNUSED(w);
00535     UNUSED(num_params);
00536     UNUSED(params);
00537 
00538     if (resource.mouse_mode == MOUSE_RULER_MODE)
00539        return;
00540 
00541     (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
00542                             event->xkey.x, event->xkey.y, &x, &y, &dummy);
00543     if (htex_handleref(x, y, False)) {
00544        block_next_mouse_event();
00545     }
00546 }
00547 
00548 static void
00549 Act_href_newwindow(Widget w, XEvent *event,
00550                  String *params, Cardinal *num_params)
00551 {
00552     int x, y;
00553     Window dummy;
00554     
00555     UNUSED(w);
00556     UNUSED(num_params);
00557     UNUSED(params);
00558     
00559     if (resource.mouse_mode == MOUSE_RULER_MODE)
00560        return;
00561     
00562     (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
00563                             event->xkey.x, event->xkey.y, &x, &y, &dummy);
00564     if (htex_handleref(x, y, True)) {
00565        block_next_mouse_event();
00566     }
00567 }
00568 
00569 void
00570 clear_ruler(void)
00571 {
00572     /* maybe we should do this only for mouse-1? */
00573     clearexpose(&mane, 0, g_ruler_pos_y,
00574               ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 2, 1);
00575     clearexpose(&mane, g_ruler_pos_x, 0,
00576               1, ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 2);
00577 }
00578 
00579 void
00580 show_ruler(XEvent *event)
00581 {
00582     if (mouse_release == null_mouse) {
00583        if (mouse_release != null_mouse && mouse_release != drag_ruler_release)
00584            return;
00585        if (mouse_release == null_mouse) {
00586            mouse_motion = drag_ruler_motion;
00587            mouse_release = drag_ruler_release;
00588        }
00589        drag_ruler_motion(event);
00590     }
00591 }
00592 
00593 static void
00594 draw_ruler(int x, int y)
00595 {
00596     /* don't draw if outside page region (will be clipped automatically by Motif,
00597        but not by Xaw, where draw widget is entire window) */
00598     if (x > (int)ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 1
00599        || y > (int)ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 1)
00600        return;
00601     
00602     XFillRectangle(DISP, mane.win, globals.gc.high,
00603                  0, y,
00604                  ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 2, 1);
00605     XFillRectangle(DISP, mane.win, globals.gc.high,
00606                  x, 0,
00607                  1, ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 2);
00608 }
00609 
00610 
00611 /* snap ruler back to origing (0,0) */
00612 void
00613 ruler_snap_origin(XEvent *event)
00614 {
00615     clear_ruler();
00616     g_ruler_pos_x = g_ruler_pos_y = 0;
00617     draw_ruler(g_ruler_pos_x, g_ruler_pos_y);
00618     /* deactivate mouse dragging */
00619     mouse_motion = mouse_release = null_mouse;
00620     show_distance_from_ruler(event, False);
00621 }
00622 
00623 void
00624 redraw_ruler(void)
00625 {
00626     draw_ruler(g_ruler_pos_x, g_ruler_pos_y);
00627 }
00628 
00629 void
00630 drag_ruler_motion(XEvent *event)
00631 {
00632     int loc_x, loc_y;
00633     if (event == NULL) { /* toggled via menu */
00634        /* hack to avoid redrawing ruler at last g_* positions when mode is
00635           toggled on via menu, then off via keystroke */
00636        g_ruler_pos_x = g_ruler_pos_y = 0;
00637        return;
00638     }
00639     
00640     loc_x = event->xbutton.x;
00641     loc_y = event->xbutton.y;
00642 
00643     if (event->xbutton.window != mane.win) {
00644        Window dummy;
00645        (void)XTranslateCoordinates(DISP,
00646                                 RootWindowOfScreen(SCRN), mane.win,
00647                                 event->xbutton.x_root,
00648                                 event->xbutton.y_root,
00649                                 &loc_x,
00650                                 &loc_y,
00651                                 &dummy);
00652     }
00653 
00654     /* map everything below 0 to the origin */
00655     if (loc_x < 0)
00656        loc_x = 0;
00657     if (loc_y < 0)
00658        loc_y = 0;
00659     
00660     clear_ruler();
00661     draw_ruler(loc_x, loc_y);
00662     g_ruler_pos_x = loc_x;
00663     g_ruler_pos_y = loc_y;
00664 }
00665 
00666 void
00667 drag_ruler_release(XEvent *event)
00668 {
00669     UNUSED(event);
00670     mouse_motion = mouse_release = null_mouse;
00671 }
00672 
00673 /****************************************************************************
00674  * Actions specific to the handling of the magnifier
00675  */
00676 
00677 static void
00678 Act_magnifier(Widget w, XEvent *event,
00679              String *params, Cardinal *num_params)
00680 {
00681     const char *p;
00682     int x, y;
00683     XSetWindowAttributes attr;
00684 #ifndef MOTIF
00685     Window throwaway;
00686 #endif
00687     UNUSED(w);
00688 
00689     MYTRACE((stderr, "magnifier!\n"));
00690     
00691     if (block_this_mouse_event())
00692        return;
00693     else if (dvi_file_changed()) {
00694        globals.ev.flags |= EV_RELOAD;
00695        return;
00696     }
00697 
00698 #ifdef MOTIF
00699     /* see xm_menu.c for an explanation of this */
00700     if (pulldown_menu_active(event->xany.serial)) {
00701        return;
00702     }
00703 #endif
00704     
00705     if (bg_current == NULL) {
00706        /*
00707          HACK ALERT: we can arrive here after loading a new file via the file selector
00708          for which not all fonts have been generated. In that case, dereferencing
00709          bg_current would bomb. Try to recover by simply returning here.
00710         */
00711        return;
00712     }
00713 
00714     if (resource.mouse_mode == MOUSE_RULER_MODE && event->xbutton.button == 1) {
00715        show_ruler(event);
00716        XDefineCursor(DISP, CURSORWIN, globals.cursor.rule);
00717        show_distance_from_ruler(event, False);
00718        return;
00719     }
00720     else if (resource.mouse_mode == MOUSE_TEXT_MODE && event->xbutton.button == 1) {
00721        text_selection_start(event);
00722        text_motion(event);
00723        return;
00724     }
00725     
00726     if (event->type != ButtonPress || mouse_release != null_mouse
00727        || magnifier.win != (Window)0 || mane.shrinkfactor == 1 || *num_params != 1) {
00728        XBell(DISP, 0);
00729        if (mane.shrinkfactor == 1) {
00730            statusline_print(STATUS_SHORT,
00731                           "No magnification available at shrink factor 1");
00732        }
00733        return;
00734     }
00735 
00736     p = *params;
00737     if (*p == '*') {
00738        int n = atoi(p + 1) - 1;
00739 
00740        if (n < 0 || n >= (int)get_magglass_items() || get_magglass_width(n) <= 0) {
00741            XBell(DISP, 0);
00742            return;
00743        }
00744        magnifier.width = get_magglass_width(n);
00745        magnifier.height = get_magglass_height(n);
00746     }
00747     else {
00748        magnifier.width = magnifier.height = atoi(p);
00749        p = strchr(p, 'x');
00750        if (p != NULL) {
00751            magnifier.height = atoi(p + 1);
00752            if (magnifier.height == 0)
00753               magnifier.width = 0;
00754        }
00755        if (magnifier.width == 0) {
00756            XBell(DISP, 0);
00757            return;
00758        }
00759     }
00760 #ifndef MOTIF
00761     XTranslateCoordinates(DISP, event->xbutton.window, mane.win,
00762                        0, 0, &mag_conv_x, &mag_conv_y, &throwaway);
00763 #endif
00764 
00765     globals.cursor.flags |= CURSOR_MAG;
00766     globals.ev.flags |= EV_CURSOR;
00767     
00768     mag_x = event->xbutton.x + mag_conv_x;
00769     mag_y = event->xbutton.y + mag_conv_y;
00770     main_x = event->xbutton.x_root - mag_x;
00771     main_y = event->xbutton.y_root - mag_y;
00772     compute_mag_pos(&x, &y);
00773     magnifier.base_x = (mag_x + mane_base_x) * mane.shrinkfactor - magnifier.width / 2;
00774     magnifier.base_y = (mag_y + mane_base_y) * mane.shrinkfactor - magnifier.height / 2;
00775     attr.save_under = True;
00776     attr.border_pixel = resource.rule_pixel;
00777 #if COLOR
00778     attr.background_pixel = bg_current->pixel;
00779 #else
00780     attr.background_pixel = resource.back_Pixel;
00781 #endif
00782     attr.override_redirect = True;
00783 #ifdef GREY
00784     attr.colormap = G_colormap;
00785 #endif
00786     magnifier.win = XCreateWindow(DISP, RootWindowOfScreen(SCRN),
00787                               x, y, magnifier.width, magnifier.height, MAGBORD,
00788                               G_depth, InputOutput, G_visual,
00789                               CWSaveUnder | CWBorderPixel | CWBackPixel |
00790 #ifdef GREY
00791                               CWColormap |
00792 #endif
00793                               CWOverrideRedirect, &attr);
00794     XSelectInput(DISP, magnifier.win, ExposureMask);
00795     XMapWindow(DISP, magnifier.win);
00796 
00797     /*
00798      * This call will draw the point rulers when the magnifier first pops up,
00799      * if the XDvi*delayRulers resource is false.  Some users may prefer rulers
00800      * to remain invisible until the magnifier is moved, so the default is
00801      * true.  Rulers can be suppressed entirely by setting the XDvi*tickLength
00802      * resource to zero or negative.
00803      */
00804 
00805     if (!resource.delay_rulers)
00806        draw_ticks(magnifier.width, magnifier.height, globals.gc.ruler);
00807 
00808     magnifier_stat = 1;     /* waiting for exposure */
00809     mouse_motion = mag_motion;
00810     mouse_release = mag_release;
00811 }
00812 
00813 static void
00814 Act_switch_magnifier_units(Widget w, XEvent *event,
00815                         String *params, Cardinal *num_params)
00816 {
00817     size_t k = 0;
00818     static char *TeX_units[] = {
00819        "mm", "pt", "in", "sp", "bp", "cc", "dd", "pc", "px",
00820     };
00821 
00822     UNUSED(w);
00823     UNUSED(event);
00824     UNUSED(params);
00825     UNUSED(num_params);
00826     
00827     for (k = 0; k < XtNumber(TeX_units); ++k)
00828        if (strcmp(resource.tick_units, TeX_units[k]) == 0)
00829            break;
00830     k++;
00831     if (k >= XtNumber(TeX_units))
00832        k = 0;
00833     resource.tick_units = TeX_units[k];
00834     if (resource.mouse_mode != MOUSE_RULER_MODE) {
00835        statusline_print(STATUS_SHORT, "Ruler units: %s\n", resource.tick_units);
00836     }
00837     else {
00838        show_distance_from_ruler(event, False);
00839     }
00840 }
00841 
00842 XtActionsRec mag_actions[] = {
00843     {"magnifier", Act_magnifier},
00844     {"do-href", Act_href},
00845     {"do-href-newwindow", Act_href_newwindow},
00846     {"switch-magnifier-units", Act_switch_magnifier_units},
00847 };
00848 
00849 /*
00850  * This isn't creating the actual magnifier. It is created lazily on demand
00851  * if one of the corresponding actions is taken.  Therefore we are here
00852  * just adding the record of actions related to magnifier handling to the
00853  * application.
00854  */
00855 
00856 void
00857 create_magnifier(void)
00858 {
00859     XtAppAddActions(app, mag_actions, XtNumber(mag_actions));
00860 }