Back to index

tetex-bin  3.0
xm_menu.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2001 Marcin Dalecki and others
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy
00005  * of this software and associated documentation files (the "Software"), to
00006  * deal in the Software without restriction, including without limitation the
00007  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
00008  * sell copies of the Software, and to permit persons to whom the Software is
00009  * furnished to do so, subject to the following conditions:
00010  *
00011  * The above copyright notice and this permission notice shall be included in
00012  * all copies or substantial portions of the Software.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00017  * PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
00018  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00020  * OTHER DEALINGS IN THE SOFTWARE.
00021  *
00022  */
00023 
00024 #include "xdvi-config.h"
00025 #include "xdvi.h"
00026 
00027 #include "xm_menu.h"
00028 #include "xm_toolbar.h"
00029 #include "my-snprintf.h"
00030 #include "util.h"
00031 #include "x_util.h"
00032 #include "version.h"
00033 #include "statusline.h"
00034 #include "pagehist.h"
00035 #include "dvi-init.h"
00036 #include "filehist.h"
00037 #include "c-openmx.h"
00038 #include "message-window.h"
00039 
00040 #ifdef MOTIF /* needed for `make depend' */
00041 
00042 #include <Xm/RowColumn.h>
00043 #include <Xm/Frame.h>
00044 #include <Xm/PushB.h>
00045 #include <Xm/PushBG.h>
00046 #include <Xm/CascadeBG.h>
00047 #include <Xm/SeparatoG.h>
00048 #include <Xm/ToggleB.h>
00049 #include <Xm/ToggleBG.h>
00050 #include <Xm/Separator.h>
00051 
00052 
00053 /*
00054  * There's an apparent bug in Motif related to the interaction between
00055  * the menubar menus and the magnifier.
00056  *
00057  * If you click on a menu on the menubar and then click on the drawing
00058  * widget to pop up a magnifier, the keyboard and pointer are still
00059  * grabbed, leading to a weird situation in which the magnifier stays
00060  * around even after you release the pointer.  The ungrab_serial
00061  * counter works around this bug by ignoring such pointer events.
00062  *
00063  * This bug occurs in Motif 1.2 and OpenMotif 2.2.2 (at least).  It
00064  * does not occur in Lesstif.
00065  */
00066 static unsigned long ungrab_event_num = 0;
00067 
00068 void
00069 popdown_callback(Widget w, XtPointer client_data, XtPointer call_data)
00070 {
00071     UNUSED(w);
00072     UNUSED(client_data);
00073     /* Lesstif gives call_data == NULL */
00074     if (call_data != NULL && *((XtGrabKind *) call_data) != XtGrabNone) {
00075        ungrab_event_num = LastKnownRequestProcessed(DISP);
00076     }
00077 }
00078 
00079 Boolean
00080 pulldown_menu_active(unsigned long event_num)
00081 {
00082     return event_num < ungrab_event_num;
00083 }
00084 
00085 
00086 void
00087 toggle_menubar(void)
00088 {
00089     if ((resource.expert_mode & XPRT_SHOW_MENUBAR) == 0)
00090        XtUnmanageChild(globals.widgets.menu_bar);
00091     else
00092        XtManageChild(globals.widgets.menu_bar);
00093 }    
00094 
00095 static void
00096 update_menu_labels(Widget menu)
00097 {
00098     WidgetList children;
00099     int num_children;
00100     int i;
00101 
00102     static char *buf = NULL;
00103     static size_t buf_len = 0;
00104     
00105     XtVaGetValues(menu,
00106                 XmNnumChildren, &num_children,
00107                 XmNchildren, &children,
00108                 NULL);
00109 
00110 /*      for (i = 0; i < num_children; i++) { */
00111     for (i = 0; i < (int)file_history_size(); i++) {
00112        int dummy_page;
00113        char *filename;
00114        size_t new_len;
00115        XmString str;
00116        
00117        if ((filename = file_history_get_elem(i, &dummy_page)) == NULL) {
00118            XDVI_ERROR((stderr, "Error accessing element %d of file history", i));
00119            continue;
00120        }
00121        
00122        new_len = LENGTH_OF_INT + strlen(filename) + 1;
00123        if (new_len > buf_len) {
00124            buf = xrealloc(buf, new_len);
00125            buf_len = new_len;
00126        }
00127        
00128        sprintf(buf, "%d %s", i + 1, filename);
00129        str = XmStringCreateLocalized(buf);
00130        XtVaSetValues(children[i],
00131                     XmNlabelString, str,
00132                     NULL);
00133        if (i + 1 < 10) {
00134            XtVaSetValues(children[i], XmNmnemonic, buf[0], NULL);
00135        }
00136        XmStringFree(str);
00137     }
00138 
00139     /* if history size < number of menu entries, destroy excess menu entries */
00140     for (; i < num_children; i++) {
00141        XtDestroyWidget(children[i]);
00142     }
00143 }
00144 
00145 static void
00146 filehist_select_cb(Widget w, XtPointer client_data, XtPointer call_data)
00147 {
00148     Widget menu;
00149     XmString label;
00150     char *label_ptr;
00151     int pageno;
00152     char *fname;
00153     int idx;
00154     
00155 /*     UNUSED(w); */
00156     UNUSED(call_data);
00157     UNUSED(client_data);
00158     
00159     XtVaGetValues(w,
00160                 XmNuserData, &menu,
00161                 XmNlabelString, &label,
00162                 NULL);
00163 
00164     XmStringGetLtoR(label, G_charset, &label_ptr);
00165     idx = strtol(label_ptr, (char **)NULL, 10) - 1;
00166     XtFree(label_ptr);
00167     
00168     ASSERT(menu != NULL, "XmNuserData in filehist_select_cb musn't be NULL!");
00169 
00170     if (idx == 0) { /* user re-selected current file: reload */
00171        globals.ev.flags |= EV_RELOAD;
00172        return;
00173     }
00174     if ((fname = file_history_get_elem(idx, &pageno)) == NULL) {
00175        statusline_print(STATUS_MEDIUM, "Error accessing file %d of history", idx);
00176        return;
00177     }
00178 
00179     file_history_open(fname);
00180 }
00181 
00182 void
00183 filehist_menu_refresh(void)
00184 {
00185     Widget menu;
00186     if (get_widget_by_name(&menu, globals.widgets.menu_bar, Xdvi_FILEHIST_MENU, True)) {
00187        update_menu_labels(menu);
00188     }
00189 }
00190 
00191 void
00192 filehist_menu_add_entry(const char *filename)
00193 {
00194     Widget menu;
00195 
00196     static char *buf = NULL;
00197     static size_t buf_len = 0;
00198     size_t new_len = LENGTH_OF_INT + strlen(filename) + 1;
00199     
00200     if (get_widget_by_name(&menu, globals.widgets.menu_bar, Xdvi_FILEHIST_MENU, True)) {
00201        int num_children;
00202        Widget w;
00203 
00204        if (new_len > buf_len) {
00205            buf = xrealloc(buf, new_len);
00206            buf_len = new_len;
00207        }
00208        
00209        XtVaGetValues(menu, XmNnumChildren, &num_children, NULL);
00210 
00211        sprintf(buf, "%d %s", num_children + 1, filename);
00212        
00213        w = XtVaCreateManagedWidget(buf, xmPushButtonGadgetClass, menu,
00214                                 XmNuserData, menu,
00215                                 NULL);
00216        if (num_children + 1 < 10) {
00217            XtVaSetValues(w, XmNmnemonic, buf[0], NULL);
00218        }
00219        XtAddCallback(w, XmNactivateCallback, filehist_select_cb, (XtPointer)(num_children + 1));
00220 
00221        update_menu_labels(menu);
00222        
00223     }
00224 }
00225 
00226 static void
00227 filehist_submenu(int idx, const char *filename, int pageno, void *data)
00228 {
00229     Widget menu = (Widget)data;
00230     Widget w;
00231     static char *buf = NULL;
00232     static size_t buf_len = 0;
00233     size_t new_len = LENGTH_OF_INT + strlen(filename) + 1;
00234 
00235     if (new_len > buf_len) {
00236        buf = xrealloc(buf, new_len);
00237        buf_len = new_len;
00238     }
00239     
00240     UNUSED(pageno);
00241 
00242     sprintf(buf, "%d %s", idx + 1, filename);
00243     TRACE_GUI((stderr, "Creating menu `%s'", buf));
00244     w = XtVaCreateManagedWidget(buf, xmPushButtonGadgetClass, menu,
00245 /*                          XmNmnemonic, buf[0], */
00246                             XmNuserData, menu,
00247                             NULL);
00248     if (idx + 1 < 10) {
00249        XtVaSetValues(w, XmNmnemonic, buf[0], NULL);
00250     }
00251     XtAddCallback(w, XmNactivateCallback, filehist_select_cb, (XtPointer)idx);
00252 }
00253 
00254 
00255 
00256 static Widget
00257 recent_files_submenu(Widget parent, char *title, char mnemonic)
00258 {
00259     Widget menu = 0, cascade = 0;
00260     XmString str;
00261 
00262     menu = XmCreatePulldownMenu(parent, Xdvi_FILEHIST_MENU, NULL, 0);
00263     str = XmStringCreateLocalized(title);
00264     cascade =  XtVaCreateManagedWidget(title, xmCascadeButtonGadgetClass, parent,
00265                                    XmNsubMenuId, menu,
00266                                    XmNlabelString, str,
00267                                    XmNmnemonic, mnemonic,
00268                                    NULL);
00269     XmStringFree(str);
00270 
00271     file_history_enumerate(filehist_submenu, menu);
00272     return cascade;
00273 }
00274 
00275 Widget
00276 xm_create_menu(Widget parent, char *title, char mnemonic, struct button_info *item)
00277 {
00278     Widget menu = 0, cascade = 0, w = 0;
00279     size_t i;
00280     XmString str;
00281 
00282     menu = XmCreatePulldownMenu(parent, "_pulldown", NULL, 0);
00283     str = XmStringCreateLocalized(title);
00284     cascade =  XtVaCreateManagedWidget(title, xmCascadeButtonGadgetClass, parent,
00285                                    XmNsubMenuId, menu,
00286                                    XmNlabelString, str,
00287                                    XmNmnemonic, mnemonic,
00288                                    NULL);
00289     XmStringFree(str);
00290     
00291     /* add the menu items */
00292     for (i = 0; item != NULL && i < item->size; i++) {
00293        item->elems[i].widget = 0;
00294        /* if there's a subitem, call this function recursively */
00295        if (item->elems[i].submenu != NULL) {
00296            w = xm_create_menu(menu, item->elems[i].title,
00297                             item->elems[i].mnemonic, item->elems[i].submenu);
00298            /*
00299              workaround for pointer grabbing bug; add additional callback to all menus
00300              (they have the same parent)
00301            */
00302            XtAddCallback(XtParent(menu), XtNpopdownCallback,
00303                        popdown_callback, (XtPointer) 0);
00304        }
00305        else if (item->elems[i].action != NULL && item->elems[i].action->proc == Act_recent_files) {
00306            /* special case: submenu with recent files */
00307            w = recent_files_submenu(menu, item->elems[i].title, item->elems[i].mnemonic);
00308            XtAddCallback(XtParent(menu), XtNpopdownCallback,
00309                        popdown_callback, (XtPointer) 0);
00310        }
00311        else {
00312            switch(item->elems[i].type) {
00313            case BT_PUSH:
00314               w = XtVaCreateManagedWidget(item->elems[i].title, xmPushButtonGadgetClass, menu,
00315                                        NULL);
00316               break;
00317            case BT_RADIO:
00318               w = XtVaCreateManagedWidget(item->elems[i].title, xmToggleButtonGadgetClass, menu,
00319                                        XmNindicatorType, XmONE_OF_MANY,
00320                                        NULL);
00321               break;
00322            case BT_CHECK:
00323               w = XtVaCreateManagedWidget(item->elems[i].title, xmToggleButtonGadgetClass, menu,
00324                                        XmNindicatorType, XmN_OF_MANY,
00325                                        NULL);
00326               break;
00327            case BT_SEP:
00328               w = XtVaCreateManagedWidget(item->elems[i].title, xmSeparatorGadgetClass, menu,
00329                                        NULL);
00330               break;
00331            default:
00332               XDVI_WARNING((stderr, "unrecognized button type %d in menu %s (skipping this item).\n",
00333                            item->elems[i].type, item->elems[i].title));
00334               break;
00335            }
00336            item->elems[i].widget = w;
00337        }
00338 
00339        if (item->elems[i].mnemonic != 0)
00340            XtVaSetValues(w, XmNmnemonic, item->elems[i].mnemonic, NULL);
00341        
00342        if (item->elems[i].accelerator != NULL) {
00343            str = XmStringCreateLocalized(item->elems[i].accelerator);
00344            XtVaSetValues(w, XmNacceleratorText, str, NULL);
00345            XmStringFree(str);
00346        }
00347        
00348        if (item->elems[i].action != NULL) {
00349            String cb_type;
00350            if (XmIsToggleButton(w) || XmIsToggleButtonGadget(w))
00351               cb_type = XmNvalueChangedCallback;
00352            else
00353               cb_type = XmNactivateCallback;
00354            XtAddCallback(w, cb_type, handle_command, item->elems[i].action);
00355        }
00356     }
00357     return cascade;
00358 }
00359 
00360 #else
00361 /* silence `empty compilation unit' warnings */
00362 static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); }
00363 #endif /* MOTIF */
00364