Back to index

tetex-bin  3.0
x_util.c
Go to the documentation of this file.
00001 #include "xdvi-config.h"
00002 
00003 #include <X11/Intrinsic.h>
00004 #include <X11/StringDefs.h>
00005 #include <X11/Shell.h>
00006 #include <X11/StringDefs.h>
00007 
00008 
00009 #if !MOTIF
00010 # include <X11/Xaw/Form.h>
00011 #endif
00012 
00013 #include "x_util.h"
00014 #include "string-utils.h"
00015 #include "util.h"
00016 #include "statusline.h"
00017 #include "message-window.h"
00018 #include "events.h" /* for set_bar_value() */
00019 
00020 #define DEBUG_SCROLL_IF_NEEDED 0
00021 
00022 Boolean do_autoscroll = False;
00023 
00024 
00025 /* Center window wa over window wb */
00026 void
00027 center_window(Widget wa, Widget wb)
00028 {
00029     Position x, y;
00030     Dimension w1, h1, w2, h2;
00031 
00032     if (!XtIsRealized(wa) || !XtIsRealized(wb))
00033        return;
00034     
00035     XtVaGetValues(wa,
00036                 XtNwidth, &w1,
00037                 XtNheight, &h1,
00038                 NULL);
00039     XtVaGetValues(wb,
00040                 XtNwidth, &w2,
00041                 XtNheight, &h2,
00042                 XtNx, &x,
00043                 XtNy, &y,
00044                 NULL);
00045     XtVaSetValues(wa, XtNx, x + (w2 - w1) / 2,
00046                 XtNy, y + (h2 - h1) / 2,
00047                 NULL);
00048 }
00049 
00050 /* Position window w at coordinates x, y */
00051 void
00052 position_window(Widget w, Position x, Position y)
00053 {
00054     if (!XtIsRealized(w))
00055        return;
00056 
00057     TRACE_GUI((stderr, "positioning %ld at %d, %d", (unsigned long)w, x, y));
00058     XtVaSetValues(w, XtNx, x, XtNy, y, NULL);
00059 }
00060 
00061 /*
00062   Used for the hyperref and forward search markers: scroll the page
00063   to make the marker visible.
00064 */
00065 void
00066 scroll_page_if_needed(int x_min, int x_max, int y_min, int y_max)
00067 {
00068     Position drawing_x, drawing_y, drawing_h, clip_x, clip_y, clip_h, clip_w;
00069     int test_scroll, need_v_scroll = 0, need_h_scroll = 0;
00070 
00071     if (!do_autoscroll)
00072        return;
00073     
00074     XtVaGetValues(globals.widgets.clip_widget,
00075                 XtNx, &clip_x, XtNy, &clip_y,
00076                 XtNheight, &clip_h, XtNwidth, &clip_w,
00077                 NULL);
00078     XtVaGetValues(globals.widgets.draw_widget,
00079                 XtNx, &drawing_x, XtNy, &drawing_y,
00080                 XtNheight, &drawing_h,
00081                 NULL);
00082 
00083 #if DEBUG_SCROLL_IF_NEEDED
00084     fprintf(stderr, "y: %d, drawing_y: %d, clip_h: %d\n", y, drawing_y, clip_h);
00085 #endif
00086     /* check if we need to scroll vertically; first, down for y_min */
00087     test_scroll = y_min + drawing_y - clip_h;
00088     if ((resource.expert_mode & XPRT_SHOW_STATUSLINE) != 0)
00089        test_scroll += global_statusline_h;
00090 
00091     TRACE_SRC((stderr, "test_scroll vertically: %d", test_scroll));
00092 #if DEBUG_SCROLL_IF_NEEDED
00093     fprintf(stderr, "%d + %d > %d?\n", drawing_y, y_min, clip_h);
00094 #endif
00095     if (test_scroll > 0) { /* need to scroll down? */
00096        need_v_scroll = test_scroll;
00097        TRACE_SRC((stderr, "need_v_scroll down: %d", need_v_scroll));
00098     }
00099     else if (abs(drawing_y) + 2 > y_max) { /* need to scroll up? */
00100        need_v_scroll = -((abs(drawing_y) - y_max) + 1);
00101        TRACE_SRC((stderr, "need_v_scroll up: %d (%d > %d; %d)", need_v_scroll, abs(drawing_y), y_max, clip_y));
00102     }
00103 
00104     /* check if we need to scroll horizontally; x_min < 0 blocks this
00105        (e.g. for hyperref, where we don't want it) */
00106     if (x_min >= 0) {
00107        test_scroll = x_min + drawing_x - clip_w + 1;
00108        TRACE_SRC((stderr, "test_scroll horizontally: %d", test_scroll));
00109        
00110        if (test_scroll > 0) {
00111            /* need to scroll to right (i.e. make stuff on right-hand side visible)? */
00112            need_h_scroll = test_scroll;
00113            TRACE_SRC((stderr, "need_h_scroll right: %d", need_h_scroll));
00114        }
00115        else if (abs(drawing_x) > x_max) {
00116            /* need to scroll to left (i.e. make stuff on left-hand side visible)? */
00117            need_h_scroll =  -(abs(drawing_x) - x_max);
00118            TRACE_SRC((stderr, "need_h_scroll left: %d", need_h_scroll));
00119        }
00120     }
00121 
00122     /* FIXME: should we not scroll if keep_flag is active? */
00123     if (need_v_scroll != 0 && globals.widgets.y_bar != NULL) {
00124 #ifdef MOTIF
00125        /* need to add new value to current one */
00126        XtVaGetValues(globals.widgets.y_bar, XmNvalue, &test_scroll, NULL);
00127        (void)set_bar_value(globals.widgets.y_bar, test_scroll + need_v_scroll, (int)(globals.page.h - mane.height));
00128 #else
00129        XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer)need_v_scroll);
00130 #endif
00131     }
00132 
00133     if (need_h_scroll != 0 && globals.widgets.x_bar != NULL) {
00134 #ifdef MOTIF
00135        /* need to add new value to current one */
00136        XtVaGetValues(globals.widgets.x_bar, XmNvalue, &test_scroll, NULL);
00137        (void)set_bar_value(globals.widgets.x_bar, test_scroll + need_h_scroll, (int)(globals.page.w - mane.width));
00138 #else
00139        XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer)need_h_scroll);
00140 #endif
00141     }
00142 
00143     do_autoscroll = False;
00144 }
00145 
00146 void adjust_width(Widget a, Widget b)
00147 {
00148     Dimension w1, w2;
00149     
00150     XtVaGetValues(a, XtNwidth, &w1, NULL);
00151     XtVaGetValues(b, XtNwidth, &w2, NULL);
00152     
00153     if (w1 < w2)
00154        XtVaSetValues(a, XtNwidth, w2, NULL);
00155     else if (w2 > w1)
00156        XtVaSetValues(b, XtNwidth, w1, NULL);
00157 }
00158 
00159 /* change a GC and return it, or create one if it doesn't exist yet */
00160 GC
00161 set_or_make_gc(GC gc, int function, Pixel fg, Pixel bg)
00162 {
00163     XGCValues values;
00164     values.function = function;
00165     values.foreground = fg;
00166     values.background = bg;
00167 
00168     /* Since print is in round dots we make drawings as "smooth" as possible. */
00169     values.cap_style = CapRound;
00170     values.join_style = JoinRound;
00171 
00172     if (gc != NULL)
00173        XChangeGC(DISP, gc, GCFunction | GCForeground | GCBackground
00174                 | GCCapStyle | GCJoinStyle, &values);
00175     else
00176        gc = XCreateGC(DISP, XtWindow(globals.widgets.top_level), GCFunction | GCForeground | GCBackground
00177                      | GCCapStyle | GCJoinStyle, &values);
00178 
00179     return gc;
00180 }
00181 
00182 /*
00183  *     Atom handling.
00184  */
00185 
00186 static char *atom_names[] = {
00187     "XDVI_WINDOWS",
00188     "DVI_NAME",
00189     "SRC_GOTO",
00190     "RELOAD",
00191     "NEWDOC",
00192     "NEWPAGE",
00193     "RAISE",
00194     "FIND_STRING",
00195     "REREAD_PREFS"
00196 };
00197 static Atom atoms[XtNumber(atom_names)];
00198 
00199 Window
00200 get_window_id(char *window_p)
00201 {
00202     Window w;
00203     
00204 #ifndef WORD64
00205     w = (*((xuint32 *) window_p));
00206 #else
00207 # if WORDS_BIGENDIAN
00208     w = ((unsigned long)wp[0] << 24) |
00209        ((unsigned long)wp[1] << 16) |
00210        ((unsigned long)wp[2] << 8)  |
00211        (unsigned long)wp[3];
00212 # else
00213     w = ((unsigned long)wp[3] << 24) |
00214        ((unsigned long)wp[2] << 16) |
00215        ((unsigned long)wp[1] << 8)  |
00216        (unsigned long)wp[0];
00217 # endif
00218 #endif
00219     return w;
00220 }
00221 
00222 size_t
00223 property_get_data(Window w, Atom a, char **ret_buf,
00224                 int (*x_get_property)(Display *, Window, Atom, long,
00225                                    long, Bool, Atom, Atom *, int *, unsigned long *,
00226                                    unsigned long *, unsigned char **))
00227 {
00228     /* all of these are in 8-bit units */
00229     unsigned long byte_offset = 0;
00230     Atom type_ret;
00231     int format_ret;
00232     unsigned long nitems_ret;
00233     unsigned long bytes_after_ret = 0;
00234     unsigned char *prop_ret = NULL;
00235     
00236     /*
00237      * buffer for collecting returned data; this is static to
00238      * avoid expensive malloc()s at every call (which is often!)
00239      */
00240     static unsigned char *buffer = NULL;
00241     static size_t buffer_len = 0;
00242 
00243     while (x_get_property(DISP, w,
00244                        a, byte_offset / 4, (bytes_after_ret + 3) / 4, False,
00245                        a, &type_ret, &format_ret, &nitems_ret,
00246                        &bytes_after_ret, &prop_ret)
00247           == Success) {
00248 
00249        if (type_ret != a || format_ret == 0)
00250            break;
00251 
00252        nitems_ret *= (format_ret / 8);    /* convert to bytes */
00253 
00254        while ((byte_offset + nitems_ret) >= buffer_len) {
00255            buffer_len += 256;
00256            buffer = xrealloc(buffer, buffer_len);
00257        }
00258 
00259        /* the +1 captures the extra '\0' that Xlib puts after the end.  */
00260        memcpy(buffer + byte_offset, prop_ret, nitems_ret + 1);
00261        byte_offset += nitems_ret;
00262 
00263        XFree(prop_ret);
00264        prop_ret = NULL;
00265 
00266        if (bytes_after_ret == 0)   /* got all data */
00267            break;
00268     }
00269 
00270     if (prop_ret != NULL)
00271        XFree(prop_ret);
00272 
00273     *ret_buf = (char *)buffer;
00274     return byte_offset;
00275 }
00276 
00277 size_t
00278 property_get_window_list(char **window_list)
00279 {
00280     size_t len = property_get_data(DefaultRootWindow(DISP),
00281                                atom_xdvi_windows(), window_list,
00282                                XGetWindowProperty);
00283     if (len == 0) {
00284        TRACE_CLIENT((stderr, "No \"xdvi windows\" property found"));
00285        return 0;
00286     }
00287     
00288     if (len % 4 != 0) {
00289        TRACE_CLIENT((stderr, "\"XDVI_WINDOWS\" property had incorrect size; deleting it."));
00290        XDeleteProperty(DISP, DefaultRootWindow(DISP), atom_xdvi_windows());
00291        return 0;
00292     }
00293     return len;
00294 }
00295 
00296 void
00297 set_window_id(Window w, unsigned char *data)
00298 {
00299 #if WORDS_BIGENDIAN
00300     data[0] = (unsigned int)w >> 24;
00301     data[1] = (unsigned int)w >> 16;
00302     data[2] = (unsigned int)w >> 8;
00303     data[3] = (unsigned int)w;
00304 #else
00305     data[0] = (unsigned int)w;
00306     data[1] = (unsigned int)w >> 8;
00307     data[2] = (unsigned int)w >> 16;
00308     data[3] = (unsigned int)w >> 24;
00309 #endif
00310 }
00311 
00317 void
00318 set_dvi_property(void)
00319 {
00320     XChangeProperty(DISP, XtWindow(globals.widgets.top_level), atom_dvi_file(), atom_dvi_file(),
00321                   8, PropModeReplace, (unsigned char *)dvi_property, dvi_property_length);
00322 }
00323 
00324 
00325 /*
00326  * Delete all occurences of window w from the window list property. Then,
00327  * if `prepend' is true, prepend the window ID to the existing list.
00328  */
00329 void
00330 update_window_property(Window w, Boolean prepend)
00331 {
00332     char *wlist;
00333     size_t wlist_len;
00334     char *wlist_end;
00335     char *wp;
00336 #if 0
00337     int i;
00338 #endif /* 0 */
00339 
00340     /* this allocates wlist */
00341     if ((wlist_len = property_get_window_list(&wlist)) == 0)
00342        return;
00343 
00344     /* Loop over list of windows.  */
00345     wlist_end = wlist + wlist_len;
00346 
00347 #if 0
00348     for (i = 0, wp = wlist; wp < wlist_end; wp += 4, i++) {
00349        fprintf(stderr, "WIN %d: %08lx; len: %d\n", i, get_window_id(wp), wlist_len);
00350     }
00351 #endif /* 0 */
00352     
00353     for (wp = wlist; wp < wlist_end; wp += 4) {
00354        if (get_window_id(wp) == w) { /* match, remove our ID */
00355            wlist_len -= 4;
00356            wlist_end -= 4;
00357            memmove(wp, wp + 4, wlist_end - wp);
00358            wp -= 4; /* new item is now at wp; don't skip it in next iteration */
00359        }
00360     }
00361     
00362     if (prepend) { /* add our ID again to front */
00363 #ifdef WORD64
00364        unsigned char data[4];
00365        set_window_id(w, data);
00366 #else
00367        xuint32 data = w;
00368 #endif
00369        /* Note: no need to realloc wlist, since the original length
00370           was sufficient for all elements.
00371        */
00372        memmove(wlist + 4, wlist, wlist_len);
00373        wlist_len += 4;
00374        memcpy(wlist, &data, 4);
00375     }
00376            
00377     if (wlist_len == 0)
00378        XDeleteProperty(DISP, DefaultRootWindow(DISP),
00379                      atom_xdvi_windows());
00380     else
00381        XChangeProperty(DISP, DefaultRootWindow(DISP),
00382                      atom_xdvi_windows(), atom_xdvi_windows(), 32,
00383                      PropModeReplace, (unsigned char *)wlist,
00384                      wlist_len / 4);
00385     
00386     XFlush(DISP);
00387 }
00388 
00389 void
00390 property_initialize(void)
00391 {
00392     size_t i;
00393     
00394 #if XlibSpecificationRelease >= 6
00395     if (!XInternAtoms(DISP, atom_names, XtNumber(atom_names), False, atoms))
00396        XDVI_FATAL((stderr, "XtInternAtoms failed."));
00397 #else
00398     for (i = 0; i < XtNumber(atom_names); i++) {
00399        if ((atoms[i] = XInternAtom(DISP, atom_names[i], False)) == None)
00400            XDVI_FATAL((stderr, "XtInternAtoms failed."));
00401     }
00402 #endif
00403 
00404     if (globals.debug & DBG_CLIENT) {
00405        for (i = 0; i < XtNumber(atom_names); i++)
00406            TRACE_CLIENT((stderr, "Atom(%s) = %lu", atom_names[i], atoms[i]));
00407     }
00408 }
00409 
00410 
00411 Atom atom_xdvi_windows(void)
00412 {
00413     return atoms[0];
00414 }
00415 
00416 Atom atom_dvi_file(void)
00417 {
00418     return atoms[1];
00419 }
00420 
00421 Atom atom_src_goto(void)
00422 {
00423     return atoms[2];
00424 }
00425 
00426 Atom atom_reload(void)
00427 {
00428     return atoms[3];
00429 }
00430 
00431 Atom atom_newdoc(void)
00432 {
00433     return atoms[4];
00434 }
00435 
00436 Atom atom_newpage(void)
00437 {
00438     return atoms[5];
00439 }
00440 
00441 Atom atom_raise(void)
00442 {
00443     return atoms[6];
00444 }
00445 
00446 Atom atom_find_string(void)
00447 {
00448     return atoms[7];
00449 }
00450 
00451 Atom atom_reread_prefs(void)
00452 {
00453     return atoms[8];
00454 }
00455 
00456 /*
00457  * Syntetisize a button press object for the `button' passed as
00458  * the second argument.
00459  */
00460 void
00461 synthetisize_event(XEvent *ev, Widget button)
00462 {
00463     memset(ev, 0, sizeof(XButtonPressedEvent));
00464     ev->type = ButtonPress;
00465     ev->xbutton.serial = 1;
00466     ev->xbutton.send_event = True;
00467     ev->xbutton.button = 1;
00468     ev->xbutton.display = XtDisplayOfObject(button);
00469     ev->xbutton.window = XtWindowOfObject(button);
00470 }
00471 
00472 #ifdef MOTIF
00473 int xm_get_height(Widget w)
00474 {
00475     int ret_h = 0;
00476     static Arg args = { XtNheight, (XtArgVal)0 };
00477     ASSERT(w != NULL, "widget in xm_get_width mustn't be NULL!");
00478 
00479     args.value = (XtArgVal)&ret_h;
00480     XtGetValues(w, &args, 1);
00481     TRACE_GUI((stderr, "xm_get_height: %d", ret_h));
00482     return ret_h;
00483 }
00484 
00485 int xm_get_width(Widget w)
00486 {
00487     int ret_w = 0;
00488     static Arg args = { XtNwidth, (XtArgVal)0 };
00489     ASSERT(w != NULL, "widget in xm_get_width mustn't be NULL!");
00490 
00491     args.value = (XtArgVal)&ret_w;
00492     XtGetValues(w, &args, 1);
00493     TRACE_GUI((stderr, "xm_get_width: %d", ret_w));
00494     return ret_w;
00495 }
00496 
00497 /*
00498  * Get pixel from color `colorname'. We try to keep this as
00499  * high-level as possible, with simple black as fallback.
00500 */
00501 void
00502 str_to_pixel(Widget w, const char *colorname, Pixel *ret)
00503 {
00504     XrmValue from, to;
00505 
00506     from.addr = (char *)colorname;
00507     from.size = strlen(from.addr) + 1;
00508     to.addr = (XtPointer)ret;
00509     to.size = sizeof(Pixel);
00510     if (!XtConvertAndStore(w, XtRString, &from, XtRPixel, &to)) {
00511        fprintf(stderr, "String to pixel conversion failed for %s!\n", colorname);
00512        from.addr = (char *)"black";
00513        from.size = strlen(from.addr) + 1;
00514        to.addr = (XtPointer)ret;
00515        to.size = sizeof(Pixel);
00516        XtConvertAndStore(w, XtRString, &from, XtRPixel, &to);
00517     }
00518 }
00519 
00520 /*
00521  * Convert pixel value `pix' to color name passed as str,
00522  * of length len, or `black' if conversion failed.
00523  * -- FIXME: This is broken!!!
00524 */
00525 void
00526 pixel_to_str(Widget w, Pixel pix, char *str, size_t len)
00527 {
00528     XrmValue from, to;
00529 
00530     from.addr = (XtPointer)&pix;
00531     from.size = sizeof(Pixel);
00532     to.addr = str;
00533     to.size = len;
00534 
00535     if (!XtConvertAndStore(w, XtRString, &from, XtRPixel, &to)) {
00536        fprintf(stderr, "Pixel to String conversion failed for %ld!\n", pix);
00537        sprintf(str, "white");
00538     }
00539 }
00540 
00541 /* Free color, and initialize it anew with pixel value `pix' */
00542 void
00543 pixel_to_color(Pixel pix, XColor *color, Display *display, Colormap colormap)
00544 {
00545     XColor test;
00546     test.pixel = pix;
00547     XQueryColor(display, colormap, &test);
00548 
00549     color->red = test.red;
00550     color->green = test.green;
00551     color->blue = test.blue;
00552     
00553     if (!XAllocColor(display, colormap, color)) {
00554        fprintf(stderr, "Fatal: Couldn't XAllocColor!");
00555        exit(1);
00556     }
00557 }
00558 #endif /* MOTIF */
00559 
00560 /* helper routine for get_matching_parent() */
00561 static Widget
00562 matches_parent(Widget w, const char *name)
00563 {
00564     for (; w != NULL; w = XtParent(w)) {
00565        char *ptr;
00566        ptr = XtName(w);
00567        TRACE_GUI((stderr, "parent: %s", ptr == NULL ? "<NULL>" : ptr));
00568        if (ptr != NULL && strcmp(ptr, name) == 0) { /* found */
00569            TRACE_GUI((stderr, "match!"));
00570            break;
00571        }
00572     }
00573     TRACE_GUI((stderr, "returning: %p (0x%lx)", (void *)w, w ? XtWindow(w) : 0));
00574     return w;
00575 }
00576 
00577 /* Traverse widget hieararchy upwards until a widget matches
00578    a name in the (NULL-terminated) list fmt, or Widget d (`default')
00579    if none is found.
00580 */
00581 Widget
00582 get_matching_parent(Widget w, Widget d, const char *fmt, ...)
00583 {
00584     Widget parent = d;
00585     const char *str = fmt;
00586 
00587     va_list argp;
00588     va_start(argp, fmt);
00589 
00590     TRACE_GUI((stderr, "get_matching_parent of %p (0x%lx)", (void *)w, XtWindow(w)));
00591     while (str != NULL) {
00592        Widget p;
00593        if ((p = matches_parent(w, str)) != NULL) {
00594            parent = p;
00595            break;
00596        }
00597        str = va_arg(argp, char *);
00598     }
00599        
00600     va_end(argp);
00601 
00602     return parent;
00603 }
00604 
00605 /* Return True if p is a parent of widget w, stopping (and returning FALSE)
00606    if s is reached (which should be the toplevel window of the hierarchy).
00607 */
00608 Boolean
00609 widget_is_parent(Widget w, Widget p, Widget s)
00610 {
00611     Widget curr = XtParent(w);
00612     while (curr != NULL && curr != s) {
00613        fprintf(stderr, "Comparing: %p - %p\n", (void *)curr, (void *)p);
00614        if (curr == p)
00615            return True;
00616        curr = XtParent(curr);
00617     }
00618     return False;
00619 }
00620 
00621 /*
00622   Adjust height in a NULL-terminated list of widgets. For Motif,
00623   this works better than the following adjust_vertically().
00624 */
00625 void
00626 adjust_heights(Widget w, ...)
00627 {
00628     va_list ap;
00629     Widget curr;
00630     Dimension h, max;
00631 
00632     ASSERT(w != NULL, "Must have at least one element in va_list for adjust_heights!");
00633 
00634 #if MOTIF
00635 #define HEIGHT XmNheight
00636 #else
00637 #define HEIGHT XtNheight
00638 #endif
00639     
00640     /* initialize maximum */
00641     XtVaGetValues(w, HEIGHT, &max, NULL);
00642 
00643     /* get maximum height */
00644     va_start(ap, w);
00645     while ((curr = va_arg(ap, Widget)) != NULL) {
00646        XtVaGetValues(curr, HEIGHT, &h, NULL);
00647        if (h > max)
00648            max = h;
00649     }
00650     va_end(ap);
00651 
00652     /* set maximum height for all widgets */
00653     XtVaSetValues(w, HEIGHT, max, NULL);
00654     
00655     va_start(ap, w);
00656     while ((curr = va_arg(ap, Widget)) != NULL)
00657        XtVaSetValues(curr, HEIGHT, max, NULL);
00658     va_end(ap);
00659 
00660 #undef HEIGHT
00661 
00662 }
00663 
00664 /*
00665   Adjust height in a NULL-terminated list of widgets. For Motif,
00666   this works better than the following adjust_vertically().
00667 */
00668 void
00669 adjust_heights_min(Widget w, ...)
00670 {
00671     va_list ap;
00672     Widget curr;
00673     Dimension h, min;
00674 
00675     ASSERT(w != NULL, "Must have at least one element in va_list for adjust_heights!");
00676 
00677 #if MOTIF
00678 #define HEIGHT XmNheight
00679 #else
00680 #define HEIGHT XtNheight
00681 #endif
00682     
00683     /* initialize minimum */
00684     XtVaGetValues(w, HEIGHT, &min, NULL);
00685 
00686     /* get minimum height */
00687     va_start(ap, w);
00688     while ((curr = va_arg(ap, Widget)) != NULL) {
00689        XtVaGetValues(curr, HEIGHT, &h, NULL);
00690        if (h < min)
00691            min = h;
00692     }
00693     va_end(ap);
00694 
00695     /* set maximum height for all widgets */
00696     XtVaSetValues(w, HEIGHT, min, NULL);
00697     
00698     va_start(ap, w);
00699     while ((curr = va_arg(ap, Widget)) != NULL)
00700        XtVaSetValues(curr, HEIGHT, min, NULL);
00701     va_end(ap);
00702 
00703 #undef HEIGHT
00704 
00705 }
00706 
00707 /* adjust two widgets vertically */
00708 void
00709 adjust_vertically(Widget w1, Widget w2, int default_dist)
00710 {
00711     Dimension h1, h2;
00712 #if MOTIF
00713     XtVaGetValues(w1, XmNheight, &h1, NULL);
00714     XtVaGetValues(w2, XmNheight, &h2, NULL);
00715     XtVaSetValues(w1, XmNtopOffset, default_dist + (h2 - h1) / 2, NULL);
00716     /*     XtVaSetValues(w2, XmNtopOffset, default_dist + (h2 - h1) / 2, NULL); */
00717 #else
00718     XtVaGetValues(w1, XtNheight, &h1, NULL);
00719     XtVaGetValues(w2, XtNheight, &h2, NULL);
00720     XtVaSetValues(w1, XtNvertDistance, default_dist + (h2 - h1) / 2, NULL);
00721     XtVaSetValues(w2, XtNvertDistance, default_dist + (h2 - h1) / 2, NULL);
00722 #endif /* MOTIF */
00723 }
00724 
00725 
00726 /*
00727  * This is a hack to block further processing of some events on widgets:
00728  * Add as an event handler for all mouse/key events for a specific widget.
00729  */
00730 void
00731 block_event_callback(Widget w, XtPointer client_data,
00732                    XEvent *ev, Boolean *cont)
00733 {
00734     UNUSED(w);
00735     UNUSED(client_data);
00736     UNUSED(ev);
00737     
00738     /* Don't propagate this event further down... */
00739     *cont = False;
00740 
00741     return;
00742 }
00743 
00744 
00745 /* Get a widget with `name' somewhere in the widget hierarchy below `parent'
00746    (matching is done against `*name') and return it in `ret'.
00747    If `report_error' is True, a warning message is popped up if the widget isn't found.
00748 */
00749 Boolean
00750 get_widget_by_name(Widget *ret, Widget parent, const char *name, Boolean report_error)
00751 {
00752     char buf[1024];
00753     Widget test;
00754 
00755 /*      if (parent == 0 || !XtIsManaged(parent)) { */
00756 /*     fprintf(stderr, "Widget %p not managed!\n", parent); */
00757 /*     return False; */
00758 /*      } */
00759     
00760     if (strlen(name) > 1023) {
00761        popup_message(globals.widgets.top_level,
00762                     MSG_ERR,
00763                     REPORT_XDVI_BUG_TEMPLATE,
00764                     "Widget name `%s' too long, couldn't get parent", name);
00765        return False;
00766     }
00767 
00768     buf[0] = '*'; /* add wildcard to also match paths */
00769     strcpy(buf + 1, name);
00770 
00771     if ((test = XtNameToWidget(parent, buf)) != NULL) {
00772        *ret = test;
00773        return True;
00774     }
00775     else {
00776        if (report_error)
00777            popup_message(globals.widgets.top_level,
00778                        MSG_ERR,
00779                        REPORT_XDVI_BUG_TEMPLATE,
00780                        "XtNameToWidget failed for `%s', parent `%s'", name, XtName(parent));
00781        return False;
00782     }
00783 }
00784 
00785 void
00786 unexpected_widget_in_callback(Widget w, const char *callback)
00787 {
00788     ASSERT(w != NULL, "Widget mustn't be NULL!");
00789     popup_message(globals.widgets.top_level,
00790                 MSG_ERR,
00791                 REPORT_XDVI_BUG_TEMPLATE,
00792                 "Unexpected widget `%s' in callback `%s'",
00793                 XtName(w), callback);
00794 }
00795 
00796 static XrmDatabase m_user_db = NULL;
00797 
00798 /*
00799  * Merge the resource `name' with a value specified by `fmt' into
00800  * the database `db' (if db == NULL, use m_user_db).
00801  */
00802 void
00803 store_preference(XrmDatabase *db, const char *name, const char *fmt, ...)
00804 {
00805     size_t offset = strlen("xdvi.");
00806     size_t name_len = strlen(name);
00807     char *name_buf = xmalloc(name_len + offset + 1);
00808     char *buf = NULL;
00809     XrmDatabase tmp_db = NULL;
00810     
00811     if (db == NULL)
00812        db = &m_user_db;
00813 
00814     XDVI_GET_STRING_ARGP(buf, fmt);
00815 
00816     memcpy(name_buf, "xdvi.", offset);
00817     strcpy(name_buf + offset, name);
00818 
00819     TRACE_GUI((stderr, "storing resource: `%s: %s'", name_buf, buf));
00820     XrmPutStringResource(&tmp_db, name_buf, buf);
00821     XrmMergeDatabases(tmp_db, db);
00822    
00823     free(buf);
00824     free(name_buf);
00825 }
00826 
00827 void
00828 merge_into_user_db(XrmDatabase db)
00829 {
00830     XrmMergeDatabases(db, &m_user_db);
00831 }
00832 
00833 const char *const xdvirc_filename = ".xdvirc";
00834 const char *const xdvirc_signature_line = "!!! ~/.xdvirc, used by xdvi(1) to save user preferences.\n";
00835 const char *const xdvirc_header = ""
00836        "!!!\n"
00837        "!!! Do not edit this file, it will be overwritten by xdvi.\n"
00838        "!!! This file contains resources that have been set via the\n"
00839        "!!! menus/dialogs. The contents of this file will override\n"
00840        "!!! the entries in your ~/.Xdefaults file (but not the command\n"
00841        "!!! line arguments passed to xdvi). Remove this file\n"
00842        "!!! if you want to get rid of all these customizations,\n"
00843         "!!! or start xdvi with the `-q' option to ignore this file.\n"
00844        "!!!\n";
00845 
00846 static char *
00847 get_xdvirc_path(const char *basename)
00848 {
00849     const char *homedir;
00850     size_t len;
00851     char *str;
00852 
00853     if (basename == NULL)
00854        return NULL;
00855 
00856     homedir = getenv("HOME");
00857     len = strlen(homedir) + strlen(basename) + 2; /* 1 more for '/' */
00858     str = xmalloc(len);
00859     
00860     sprintf(str, "%s/%s", homedir, basename);
00861     return str;
00862 }
00863 
00864 static void
00865 save_geometry(void)
00866 {
00867     int x_off, y_off;
00868     Dimension w, h;
00869     Window dummy;
00870 /*     char *geom_str; */
00871     
00872     (void)XTranslateCoordinates(DISP, XtWindow(globals.widgets.top_level),
00873                             RootWindowOfScreen(SCRN),
00874                             0, 0,
00875                             &x_off, &y_off,
00876                             &dummy);
00877     XtVaGetValues(globals.widgets.top_level,
00878                 XtNwidth, &w,
00879                 XtNheight, &h,
00880 #ifdef MOTIF
00881 /*              XmNgeometry, &geom_str, */
00882 #endif
00883                 NULL);
00884     TRACE_GUI((stderr, "geometry: %dx%d+%d+%d", w, h, x_off, y_off));
00885 
00886     store_preference(NULL, "windowSize", "%dx%d", w, h);
00887 }
00888 
00889 /* Save user preferences to ~/.xdvirc. If `backup_only' is True,
00890    it only writes to ~/.xdvirc.tmp and does not remove this temporary
00891    file (this is used for synchronization between several xdvi instances).
00892 */
00893 Boolean
00894 save_user_preferences(Boolean full_save)
00895 {
00896     char testbuf[1024];
00897     char *xdvirc_name;
00898     char *tmpname;
00899     FILE *from_fp, *to_fp;
00900     int fd;
00901 
00902     if (resource.no_init_file
00903        || m_user_db == NULL) /* nothing to do */
00904        return True;
00905     
00906     if (resource.remember_windowsize)
00907        save_geometry();
00908     
00909     xdvirc_name = get_xdvirc_path(xdvirc_filename);
00910 
00911     if ((to_fp = fopen(xdvirc_name, "r")) != NULL) {
00912        TRACE_GUI((stderr, "~/.xdvirc exists, checking file contents ..."));
00913        if (fgets(testbuf, 1024, to_fp) != NULL &&
00914            memcmp(testbuf, xdvirc_signature_line, strlen(xdvirc_signature_line)) != 0) {
00915            popup_message(globals.widgets.top_level,
00916                        MSG_WARN,
00917                        "Xdvi uses the file ~/.xdvirc to save the preferences set via "
00918                        "the menu bar or the preferences dialog (in the Motif version only). "
00919                        "To avoid overwriting files created by the user, the first line of the "
00920                        "file is compared with a special signature line. If that signature line "
00921                        "is not found, the preferences won't be written. Your file doesn't seem "
00922                        "to contain that signature line. You should move the file to a safe location, "
00923                        "and then try to quit xdvi again.",
00924                        /* message */
00925                        "The file `%s' was apparently not written by xdvi(k). "
00926                        "Please move or delete this file first, then try to exit xdvi again. ",
00927                        xdvirc_name);
00928            return False;
00929        }
00930        fclose(to_fp);
00931     }
00932 
00933     /* don't use xdvi_temp_fd here, since XrmPutFileDatabase()
00934        closes the FILE*, creating a temp race */
00935     tmpname = xstrdup(xdvirc_name);
00936     tmpname = xstrcat(tmpname, ".tmp");
00937 
00938     /* since XrmPutFileDatabase doesn't give a useful error message if it fails,
00939        check that creating the file works beforehand. The file is created with 0600 permissions. */
00940     if ((fd = try_open_mode(tmpname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
00941        XDVI_ERROR((stderr, "Could not save preferences!\nOpening %s for writing failed: %s", tmpname, strerror(errno)));
00942        return True;
00943     }
00944     close(fd);
00945     
00946     XrmPutFileDatabase(m_user_db, tmpname);
00947 
00948     if (full_save) {
00949        if ((from_fp = try_fopen(tmpname, "r")) == NULL) {
00950            XDVI_ERROR((stderr, "Could not save preferences!\nOpening %s for reading failed: %s", tmpname, strerror(errno)));
00951            return True;
00952        }
00953        
00954        /* again, create the file with 600 permissions */
00955        if ((fd = try_open_mode(xdvirc_name, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) {
00956            XDVI_ERROR((stderr, "Could not save preferences!\nOpening %s for writing failed: %s", xdvirc_name, strerror(errno)));
00957            return True;
00958        }
00959        
00960        if ((to_fp = fdopen(fd, "w")) == NULL) {
00961            XDVI_ERROR((stderr, "Could not save preferences!\nfdopen for %s for writing failed: %s", xdvirc_name, strerror(errno)));
00962            return True;
00963        }
00964        
00965        if (fputs(xdvirc_signature_line, to_fp) == EOF
00966            || fputs(xdvirc_header, to_fp) == EOF
00967            || !copy_fp(from_fp, to_fp)) {
00968            XDVI_ERROR((stderr, "Could not save preferences!\nError writing to %s: %s", xdvirc_name, strerror(errno)));
00969        }
00970 
00971        fclose(from_fp);
00972        fclose(to_fp);
00973     }
00974     
00975     free(xdvirc_name);
00976 
00977     if (full_save)
00978        unlink(tmpname);
00979     free(tmpname);
00980 
00981     return True;
00982 }
00983 
00984 /*
00985  * Read the user preferences from xdvirc_filename and merge them into the
00986  * current resource database *and* into m_user_db so that all of them are
00987  * saved again when xdvi exits.
00988  */
00989 void
00990 read_user_preferences(Widget toplevel, const char *filename)
00991 {
00992     char *fullpath;
00993     XrmDatabase db;
00994 #if XtSpecificationRelease == 4
00995     XrmDatabase file_db;
00996 #endif
00997 
00998     fullpath = get_xdvirc_path(filename);
00999     TRACE_GUI((stderr, "Reading resources from `%s'", fullpath));
01000     db = XtDatabase(XtDisplay(toplevel));
01001     
01002 #if XtSpecificationRelease == 4
01003     file_db  = XrmGetFileDatabase(fullpath);
01004     XrmMergeDatabases(file_db, &db);
01005     XrmMergeDatabases(file_db, &m_user_db);
01006 #else /* Xt >= X11R5 */
01007     XrmCombineFileDatabase(fullpath, &db, True);
01008     XrmCombineFileDatabase(fullpath, &m_user_db, True);
01009 #endif
01010     free(fullpath);
01011 }
01012 
01013 
01014 /*
01015  *     Routines for running as source-special client.
01016  */
01017 
01018 static unsigned long xdvi_next_request = 0;
01019 static int xerrno;
01020 static int (*XdviOldErrorHandler)(Display *, XErrorEvent *);
01021 
01022 static int
01023 XdviErrorHandler(Display *d, XErrorEvent *event)
01024 {
01025     if (event->serial != xdvi_next_request || event->error_code != BadWindow)
01026        return XdviOldErrorHandler(d, event);
01027 
01028     xerrno = 1;
01029     return 0;
01030 }
01031 
01032 static int
01033 XdviGetWindowProperty(Display *display,
01034                     Window w,
01035                     Atom property,
01036                     long long_offset,
01037                     long long_length,
01038                     Bool delete,
01039                     Atom req_type,
01040                     Atom *actual_type_return,
01041                     int *actual_format_return,
01042                     unsigned long *nitems_return,
01043                     unsigned long *bytes_after_return,
01044                     unsigned char **prop_return)
01045 {
01046     int retval;
01047 
01048     xdvi_next_request = NextRequest(display);
01049     xerrno = 0;
01050 
01051     retval = XGetWindowProperty(display, w, property, long_offset,
01052                             long_length, delete, req_type,
01053                             actual_type_return, actual_format_return,
01054                             nitems_return, bytes_after_return, prop_return);
01055 
01056     return (xerrno != 0 ? BadWindow : retval);
01057 }
01058 
01059 /* helper function to set a string property of type `prop' for window `win' */
01060 void
01061 set_string_property(const char *str, Atom prop, Window win)
01062 {
01063     XChangeProperty(DISP, win, prop, prop, 8, PropModeReplace,
01064                   (const unsigned char *)str, strlen(str));
01065     XFlush(DISP);    /* necessary to get the property set */
01066 }
01067 
01068 /*
01069  * Check for another running copy of xdvi. If same_file is true, return
01070  * the window ID of that other instance only if it has currently loaded the
01071  * same file; else, return 0.
01072  * If same_file is false, return the first valid xdvi window ID.
01073  */
01074 Window
01075 get_xdvi_window_id(Boolean same_file, property_cbT callback)
01076 {
01077     char *window_list;
01078     size_t window_list_len;
01079     char *window_list_end;
01080     char *wp;
01081     char *p;
01082     Boolean need_rewrite = False;
01083     Window ret_window = 0;
01084 
01085     /*
01086      * Get window list.  Copy it over (we'll be calling property_get_data()
01087      * again).
01088      */
01089     if ((window_list_len = property_get_window_list(&p)) == 0)
01090        return 0;
01091 
01092     window_list = xmalloc(window_list_len);
01093     memcpy(window_list, p, window_list_len);
01094 
01095     XdviOldErrorHandler = XSetErrorHandler(XdviErrorHandler);
01096 
01097     /* Loop over list of windows.  */
01098 
01099     window_list_end = window_list + window_list_len;
01100     TRACE_CLIENT((stderr, "My property: `%s'", dvi_property));
01101 
01102     for (wp = window_list; wp < window_list_end; wp += 4) {
01103        Window w;
01104        char *buf_ret;
01105        size_t len;
01106 
01107        w = get_window_id(wp);
01108 
01109        TRACE_CLIENT((stderr, "Checking window %08lx", w));
01110        
01111        len = property_get_data(w, atom_dvi_file(), &buf_ret,
01112                             XdviGetWindowProperty);
01113 
01114        if (len == 0) {
01115            /* not getting back info for a window usually indicates
01116               that the application the window had belonged to had
01117               been killed with signal 9
01118            */
01119            TRACE_CLIENT((stderr, "Window %08lx: doesn't exist any more, deleting", w));
01120            window_list_len -= 4;
01121            window_list_end -= 4;
01122            memmove(wp, wp + 4, window_list_end - wp);
01123            wp -= 4; /* new item is now at wp; don't skip it in next iteration */
01124            need_rewrite = True;
01125            continue;
01126        }
01127        else { /* window still alive */
01128            if (globals.debug & DBG_CLIENT) {
01129 #if 0
01130               unsigned long ino;
01131               int i;
01132               
01133               ino = 0;
01134               for (i = 7; i >= 0; --i)
01135                   ino = (ino << 8) | (unsigned char)(buf_ret[i]);
01136 #endif
01137               TRACE_CLIENT((stderr, "Window %08lx: property: `%s'", w, buf_ret));
01138            }
01139 
01140            /* invoke callback if given */
01141            if (callback != NULL) {
01142               callback(w);
01143            }
01144            
01145            if (!same_file && ret_window == 0) {
01146               ret_window = w;
01147               if (callback == 0) /* can return early */
01148                   break;
01149            }
01150            else if (strcmp(buf_ret, dvi_property) == 0 && ret_window == 0) { /* match */
01151               ret_window = w;
01152               if (callback == 0) /* can return early */
01153                   break;
01154            }
01155        }
01156     }
01157 
01158     XSetErrorHandler(XdviOldErrorHandler);
01159 
01160     if (need_rewrite)
01161        XChangeProperty(DISP, DefaultRootWindow(DISP),
01162                      atom_xdvi_windows(), atom_xdvi_windows(), 32,
01163                      PropModeReplace, (unsigned char *)window_list,
01164                      window_list_len / 4);
01165 
01166     return ret_window;
01167 }
01168 
01169 Boolean
01170 clip_region(int *x, int *y, int *w, int *h)
01171 {
01172 #if 0
01173     fprintf(stderr, "globals.win_expose.min_x: %d, globals.win_expose.max_x: %d, "
01174            "globals.win_expose.min_y: %d, globals.win_expose.max_y: %d\n",
01175            globals.win_expose.min_x, globals.win_expose.max_x,
01176            globals.win_expose.min_y, globals.win_expose.max_y);
01177 #endif
01178     /* check for <= so that we also return false if *w or *h == 0 */
01179     if (*x + *w <= globals.win_expose.min_x
01180        || *x >= globals.win_expose.max_x
01181        || *y + *h <= globals.win_expose.min_y
01182        || *y >= globals.win_expose.max_y
01183        /* extra protection agains *w or *h == 0; don't know if this can actually happen ... */
01184        || globals.win_expose.max_y == globals.win_expose.min_y
01185        || globals.win_expose.max_x == globals.win_expose.min_x) {
01186        return False;
01187     }
01188     if (*x < globals.win_expose.min_x) {
01189        *w -= globals.win_expose.min_x - *x;
01190        *x = globals.win_expose.min_x;
01191     }
01192     if (*x + *w > globals.win_expose.max_x) {
01193        *w = globals.win_expose.max_x - *x;
01194     }
01195     if (*y < globals.win_expose.min_y) {
01196        *h -= globals.win_expose.min_y - *y;
01197        *y = globals.win_expose.min_y;
01198     }
01199     if (*y + *h > globals.win_expose.max_y) {
01200        *h = globals.win_expose.max_y - *y;
01201     }
01202     return True;
01203 }
01204 
01205 Boolean
01206 clip_region_to_rect(XRectangle *rect)
01207 {
01208     int x = rect->x;
01209     int y = rect->y;
01210     int w = rect->width;
01211     int h = rect->height;
01212     Boolean ret = clip_region(&x, &y, &w, &h);
01213     if (ret) {
01214        rect->x = x;
01215        rect->y = y;
01216        rect->width = w;
01217        rect->height = h;
01218     }
01219     return ret;
01220 }
01221