/* * Copyright (c) 2003-2004 the xdvik development team * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * */ /* * Common code for Xaw and Motif menu bar creation. */ #include "xdvi-config.h" #include "xdvi.h" #include "events.h" #include "menu.h" #include "util.h" #if defined(NEW_MENU_CREATION) || defined(MOTIF) #ifdef MOTIF # include # include #else # include # include # include # include #endif /* translate string argument into corresponding buttonTypeT */ static buttonTypeT get_type(const char *str) { if (strcmp(str, "PUSH") == 0) return BT_PUSH; else if (strcmp(str, "RADIO") == 0) return BT_RADIO; else if (strcmp(str, "CHECK") == 0) return BT_CHECK; else if (strcmp(str, "SEP") == 0) return BT_SEP; else return BT_INVALID; } static struct button_info *m_button_info = NULL; /* toplevel node of pulldown menu structure */ static void set_menu_info(void *val, XtActionProc proc, Boolean (*cmp)(), struct button_info *item) { size_t i; ASSERT(item != NULL, "item in set_menu_info musn't be NULL!"); for (i = 0; i < item->size; i++) { if ((item->elems[i].type == BT_RADIO || item->elems[i].type == BT_CHECK) && item->elems[i].action != NULL && item->elems[i].action->proc != NULL && item->elems[i].action->proc == proc && item->elems[i].action->param != NULL) { Boolean on; ASSERT(cmp != NULL, "comparison function musn't be NULL!"); on = cmp(val, item->elems[i].action->param); #ifdef MOTIF ASSERT(item->elems[i].widget != 0, "Widget musn't be NULL!"); XmToggleButtonSetState(item->elems[i].widget, on, False); #else #ifdef NEW_MENU_CREATION ASSERT(item->elems[i].widget != 0, "Widget musn't be NULL!"); xaw_set_button_state(item->elems + i, on); #else static Arg args = { XtNleftBitmap, (XtArgVal) 0 }; if (on) args.value = (XtArgVal) menu_check_bitmap; else args.value = (XtArgVal) menu_uncheck_bitmap; XtSetValues(item->elems[i].widget, &args, 1); #endif /* NEW_MENU_CREATION */ #endif } if (item->elems[i].submenu != NULL) { /* invoke it recursively */ set_menu_info(val, proc, cmp, item->elems[i].submenu); } } } /* set a menu according to val and the compare function cmp */ void set_menu(void *val, XtActionProc proc, Boolean (*cmp)()) { /* non-standard cast function ptr -> void ptr */ /* TRACE_GUI((stderr, "set_menu_info: %d, %p, %p", *(int *)val, (void *)proc, (void *)cmp)); */ set_menu_info(val, proc, cmp, m_button_info); } static void initialize_menus(void) { int use_gs; int shrinkval; /* initialize tickmarks for all possible actions */ use_gs = resource.postscript; #ifdef PS_GS if (!resource.useGS) use_gs = 0; #endif set_menu(&use_gs, Act_set_ps, check_int); set_menu(&resource.gs_alpha, Act_set_gs_alpha, check_toggle); set_menu(&resource.keep_flag, Act_set_keep_flag, check_toggle); shrinkval = resource.pixels_per_inch / mane.shrinkfactor; set_menu(&shrinkval, Act_shrink_to_dpi, check_int); set_menu(&mane.shrinkfactor, Act_set_shrink_factor, check_int); set_menu(&resource.use_tex_pages, Act_use_tex_pages, check_toggle); set_menu((char *)resource.paper, Act_set_paper_landscape, check_paper_landscape); set_menu((char *)resource.paper, Act_set_papersize, check_papersize); set_menu(&resource.mouse_mode, Act_switch_mode, check_int); set_menu(&resource.expert_mode, Act_set_expert_mode, check_resource_expert); } static void free_items(char **items, size_t len) { size_t curr = 0; while(curr < len) { free(items[curr++]); } free(items); } #if 0 static void show_items(char *descr, char **items, size_t len) { size_t i; for (i = 0; i < len; i++) { fprintf(stderr, "%s %d: |%s|\n", descr, i, items[i]); } } #endif static void add_info(struct button_info **info, buttonTypeT bt_type, char mnemonic, const char *title, const char *accelerator, struct xdvi_action *action) { /* fprintf(stderr, "creating new item for info %p: |%s|\n", *info, title); */ size_t idx = (*info)->size++; (*info)->elems = xrealloc((*info)->elems, (*info)->size * sizeof *((*info)->elems)); (*info)->elems[idx].title = xstrdup(title); (*info)->elems[idx].type = bt_type; if (accelerator == NULL || accelerator[0] == '\0') (*info)->elems[idx].accelerator = NULL; else (*info)->elems[idx].accelerator = xstrdup(accelerator); (*info)->elems[idx].mnemonic = mnemonic; (*info)->elems[idx].action = action; (*info)->elems[idx].widget = 0; (*info)->elems[idx].submenu = NULL; } static void insert_items(struct button_info **info, char **items, size_t num_items, const char *button_type, const char *accelerator, const char *action) { const char ENTRY_SEP = '|'; size_t i = 0; size_t entry_len; size_t entry_count = 0; char **entry_items = NULL; size_t idx; Boolean found = False; Boolean have_error = False; /* fprintf(stderr, "------ insert_items called for |%s|, %p, |%s|\n", *items, *info, action); */ if (*items == NULL) { /* should be a separator, which is treated as a special case since there's no menu title: */ if (strcmp (button_type, "SEP") == 0) add_info(info, BT_SEP, '\0', "SEP", NULL, NULL); else XDVI_WARNING((stderr, "Shouldn't happen: items == NULL!")); return; } entry_len = strlen(items[0]); entry_items = split_line(items[0], ENTRY_SEP, 0, entry_len, &entry_count); if (entry_count < 2) { XDVI_WARNING((stderr, "Missing Mnemonic in button info `%s'\n", items[0])); entry_count++; entry_items = xrealloc(entry_items, entry_count * sizeof *entry_items); entry_items[1] = xstrdup(""); entry_items[2] = NULL; } for (i = 0; i < (*info)->size; i++) { if (strcmp(entry_items[0], (*info)->elems[i].title) == 0) { found = true; break; } } idx = i; if (!found) { /* new item, resize info and add this item */ struct xdvi_action *my_action = NULL; buttonTypeT my_type = BT_NONE; /* if it's a `leaf' in the menu hierarchy, compile the action and set the button type */ if (num_items == 1) { /* fprintf(stderr, "button_type: |%s|\n", button_type); */ char *fmt = strchr(entry_items[0], '$'); if (fmt != NULL && (fmt == entry_items[0] || (fmt > entry_items[0] && *(fmt - 1) != '\\')) && (fmt[1] == '#' || fmt[1] == '%' ||fmt[1] == '_')) { XDVI_WARNING((stderr, "Xdvik doesn't support format characters in button labels; " "skipping button \"%s\"", items[0])); have_error = True; } if (strlen(action) == 0 || (my_action = compile_action(action)) == NULL) { XDVI_WARNING((stderr, "Invalid action \"%s\" for button \"%s\" (skipping this line).", action, items[0])); have_error = True; } if ((my_type = get_type(button_type)) == BT_INVALID) { XDVI_WARNING((stderr, "Invalid type \"%s\" for button \"%s\" (skipping this line).", button_type, items[0])); have_error = True; } } if (!have_error) { add_info(info, my_type, entry_items[1][0], entry_items[0], accelerator, my_action); } } free_items(entry_items, entry_count); if (num_items > 1 || (num_items == 1 && strcmp(button_type, "SEP") == 0)) { /* not a leaf, invoke recursivly for next level */ /* fprintf(stderr, "invoking recursively for %d, %p\n", idx, (*info)->elems[idx].submenu); */ if ((*info)->elems[idx].submenu == NULL) { /* submenu didn't exist yet, create it */ struct button_info *new_submenu = xmalloc(sizeof *new_submenu); new_submenu->elems = NULL; new_submenu->size = 0; (*info)->elems[idx].submenu = new_submenu; } insert_items(&((*info)->elems[idx].submenu), items + 1, num_items - 1, button_type, accelerator, action); } } static void show_button_info(int depth, struct button_info *info) { size_t i; for (i = 0; i < info->size; i++) { TRACE_GUI((stderr, "%*c-->%s; type=%d; mnemonic=%c; accel=%s; submenu=%p; w=%lu; action: %p", depth, ' ', info->elems[i].title, info->elems[i].type, info->elems[i].mnemonic, info->elems[i].accelerator ? info->elems[i].accelerator : "", (void *)info->elems[i].submenu, (unsigned long)info->elems[i].widget, (void *)info->elems[i].action)); if (info->elems[i].submenu != NULL) { show_button_info(depth + 3, info->elems[i].submenu); } } } static void parse_button_translations(struct button_info **info) { const char *curr_p, *end_p; const char LINE_SEP = ':'; const char MENU_SEP = '>'; for (curr_p = resource.menu_translations; curr_p != NULL && *curr_p != '\0'; curr_p = end_p + 1) { end_p = strchr(curr_p, '\n'); if (end_p != NULL) { size_t line_len = end_p - curr_p; size_t line_count = 0; char **line_items; line_items = split_line(curr_p, LINE_SEP, 0, line_len, &line_count); /* fprintf(stderr, "length of line: %d; %d items\n", line_len, line_count); */ /* show_items("LINE", line_items, line_count); */ if (line_count != 4) { /* error */ XDVI_WARNING((stderr, "Wrong number of items (%lu) in translations line:\n\"%.*s\" " "(skipping this line).\n", (unsigned long)line_count, (int)line_len, curr_p)); free_items(line_items, line_count); continue; } else { /* split first elem into menu description */ size_t menu_len = strlen(line_items[0]); size_t menu_count = 0; char **menu_items; if (menu_len == 0) { /* error */ XDVI_WARNING((stderr, "Menu description (1st item) mustn't be empty:\n\"%.*s\" " "(skipping this line).\n", (int)line_len, curr_p)); free_items(line_items, line_count); continue; } menu_items = split_line(line_items[0], MENU_SEP, 0, menu_len, &menu_count); /* show_items(" MENU", menu_items, menu_count); */ insert_items(info, menu_items, menu_count, line_items[1], line_items[2], line_items[3]); free_items(menu_items, menu_count); } free_items(line_items, line_count); } } show_button_info(0, *info); } /* Top-level routine: creates the pulldown menu buttons for Motif and Xaw. For Motif, sets *globals.widgets.menu_bar to the address of the new widget created, for Xaw, sets *width to the width of the button panel created. */ void create_menu_buttons(Widget parent, #ifdef MOTIF Widget *menu_bar #else int *width #endif ) { #ifdef MOTIF Widget menu = 0; #else /* MOTIF */ Widget panel = 0; Boolean first_time = True; int menu_depth = 0; #endif size_t i; m_button_info = xmalloc(sizeof *m_button_info); m_button_info->elems = NULL; m_button_info->size = 0; #ifdef MOTIF *menu_bar = XmCreateMenuBar(parent, "menuBar", NULL, 0); parse_button_translations(&m_button_info); /* menu = xm_create_menu(*menu_bar, m_button_info); */ for (i = 0; i < m_button_info->size; i++) { menu = xm_create_menu(*menu_bar, m_button_info->elems[i].title, m_button_info->elems[i].mnemonic, m_button_info->elems[i].submenu); } if (menu != 0) { XtVaSetValues(*menu_bar, XmNmenuHelpWidget, menu, NULL); } #else /* MOTIF */ if (first_time) { first_time = False; xaw_initialize_menu_bitmaps(); panel = xaw_create_menu_widgets(parent); parse_button_translations(&m_button_info); } xaw_create_menu(m_button_info, panel, menu_depth, width); #endif /* MOTIF */ initialize_menus(); } #endif /* NEW_MENU_CREATION || MOTIF */