This is uimenu.c in view mode; [Download] [Up]
/* * uimenu.c - Simple and ugly cascaded pop-up menu implementation. * * Written by * Ettore Perazzoli (ettore@comm2000.it) * * This file is part of VICE, the Versatile Commodore Emulator. * See README for copyright notice. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA. * */ /* Warning: this code sucks. It does work, but it sucks. */ #include "vice.h" #include <stdio.h> #include <stdarg.h> #include <stdlib.h> #include <X11/Xlib.h> #include <X11/Intrinsic.h> #include <X11/Xaw/SimpleMenu.h> #include <X11/Xaw/SmeLine.h> #include <X11/Xaw/SmeBSB.h> #include "checkmark.xbm" #include "right_arrow.xbm" #include "uimenu.h" #include "vsync.h" #include "utils.h" /* Separator item. */ ui_menu_entry_t ui_menu_separator[] = { { "--" }, { NULL }, }; static Display *display; static int screen; /* Bitmaps for the menus: "tick" and right arrow (for submenus). */ static Pixmap checkmark_bitmap, right_arrow_bitmap; static int menu_popup = 0; #define MAX_SUBMENUS 1024 static struct { Widget widget; Widget parent; int level; } submenus[MAX_SUBMENUS]; static int num_submenus = 0; static Widget active_submenu, active_entry; static int submenu_popped_up = 0; static Widget top_menu; /* This keeps a list of the menus with a checkmark on the left. Each time some setting is changed, we have to update them. */ #define MAX_UPDATE_MENU_LIST_SIZE 1024 static Widget checkmark_menu_items[MAX_UPDATE_MENU_LIST_SIZE]; static int num_checkmark_menu_items = 0; /* ------------------------------------------------------------------------- */ /* This makes sure the submenu is fully visible. */ static void position_submenu(Widget w, Widget parent) { Position parent_x, parent_y, my_x, my_y; Dimension parent_width, my_width, my_height; int root_width, root_height, foo; Window foowin; /* Make sure the widget is realized--otherwise, we get 0 as width and height. */ XtRealizeWidget(w); XtVaGetValues(parent, XtNx, &parent_x, XtNy, &parent_y, XtNwidth, &parent_width, NULL); XtVaGetValues(w, XtNwidth, &my_width, XtNheight, &my_height, NULL); XtTranslateCoords(XtParent(parent), parent_x, parent_y, &parent_x, &parent_y); my_x = parent_x + parent_width - 2; my_y = parent_y + 1; XGetGeometry(display, RootWindow(display, screen), &foowin, &foo, &foo, &root_width, &root_height, &foo, &foo); if (my_x + my_width > root_width) my_x -= my_width + parent_width - 2; if (my_y + my_height > root_height) my_y = root_height - my_height; XtVaSetValues(w, XtNx, my_x, XtNy, my_y, NULL); XtPopup(w, XtGrabNonexclusive); } static UI_CALLBACK(menu_popup_callback) { if (menu_popup == 0) top_menu = w; menu_popup++; suspend_speed_eval(); } static UI_CALLBACK(menu_popdown_callback) { if (menu_popup > 0) menu_popup--; else top_menu = NULL; } static UI_CALLBACK(submenu_popup_callback) { submenu_popped_up++; } static UI_CALLBACK(submenu_popdown_callback) { submenu_popped_up--; if (XawSimpleMenuGetActiveEntry(w)) XtPopdown((Widget)client_data); } /* Yes, this sucks. Sorry. */ static void position_submenu_action(Widget w, XEvent * event, String * params, Cardinal * num_params) { Widget new_active_submenu, new_active_entry; new_active_entry = XawSimpleMenuGetActiveEntry(w); if (new_active_entry != active_entry) { int i, level; int level_found, active_found; new_active_submenu = NULL; /* Find the submenu for the current active menu item and the level of this submenu. */ for (level_found = active_found = 0, level = 0, i = 0; i < num_submenus && !(level_found && active_found); i++) { if (!active_found && submenus[i].parent == new_active_entry) { new_active_submenu = submenus[i].widget; active_found = 1; } if (!level_found && submenus[i].widget == w) { level = submenus[i].level; level_found = 1; } } /* Remove all the submenus whose level is higher than this submenu. */ for (i = 0; i < num_submenus; i++) { if (submenus[i].level > level) XtPopdown(submenus[i].widget); } /* Position the submenu for this menu item. */ if (new_active_submenu != NULL && new_active_entry != NULL) position_submenu(new_active_submenu, new_active_entry); active_submenu = new_active_submenu; active_entry = new_active_entry; } } static void popdown_submenus_action(Widget w, XEvent * event, String * params, Cardinal * num_params) { int i; if (menu_popup == 0) return; /* Pop down all the submenus and the top ones. */ for (i = 0; i < num_submenus; i++) XtPopdown(submenus[i].widget); XtPopdown(top_menu); top_menu = NULL; menu_popup = 0; } static void menu_unhighlight_action(Widget w, XEvent * event, String * params, Cardinal * num_params) { XtCallActionProc(w, "unhighlight", event, params, *num_params); } /* ------------------------------------------------------------------------- */ static char *make_menu_label(ui_menu_entry_t *e) { const char *key_string; char *tmp = alloca(1024); if (e->hotkey_keysym == (KeySym) 0) return e->string; *tmp = '\0'; if (e->hotkey_modifier & UI_HOTMOD_CONTROL) strcat(tmp, "C-"); if (e->hotkey_modifier & UI_HOTMOD_META) strcat(tmp, "M-"); if (e->hotkey_modifier & UI_HOTMOD_ALT) strcat(tmp, "A-"); if (e->hotkey_modifier & UI_HOTMOD_SHIFT) strcat(tmp, "S-"); key_string = strchr(XKeysymToString(e->hotkey_keysym), '_'); if (key_string == NULL) key_string = XKeysymToString(e->hotkey_keysym); else key_string++; return concat(e->string, " (", tmp, key_string, ")", NULL); } /* ------------------------------------------------------------------------- */ int ui_menu_init(XtAppContext app_context, Display *d, int s) { static XtActionsRec actions[] = { { "PositionSubmenu", position_submenu_action }, { "Popdownsubmenus", popdown_submenus_action }, { "Unhighlight", menu_unhighlight_action } }; display = d; screen = s; checkmark_bitmap = XCreateBitmapFromData(display, DefaultRootWindow(display), checkmark_bits, checkmark_width, checkmark_height); right_arrow_bitmap = XCreateBitmapFromData(display, DefaultRootWindow(display), right_arrow_bits, right_arrow_width, right_arrow_height); XtAppAddActions(app_context, actions, XtNumber(actions)); XawSimpleMenuAddGlobalActions(app_context); return 0; } Widget ui_menu_create(const char *name, ...) { static int level = 0; Widget w; unsigned int i, j; ui_menu_entry_t *list; va_list ap; level++; w = XtCreatePopupShell(name, simpleMenuWidgetClass, _ui_top_level, NULL, 0); if (level == 1) { XtAddCallback(w, XtNpopupCallback, menu_popup_callback, NULL); XtAddCallback(w, XtNpopdownCallback, menu_popdown_callback, NULL); } XtOverrideTranslations (w, XtParseTranslationTable ("<BtnMotion>: highlight() PositionSubmenu()\n" "@Num_Lock<BtnMotion>: highlight() PositionSubmenu()\n" "<LeaveWindow>: Unhighlight()\n" "<BtnUp>: Popdownsubmenus() MenuPopdown() notify() unhighlight()")); va_start(ap, name); while ((list = va_arg(ap, ui_menu_entry_t *)) != NULL) { for (i = j = 0; list[i].string; i++) { Widget new_item; char name[256]; sprintf(name, "MenuItem%d", j); /* ugly... */ switch (*list[i].string) { case '-': /* line */ new_item = XtCreateManagedWidget("separator", smeLineObjectClass, w, NULL, 0); break; case '*': /* toggle */ new_item = XtVaCreateManagedWidget(name, smeBSBObjectClass, w, XtNrightMargin, 20, XtNleftMargin, 20, XtNlabel, make_menu_label(&list[i]) + 1, NULL); /* Add this item to the list of calls to perform to update the menu status. */ if (list[i].callback) { if (num_checkmark_menu_items < MAX_UPDATE_MENU_LIST_SIZE) checkmark_menu_items[num_checkmark_menu_items++] = new_item; else { fprintf(stderr, "Maximum number of menus reached! " "Please fix the code.\n"); exit(-1); } } j++; break; default: new_item = XtVaCreateManagedWidget(name, smeBSBObjectClass, w, XtNleftMargin, 20, XtNrightMargin, 20, XtNlabel, make_menu_label(&list[i]), NULL); j++; } if (list[i].callback) XtAddCallback(new_item, XtNcallback, (XtCallbackProc) list[i].callback, list[i].callback_data); if (list[i].sub_menu) { Widget sub; if (num_submenus > MAX_SUBMENUS) { fprintf(stderr, "Maximum number of sub menus reached! " "Please fix the code.\n"); exit(-1); } XtVaSetValues(new_item, XtNrightBitmap, right_arrow_bitmap, NULL); sub = ui_menu_create("SUB", list[i].sub_menu, NULL); submenus[num_submenus].widget = sub; submenus[num_submenus].parent = new_item; submenus[num_submenus].level = level; XtAddCallback(sub, XtNpopupCallback, submenu_popup_callback, submenus + num_submenus); XtAddCallback(sub, XtNpopdownCallback, submenu_popdown_callback, (XtPointer) w); num_submenus++; } else { /* no submenu */ if (list[i].hotkey_keysym != (KeySym) 0 && list[i].callback != NULL) ui_hotkey_register(list[i].hotkey_modifier, list[i].hotkey_keysym, list[i].callback, list[i].callback_data); } } } level--; va_end(ap); return w; } int ui_menu_any_open(void) { return menu_popup; } void ui_menu_update_all(void) { int i; for (i = 0; i < num_checkmark_menu_items; i++) XtCallCallbacks(checkmark_menu_items[i], XtNcallback, (XtPointer) !NULL); } void ui_menu_set_tick(Widget w, int flag) { XtVaSetValues(w, XtNleftBitmap, flag ? checkmark_bitmap : 0, NULL); } void ui_menu_set_sensitive(Widget w, int flag) { XtVaSetValues(w, XtNsensitive, flag, NULL); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.