# HG changeset patch # User Jason Rumney # Date 948597741 0 # Node ID da3ee40952bf10fe5828d3d521a7ed22045aa1ee # Parent 22e4449438ff39c2a1ff1d64dbd9ade3a6e6316c Add skeleton support for help strings on menus. (add_menu_item): Native checkbox and radio support added, but not yet enabled due to bugs. (push_menu_item): Add parameters type, selection and help. Callers updated. Formatting changes to reduce unnecessary diffs with xmenu.c. diff -r 22e4449438ff -r da3ee40952bf src/w32menu.c --- a/src/w32menu.c Sun Jan 23 03:21:01 2000 +0000 +++ b/src/w32menu.c Sun Jan 23 03:22:21 2000 +0000 @@ -20,6 +20,7 @@ #include #include + #include #include "lisp.h" #include "termhooks.h" @@ -41,6 +42,10 @@ #include "dispextern.h" +#undef HAVE_MULTILINGUAL_MENU +#undef HAVE_BOXES /* NTEMACS_TODO: Fix native checkmarks and radios. */ +#undef HAVE_DIALOGS /* NTEMACS_TODO: Fix native dialogs. */ + /******************************************************************/ /* Definitions copied from lwlib.h */ @@ -50,6 +55,7 @@ #define True 1 #define False 0 +#if 0 /* Not used below. */ typedef enum _change_type { NO_CHANGE = 0, @@ -57,6 +63,14 @@ VISIBLE_CHANGE = 2, STRUCTURAL_CHANGE = 3 } change_type; +#endif + +enum button_type +{ + BUTTON_TYPE_NONE, + BUTTON_TYPE_TOGGLE, + BUTTON_TYPE_RADIO +}; typedef struct _widget_value { @@ -66,10 +80,14 @@ char* value; /* keyboard equivalent. no implications for XtTranslations */ char* key; + /* Help string or null if none. */ + char *help; /* true if enabled */ Boolean enabled; /* true if selected */ Boolean selected; + /* The type of a button. */ + enum button_type button_type; /* true if menu title */ Boolean title; #if 0 @@ -133,8 +151,11 @@ void set_frame_menubar (); +static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object, + Lisp_Object, Lisp_Object, Lisp_Object, + Lisp_Object, Lisp_Object)); +static Lisp_Object w32_dialog_show (); static Lisp_Object w32_menu_show (); -static Lisp_Object w32_dialog_show (); static void keymap_panes (); static void single_keymap_panes (); @@ -168,12 +189,18 @@ #define MENU_ITEMS_PANE_PREFIX 2 #define MENU_ITEMS_PANE_LENGTH 3 -#define MENU_ITEMS_ITEM_NAME 0 -#define MENU_ITEMS_ITEM_ENABLE 1 -#define MENU_ITEMS_ITEM_VALUE 2 -#define MENU_ITEMS_ITEM_EQUIV_KEY 3 -#define MENU_ITEMS_ITEM_DEFINITION 4 -#define MENU_ITEMS_ITEM_LENGTH 5 +enum menu_item_idx +{ + MENU_ITEMS_ITEM_NAME = 0, + MENU_ITEMS_ITEM_ENABLE, + MENU_ITEMS_ITEM_VALUE, + MENU_ITEMS_ITEM_EQUIV_KEY, + MENU_ITEMS_ITEM_DEFINITION, + MENU_ITEMS_ITEM_TYPE, + MENU_ITEMS_ITEM_SELECTED, + MENU_ITEMS_ITEM_HELP, + MENU_ITEMS_ITEM_LENGTH +}; static Lisp_Object menu_items; @@ -194,6 +221,8 @@ Xt on behalf of one of the widget sets. */ static int popup_activated_flag; +static int next_menubar_widget_id; + /* This is set nonzero after the user activates the menu bar, and set to zero again after the menu bars are redisplayed by prepare_menu_bar. While it is nonzero, all calls to set_frame_menubar go deep. @@ -205,10 +234,11 @@ /* Return the frame whose ->output_data.w32->menubar_widget equals - MENU, or 0 if none. */ + ID, or 0 if none. */ static struct frame * -menubar_id_to_frame (HMENU menu) +menubar_id_to_frame (id) + HMENU id; { Lisp_Object tail, frame; FRAME_PTR f; @@ -219,9 +249,9 @@ if (!GC_FRAMEP (frame)) continue; f = XFRAME (frame); - if (!FRAME_W32_P (f)) + if (!FRAME_WINDOW_P (f)) continue; - if (f->output_data.w32->menubar_widget == menu) + if (f->output_data.w32->menubar_widget == id) return f; } return 0; @@ -334,17 +364,17 @@ XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec; } -/* Push one menu item into the current pane. - NAME is the string to display. ENABLE if non-nil means - this item can be selected. KEY is the key generated by - choosing this item, or nil if this item doesn't really have a definition. - DEF is the definition of this item. - EQUIV is the textual description of the keyboard equivalent for - this item (or nil if none). */ +/* Push one menu item into the current pane. NAME is the string to + display. ENABLE if non-nil means this item can be selected. KEY + is the key generated by choosing this item, or nil if this item + doesn't really have a definition. DEF is the definition of this + item. EQUIV is the textual description of the keyboard equivalent + for this item (or nil if none). TYPE is the type of this menu + item, one of nil, `toggle' or `radio'. */ static void -push_menu_item (name, enable, key, def, equiv) - Lisp_Object name, enable, key, def, equiv; +push_menu_item (name, enable, key, def, equiv, type, selected, help) + Lisp_Object name, enable, key, def, equiv, type, selected, help; { if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated) grow_menu_items (); @@ -354,6 +384,9 @@ XVECTOR (menu_items)->contents[menu_items_used++] = key; XVECTOR (menu_items)->contents[menu_items_used++] = equiv; XVECTOR (menu_items)->contents[menu_items_used++] = def; + XVECTOR (menu_items)->contents[menu_items_used++] = type; + XVECTOR (menu_items)->contents[menu_items_used++] = selected; + XVECTOR (menu_items)->contents[menu_items_used++] = help; } /* Look through KEYMAPS, a vector of keymaps that is NMAPS long, @@ -475,7 +508,7 @@ int maxdepth, notreal; int *notbuttons_ptr; { - Lisp_Object def, map, item_string, enabled; + Lisp_Object map, item_string, enabled; struct gcpro gcpro1, gcpro2; int res; @@ -570,7 +603,7 @@ if (!NILP (prefix)) item_string = concat2 (prefix, item_string); } -#endif /* not HAVE_BOXES */ +#endif /* HAVE_BOXES */ #if 0 if (!NILP(map)) @@ -580,7 +613,10 @@ push_menu_item (item_string, enabled, key, XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF], - XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ]); + XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ], + XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE], + XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED], + XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]); #if 1 /* Display a submenu using the toolkit. */ @@ -632,7 +668,7 @@ { item = Fcar (tail); if (STRINGP (item)) - push_menu_item (item, Qnil, Qnil, Qt, Qnil); + push_menu_item (item, Qnil, Qnil, Qt, Qnil, Qnil, Qnil, Qnil); else if (NILP (item)) push_left_right_boundary (); else @@ -640,7 +676,7 @@ CHECK_CONS (item, 0); item1 = Fcar (item); CHECK_STRING (item1, 1); - push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil); + push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil, Qnil, Qnil, Qnil); } } } @@ -681,13 +717,11 @@ (position, menu) Lisp_Object position, menu; { - int number_of_panes, panes; Lisp_Object keymap, tem; int xpos, ypos; Lisp_Object title; char *error_name; Lisp_Object selection; - int i, j; FRAME_PTR f; Lisp_Object x, y, window; int keymaps = 0; @@ -704,9 +738,9 @@ || (CONSP (position) && EQ (XCAR (position), Qmenu_bar))) { /* Use the mouse's current position. */ - FRAME_PTR new_f = selected_frame; + FRAME_PTR new_f = SELECTED_FRAME (); Lisp_Object bar_window; - int part; + enum scroll_bar_part part; unsigned long time; if (mouse_position_hook) @@ -757,9 +791,9 @@ CHECK_LIVE_WINDOW (window, 0); f = XFRAME (WINDOW_FRAME (XWINDOW (window))); - xpos = (FONT_WIDTH (f->output_data.w32->font) + xpos = (FONT_WIDTH (FRAME_FONT (f)) * XFASTINT (XWINDOW (window)->left)); - ypos = (f->output_data.w32->line_height + ypos = (FRAME_LINE_HEIGHT (f) * XFASTINT (XWINDOW (window)->top)); } else @@ -903,7 +937,7 @@ { #if 0 /* Using the frame the mouse is on may not be right. */ /* Use the mouse's current position. */ - FRAME_PTR new_f = selected_frame; + FRAME_PTR new_f = SELECTED_FRAME (); Lisp_Object bar_window; int part; unsigned long time; @@ -949,7 +983,7 @@ but I don't want to make one now. */ CHECK_WINDOW (window, 0); -#if 1 +#ifndef HAVE_DIALOGS /* Display a menu with these alternatives in the middle of frame F. */ { @@ -962,7 +996,7 @@ return Fx_popup_menu (newpos, Fcons (Fcar (contents), Fcons (contents, Qnil))); } -#else +#else /* HAVE_DIALOGS */ { Lisp_Object title; char *error_name; @@ -984,7 +1018,7 @@ if (error_name) error (error_name); return selection; } -#endif +#endif /* HAVE_DIALOGS */ } /* Activate the menu bar of frame F. @@ -999,6 +1033,7 @@ This way we can safely execute Lisp code. */ +void x_activate_menubar (f) FRAME_PTR f; { @@ -1148,7 +1183,6 @@ int len; Lisp_Object *mapvec; widget_value **submenu_stack; - int mapno; int previous_items = menu_items_used; int top_level_items = 0; @@ -1177,7 +1211,8 @@ as opposed to a submenu. */ top_level_items = 1; push_menu_pane (Qnil, Qnil); - push_menu_item (item_name, Qt, item_key, mapvec[i], Qnil); + push_menu_item (item_name, Qt, item_key, mapvec[i], + Qnil, Qnil, Qnil, Qnil); } else single_keymap_panes (mapvec[i], item_name, item_key, 0, 10); @@ -1192,6 +1227,7 @@ wv->name = "menu"; wv->value = 0; wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; first_wv = wv; save_wv = 0; prev_wv = 0; @@ -1230,6 +1266,10 @@ char *pane_string; pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME]; prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX]; +#ifndef HAVE_MULTILINGUAL_MENU + if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name)) + pane_name = string_make_unibyte (pane_name); +#endif pane_string = (NILP (pane_name) ? "" : (char *) XSTRING (pane_name)->data); /* If there is just one top-level pane, put all its items directly @@ -1254,6 +1294,7 @@ wv->name++; wv->value = 0; wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; } save_wv = wv; prev_wv = 0; @@ -1262,12 +1303,24 @@ else { /* Create a new item within current pane. */ - Lisp_Object item_name, enable, descrip, def; + Lisp_Object item_name, enable, descrip, def, type, selected; + Lisp_Object help; + item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME]; enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE]; descrip = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY]; def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION]; + type = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_TYPE]; + selected = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_SELECTED]; + help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP]; + +#ifndef HAVE_MULTILINGUAL_MENU + if (STRING_MULTIBYTE (item_name)) + item_name = string_make_unibyte (item_name); + if (STRINGP (descrip) && STRING_MULTIBYTE (descrip)) + descrip = string_make_unibyte (descrip); +#endif wv = xmalloc_widget_value (); if (prev_wv) @@ -1283,6 +1336,20 @@ as long as pointers have enough bits to hold small integers. */ wv->call_data = (!NILP (def) ? (void *) (EMACS_INT) i : 0); wv->enabled = !NILP (enable); + + if (NILP (type)) + wv->button_type = BUTTON_TYPE_NONE; + else if (EQ (type, QCradio)) + wv->button_type = BUTTON_TYPE_RADIO; + else if (EQ (type, QCtoggle)) + wv->button_type = BUTTON_TYPE_TOGGLE; + else + abort (); + + wv->selected = !NILP (selected); + if (STRINGP (help)) + wv->help = XSTRING (help)->data; + prev_wv = wv; i += MENU_ITEMS_ITEM_LENGTH; @@ -1312,7 +1379,7 @@ int deep_p; { HMENU menubar_widget = f->output_data.w32->menubar_widget; - Lisp_Object tail, items, frame; + Lisp_Object items; widget_value *wv, *first_wv, *prev_wv = 0; int i; @@ -1331,6 +1398,7 @@ wv->name = "menubar"; wv->value = 0; wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; first_wv = wv; if (deep_p) @@ -1403,6 +1471,7 @@ first_wv->contents = wv; /* Don't set wv->name here; GC during the loop might relocate it. */ wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; prev_wv = wv; } @@ -1446,30 +1515,9 @@ else { /* Make a widget-value tree containing - just the top level menu bar strings. - - It turns out to be worth comparing the new contents with the - previous contents to avoid unnecessary rebuilding even of just - the top-level menu bar, which turns out to be fairly slow. We - co-opt f->menu_bar_vector for this purpose, since its contents - are effectively discarded at this point anyway. - - Note that the lisp-level hooks have already been run by - update_menu_bar - it's kinda a shame the code is duplicated - above as well for deep_p, but there we are. */ + just the top level menu bar strings. */ items = FRAME_MENU_BAR_ITEMS (f); - - /* If there has been no change in the Lisp-level contents of just - the menu bar itself, skip redisplaying it. Just exit. */ - for (i = 0; i < f->menu_bar_items_used; i += 4) - if (i == XVECTOR (items)->size - || (XVECTOR (f->menu_bar_vector)->contents[i] - != XVECTOR (items)->contents[i])) - break; - if (i == XVECTOR (items)->size && i == f->menu_bar_items_used && i != 0) - return; - for (i = 0; i < XVECTOR (items)->size; i += 4) { Lisp_Object string; @@ -1482,6 +1530,7 @@ wv->name = (char *) XSTRING (string)->data; wv->value = 0; wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; /* This prevents lwlib from assuming this menu item is really supposed to be empty. */ /* The EMACS_INT cast avoids a warning. @@ -1495,16 +1544,10 @@ prev_wv = wv; } - /* Remember the contents of FRAME_MENU_BAR_ITEMS (f) in - f->menu_bar_vector, so we can check whether the top-level - menubar contents have changed next time. */ - if (XVECTOR (f->menu_bar_vector)->size < XVECTOR (items)->size) - f->menu_bar_vector - = Fmake_vector (make_number (XVECTOR (items)->size), Qnil); - bcopy (XVECTOR (items)->contents, - XVECTOR (f->menu_bar_vector)->contents, - XVECTOR (items)->size * sizeof (Lisp_Object)); - f->menu_bar_items_used = XVECTOR (items)->size; + /* Forget what we thought we knew about what is in the + detailed contents of the menu bar menus. + Changing the top level always destroys the contents. */ + f->menu_bar_items_used = 0; } /* Create or update the menu bar widget. */ @@ -1612,7 +1655,6 @@ Lisp_Object *subprefix_stack = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object)); int submenu_depth = 0; - int first_pane; int next_release_must_exit = 0; @@ -1630,6 +1672,7 @@ wv->name = "menu"; wv->value = 0; wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; first_wv = wv; first_pane = 1; @@ -1666,6 +1709,10 @@ char *pane_string; pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME]; prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX]; +#ifndef HAVE_MULTILINGUAL_MENU + if (!NILP (pane_name) && STRING_MULTIBYTE (pane_name)) + pane_name = string_make_unibyte (pane_name); +#endif pane_string = (NILP (pane_name) ? "" : (char *) XSTRING (pane_name)->data); /* If there is just one top-level pane, put all its items directly @@ -1688,6 +1735,7 @@ wv->name++; wv->value = 0; wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; save_wv = wv; prev_wv = 0; } @@ -1702,12 +1750,21 @@ else { /* Create a new item within current pane. */ - Lisp_Object item_name, enable, descrip, def; + Lisp_Object item_name, enable, descrip, def, type, selected; item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME]; enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE]; descrip = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY]; def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION]; + type = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_TYPE]; + selected = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_SELECTED]; + +#ifndef HAVE_MULTILINGUAL_MENU + if (STRINGP (item_name) && STRING_MULTIBYTE (item_name)) + item_name = string_make_unibyte (item_name); + if (STRINGP (descrip) && STRING_MULTIBYTE (descrip)) + item_name = string_make_unibyte (descrip); +#endif wv = xmalloc_widget_value (); if (prev_wv) @@ -1722,6 +1779,18 @@ restricted to 16-bits.. */ wv->call_data = !NILP (def) ? (void *) (EMACS_INT) i : 0; wv->enabled = !NILP (enable); + + if (NILP (type)) + wv->button_type = BUTTON_TYPE_NONE; + else if (EQ (type, QCtoggle)) + wv->button_type = BUTTON_TYPE_TOGGLE; + else if (EQ (type, QCradio)) + wv->button_type = BUTTON_TYPE_RADIO; + else + abort (); + + wv->selected = !NILP (selected); + prev_wv = wv; i += MENU_ITEMS_ITEM_LENGTH; @@ -1739,9 +1808,13 @@ wv_sep->name = "--"; wv_sep->next = first_wv->contents; +#ifndef HAVE_MULTILINGUAL_MENU + if (STRING_MULTIBYTE (title)) + title = string_make_unibyte (title); +#endif wv_title->name = (char *) XSTRING (title)->data; - /* Handle title specially, so it looks better. */ - wv_title->title = True; + wv_title->enabled = True; + wv_title->button_type = BUTTON_TYPE_NONE; wv_title->next = wv_sep; first_wv->contents = wv_title; } @@ -1749,7 +1822,7 @@ /* Actually create the menu. */ menu = CreatePopupMenu (); fill_in_menu (menu, first_wv->contents); - + /* Adjust coordinates to be root-window-relative. */ pos.x = x; pos.y = y; @@ -1952,7 +2025,7 @@ } /* Actually create the dialog. */ -#if 0 +#ifdef HAVE_DIALOGS dialog_id = widget_id_tick++; menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv, f->output_data.w32->widget, 1, 0, @@ -1967,7 +2040,7 @@ menu_item_selection = 0; /* Display the menu. */ -#if 0 +#ifdef HAVE_DIALOGS lw_pop_up_all_widgets (dialog_id); popup_activated_flag = 1; @@ -2069,6 +2142,47 @@ #endif fuFlags = MF_OWNERDRAW | MF_DISABLED; } + +#ifdef HAVE_BOXES + /* Draw radio buttons and tickboxes. */ + { + switch (wv->button_type) + { + case BUTTON_TYPE_TOGGLE: + CheckMenuItem (menu, (UINT)item, + wv->selected ? MF_CHECKED : MF_UNCHECKED); + break; + + case BUTTON_TYPE_RADIO: + /* CheckMenuRadioItem does not exist on NT 3.51 and + earlier. Fallback on CheckMenuItem. */ + { + HMODULE user32 = GetModuleHandle ("user32.dll"); + FARPROC set_menu_item_info + = GetProcAddress (user32, "SetMenuItemInfo"); + if (set_menu_item_info) + { + MENUITEMINFO info; + bzero (&info, sizeof (info)); + info.cbSize = sizeof (info); + info.fMask = MIIM_TYPE | MIIM_STATE; + info.fType = MFT_RADIOCHECK; + info.fState = wv->selected ? MFS_CHECKED : MFS_UNCHECKED; + + set_menu_item_info (menu, item, FALSE, &info); + } + else + CheckMenuItem (menu, (UINT)item, wv->selected ? + MF_CHECKED : MF_UNCHECKED); + } + break; + + default: + CheckMenuItem (menu, (UINT)item, MF_UNCHECKED); + break; + } + } +#endif } if (item != NULL) @@ -2111,6 +2225,13 @@ return 1; } +int +popup_activated () +{ + /* popup_activated_flag not actually used on W32 */ + return 0; +} + #endif /* HAVE_MENUS */ syms_of_w32menu ()