Back to index

tetex-bin  3.0
xaw_menu.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2001-2004 the xdvik development team
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  */
00023 
00024 /*
00025  * Menu bar implementation for the Athena widget set.
00026  */
00027 #include "xdvi-config.h"
00028 #include "xdvi.h"
00029 
00030 #include "c-openmx.h"
00031 #include "events.h"
00032 #include "dvi-draw.h"
00033 #include "dvi-init.h"
00034 #include "statusline.h"
00035 #include "pagesel.h"
00036 #include "util.h"
00037 #include "x_util.h"
00038 #include "xaw_menu.h"
00039 #include "message-window.h"
00040 #include "my-snprintf.h"
00041 #include "filehist.h"
00042 #ifdef NEW_MENU_CREATION
00043 #include "menu.h"
00044 #endif /* NEW_MENU_CREATION */
00045 
00046 #ifndef MOTIF /* entire file */
00047 
00048 #include <ctype.h>
00049 
00050 #include <X11/Intrinsic.h>
00051 #include <X11/Xatom.h>
00052 #include <X11/StringDefs.h>
00053 #include <X11/Shell.h>      /* needed for def. of XtNiconX */
00054 
00055 #include <X11/Xaw/Viewport.h>
00056 #include <X11/Xaw/SimpleMenu.h>
00057 #include <X11/Xaw/MenuButton.h>
00058 #include <X11/Xaw/Sme.h>
00059 #include <X11/Xaw/SmeBSB.h>
00060 #include <X11/Xaw/SmeLine.h>
00061 #include <X11/Xaw/Dialog.h>
00062 #include <X11/Xaw/Text.h>
00063 #include <X11/Xaw/Panner.h> 
00064 #include <X11/Xaw/Porthole.h>      
00065 #include <X11/Xaw/Command.h>
00066 
00067 #ifndef       MAX
00068 # define MAX(i, j)       ( (i) > (j) ? (i) : (j) )
00069 #endif
00070 
00071 
00072 #ifndef NEW_MENU_CREATION
00073 #define SEP_CHAR ':' /* character separating entries in translations lines */
00074 #endif
00075 
00076 /* needed to count the callbacks */
00077 static int destroy_count = 0;
00078 
00079 /* width of button panel */
00080 static int my_panel_width = 0;
00081 
00082 /* access method for panel width */
00083 int
00084 get_panel_width(void)
00085 {
00086     int retval = 0;
00087     if (resource.expert_mode & XPRT_SHOW_BUTTONS)
00088        retval = my_panel_width;
00089     TRACE_GUI((stderr, "get_panel_width: %d", retval));
00090     return retval;
00091 }
00092 
00093 #ifndef NEW_MENU_CREATION
00094 typedef enum { INVALID, /* some error */
00095               NONE,  /* no new button created */
00096               MENU_SEPARATOR, MENU_BUTTON, MENU_ENTRY, COMMAND_BUTTON } buttonTypeT;
00097 
00098 struct button_info {
00099     struct button_info *next;
00100     char *label;
00101     struct xdvi_action *action;
00102     Widget widget;
00103     buttonTypeT type;
00104 };
00105 
00106 static Widget panner = 0;
00107 static struct button_info *b_head = NULL;
00108 
00109 #define MAX_MENU_NUM 128
00110 #define MAX_BUTTON_NUM 128
00111 
00112 static struct menu_list_ {
00113     char *name;
00114     Widget button_widget;
00115     Widget popup_widget;
00116 } menu_list[MAX_MENU_NUM];
00117 
00118 #endif /* not NEW_MENU_CREATION */
00119 
00120 #ifdef NEW_MENU_CREATION
00121 /*
00122   ================================================================================
00123   Pixmaps indicating the state of menu buttons (radiobutton/checkbox
00124   on/off, cascading menu). Inspired by menu.c, `check_bits' in the xterm source.
00125   ================================================================================
00126 */
00127 #include "xaw_bitmaps.h"
00128 static Pixmap menu_check_on_bitmap;
00129 static Pixmap menu_check_off_bitmap;
00130 static Pixmap menu_radio_on_bitmap;
00131 static Pixmap menu_radio_off_bitmap;
00132 static Pixmap menu_arrow_bitmap;
00133 #else
00134 #include "xaw_bitmaps.h"
00135 /* lifted from menu.c, check_bits in xterm */
00136 static Pixmap menu_check_bitmap;
00137 static Pixmap menu_arrow_bitmap;
00138 #endif /* NEW_MENU_CREATION */
00139 
00140 /*
00141   ============================================================
00142   Hack for pullright menus part I: data
00143   ============================================================
00144 */
00145 
00146 /* There are a few custom widgets for pullright menus out there, but
00147  * these are old and potentially buggy, so just do it manually via an
00148  * event handler, similar to Motif tooltips.
00149  */
00150 static XtIntervalId m_timeout = 0;
00151 static Widget m_active_submenu = NULL;  /* if not NULL, the currently active pullright */
00152 static Widget m_submenu = NULL;           /* parent of the currently active pullright
00153                                       (i.e. the menu label in the parent window) */
00154 
00155 static void ActPopdownSubmenus(Widget w, XEvent *event, String *params, Cardinal *num_params);
00156 
00157 /* to safely pop down the pullright, this callback is added to its parent menu */
00158 static XtActionsRec menu_actions[] = {
00159     { "popdown-submenus", ActPopdownSubmenus }
00160 };
00161 
00162 static XtAccelerators menu_accels;
00163 
00164 struct pullright_position_info {
00165     Position y;
00166     Dimension w;
00167     Dimension h;
00168     Dimension border_width;
00169     Widget menu;
00170 };
00171 
00172 
00173 #ifdef NEW_MENU_CREATION
00174 /*
00175  * Set all pixmaps indicating the state of the wigdet pointed to by `elems'.
00176  */
00177 void
00178 xaw_set_button_state(struct button_elems *elems, Boolean on)
00179 {
00180     static Arg args[] = {
00181        { XtNleftBitmap, (XtArgVal)0  },
00182        { XtNrightBitmap, (XtArgVal)0 }
00183     };
00184 
00185     if (elems->type == BT_CHECK)
00186        args[0].value = on ? menu_check_on_bitmap : menu_check_off_bitmap;
00187     else if (elems->type == BT_RADIO)
00188        args[0].value = on ? menu_radio_on_bitmap : menu_radio_off_bitmap;
00189     if (elems->submenu != NULL)
00190        args[1].value = menu_arrow_bitmap;
00191 
00192     XtSetValues(elems->widget, args, XtNumber(args));
00193 }
00194 
00195 /*
00196  * Initialize the bitmaps.
00197  */
00198 void
00199 xaw_initialize_menu_bitmaps(void)
00200 {
00201     static Boolean initialized = False;
00202     if (!initialized) {
00203        initialized = True;
00204        menu_check_on_bitmap
00205            = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level),
00206                                 RootWindowOfScreen(XtScreen(globals.widgets.top_level)),
00207                                 (char *)menu_check_on_bits, MENU_BITMAP_W, MENU_BITMAP_H);
00208        menu_check_off_bitmap
00209            = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level),
00210                                 RootWindowOfScreen(XtScreen(globals.widgets.top_level)),
00211                                 (char *)menu_check_off_bits, MENU_BITMAP_W, MENU_BITMAP_H);
00212        menu_radio_on_bitmap
00213            = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level),
00214                                 RootWindowOfScreen(XtScreen(globals.widgets.top_level)),
00215                                 (char *)menu_radio_on_bits, MENU_BITMAP_W, MENU_BITMAP_H);
00216        menu_radio_off_bitmap
00217            = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level),
00218                                 RootWindowOfScreen(XtScreen(globals.widgets.top_level)),
00219                                 (char *)menu_radio_off_bits, MENU_BITMAP_W, MENU_BITMAP_H);
00220        menu_arrow_bitmap
00221            = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level),
00222                                 RootWindowOfScreen(XtScreen(globals.widgets.top_level)),
00223                                 (char *)menu_arrow_bits, MENU_ARROW_W, MENU_ARROW_H);
00224     }
00225 }
00226 
00227 #else
00228 
00229 /* toggle `checked' symbol on xaw menu w */
00230 void
00231 toggle_tick(Boolean val, Widget w)
00232 {
00233     static Arg args = { XtNleftBitmap, (XtArgVal) 0 };
00234     if (val)
00235        args.value = (XtArgVal) menu_check_bitmap;
00236     else
00237        args.value = None;
00238     XtSetValues(w, &args, 1);
00239 }
00240 
00241 void
00242 toggle_menu(int val, XtActionProc proc)
00243 {
00244     struct button_info *bp;
00245 
00246     for (bp = b_head; bp != NULL; bp = bp->next) {
00247        if (bp->action != NULL && bp->action->proc == proc && bp->action->param != NULL) {
00248            TRACE_GUI((stderr, "found proc; param: |%s|", bp->action->param));
00249            if (strcmp(bp->action->param, "toggle") == 0) {
00250               if (val != 0)
00251                   toggle_tick(True, bp->widget);
00252               else
00253                   toggle_tick(False, bp->widget);
00254            }
00255            else if (strcmp(bp->action->param, "a") == 0) {
00256               if (val == shrink_to_fit())
00257                   toggle_tick(True, bp->widget);
00258               else
00259                   toggle_tick(False, bp->widget);
00260            }
00261            else {
00262               int testval = strtoul(bp->action->param, (char **)NULL, 10);
00263               if (testval == val) {
00264                   TRACE_GUI((stderr, "enabling param |%s|", bp->action->param));
00265                   toggle_tick(True, bp->widget);
00266               }
00267               else
00268                   toggle_tick(False, bp->widget);
00269            }
00270        }
00271     }
00272 }
00273 
00274 /* initialize `checked' symbol on xaw menu w */
00275 static void
00276 initialize_tick_marks(void)
00277 {
00278     int use_gs;
00279     if (menu_check_bitmap == None) {
00280        int check_width = 9;
00281        int check_height = 8;
00282        unsigned char check_bits[] = {
00283            0x00, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00,
00284            0x31, 0x00, 0x1b, 0x00, 0x0e, 0x00, 0x04, 0x00
00285        };
00286 
00287        menu_check_bitmap = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level),
00288                                             RootWindowOfScreen(XtScreen(globals.widgets.top_level)),
00289                                             (char *)check_bits, check_width, check_height);
00290     }
00291     
00292     /* initialize tickmarks for all possible actions */
00293      use_gs = resource.postscript;
00294 #ifdef PS_GS
00295     if (!resource.useGS)
00296        use_gs = 0;
00297 #endif
00298 
00299     toggle_menu(use_gs, Act_set_ps);
00300     toggle_menu(resource.gs_alpha, Act_set_gs_alpha);
00301     toggle_menu(resource.keep_flag, Act_set_keep_flag);
00302     toggle_menu(resource.pixels_per_inch / mane.shrinkfactor, Act_shrink_to_dpi);
00303     toggle_menu(mane.shrinkfactor, Act_set_shrink_factor);
00304     toggle_menu(resource.use_tex_pages, Act_use_tex_pages);
00305     toggle_menu(resource.mouse_mode, Act_switch_mode);
00306 }
00307 #endif /* NEW_MENU_CREATION */
00308 
00309 /* ================================================================================
00310    ================================================================================
00311 */
00312 
00313 static Widget line_widget, panel_widget;
00314 
00315 /* used for communication with the pagelist in xaw_create_pagelist */
00316 static int my_y_pos;
00317 
00318 /* access function: panel, height of buttons etc. */
00319 void
00320 xaw_create_pagelist(void)
00321 {
00322     Dimension height, width;
00323     int button_width = get_panel_width() - 2 * (resource.btn_side_spacing + resource.btn_border_width);
00324     
00325     XtVaGetValues(globals.widgets.clip_widget, XtNheight, &height, NULL);
00326     width = MAX(button_width, xaw_get_pagelist_size());
00327     height -= resource.btn_top_spacing + resource.btn_border_width + my_y_pos;
00328     xaw_create_pagelist_widgets(height, width, my_y_pos, panel_widget);
00329 }
00330 
00331 
00332 static XtCallbackRec command_call[] = {
00333     {handle_command, NULL},
00334     {NULL, NULL},
00335 };
00336 
00337 #ifdef NEW_MENU_CREATION
00338 void xaw_create_menu(struct button_info *items, Widget parent, int menu_depth, int *ret_width)
00339 {
00340     
00341 }
00342 
00343 Widget
00344 xaw_create_menu_widgets(Widget parent)
00345 {
00346     /* hack to disable the magnifier in the panel: */
00347     XtTranslations panel_translations = XtParseTranslationTable("#augment <ButtonPress>:");
00348     
00349     line_widget = XtVaCreateWidget("line", widgetClass, parent,
00350                                XtNbackground, (XtArgVal)resource.fore_Pixel,
00351                                XtNwidth, (XtArgVal)1,
00352                                XtNfromHoriz, (XtArgVal)globals.widgets.vport_widget,
00353                                XtNborderWidth, (XtArgVal)0,
00354                                XtNtop, (XtArgVal)XtChainTop,
00355                                XtNbottom, (XtArgVal)XtChainBottom,
00356                                XtNleft, (XtArgVal)XtChainRight,
00357                                XtNright, (XtArgVal)XtChainRight,
00358                                NULL);
00359     panel_widget = XtVaCreateWidget("panel", compositeWidgetClass, parent,
00360                                 XtNborderWidth, (XtArgVal)0,
00361                                 XtNfromHoriz, (XtArgVal)line_widget,
00362                                 XtNtranslations, (XtArgVal)panel_translations,
00363                                 XtNtop, (XtArgVal)XtChainTop,
00364                                 XtNbottom, (XtArgVal)XtChainBottom,
00365                                 XtNleft, (XtArgVal)XtChainRight,
00366                                 XtNright, (XtArgVal)XtChainRight,
00367                                 NULL);
00368     return panel_widget;
00369 }
00370 
00371 #else /* NEW_MENU_CREATION */
00372 
00373 
00374 static void
00375 filehist_select_cb(Widget w, XtPointer client_data, XtPointer call_data)
00376 {
00377     char *label, *ptr;
00378     int idx;
00379 
00380     UNUSED(client_data);
00381     UNUSED(call_data);
00382     
00383     XtVaGetValues(w, XtNlabel, &label, NULL);
00384 
00385     idx = strtol(label, &ptr, 10) - 1;
00386     while (isspace(*ptr))
00387        ptr++;
00388     TRACE_GUI((stderr, "User selected: %d, `%s'", idx, ptr));
00389     if (idx == 0) {
00390        globals.ev.flags |= EV_RELOAD;
00391        return;
00392     }
00393     file_history_open(ptr);
00394 }
00395 
00396 static void
00397 update_menu_labels(Widget menu)
00398 {
00399     WidgetList children;
00400     int num_children;
00401     int i;
00402 
00403     static char *buf = NULL;
00404     static size_t buf_len = 0;
00405     size_t new_len;
00406     
00407     XtVaGetValues(menu,
00408                 XtNnumChildren, &num_children,
00409                 XtNchildren, &children,
00410                 NULL);
00411     for (i = 0; i < (int)file_history_size(); i++) {
00412        int dummy_page;
00413        char *filename;
00414        
00415        if ((filename = file_history_get_elem(i, &dummy_page)) == NULL) {
00416            XDVI_ERROR((stderr, "Error accessing element %d of file history", i));
00417            continue;
00418        }
00419 
00420        new_len = LENGTH_OF_INT + strlen(filename) + 1;
00421        if (new_len > buf_len) {
00422            buf = xrealloc(buf, new_len);
00423            buf_len = new_len;
00424        }
00425        
00426        sprintf(buf, "%d %s", i + 1, filename);
00427        XtVaSetValues(children[i], XtNlabel, buf, NULL);
00428        TRACE_GUI((stderr, "child %d: `%s'", i, buf));
00429     }
00430 
00431     /* if history size < number of menu entries, destroy excess menu entries */
00432     for (; i < num_children; i++) {
00433        XtDestroyWidget(children[i]);
00434     }
00435 }
00436 
00437 void
00438 filehist_menu_add_entry(const char *filename)
00439 {
00440     static char *buf = NULL;
00441     static size_t buf_len = 0;
00442     size_t new_len = LENGTH_OF_INT + strlen(filename) + 1;
00443     
00444     Widget menu;
00445     /* Don't report an error here, since "filehist_pullright" is only created on-demand
00446        when user clicks on menu, but this may be called before from the event loop.
00447        (The menu will still contain this entry when it's created later.) */
00448     if (get_widget_by_name(&menu, globals.widgets.top_level, "filehist_pullright", False)) {
00449        int num_children;
00450        Widget w;
00451 
00452        if (new_len > buf_len) {
00453            buf = xrealloc(buf, new_len);
00454            buf_len = new_len;
00455        }
00456        
00457        XtVaGetValues(menu, XtNnumChildren, &num_children, NULL);
00458        sprintf(buf, "%d %s", num_children + 1, filename);
00459        
00460        w = XtVaCreateManagedWidget("_filehist", smeBSBObjectClass, menu,
00461                                 XtNlabel, buf,
00462                                 XtNleftMargin, 10,
00463                                 NULL);
00464        XtAddCallback(w, XtNcallback, filehist_select_cb, NULL);
00465        update_menu_labels(menu);
00466     }
00467 }
00468 
00469 void
00470 filehist_menu_refresh(void)
00471 {
00472     Widget menu;
00473 
00474     /* Don't report an error here, since "filehist_pullright" is only created on-demand
00475        when user clicks on menu, but this may be called before from the event loop.
00476        (The menu will still contain this entry when it's created later.) */
00477     if (get_widget_by_name(&menu, globals.widgets.top_level, "filehist_pullright", False)) {
00478        update_menu_labels(menu);
00479     }
00480 }
00481 
00482 
00483 static void
00484 filehist_insert_submenu(int idx, const char *filename, int pageno, void *data)
00485 {
00486     Widget menu = (Widget)data;
00487     Widget w;
00488     static char *buf = NULL;
00489     static size_t buf_len = 0;
00490     size_t new_len = LENGTH_OF_INT + strlen(filename) + 1;
00491     
00492     UNUSED(pageno);
00493 
00494     if (new_len > buf_len) {
00495        buf = xrealloc(buf, new_len);
00496        buf_len = new_len;
00497     }
00498 
00499     sprintf(buf, "%d %s", idx + 1, filename);
00500     TRACE_GUI((stderr, "Creating menu `%s'", buf));
00501     w = XtVaCreateManagedWidget("_filehist", smeBSBObjectClass, menu,
00502                             XtNlabel, buf,
00503                             XtNleftMargin, 10,
00504                             NULL);
00505     XtAddCallback(w, XtNcallback, filehist_select_cb, NULL);
00506 }
00507 
00508 
00509 /*
00510   ============================================================
00511   Hack for pullright menus part II: callbacks and functions
00512   ============================================================
00513 */
00514 
00515 /* callback to pop down the currently active pullright */
00516 static void
00517 ActPopdownSubmenus(Widget w, XEvent *event, String *params, Cardinal *num_params)
00518 {
00519     UNUSED(w);
00520     UNUSED(event);
00521     UNUSED(params);
00522     UNUSED(num_params);
00523     
00524     if (m_timeout != 0)
00525        XtRemoveTimeOut(m_timeout);
00526     m_timeout = 0;
00527     if (m_active_submenu != NULL)
00528        XtPopdown(m_active_submenu);
00529 }
00530 /* create a parent shell for the pullright menu entries */
00531 static Widget
00532 create_files_submenu(void)
00533 {
00534     Widget popup = XtCreatePopupShell("filehist_pullright", simpleMenuWidgetClass, globals.widgets.top_level,
00535                                   NULL, 0);
00536     file_history_enumerate(filehist_insert_submenu, popup);
00537     return popup;
00538 }
00539 
00540 /* Acutally pop up the pullright menu */
00541 static void 
00542 popup_pullright(XtPointer client_data, XtIntervalId *id)
00543 {
00544     int pos_x, pos_y;
00545     Dimension w1;
00546     Window dummy;
00547     static Widget files_submenu = NULL;
00548     struct pullright_position_info *info = (struct pullright_position_info *)client_data;
00549     
00550     UNUSED(id);
00551 
00552     if (files_submenu == NULL)
00553        files_submenu = create_files_submenu();
00554     /*                   XtManageChild(files_submenu); */
00555     XTranslateCoordinates(DISP, XtWindow(XtParent(m_submenu)), RootWindowOfScreen(SCRN),
00556                        info->w, info->y, &pos_x, &pos_y, &dummy);
00557     XtRealizeWidget(files_submenu);
00558     XtVaGetValues(files_submenu, XtNwidth, &w1, NULL);
00559     TRACE_GUI((stderr, "Popping up at %d, %d, %d, %d", pos_x, pos_y, w1, WidthOfScreen(SCRN)));
00560     
00561     /* if not sufficient place on the right, pop it up on the left */
00562     /*  fprintf(stderr, "border_width: %d\n", info->border_width); */
00563     if (pos_x + w1 > WidthOfScreen(SCRN)) {
00564        /*  fprintf(stderr, "%d > %d!\n", pos_x + w1, WidthOfScreen(SCRN)); */
00565        pos_x -= (w1 + info->w + 3 * info->border_width);
00566        /*  fprintf(stderr, "new x: %d\n", pos_x); */
00567     }
00568     else {
00569        pos_x += info->border_width;
00570     }
00571     XtVaSetValues(files_submenu,
00572                 XtNx, pos_x,
00573                 XtNy, pos_y,
00574                 NULL);
00575     /* use XtPopupSpringLoaded() instead of XtPopup() since it does a few things
00576        that make the pullright behave like a proper menu, like highlighting the
00577        current selection, setting the cursor shape etc. */
00578     XtPopupSpringLoaded(files_submenu);
00579     m_active_submenu = files_submenu;
00580 }
00581 
00582 /*
00583  * This event handler (to be called by read_events(), the main event handling loop)
00584  * creates a timeout for the pullright to pop up.
00585 */
00586 void
00587 SubMenuHandleEvent(XtAppContext app, XEvent *event)
00588 {
00589     static int flag = 0;
00590     static struct pullright_position_info info = { -1, 0, 0, 0, NULL };
00591     
00592     UNUSED(app);
00593 
00594     if (m_submenu == NULL)
00595        return;
00596     
00597     if (event->type == EnterNotify
00598        || event->type == MotionNotify
00599        || event->type == LeaveNotify
00600        || event->type == ButtonPress) {
00601 
00602 /*     fprintf(stderr, "SubMenuHandleEvent: 0x%lx, 0x%lx\n", event->xany.window, XtWindow(m_submenu)); */
00603        
00604        /* Could also loop through a list of windows here ...
00605           We need to check for the parent of the menu item, since smeBSBObject is not
00606           a real window, and then manually check whether the pointer is inside the menu
00607           item.
00608         */
00609        if (XtParent(m_submenu) != NULL &&
00610            event->xany.window == XtWindow(XtParent(m_submenu))) {
00611            /* don't need to check for x coordinates since we already
00612               know that pointer is inside the parent */
00613            if (info.y == -1) { /* initialize info */
00614               XtVaGetValues(m_submenu,
00615                            XtNy, &(info.y),
00616                            XtNwidth, &(info.w),
00617                            XtNheight, &(info.h),
00618                            NULL);
00619               XtVaGetValues(XtParent(m_submenu),
00620                            XtNborderWidth, &(info.border_width),
00621                            NULL);
00622 
00623               info.menu = m_submenu;
00624            }
00625            if (info.y < event->xbutton.y && info.y + info.h > event->xbutton.y) {
00626               if (flag == 0) {
00627                   /* Create a timeout of 200ms to pop up the menu, so that it doesn't
00628                      pop up always when the cursor is only moved over the pulldown menu.
00629                      I think this is about the same delay as the one used by Motif.
00630                    */
00631                   flag = 1;
00632                   TRACE_GUI((stderr, "ENTER: %d, %d, %d; %d, %d",
00633                             info.y, info.w, info.h, event->xbutton.x, event->xbutton.y));
00634                   m_timeout = XtAppAddTimeOut(app, 200, popup_pullright, &info);
00635                   return;
00636               }
00637            }
00638            else if (flag == 1) {
00639               flag = 0;
00640               TRACE_GUI((stderr, "LEAVE!"));
00641               if (m_timeout != 0)
00642                   XtRemoveTimeOut(m_timeout);
00643               m_timeout = 0;
00644               if (m_active_submenu != NULL)
00645                   XtPopdown(m_active_submenu);
00646               m_active_submenu = NULL;
00647            }
00648        }
00649     }
00650 }
00651 
00652 static Widget
00653 create_pulldown_entry(const char *menu_name,
00654                     Widget parent,
00655                     const char *item_name,
00656                     const char *accelerator, /* accelerator, or NULL */
00657                     struct xdvi_action *action,
00658                     buttonTypeT *type)
00659 {
00660     Widget w;
00661     char buf[1024];
00662     char *m_name = xstrdup(menu_name);
00663     m_name = xstrcat(m_name, item_name);
00664 
00665 #ifdef SHOW_ACCELERATORS
00666     if (accelerator != NULL && *accelerator != '\0')
00667        SNPRINTF(buf, 1024, "%s  [%s]", item_name, accelerator);
00668     else
00669        strncpy(buf, item_name, 1024);
00670 #else
00671     UNUSED(accelerator);
00672     strncpy(buf, item_name, 1024);
00673 #endif
00674 
00675 /*      fprintf(stderr, "creating pulldown for parent %p: `%s', `%s'\n", */
00676 /*         (void *)parent, menu_name, item_name); */
00677     
00678     if (strcmp(item_name, "SEP") == 0) { /* it's a separator */
00679        *type = MENU_SEPARATOR;
00680        w = XtCreateManagedWidget(m_name, smeLineObjectClass, parent, NULL, 0);
00681     }
00682     else if (action->proc != NULL && action->proc == Act_recent_files) { /* special case: submenu with recent files */
00683 /*     w = recent_files_submenu(m_name, parent, buf); */
00684 /*     *type = MENU_ENTRY; */
00685        w = XtVaCreateManagedWidget(m_name, smeBSBObjectClass, parent,
00686                                 XtNlabel, buf,
00687                                 XtNleftMargin, 20,
00688                                 XtNrightMargin, 16,
00689                                 XtNrightBitmap, menu_arrow_bitmap,
00690                                 NULL);
00691        m_submenu = w;
00692        /*  fprintf(stderr, "setting translations for %p\n", (void *)XtParent(parent)); */
00693        XtOverrideTranslations(parent, menu_accels);
00694        /*     XtOverrideTranslations(XtParent(parent), menu_accels); */
00695                             
00696        *type = MENU_ENTRY;
00697     }
00698     else { /* normal menu item */
00699        w = XtVaCreateManagedWidget(m_name, smeBSBObjectClass, parent,
00700                                 XtNlabel, buf,
00701                                 XtNleftMargin, 20,
00702                                 NULL);
00703        *type = MENU_ENTRY;
00704        XtAddCallback(w, XtNcallback, handle_command, action);
00705     }
00706     free(m_name);
00707     return w;
00708 }
00709 
00710 static Widget
00711 create_pulldown_menu(const char *menu_name,
00712                    Dimension *width,
00713                    int *index,
00714                    Dimension *y_pos,
00715                    buttonTypeT *type,
00716                    int *menu_number)
00717 {
00718     int i;
00719     
00720     /* check whether a pulldown menu with this name already exists,
00721        and if yes, return its position in index: */
00722     for (i = 0; i < *menu_number; i++) {
00723        if (menu_list[i].name != NULL && (strcmp(menu_list[i].name, menu_name)) == 0) {
00724            *index = i;
00725            break;
00726        }
00727     }
00728     if (i == *menu_number) { /* doesn't exist, create one */
00729        Dimension w, h;
00730        Widget n_button_widget, n_popup_widget;
00731        char *button_widget_name = NULL;
00732 
00733        (*menu_number)++;
00734        if (*menu_number > MAX_MENU_NUM) {
00735            XDVI_WARNING((stderr, "Too many menus (max=%d), skipping all from \"%s\"",
00736                        MAX_MENU_NUM, menu_name));
00737            *type = INVALID;
00738            return 0;
00739        }
00740        
00741        /* save menu_name to list for later comparison */
00742        menu_list[i].name = xrealloc(menu_list[i].name, strlen(menu_name) + 1);
00743        strcpy(menu_list[i].name, menu_name);
00744 
00745        button_widget_name = xstrdup(menu_name);
00746        button_widget_name = xstrcat(button_widget_name, "B"); /* make it unique */
00747 
00748        if (globals.debug & DBG_ALL) {
00749            fprintf(stderr, "ypos: %d; h: %d; between: %d; border: %d\n",
00750                   *y_pos,
00751                   h,
00752                   resource.btn_between_spacing,
00753                   resource.btn_border_width);
00754        }
00755 
00756        n_button_widget = XtVaCreateWidget(menu_list[i].name, menuButtonWidgetClass, panel_widget,
00757                                       XtNmenuName, button_widget_name,
00758                                       XtNx, resource.btn_side_spacing,
00759                                       XtNy, *y_pos,
00760                                       XtNborderWidth, resource.btn_border_width,
00761                                       NULL);
00762        n_popup_widget = XtVaCreatePopupShell(button_widget_name, simpleMenuWidgetClass, n_button_widget,
00763                                          NULL);
00764        /* Mustn't free this one?? */
00765 /*     free(button_widget_name); */
00766        menu_list[i].button_widget = n_button_widget;
00767        menu_list[i].popup_widget = n_popup_widget;
00768        XtVaGetValues(n_button_widget, XtNwidth, &w, XtNheight, &h, NULL);
00769        *width = w;
00770        *index = i;
00771        *y_pos += h + resource.btn_between_spacing + 2 * resource.btn_border_width;
00772        *type = MENU_BUTTON;
00773        return n_button_widget;
00774     }
00775     *type = NONE;
00776     return 0;
00777 }
00778 
00779 static struct button_info *
00780 create_button_info(const char *label, struct xdvi_action *action, Widget w, buttonTypeT type)
00781 {
00782     struct button_info *info = xmalloc(sizeof *info);
00783     info->label = xstrdup(label);
00784     info->action = action;
00785     info->widget = w;
00786     info->type = type;
00787     return info;
00788 }
00789 
00790 typedef enum shrinkArgT_ { NO_SHRINK_ARG, HASH, PERCENT, UNDERSCORE } shrinkArgT;
00791 
00792 static shrinkArgT
00793 test_for_shrink_arg(const char *item, int *idx)
00794 {
00795     const char *ptr = item;
00796     size_t i;
00797     for (i = 0; ptr[i] != '\0'; i++) {
00798        if (ptr[i] == '$') {
00799            i++;
00800            if (ptr[i]  == '#') {
00801               *idx = i;
00802               return HASH;
00803            }
00804            else if (ptr[i]  == '%') {
00805               *idx = i;
00806               return PERCENT;
00807            }
00808            else if (ptr[i]  == '_') {
00809               *idx = i;
00810               return UNDERSCORE;
00811            }
00812        }
00813     }
00814     *idx = 0;
00815     return NO_SHRINK_ARG;
00816 }
00817 
00818 static Boolean
00819 create_shrink_button(char **label, int offset, shrinkArgT type, struct xdvi_action *action)
00820 {
00821     int shrink_arg = 0;
00822 #define TEMPSTR_LEN 128 /* enough for printing a number into */
00823     char tempstr[TEMPSTR_LEN];
00824     char *new_label = NULL;
00825     
00826     for (;; action = action->next) {
00827        if (action == NULL) {
00828            return False;
00829        }
00830        if (action->proc == Act_set_shrink_factor || action->proc == Act_shrink_to_dpi) {
00831            /* NOTE: the shrinkbutton[] resource isn't implemented. It probably makes
00832               less sense with the pulldown menu resources??
00833            */
00834            if (action->num_params > 0) {
00835               shrink_arg = atoi(action->param);
00836               if (action->proc == Act_shrink_to_dpi)
00837                   shrink_arg = (double)resource.pixels_per_inch / shrink_arg + 0.5;
00838               if (shrink_arg < 1)
00839                   shrink_arg = 1;
00840               else if (shrink_arg > 99)
00841                   shrink_arg = 99;
00842            }
00843            break; /* found shrink action */
00844        }
00845     }
00846 
00847     if (type == HASH)
00848        SNPRINTF(tempstr, TEMPSTR_LEN, "%d", shrink_arg);
00849     else if (type == PERCENT) {
00850        if (shrink_arg <= 15)
00851            SNPRINTF(tempstr, TEMPSTR_LEN, "%d", (int)(100.0 / shrink_arg + .5));
00852        else
00853            SNPRINTF(tempstr, TEMPSTR_LEN, "%.2f", 100.0 / shrink_arg);
00854     }
00855     /* NOTE: I think UNDERSCORE is related to the shrinkbutton[] resource,
00856        which is not implemented */
00857 
00858     /* return resized label */
00859     new_label = xmalloc(offset - 1  + strlen(tempstr) + strlen(*label + offset + 1) + 1);
00860     memcpy(new_label, *label, offset - 1);
00861     strcpy(new_label + offset - 1, tempstr); /* now it's null-terminated */
00862     strcat(new_label, *label + offset + 1);
00863     free(*label);
00864     *label = new_label;
00865 
00866 #undef TEMPSTR_LEN
00867     return True;
00868 }
00869 
00870 static Dimension
00871 create_button(int button_number,   /* number of this button */
00872              int *menu_num,        /* number of submenus in this button */
00873              char **items,         /* description of label and action strings */
00874              size_t item_count,    /* number of items in description */
00875              Widget parent,        /* parent of widget to create */
00876              struct button_info ***bipp,  /* button info to insert new button into */
00877              Dimension *y_pos,            /* vertical position after creating button */
00878              const char *c_ptr,    /* entire rest of resource string, for error messages */
00879              size_t len)           /* length of current line in resource string, for error messages*/
00880 {
00881     int menu_idx = -1, label_idx = -1, accelerator_idx = -1, action_idx = -1;
00882 #define NAME_LEN 16
00883     char name[NAME_LEN];
00884     struct button_info *bp;
00885     shrinkArgT shrink_arg_type;
00886     struct xdvi_action *action;
00887     Widget widget;
00888     buttonTypeT b_type;
00889 /*     static int menu_num = 0; /\* count number of created menus *\/ */
00890     Dimension width = 0;
00891 
00892     ASSERT(item_count >= 2, "Too few items");
00893     
00894     if (item_count == 4) { /* submenu for a button */
00895        menu_idx = 0;
00896        label_idx = 1;
00897        accelerator_idx = 2;
00898        action_idx = 3;
00899     }
00900     else if (item_count == 3) { /* command button */
00901        label_idx = 0;
00902        accelerator_idx = 1;
00903        action_idx = 2;
00904     }
00905     else { /* separator */
00906        if (strcmp(items[1], "SEP") == 0) { /* separator */
00907            menu_idx = 0;
00908            label_idx = 1;
00909        }
00910        else {
00911            XDVI_WARNING((stderr, "Wrong number of items (%d) in translations line:\n\"%.*s\" (skipping this line).",
00912                        item_count, (int)len, c_ptr));
00913            return 0;
00914        }
00915     }
00916 
00917     if (action_idx != -1) {
00918        int offset;
00919        shrink_arg_type = test_for_shrink_arg(items[label_idx], &offset);
00920 
00921 /*     fprintf(stderr, "compiling action: |%s|\n", items[action_idx]); */
00922        if ((action = compile_action(items[action_idx])) == NULL) {
00923            XDVI_WARNING((stderr, "Invalid action \"%s\" in translations line:\n\"%.*s\" (skipping this line).",
00924                        items[action_idx], (int)len, c_ptr));
00925            return 0;
00926        }
00927     
00928        if (shrink_arg_type != NO_SHRINK_ARG) { /* search for corresponding shrink action */
00929            if (!create_shrink_button(&(items[label_idx]), offset, shrink_arg_type, action)) {
00930               XDVI_WARNING((stderr, "Non-existent shrink action \"%s\" in translations line:\n\"%.*s\"",
00931                            items[action_idx], (int)len, c_ptr));
00932               return 0;
00933            }
00934        }
00935     }
00936     else {
00937        action = NULL;
00938     }
00939     
00940     command_call[0].closure = (XtPointer) action;
00941     SNPRINTF(name, NAME_LEN, "button%d", button_number);
00942 
00943     if (menu_idx != -1) { /* it's a pulldown menu */
00944        int index;
00945 
00946        /* create a new pulldown menu if it doesn't exist yet */
00947        widget = create_pulldown_menu(items[menu_idx], &width, &index, y_pos, &b_type, menu_num);
00948        if (b_type == INVALID)
00949            return 0;
00950        if (b_type != NONE) { /* it didn't exist yet */
00951            bp = create_button_info(items[label_idx], NULL, widget, b_type);
00952            **bipp = bp;
00953            *bipp = &bp->next;
00954        }
00955 
00956        /* create an entry for this item */
00957        widget = create_pulldown_entry(menu_list[index].name,
00958                                    menu_list[index].popup_widget,
00959                                    items[label_idx],
00960                                    accelerator_idx > 0 ? items[accelerator_idx] : NULL,
00961                                    action,
00962                                    &b_type);
00963        bp = create_button_info(items[label_idx], action, widget, b_type);
00964        **bipp = bp;
00965        *bipp = &bp->next;
00966     }
00967     else {  /* not a pulldown menu, but a simple command button */
00968        command_call[0].closure = (XtPointer) action;
00969        widget = XtVaCreateWidget(name, commandWidgetClass, parent,
00970                               XtNlabel, (XtArgVal)items[label_idx],
00971                               XtNx, resource.btn_side_spacing,
00972                               XtNy, (XtArgVal)*y_pos,
00973                               XtNborderWidth, resource.btn_border_width,
00974                               XtNcallback, (XtArgVal)command_call,
00975                               NULL);
00976        bp = create_button_info(items[label_idx], action, widget, COMMAND_BUTTON);
00977        **bipp = bp;
00978        *bipp = &bp->next;
00979     }
00980 #undef NAME_LEN
00981     return width;
00982 }
00983 
00984 void
00985 scroll_x_panner(int x)
00986 {
00987     static int old_x = 0;
00988     if (panner != 0 && ABS(x - old_x) > 8) {
00989        XtVaSetValues(panner, XtNsliderX, x, NULL);
00990        old_x = x;
00991     }
00992 }
00993 
00994 void
00995 scroll_y_panner(int y)
00996 {
00997     static int old_y = 0;
00998     if (panner != 0 && ABS(y - old_y) > 8) {
00999        XtVaSetValues(panner, XtNsliderY, y, NULL);
01000        old_y = y;
01001     }
01002 }
01003 
01004 #ifdef USE_PANNER
01005 static void 
01006 panner_cb(Widget widget, XtPointer closure, XtPointer report_ptr)
01007 {
01008     XawPannerReport *report = (XawPannerReport *)report_ptr;
01009     static int orig_x = 0, orig_y = 0;
01010     int x = report->slider_x;
01011     int y = report->slider_y;
01012     static Dimension w, h;
01013     static Arg arg_wh_clip[] = {
01014        {XtNwidth, (XtArgVal) &w},
01015        {XtNheight, (XtArgVal) &h},
01016     };
01017     
01018     UNUSED(closure);
01019 
01020     XtGetValues(globals.widgets.clip_widget, arg_wh_clip, XtNumber(arg_wh_clip));
01021     
01022     fprintf(stderr, "w: %d, h: %d, globals.page.w: %d, globals.page.h: %d\n",
01023            w, h, globals.page.w, globals.page.h);
01024     XtVaSetValues(widget,
01025                 XtNsliderWidth, w, XtNsliderHeight, h,
01026                 XtNcanvasWidth, globals.page.w, XtNcanvasHeight, globals.page.h,
01027                 NULL);
01028     
01029     fprintf(stderr, "panner moved: %d, %d\n", report->slider_x, report->slider_y);
01030     if (globals.widgets.x_bar != NULL)
01031        XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer)(x - orig_x));
01032     if (globals.widgets.y_bar != NULL)
01033        XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer)(y - orig_y));
01034     orig_x = x;
01035     orig_y = y;
01036 }
01037 #endif /* USE_PANNER */
01038 
01039 void
01040 create_menu_buttons(Widget form, int *ret_panel_width)
01041 {
01042     Dimension max_button_width = 0, curr_width;
01043     Dimension y_pos;
01044     struct button_info **bipp;
01045     struct button_info *bp;
01046     const char *c_ptr, *e_ptr;
01047     int max_size;
01048     int button_number = 0;
01049     int menu_number = 0; /* number of menus created; passed through to create_pulldown_menu */
01050     
01051     /* disable the magnifier in the panel: */
01052     XtTranslations panel_translations = XtParseTranslationTable("#augment <ButtonPress>:");
01053 
01054     XtAppAddActions(XtWidgetToApplicationContext(form), menu_actions, XtNumber(menu_actions));
01055     /* add our own popdown-submenus() action to the default translations of this menu */
01056     menu_accels = XtParseAcceleratorTable("<BtnUp>:MenuPopdown()notify()unhighlight()popdown-submenus()");
01057     
01058     line_widget = XtVaCreateWidget("line", widgetClass, form,
01059                                XtNbackground, (XtArgVal)resource.fore_Pixel,
01060                                XtNwidth, (XtArgVal) 1,
01061                                XtNfromHoriz, (XtArgVal)globals.widgets.vport_widget,
01062                                XtNborderWidth, (XtArgVal)0,
01063                                XtNtop, (XtArgVal)XtChainTop,
01064                                XtNbottom, (XtArgVal)XtChainBottom,
01065                                XtNleft, (XtArgVal)XtChainRight,
01066                                XtNright, (XtArgVal)XtChainRight,
01067                                NULL);
01068     panel_widget = XtVaCreateWidget("panel", compositeWidgetClass, form,
01069                                 XtNborderWidth, (XtArgVal)0,
01070                                 XtNfromHoriz, (XtArgVal)line_widget,
01071                                 XtNtranslations, (XtArgVal)panel_translations,
01072                                 XtNtop, (XtArgVal)XtChainTop,
01073                                 XtNbottom, (XtArgVal)XtChainBottom,
01074                                 XtNleft, (XtArgVal)XtChainRight,
01075                                 XtNright, (XtArgVal)XtChainRight,
01076                                 NULL);
01077     menu_arrow_bitmap
01078        = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level),
01079                             RootWindowOfScreen(XtScreen(globals.widgets.top_level)),
01080                             (char *)menu_arrow_bits, MENU_ARROW_W, MENU_ARROW_H);
01081     
01082     b_head = NULL;
01083     bipp = &b_head;
01084 
01085     *ret_panel_width = 0;
01086     y_pos = resource.btn_top_spacing;
01087 
01088 #define FREE_LINE_ITEMS do { \
01089            for (curr = 0; curr < item_count; curr++) { \
01090                free(line_items[curr]); \
01091            } \
01092            free(line_items); \
01093        } while (0)
01094     
01095     for (c_ptr = resource.menu_translations;
01096         c_ptr != NULL && *c_ptr != '\0';
01097         c_ptr = e_ptr + 1) {
01098        e_ptr = strchr(c_ptr, '\n');
01099        if (e_ptr != NULL) {
01100            char **line_items = NULL;
01101            size_t len, curr, item_count = 0;
01102 
01103            if (e_ptr == c_ptr) { /* single '\n' marks additional space between buttons */
01104               y_pos += resource.btn_between_extra;
01105               continue;
01106            }
01107            len = e_ptr - c_ptr;
01108 
01109            if (++button_number > MAX_BUTTON_NUM) {
01110               XDVI_WARNING((stderr, "Too many buttons (max=%d), skipping all from:\n\"%.*s\"",
01111                            MAX_BUTTON_NUM, (int)len, c_ptr));
01112               break;
01113            }
01114            
01115            line_items = split_line(c_ptr, SEP_CHAR, 0, len, &item_count);
01116            if (item_count < 3 && !(item_count > 1 && strcmp(line_items[1], "SEP") == 0)) {
01117               XDVI_WARNING((stderr, "Too few separators \"%c\" in translations line:\n\"%.*s\" (skipping this line).\n",
01118                            SEP_CHAR, (int)len, c_ptr));
01119               FREE_LINE_ITEMS;
01120               continue;
01121            }
01122            curr_width = create_button(button_number,
01123                                    &menu_number,
01124                                    line_items,
01125                                    item_count,
01126                                    panel_widget,
01127                                    &bipp,
01128                                    &y_pos,
01129                                    c_ptr, len);
01130            if (curr_width == 0) { /* no button created */
01131               FREE_LINE_ITEMS;
01132               continue;
01133            }
01134            /* adjust max_button_width to this new entry */
01135            if (curr_width > max_button_width)
01136               max_button_width = curr_width;
01137 
01138            FREE_LINE_ITEMS;
01139        }
01140     }
01141 
01142 #undef FREE_LINE_ITEMS
01143     
01144     /* null-terminate button info */
01145     *bipp = NULL;
01146 
01147     max_size = xaw_get_pagelist_size();
01148     if (max_size > max_button_width) {
01149        max_button_width = max_size;
01150     }
01151 
01152     *ret_panel_width = max_button_width + 2 * (resource.btn_side_spacing + resource.btn_border_width);
01153     TRACE_GUI((stderr, "panel_widget: w: %d", *ret_panel_width));
01154     XtVaSetValues(panel_widget, XtNwidth, (XtArgVal)*ret_panel_width, NULL);
01155     ++(*ret_panel_width);
01156     my_panel_width = *ret_panel_width; /* make it available */
01157 
01158     for (bp = b_head; bp != NULL; bp = bp->next) {
01159        if (bp->type == MENU_BUTTON || bp->type == COMMAND_BUTTON) {
01160            TRACE_GUI((stderr, "bp->widget: w: %d", max_button_width));
01161            XtVaSetValues(bp->widget, XtNwidth, (XtArgVal)max_button_width, NULL);
01162            XtManageChild(bp->widget);
01163        }
01164     }
01165 
01166     my_y_pos = y_pos - resource.btn_between_spacing + resource.btn_top_spacing + 2 * resource.btn_border_width;
01167     initialize_tick_marks();
01168 #ifdef USE_PANNER
01169     {
01170        static Dimension w, h;
01171        static Arg arg_wh_clip[] = {
01172            {XtNwidth, (XtArgVal) &w},
01173            {XtNheight, (XtArgVal) &h},
01174        };
01175        XtGetValues(globals.widgets.clip_widget, arg_wh_clip, XtNumber(arg_wh_clip));
01176 
01177        fprintf(stderr, "w: %d, h: %d, globals.page.w: %d, globals.page.h: %d\n",
01178               w, h, globals.page.w, globals.page.h);
01179        if (w == 0)
01180            w = globals.page.w - 2;
01181        if (h == 0)
01182            h = globals.page.h - 2;
01183        panner = XtVaCreateManagedWidget("panner", pannerWidgetClass, panel_widget,
01184                                     XtNx, resource.btn_side_spacing,
01185                                     XtNy, my_y_pos,
01186                                     XtNheight, 60,
01187                                     XtNwidth, max_button_width,
01188                                     XtNshadowThickness, 0,
01189                                     XtNsliderWidth, w,
01190                                     XtNsliderHeight, h,
01191                                     XtNcanvasWidth, globals.page.w,
01192                                     XtNcanvasHeight, globals.page.h,
01193                                     XtNsliderX, 5,
01194                                     XtNsliderY, 7,
01195                                     XtNinternalSpace, 0,
01196                                     NULL);
01197     }
01198     my_y_pos += 60 + resource.btn_top_spacing + 2 * resource.btn_border_width;
01199     XtAddCallback(panner, XtNreportCallback, panner_cb, (XtPointer)NULL);
01200 
01201 #endif /* USE_PANNER */
01202 
01203 }
01204 #endif /* NEW_MENU_CREATION */
01205 
01206 
01207 void
01208 set_button_panel_height(XtArgVal h)
01209 {
01210     TRACE_GUI((stderr, "line_widget: h %d", (int)h));
01211     XtVaSetValues(line_widget, XtNheight, h, NULL);
01212     XtManageChild(line_widget);
01213 
01214     XtVaSetValues(panel_widget, XtNheight, h, NULL);
01215     XtManageChild(panel_widget);
01216 }
01217 
01218 /****************************************************************************
01219  * Action handling code.
01220  */
01221 
01222 
01223 static void
01224 handle_destroy_buttons(Widget widget, XtPointer client_data, XtPointer call_data)
01225 {
01226     int panel_width;
01227     Dimension window_w, window_h;
01228     
01229     UNUSED(widget);
01230     UNUSED(client_data);
01231     UNUSED(call_data);
01232     
01233     if (--destroy_count != 0) {
01234        return;
01235     }
01236     XtVaSetValues(globals.widgets.vport_widget, XtNresizable, (XtArgVal)True, NULL);
01237     XtVaGetValues(globals.widgets.form_widget,
01238                     XtNwidth, &window_w,
01239                     XtNheight, &window_h,
01240                     NULL);
01241     
01242     if ((resource.expert_mode & XPRT_SHOW_BUTTONS) == 0) {
01243        /* destroy buttons */
01244        TRACE_GUI((stderr, "globals.widgets.vport_widget: h %d, w %d", window_w, window_h));
01245        XtVaSetValues(globals.widgets.vport_widget, XtNwidth, window_w, XtNheight, window_h, NULL);
01246     }
01247     else {
01248        create_menu_buttons(globals.widgets.form_widget, &panel_width);
01249        window_w -= panel_width;
01250        TRACE_GUI((stderr, "globals.widgets.vport_widget: h %d, w %d", window_h, window_w));
01251        XtVaSetValues(globals.widgets.vport_widget, XtNwidth, window_w, XtNheight, window_h, NULL);
01252        set_button_panel_height((XtArgVal) window_h);
01253     }
01254 }
01255 
01256 
01257 static void
01258 reconfig_window(void) {
01259     Dimension x_top, y_top, h_top, w_top;
01260     XWindowChanges sizeconfigure;
01261     int sizeconfiguremask;
01262     
01263     /* brute-force method to bring the scrollbars back. Apparently a single XConfigureWindow()
01264        isn't enough to get the scrollbars back, we actually need to move the window a bit,
01265        and then move it back. */
01266     sizeconfiguremask = CWWidth | CWHeight;
01267     XtVaGetValues(globals.widgets.top_level, XtNx, &x_top, XtNy, &y_top, XtNheight, &h_top, XtNwidth, &w_top, NULL);
01268     sizeconfigure.width = w_top + 1;
01269     sizeconfigure.height = h_top + 1;
01270     XConfigureWindow(DISP, XtWindow(globals.widgets.top_level), sizeconfiguremask, &sizeconfigure);
01271     sizeconfigure.width = w_top;
01272     sizeconfigure.height = h_top;
01273     XConfigureWindow(DISP, XtWindow(globals.widgets.top_level), sizeconfiguremask, &sizeconfigure);
01274 }
01275 
01276 /* toggle scrollbars to state `visible' */
01277 void
01278 toggle_scrollbars(void)
01279 {
01280     Widget v_bar = XtNameToWidget(globals.widgets.vport_widget, "vertical");
01281     Widget h_bar = XtNameToWidget(globals.widgets.vport_widget, "horizontal");
01282     static Dimension bar_thick;
01283     static Boolean v_bar_mapped = False, h_bar_mapped = False;
01284     static Boolean initialized = False;
01285 
01286     Boolean make_v_bar_visible = False;
01287     Boolean make_v_bar_invisible = False;
01288     
01289     Boolean make_h_bar_visible = False;
01290     Boolean make_h_bar_invisible = False;
01291 
01292     if (v_bar != 0) {
01293        int test_v = 0;
01294        XtVaGetValues(v_bar, XtNwidth, &test_v, NULL);
01295        if (test_v > 1)
01296            v_bar_mapped = True;
01297     }
01298     if (h_bar != 0) {
01299        int test_h = 0;
01300        XtVaGetValues(h_bar, XtNheight, &test_h, NULL);
01301        if (test_h > 1)
01302            h_bar_mapped = True;
01303     }
01304     
01305     if (!initialized) {
01306        v_bar_mapped = h_bar_mapped = (resource.expert_mode & XPRT_SHOW_SCROLLBARS) != 0;
01307        initialized = True;
01308        if (v_bar != 0)
01309            XtVaGetValues(v_bar, XtNwidth, &bar_thick, NULL);
01310        else if (h_bar != 0)
01311            XtVaGetValues(h_bar, XtNheight, &bar_thick, NULL);
01312        else
01313            bar_thick = 15; /* FIXME */
01314     }
01315 
01316     if ((resource.expert_mode & XPRT_SHOW_SCROLLBARS) == 0) {
01317        if (v_bar_mapped)
01318            make_v_bar_invisible = True;
01319        if (h_bar_mapped)
01320            make_h_bar_invisible = True;
01321     }
01322     else {
01323        if (!v_bar_mapped)
01324            make_v_bar_visible = True;
01325        if (!h_bar_mapped)
01326            make_h_bar_visible = True;
01327     }
01328 
01329     if (make_h_bar_visible || make_v_bar_visible) {
01330        if (make_h_bar_visible && h_bar != 0) {
01331            TRACE_GUI((stderr, "h_bar: h %d", bar_thick));
01332            XtVaSetValues(h_bar, XtNheight, bar_thick, XtNx, bar_thick, XtNy, 0, XtNborderWidth, 1, NULL);
01333            XtManageChild(h_bar);
01334            h_bar_mapped = True;
01335        }
01336        if (make_v_bar_visible && v_bar != 0) {
01337            TRACE_GUI((stderr, "v_bar: w %d", bar_thick));
01338            XtVaSetValues(v_bar, XtNwidth, bar_thick, XtNx, 0, XtNy, bar_thick, XtNborderWidth, 1, NULL);
01339            XtManageChild(v_bar);
01340            v_bar_mapped = True;
01341        }
01342        if (h_bar != 0 || v_bar != 0) { /* need to reconfigure screen */
01343            reconfig_window();
01344        }
01345     }
01346     else if (make_h_bar_invisible || make_v_bar_invisible) {
01347        if (make_h_bar_invisible && h_bar != 0) {
01348            XtUnmanageChild(h_bar);
01349            XtVaSetValues(h_bar, XtNheight, 1, XtNx, 0, XtNy, 0, XtNborderWidth, 0, NULL);
01350            h_bar_mapped = False;
01351        }
01352        if (make_v_bar_invisible && v_bar != 0) {
01353            XtUnmanageChild(v_bar);
01354            XtVaSetValues(v_bar, XtNwidth, 1, XtNy, 0, XtNy, 0, XtNborderWidth, 0, NULL);
01355            v_bar_mapped = False;
01356        }
01357        if (h_bar != 0 || v_bar != 0) { /* need to reconfigure screen */
01358            reconfig_window();
01359        }
01360     }
01361     
01362 }
01363 
01364 void
01365 toggle_buttons(void)
01366 {
01367     static Boolean buttons_active = False;
01368     static Boolean initialized = False;
01369     Dimension window_w, window_h;
01370 
01371 
01372     Boolean make_buttons_visible = False;
01373     Boolean make_buttons_invisible = False;
01374 
01375     int panel_width;
01376     
01377     if (!initialized) {
01378        buttons_active = (resource.expert_mode & XPRT_SHOW_BUTTONS) != 0;
01379        initialized = True;
01380     }
01381 
01382     if ((resource.expert_mode & XPRT_SHOW_BUTTONS) == 0) {
01383        if (buttons_active)
01384            make_buttons_invisible = True;
01385     }
01386     else {
01387        if (!buttons_active)
01388            make_buttons_visible = True;
01389     }
01390            
01391     if (make_buttons_visible) {
01392        if (destroy_count != 0) {
01393            return;
01394        }
01395 
01396        create_menu_buttons(globals.widgets.form_widget, &panel_width);
01397        XtVaGetValues(globals.widgets.form_widget,
01398                     XtNwidth, &window_w,
01399                     XtNheight, &window_h,
01400                     NULL);
01401        XtVaSetValues(globals.widgets.vport_widget, XtNresizable, (XtArgVal)True, NULL);
01402        window_w -= panel_width;
01403        TRACE_GUI((stderr, "globals.widgets.vport_widget: w: %d, h: %d", window_w, window_h));
01404        XtVaSetValues(globals.widgets.vport_widget, XtNwidth, window_w, XtNheight, window_h, NULL);
01405        set_button_panel_height((XtArgVal) window_h);
01406        buttons_active = True;
01407     }
01408     else if (make_buttons_invisible) {
01409        if (destroy_count != 0) {
01410            return;
01411        }
01412        /* this counts the callback calls; 1 for the panel, 1 for the line */
01413        destroy_count = 2;
01414 
01415        XtAddCallback(panel_widget, XtNdestroyCallback,
01416                     handle_destroy_buttons, (XtPointer)0);
01417        XtAddCallback(panel_widget, XtNdestroyCallback,
01418                     handle_destroy_pagelist, (XtPointer)0);
01419        XtAddCallback(line_widget, XtNdestroyCallback,
01420                     handle_destroy_buttons, (XtPointer)0);
01421        XtDestroyWidget(panel_widget);
01422        XtDestroyWidget(line_widget);
01423        buttons_active = False;
01424 /*     window_w += get_panel_width(); */
01425 
01426        while (b_head != NULL) {
01427            struct button_info *bp = b_head;
01428            struct xdvi_action *action;
01429 
01430            b_head = bp->next;
01431            free(bp->label);
01432            /* free bp->action */
01433            for (action = bp->action; action != NULL;) {
01434               struct xdvi_action *act2 = action;
01435               action = act2->next;
01436               if (act2->num_params > 0) {
01437                   free(act2->param);
01438               }
01439               free(act2);
01440            }
01441            free(bp);
01442        }
01443     }
01444 }
01445 
01446 #else
01447 /* silence `empty compilation unit' warnings */
01448 static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); }
01449 #endif /* ifndef MOTIF */