Back to index

tetex-bin  3.0
menu.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2003-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  * Common code for Xaw and Motif menu bar creation.
00026  */
00027 
00028 #include "xdvi-config.h"
00029 #include "xdvi.h"
00030 #include "events.h"
00031 #include "menu.h"
00032 #include "util.h"
00033 
00034 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
00035 
00036 #ifdef MOTIF
00037 #  include <Xm/RowColumn.h>
00038 #  include <Xm/ToggleB.h>
00039 #else
00040 #  include <X11/Intrinsic.h>
00041 #  include <X11/Xatom.h>
00042 #  include <X11/StringDefs.h>
00043 #  include <X11/Xaw/Label.h>
00044 #endif
00045 
00046 
00047 /* translate string argument into corresponding buttonTypeT */
00048 static buttonTypeT
00049 get_type(const char *str)
00050 {
00051     if (strcmp(str, "PUSH") == 0)
00052        return BT_PUSH;
00053     else if (strcmp(str, "RADIO") == 0)
00054        return BT_RADIO;
00055     else if (strcmp(str, "CHECK") == 0)
00056        return BT_CHECK;
00057     else if (strcmp(str, "SEP") == 0)
00058        return BT_SEP;
00059     else
00060        return BT_INVALID;
00061 }
00062 
00063 static struct button_info *m_button_info = NULL; /* toplevel node of pulldown menu structure */
00064 
00065 
00066 static void
00067 set_menu_info(void *val, XtActionProc proc, Boolean (*cmp)(), struct button_info *item)
00068 {
00069     size_t i;
00070     ASSERT(item != NULL, "item in set_menu_info musn't be NULL!");
00071     for (i = 0; i < item->size; i++) {
00072        if ((item->elems[i].type == BT_RADIO || item->elems[i].type == BT_CHECK)
00073            && item->elems[i].action != NULL
00074            && item->elems[i].action->proc != NULL
00075            && item->elems[i].action->proc == proc
00076            && item->elems[i].action->param != NULL) {
00077            Boolean on;
00078            ASSERT(cmp != NULL, "comparison function musn't be NULL!");
00079            on = cmp(val, item->elems[i].action->param);
00080 #ifdef MOTIF
00081            ASSERT(item->elems[i].widget != 0, "Widget musn't be NULL!");
00082            XmToggleButtonSetState(item->elems[i].widget, on, False);
00083 #else
00084 #ifdef NEW_MENU_CREATION
00085            ASSERT(item->elems[i].widget != 0, "Widget musn't be NULL!");
00086            xaw_set_button_state(item->elems + i, on);
00087 #else
00088            static Arg args = { XtNleftBitmap, (XtArgVal) 0 };
00089            if (on)
00090               args.value = (XtArgVal) menu_check_bitmap;
00091            else
00092               args.value = (XtArgVal) menu_uncheck_bitmap;
00093            XtSetValues(item->elems[i].widget, &args, 1);
00094 #endif /* NEW_MENU_CREATION */
00095 #endif
00096        }
00097        if (item->elems[i].submenu != NULL) { /* invoke it recursively */
00098            set_menu_info(val, proc, cmp, item->elems[i].submenu);
00099        }
00100     }
00101 }
00102 
00103 
00104 /* set a menu according to val and the compare function cmp */
00105 void
00106 set_menu(void *val, XtActionProc proc, Boolean (*cmp)())
00107 {
00108     /* non-standard cast function ptr -> void ptr */
00109     /* TRACE_GUI((stderr, "set_menu_info: %d, %p, %p", *(int *)val, (void *)proc, (void *)cmp)); */
00110     set_menu_info(val, proc, cmp, m_button_info);
00111 }
00112 
00113 static void
00114 initialize_menus(void)
00115 {
00116     int use_gs;
00117     int shrinkval;
00118     
00119     /* initialize tickmarks for all possible actions */
00120     use_gs = resource.postscript;
00121 #ifdef PS_GS
00122     if (!resource.useGS)
00123        use_gs = 0;
00124 #endif
00125 
00126     set_menu(&use_gs, Act_set_ps, check_int);
00127     set_menu(&resource.gs_alpha, Act_set_gs_alpha, check_toggle);
00128     set_menu(&resource.keep_flag, Act_set_keep_flag, check_toggle);
00129     shrinkval = resource.pixels_per_inch / mane.shrinkfactor;
00130     set_menu(&shrinkval, Act_shrink_to_dpi, check_int);
00131     set_menu(&mane.shrinkfactor, Act_set_shrink_factor, check_int);
00132     set_menu(&resource.use_tex_pages, Act_use_tex_pages, check_toggle);
00133     set_menu((char *)resource.paper,  Act_set_paper_landscape, check_paper_landscape);
00134     set_menu((char *)resource.paper, Act_set_papersize, check_papersize);
00135     set_menu(&resource.mouse_mode, Act_switch_mode, check_int);
00136     set_menu(&resource.expert_mode, Act_set_expert_mode, check_resource_expert);
00137 }
00138 
00139 static void
00140 free_items(char **items, size_t len)
00141 {
00142     size_t curr = 0;
00143     while(curr < len) {
00144        free(items[curr++]);
00145     }
00146     free(items);
00147 }
00148 
00149 #if 0
00150 static void
00151 show_items(char *descr, char **items, size_t len)
00152 {
00153     size_t i;
00154     for (i = 0; i < len; i++) {
00155        fprintf(stderr, "%s %d: |%s|\n", descr, i, items[i]);
00156     }
00157 }
00158 #endif
00159 
00160 static void
00161 add_info(struct button_info **info, buttonTypeT bt_type,
00162         char mnemonic, const char *title,
00163         const char *accelerator, struct xdvi_action *action)
00164 {
00165 /*      fprintf(stderr, "creating new item for info %p: |%s|\n", *info, title); */
00166     size_t idx = (*info)->size++;
00167     (*info)->elems = xrealloc((*info)->elems, (*info)->size * sizeof *((*info)->elems));
00168     (*info)->elems[idx].title = xstrdup(title);
00169     (*info)->elems[idx].type = bt_type;
00170     if (accelerator == NULL || accelerator[0] == '\0')
00171        (*info)->elems[idx].accelerator = NULL;
00172     else
00173        (*info)->elems[idx].accelerator = xstrdup(accelerator);
00174     (*info)->elems[idx].mnemonic = mnemonic;
00175     (*info)->elems[idx].action = action;
00176     (*info)->elems[idx].widget = 0;
00177     (*info)->elems[idx].submenu = NULL;    
00178 }
00179 
00180 static void
00181 insert_items(struct button_info **info, char **items, size_t num_items,
00182             const char *button_type, const char *accelerator, const char *action)
00183 {
00184     const char ENTRY_SEP = '|';
00185     size_t i = 0;
00186     size_t entry_len;
00187     size_t entry_count = 0;
00188     char **entry_items = NULL;
00189     size_t idx;
00190     
00191     Boolean found = False;
00192     Boolean have_error = False;
00193 
00194 /*      fprintf(stderr, "------ insert_items called for |%s|, %p, |%s|\n", *items, *info, action); */
00195 
00196     if (*items == NULL) {
00197        /* should be a separator, which is treated as a special case since
00198           there's no menu title: */
00199        if (strcmp (button_type, "SEP") == 0)
00200            add_info(info, BT_SEP, '\0', "SEP", NULL, NULL);
00201        else
00202            XDVI_WARNING((stderr, "Shouldn't happen: items == NULL!"));
00203        return;
00204     }
00205        
00206     entry_len = strlen(items[0]);
00207     entry_items = split_line(items[0], ENTRY_SEP, 0, entry_len, &entry_count);
00208 
00209     if (entry_count < 2) {
00210        XDVI_WARNING((stderr, "Missing Mnemonic in button info `%s'\n", items[0]));
00211        entry_count++;
00212        entry_items = xrealloc(entry_items, entry_count * sizeof *entry_items);
00213        entry_items[1] = xstrdup("");
00214        entry_items[2] = NULL;
00215     }
00216     
00217     for (i = 0; i < (*info)->size; i++) {
00218        if (strcmp(entry_items[0], (*info)->elems[i].title) == 0) {
00219            found = true;
00220            break;
00221        }
00222     }
00223 
00224     idx = i;
00225 
00226     if (!found) { /* new item, resize info and add this item */
00227        struct xdvi_action *my_action = NULL;
00228        buttonTypeT my_type = BT_NONE;
00229 
00230        /* if it's a `leaf' in the menu hierarchy, compile the action and set the button type */
00231        if (num_items == 1) {
00232 /*         fprintf(stderr, "button_type: |%s|\n", button_type); */
00233 
00234            char *fmt = strchr(entry_items[0], '$');
00235            if (fmt != NULL
00236               && (fmt == entry_items[0] || (fmt > entry_items[0] && *(fmt - 1) != '\\'))
00237               && (fmt[1] == '#' || fmt[1] == '%' ||fmt[1] == '_')) {
00238               XDVI_WARNING((stderr, "Xdvik doesn't support format characters in button labels; "
00239                            "skipping button \"%s\"", items[0]));
00240               have_error = True;
00241            }
00242            
00243            if (strlen(action) == 0
00244               || (my_action = compile_action(action)) == NULL) {
00245               XDVI_WARNING((stderr, "Invalid action \"%s\" for button \"%s\" (skipping this line).",
00246                            action, items[0]));
00247               have_error = True;
00248            }
00249 
00250            if ((my_type = get_type(button_type)) == BT_INVALID) {
00251               XDVI_WARNING((stderr, "Invalid type \"%s\" for button \"%s\" (skipping this line).",
00252                            button_type, items[0]));
00253               have_error = True;
00254            }
00255        }
00256        
00257        if (!have_error) {
00258            add_info(info, my_type, entry_items[1][0], entry_items[0], accelerator, my_action);
00259        }
00260     }
00261     free_items(entry_items, entry_count);
00262 
00263     if (num_items > 1 || (num_items == 1 && strcmp(button_type, "SEP") == 0)) { /* not a leaf, invoke recursivly for next level */
00264 /*     fprintf(stderr, "invoking recursively for %d, %p\n", idx, (*info)->elems[idx].submenu); */
00265        if ((*info)->elems[idx].submenu == NULL) { /* submenu didn't exist yet, create it */
00266            struct button_info *new_submenu = xmalloc(sizeof *new_submenu);
00267            new_submenu->elems = NULL;
00268            new_submenu->size = 0;
00269            (*info)->elems[idx].submenu = new_submenu;
00270        }
00271        insert_items(&((*info)->elems[idx].submenu), items + 1, num_items - 1, button_type, accelerator, action);
00272     }
00273 }
00274 
00275 static void
00276 show_button_info(int depth, struct button_info *info)
00277 {
00278     size_t i;
00279     for (i = 0; i < info->size; i++) {
00280        TRACE_GUI((stderr, "%*c-->%s; type=%d; mnemonic=%c; accel=%s; submenu=%p; w=%lu; action: %p",
00281                  depth, ' ',
00282                  info->elems[i].title,
00283                  info->elems[i].type,
00284                  info->elems[i].mnemonic,
00285                  info->elems[i].accelerator ? info->elems[i].accelerator : "<NULL>",
00286                  (void *)info->elems[i].submenu,
00287                  (unsigned long)info->elems[i].widget,
00288                  (void *)info->elems[i].action));
00289        if (info->elems[i].submenu != NULL) {
00290            show_button_info(depth + 3, info->elems[i].submenu);
00291        }
00292     }
00293 }
00294 
00295 static void
00296 parse_button_translations(struct button_info **info)
00297 {
00298     const char *curr_p, *end_p;
00299     
00300     const char LINE_SEP = ':';
00301     const char MENU_SEP = '>';
00302     
00303     for (curr_p = resource.menu_translations;
00304         curr_p != NULL && *curr_p != '\0';
00305         curr_p = end_p + 1) {
00306        end_p = strchr(curr_p, '\n');
00307        if (end_p != NULL) {
00308            size_t line_len = end_p - curr_p;
00309            size_t line_count = 0;
00310            char **line_items;
00311            line_items = split_line(curr_p, LINE_SEP, 0, line_len, &line_count);
00312 /*         fprintf(stderr, "length of line: %d; %d items\n", line_len, line_count); */
00313 /*         show_items("LINE", line_items, line_count); */
00314            if (line_count != 4) { /* error */
00315               XDVI_WARNING((stderr, "Wrong number of items (%lu) in translations line:\n\"%.*s\" "
00316                            "(skipping this line).\n",
00317                            (unsigned long)line_count, (int)line_len, curr_p));
00318               free_items(line_items, line_count);
00319               continue;
00320            }
00321            else { /* split first elem into menu description */
00322               size_t menu_len = strlen(line_items[0]);
00323               size_t menu_count = 0;
00324               char **menu_items;
00325               if (menu_len == 0) { /* error */
00326                   XDVI_WARNING((stderr, "Menu description (1st item) mustn't be empty:\n\"%.*s\" "
00327                               "(skipping this line).\n",
00328                               (int)line_len, curr_p));
00329                   free_items(line_items, line_count);
00330                   continue;
00331               }
00332               menu_items = split_line(line_items[0], MENU_SEP, 0, menu_len, &menu_count);
00333 /*            show_items("   MENU", menu_items, menu_count); */
00334 
00335               insert_items(info, menu_items, menu_count, line_items[1], line_items[2], line_items[3]);
00336               free_items(menu_items, menu_count);
00337            }
00338            free_items(line_items, line_count);
00339        }
00340     }
00341     show_button_info(0, *info);
00342 }
00343 
00344 /*
00345   Top-level routine: creates the pulldown menu buttons for Motif and Xaw.
00346   For Motif, sets *globals.widgets.menu_bar to the address of the new widget created,
00347   for Xaw, sets *width to the width of the button panel created.
00348 */
00349 void
00350 create_menu_buttons(Widget parent,
00351 #ifdef MOTIF
00352                   Widget *menu_bar
00353 #else
00354                   int *width
00355 #endif
00356                   )
00357 {
00358 #ifdef MOTIF
00359     Widget menu = 0;
00360 #else /* MOTIF */
00361     Widget panel = 0;
00362     Boolean first_time = True;
00363     int menu_depth = 0;
00364 #endif
00365     size_t i;
00366     
00367     m_button_info = xmalloc(sizeof *m_button_info);
00368     m_button_info->elems = NULL;
00369     m_button_info->size = 0;
00370 
00371 #ifdef MOTIF
00372     *menu_bar = XmCreateMenuBar(parent, "menuBar", NULL, 0);
00373     parse_button_translations(&m_button_info);
00374 /*      menu = xm_create_menu(*menu_bar, m_button_info); */
00375     for (i = 0; i < m_button_info->size; i++) {
00376        menu = xm_create_menu(*menu_bar,
00377                            m_button_info->elems[i].title,
00378                            m_button_info->elems[i].mnemonic,
00379                            m_button_info->elems[i].submenu);
00380     }
00381     if (menu != 0) {
00382        XtVaSetValues(*menu_bar, XmNmenuHelpWidget, menu, NULL);
00383     }
00384 #else /* MOTIF */
00385     if (first_time) {
00386        first_time = False;
00387        xaw_initialize_menu_bitmaps();
00388        panel = xaw_create_menu_widgets(parent);
00389        parse_button_translations(&m_button_info);
00390     }
00391     xaw_create_menu(m_button_info, panel, menu_depth, width);
00392 #endif /* MOTIF */
00393     initialize_menus();
00394 }
00395 #endif /* NEW_MENU_CREATION || MOTIF */