Mercurial > hg > xemacs-beta
diff src/menubar-msw.c @ 231:557eaa0339bf r20-5b14
Import from CVS: tag r20-5b14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:13:48 +0200 |
parents | |
children | 52952cbfc5b5 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/menubar-msw.c Mon Aug 13 10:13:48 2007 +0200 @@ -0,0 +1,1045 @@ +/* Implements an elisp-programmable menubar -- Win32 + Copyright (C) 1993, 1994 Free Software Foundation, Inc. + Copyright (C) 1995 Tinker Systems and INS Engineering Corp. + Copyright (C) 1997 Kirill M. Katsnelson <kkm@kis.ru> + +This file is part of XEmacs. + +XEmacs 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, or (at your option) any +later version. + +XEmacs 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 XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Not in FSF. */ + +/* Autorship: + Initially written by kkm 12/24/97, + peeking into and copying stuff from menubar-x.c + */ + +/* Algotirhm for handling menus is as follows. When window's menubar + * is created, current-menubar is not traversed in depth. Rather, only + * top level items, both items and pulldowns, are added to the + * menubar. Each pulldown is initially empty. When a pulldown is + * selected and about to open, corresponding element of + * current-menubar is found, and the newly open pulldown is + * populated. This is made again in the same non-recursive manner. + * + * This algorithm uses hash tables to find out element of the menu + * descriptor list given menu handle. The key is an opaque ptr data + * type, keeping menu handle, and the value is a list of strings + * representing the path from the root of the menu to the item + * descriptor. Each frame has an associated hashtable. + * + * Leaf items are assigned a unique id based on item's hash. When an + * item is selected, Windows sends back the id. Unfortunately, only + * low 16 bit of the ID are sent, and there's no way to get the 32-bit + * value. Yes, Win32 is just a different set of bugs than X! Aside + * from this blame, another hasing mechanism is required to map menu + * ids to commands (which are actually Lisp_Object's). This mapping is + * performed in the same hashtable, as the lifetime of both maps is + * exactly the same. This is unabmigous, as menu handles are + * represented by lisp opaques, while command ids are by lisp + * integers. The additional advantage for this is that command forms + * are automatically GC-protected, which is important because these + * may be transient forms generated by :filter functions. + * + * The hashtable is not allowed to grow too much; it is pruned + * whenever this is safe to do. This is done by re-creating the menu + * bar, and clearing and refilling the hash table from scratch. + * + * Popup menus are handled identially to pulldowns. A static hash + * table is used for popup menus, and lookup is made not in + * current-menubar but in a lisp form supplied to the `popup' + * function. + * + * Another Windows weirdness is that there's no way to tell that a + * popup has been dismissed without making selection. We need to know + * that to cleanup the popup menu hashtable, but this is not honestly + * doable using *documented* sequence of messages. Sticking to + * particular knowledge is bad because this may break in Windows NT + * 5.0, or Windows 98, or other future version. Instead, I allow the + * hashtables to hang around, and not clear them, unless WM_COMMAND is + * received. This is worthy some memory but more safe. Hacks welcome, + * anyways! + * + */ + +#include <config.h> +#include "lisp.h" + +#include "buffer.h" +#include "commands.h" +#include "console-msw.h" +#include "emacsfns.h" +#include "elhash.h" +#include "event-msw.h" +#include "events.h" +#include "frame.h" +#include "gui.h" +#include "lisp.h" +#include "menubar.h" +#include "menubar-msw.h" +#include "opaque.h" +#include "window.h" + +#define EMPTY_ITEM_ID ((UINT)LISP_TO_VOID (Qunbound)) +#define EMPTY_ITEM_NAME "(empty)" + +/* Qnil when there's no popup being tracked, or a descriptor + for the popup. gcpro'ed */ +static Lisp_Object current_tracking_popup; + +/* Current popup has table. Qnil when no popup. gcpro'ed */ +static Lisp_Object current_popup_hash_table; + +/* Bound by menubar.el */ +static Lisp_Object Qfind_menu_item; + +/* This is used to allocate unique ids to menu items. + Items ids are in MENU_ITEM_ID_MIN to MENU_ITEM_ID_MAX. + Allocation checks that the item is not already in + the TOP_LEVEL_MENU */ +/* #### defines go to gui-msw.h */ +#define MENU_ITEM_ID_MIN 0x8000 +#define MENU_ITEM_ID_MAX 0xFFFF +#define MENU_ITEM_ID_BITS(x) ((x) & 0x7FFF | 0x8000) +static HMENU top_level_menu; + +/* ============= THIS STUFF MIGHT GO SOMEWHERE ELSE ================= */ + +/* All these functions are windows sys independent, and are candidates + to go to lisp code instead */ + +/* + * DESCRIPTOR is a list in the form ({:keyword value}+ rest...). + * This function extracts all the key-value pairs into the newly + * created plist, and returns pointer to REST. Original list is not + * modified (heaven save!) + */ +Lisp_Object +gui_parse_menu_keywords (Lisp_Object descriptor, Lisp_Object *plist) +{ + Lisp_Object pair, key, val; + *plist = Qnil; + LIST_LOOP (pair, descriptor) + { + if (!CONSP(pair)) + signal_simple_error ("Mailformed gui entity descriptor", descriptor); + key = XCAR(pair); + if (!KEYWORDP (key)) + return pair; + pair = XCDR (pair); + if (!CONSP(pair)) + signal_simple_error ("Mailformed gui entity descriptor", descriptor); + val = XCAR (pair); + internal_plist_put (plist, key, val); + } + return pair; +} + +/* + * DESC is a vector describing a menu item. The function returns menu + * item name in NAME, callback form in CALLBACK, and all key-values + * pairs in PLIST. For old-style vectors, the plist is faked. + */ +void +gui_parse_button_descriptor (Lisp_Object desc, Lisp_Object *name, + Lisp_Object *callback, Lisp_Object *plist) +{ + int length = XVECTOR_LENGTH (desc); + Lisp_Object *contents = XVECTOR_DATA (desc); + int plist_p; + + *name = Qnil; + *callback = Qnil; + *plist = Qnil; + + if (length < 3) + signal_simple_error ("button descriptors must be at least 3 long", desc); + + /* length 3: [ "name" callback active-p ] + length 4: [ "name" callback active-p suffix ] + or [ "name" callback keyword value ] + length 5+: [ "name" callback [ keyword value ]+ ] + */ + plist_p = (length >= 5 || KEYWORDP (contents [2])); + + *name = contents [0]; + *callback = contents [1]; + + if (!plist_p) + /* the old way */ + { + internal_plist_put (plist, Q_active, contents [2]); + if (length == 4) + internal_plist_put (plist, Q_suffix, contents [3]); + } + else + /* the new way */ + { + int i; + if (length & 1) + signal_simple_error ( + "button descriptor has an odd number of keywords and values", + desc); + + for (i = 2; i < length;) + { + Lisp_Object key = contents [i++]; + Lisp_Object val = contents [i++]; + if (!KEYWORDP (key)) + signal_simple_error_2 ("not a keyword", key, desc); + internal_plist_put (plist, key, val); + } + } +} + +/* + * Given PLIST of key-value pairs for a menu item or button, consult + * :included and :config properties (the latter against + * CONFLIST). Return value is non-zero when item should *not* appear. + */ +int +gui_plist_says_item_excluded (Lisp_Object plist, Lisp_Object conflist) +{ + Lisp_Object tem; + /* This function can call lisp */ + + /* Evaluate :included first */ + tem = internal_plist_get (plist, Q_included); + if (!UNBOUNDP (tem)) + { + tem = Feval (tem); + if (NILP (tem)) + return 1; + } + + /* Do :config if conflist is given */ + if (!NILP (conflist)) + { + tem = internal_plist_get (plist, Q_config); + if (!UNBOUNDP (tem)) + { + tem = Fmemq (tem, conflist); + if (NILP (tem)) + return 1; + } + } + + return 0; +} + +/* + * Given PLIST of key-value pairs for a menu item or button, consult + * :active property. Return non-zero if the item is *inactive* + */ +int +gui_plist_says_item_inactive (Lisp_Object plist) +{ + Lisp_Object tem; + /* This function can call lisp */ + + tem = internal_plist_get (plist, Q_active); + if (!UNBOUNDP (tem)) + { + tem = Feval (tem); + if (NILP (tem)) + return 1; + } + + return 0; +} + +/* + * Given PLIST of key-value pairs for a menu item or button, evaluate + * the form which is the value of :filter property. Filter function + * given DESC as argument. If there's no :filter property, DESC is + * returned, otherwise the value returned by the filter function is + * returned. + */ +Lisp_Object +gui_plist_apply_filter (Lisp_Object plist, Lisp_Object desc) +{ + Lisp_Object tem; + /* This function can call lisp */ + + tem = internal_plist_get (plist, Q_filter); + if (UNBOUNDP (tem)) + return desc; + else + return call1 (tem, desc); +} + +/* + * This is tricky because there's no menu item styles in Windows, only + * states: Each item may be given no checkmark, radio or check + * mark. This function returns required mark style as determined by + * PLIST. Return value is the value of :style property if the item is + * :seleted, or nil otherwise + */ +Lisp_Object +gui_plist_get_current_style (Lisp_Object plist) +{ + Lisp_Object style, selected; + style = internal_plist_get (plist, Q_style); + if (UNBOUNDP (style) || NILP(style)) + return Qnil; + + selected = internal_plist_get (plist, Q_selected); + if (UNBOUNDP (selected) || NILP(Feval(selected))) + return Qnil; + + return style; +} + +Lisp_Object +current_frame_menubar (CONST struct frame* f) +{ + struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f)); + return symbol_value_in_buffer (Qcurrent_menubar, w->buffer); +} + +/* ============ END IF STUFF THAT MIGHT GO SOMEWHERE ELSE =============== */ + +/* Change these together */ +#define MAX_MENUITEM_LENGTH 128 +#define DISPLAYABLE_MAX_MENUITEM_LENGTH "128" + +static void +signal_item_too_long (Lisp_Object name) +{ + signal_simple_error ("Menu item is longer than " + DISPLAYABLE_MAX_MENUITEM_LENGTH + " characters", name); +} + +/* #### If this function returned (FLUSHLEFT . FLUSHRIGHT) it also + could be moved above that line - it becomes window system + independant */ +/* + * This returns Windows-style menu item string: + * "Left Flush\tRight Flush" + */ +static CONST char* +plist_get_menu_item_name (Lisp_Object name, Lisp_Object callback, Lisp_Object plist) +{ + /* We construct the name in a static buffer. That's fine, beause + menu items longer than 128 chars are probably programming errors, + and better be caught than displayed! */ + + static char buf[MAX_MENUITEM_LENGTH]; + char* p = buf; + int buf_left = MAX_MENUITEM_LENGTH - 1; + Lisp_Object tem; + + /* Get name first */ + buf_left -= XSTRING_LENGTH (name); + if (buf_left < 0) + signal_item_too_long (name); + strcpy (p, XSTRING_DATA (name)); + p += XSTRING_LENGTH (name); + + /* Have suffix? */ + tem = internal_plist_get (plist, Q_suffix); + if (!UNBOUNDP (tem)) + { + if (!STRINGP (tem)) + signal_simple_error (":suffix must be a string", tem); + buf_left -= XSTRING_LENGTH (tem) + 1; + if (buf_left < 0) + signal_item_too_long (name); + *p++ = ' '; + strcpy (p, XSTRING_DATA (tem)); + p += XSTRING_LENGTH (tem); + } + + /* Have keys? */ + if (menubar_show_keybindings) + { + static char buf2 [1024]; + buf2[0] = 0; + + tem = internal_plist_get (plist, Q_keys); + if (!UNBOUNDP (tem)) + { + if (!STRINGP (tem)) + signal_simple_error (":keys must be a string", tem); + if (XSTRING_LENGTH (tem) > sizeof (buf2) - 1) + signal_item_too_long (name); + strcpy (buf2, XSTRING_DATA (tem)); + } + else if (SYMBOLP (callback)) + { + /* #### Warning, dependency here on current_buffer and point */ + /* #### I've borrowed this warning along with this code from + menubar-x.c. What does that mean? -- kkm */ + where_is_to_char (callback, buf2); + } + + if (buf2 [0]) + { + int n = strlen (buf2) + 1; + buf_left -= n; + if (buf_left < 0) + signal_item_too_long (name); + *p++ = '\t'; + strcpy (p, buf2); + p += n-1; + } + } + + *p = 0; + return buf; +} + +/* + * hmenu_to_lisp_object() returns an opaque ptr given menu handle. + */ +static Lisp_Object +hmenu_to_lisp_object (HMENU hmenu) +{ + return make_opaque_ptr (hmenu); +} + +/* + * Allocation tries a hash based on item's path and name first. This + * almost guarantees that the same item will override its old value in + * the hashtable rather than abandon it. + */ +static Lisp_Object +allocate_menu_item_id (Lisp_Object path, Lisp_Object name) +{ + UINT id = MENU_ITEM_ID_BITS (HASH2 (internal_hash (path, 0), + internal_hash (name, 0))); + do { + id = MENU_ITEM_ID_BITS (id + 1); + } while (GetMenuState (top_level_menu, id, MF_BYCOMMAND) != 0xFFFFFFFF); + return make_int (id); +} + +static HMENU +create_empty_popup_menu (void) +{ + HMENU submenu = CreatePopupMenu (); + /* #### It seems that really we do not need "(empty)" at this stage */ +#if 0 + AppendMenu (submenu, MF_STRING | MF_GRAYED, EMPTY_ITEM_ID, EMPTY_ITEM_NAME); +#endif + return submenu; +} + +static void +empty_menu (HMENU menu, int add_empty_p) +{ + while (DeleteMenu (menu, 0, MF_BYPOSITION)); + if (add_empty_p) + AppendMenu (menu, MF_STRING | MF_GRAYED, EMPTY_ITEM_ID, EMPTY_ITEM_NAME); +} + +static void +populate_menu_add_item (HMENU menu, Lisp_Object path, + Lisp_Object hash_tab, Lisp_Object item, int flush_right) +{ + MENUITEMINFO item_info; + struct gcpro gcpro1, gcpro2; + + item_info.cbSize = sizeof (item_info); + item_info.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID; + item_info.fState = 0; + item_info.wID = 0; + item_info.fType = 0; + + if (STRINGP (item)) + { + /* Separator or unselectable text */ + if (separator_string_p (XSTRING_DATA (item))) + item_info.fType = MFT_SEPARATOR; + else + { + item_info.fType = MFT_STRING; + item_info.fState = MFS_DISABLED; + item_info.dwTypeData = XSTRING_DATA (item); + } + } + else if (CONSP (item)) + { + /* Submenu */ + Lisp_Object subname = XCAR (item); + Lisp_Object plist; + HMENU submenu; + + if (!STRINGP (subname)) + signal_simple_error ("menu name (first element) must be a string", item); + + item = gui_parse_menu_keywords (XCDR (item), &plist); + GCPRO1 (plist); + + if (gui_plist_says_item_excluded (plist, Vmenubar_configuration)) + return; + + if (gui_plist_says_item_inactive (plist)) + item_info.fState = MFS_GRAYED; + /* Temptation is to put 'else' right here. Although, the + displayed item won't have an arrow indicating that it is a + popup. So we go ahead a little bit more and create a popup */ + submenu = create_empty_popup_menu(); + + item_info.fMask |= MIIM_SUBMENU; + item_info.dwTypeData = plist_get_menu_item_name (subname, Qnil, plist); + item_info.hSubMenu = submenu; + + UNGCPRO; /* plist */ + + if (!(item_info.fState & MFS_GRAYED)) + { + /* Now add the full submenu path as a value to the hash table, + keyed by menu handle */ + if (NILP(path)) + path = list1 (subname); + else { + Lisp_Object arg[2]; + arg[0] = path; + arg[1] = list1 (subname); + GCPRO1 (arg[1]); + path = Fappend (2, arg); + UNGCPRO; /* arg[1] */ + } + + GCPRO1 (path); + Fputhash (hmenu_to_lisp_object (submenu), path, hash_tab); + UNGCPRO; /* path */ + } + } + else if (VECTORP (item)) + { + /* An ordinary item */ + Lisp_Object plist, name, callback, style, id; + + gui_parse_button_descriptor (item, &name, &callback, &plist); + GCPRO2 (plist, callback); + + if (gui_plist_says_item_excluded (plist, Vmenubar_configuration)) + return; + + if (gui_plist_says_item_inactive (plist)) + item_info.fState |= MFS_GRAYED; + + style = gui_plist_get_current_style (plist); + if (EQ (style, Qradio)) + { + item_info.fType |= MFT_RADIOCHECK; + item_info.fState |= MFS_CHECKED; + } + else if (EQ (style, Qtoggle)) + { + item_info.fState |= MFS_CHECKED; + } + + id = allocate_menu_item_id (path, name); + Fputhash (id, callback, hash_tab); + + UNGCPRO; /* plist, callback */ + + item_info.wID = (UINT) XINT(id); + item_info.fType |= MFT_STRING; + item_info.dwTypeData = plist_get_menu_item_name (name, callback, plist); + } + else + { + signal_simple_error ("ill-constructed menu descriptor", item); + } + + if (flush_right) + item_info.fType |= MFT_RIGHTJUSTIFY; + + InsertMenuItem (menu, UINT_MAX, TRUE, &item_info); +} + +static void +populate_menu (HMENU menu, Lisp_Object path, Lisp_Object descriptor, + Lisp_Object hash_tab, int bar_p) +{ + Lisp_Object menu_name, plist, item_desc; + int deep_p, flush_right; + struct gcpro gcpro1; + + /* Will initially contain only "(empty)" */ + empty_menu (menu, 1); + + /* PATH set to nil indicates top-level popup or menubar */ + deep_p = !NILP (path); + + if (!deep_p) + top_level_menu = menu; + + if (!CONSP(descriptor)) + signal_simple_error ("menu descriptor must be a list", descriptor); + + if (STRINGP (XCAR (descriptor))) + { + menu_name = XCAR (descriptor); + descriptor = XCDR (descriptor); + } + else + { + menu_name = Qnil; + if (deep_p) /* Not a popup or bar */ + signal_simple_error ("menu must have a name", descriptor); + } + + /* Fetch keywords prepending the item list */ + descriptor = gui_parse_menu_keywords (descriptor, &plist); + GCPRO1 (plist); + descriptor = gui_plist_apply_filter (plist, descriptor); + UNGCPRO; /* plist */ + + /* Loop thru the descriptor's CDR and add items for each entry */ + flush_right = 0; + EXTERNAL_LIST_LOOP (item_desc, descriptor) + { + if (NILP (XCAR (item_desc))) + { + if (bar_p) + flush_right = 1; + } + else + populate_menu_add_item (menu, path, hash_tab, + XCAR (item_desc), flush_right); + } + + /* Remove the "(empty)" item, if there are other ones */ + if (GetMenuItemCount (menu) > 1) + RemoveMenu (menu, EMPTY_ITEM_ID, MF_BYCOMMAND); + + /* Add the header to the popup, if told so. The same as in X - an + insensitive item, and a separator (Seems to me, there were + two separators in X... In Windows this looks ugly, anywats. */ + if (!bar_p && !deep_p && popup_menu_titles && !NILP(menu_name)) + { + InsertMenu (menu, 0, MF_BYPOSITION | MF_STRING | MF_DISABLED, + 0, XSTRING_DATA(menu_name)); + InsertMenu (menu, 1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); + SetMenuDefaultItem (menu, 0, MF_BYPOSITION); + } +} + +static Lisp_Object +find_menu (Lisp_Object desc, Lisp_Object path) +{ + /* #### find-menu-item is not what's required here. + Need to write this in C, or improve lisp */ + if (!NILP (path)) + { + desc = call2 (Qfind_menu_item, desc, path); + /* desc is (supposed to be) (ITEM . PARENT). Supposed + to signal but sometimes manages to return nil */ + if (!NILP(desc)) + { + CHECK_CONS (desc); + desc = XCAR (desc); + } + } + return desc; +} + +static void +update_frame_menubar_maybe (struct frame* f) +{ + HMENU menubar = GetMenu (FRAME_MSWINDOWS_HANDLE (f)); + struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f)); + Lisp_Object desc = (!NILP (w->menubar_visible_p) + ? symbol_value_in_buffer (Qcurrent_menubar, w->buffer) + : Qnil); + + if (NILP (desc) && menubar != NULL) + { + /* Menubar has gone */ + FRAME_MSWINDOWS_MENU_HASHTABLE(f) = Qnil; + DestroyMenu (menubar); + DrawMenuBar (FRAME_MSWINDOWS_HANDLE (f)); + return; + } + + if (!NILP (desc) && menubar == NULL) + { + /* Menubar has appeared */ + menubar = CreateMenu (); + goto populate; + } + + if (NILP (desc)) + { + /* We did not have the bar and are not going to */ + return; + } + + /* Now we have to check if the menubar has really changed */ + /* #### For now we do not though */ + + /* We cannot re-create the menu, cause WM_INITMENU does not like that. + We'll clear it instead. */ + empty_menu (menubar, 0); + +populate: + /* Come with empty hash table */ + if (NILP (FRAME_MSWINDOWS_MENU_HASHTABLE(f))) + FRAME_MSWINDOWS_MENU_HASHTABLE(f) = Fmake_hashtable (make_int (50), Qequal); + else + Fclrhash (FRAME_MSWINDOWS_MENU_HASHTABLE(f)); + + Fputhash (hmenu_to_lisp_object (menubar), Qnil, + FRAME_MSWINDOWS_MENU_HASHTABLE(f)); + populate_menu (menubar, Qnil, desc, + FRAME_MSWINDOWS_MENU_HASHTABLE(f), 1); + SetMenu (FRAME_MSWINDOWS_HANDLE (f), menubar); + DrawMenuBar (FRAME_MSWINDOWS_HANDLE (f)); +} + +static void +prune_menubar (struct frame *f) +{ + HMENU menubar = GetMenu (FRAME_MSWINDOWS_HANDLE (f)); + Lisp_Object desc = current_frame_menubar (f); + if (menubar == NULL) + return; + + /* #### If a filter function has set desc to Qnil, this abort() + triggers. To resolve, we must prevent explicitely filters from + mangling with te active menu. In apply_filter probably? + Is copy-tree on the whole menu too expensive? */ + if (NILP(desc)) + /* abort(); */ + return; + + /* We do the trick by removing all items and re-populating top level */ + empty_menu (menubar, 0); + + assert (HASHTABLEP (FRAME_MSWINDOWS_MENU_HASHTABLE(f))); + Fclrhash (FRAME_MSWINDOWS_MENU_HASHTABLE(f)); + + Fputhash (hmenu_to_lisp_object (menubar), Qnil, + FRAME_MSWINDOWS_MENU_HASHTABLE(f)); + populate_menu (menubar, Qnil, desc, + FRAME_MSWINDOWS_MENU_HASHTABLE(f), 1); +} + +/* + * This is called when cleanup is possible. It is better not to + * clean things up at all than do it too earaly! + */ +static void +menu_cleanup (struct frame *f) +{ + /* This function can GC */ + if (!NILP (current_tracking_popup)) + { + current_tracking_popup = Qnil; + current_popup_hash_table = Qnil; + } + else + prune_menubar (f); +} + + +/*------------------------------------------------------------------------*/ +/* Message handlers */ +/*------------------------------------------------------------------------*/ +static Lisp_Object +unsafe_handle_wm_initmenupopup_1 (HMENU menu, struct frame* f) +{ + /* This function can call lisp, beat dogs and stick chewing gum to + everything! */ + + Lisp_Object path, desc, hash_tab; + struct gcpro gcpro1; + + if (!NILP (current_tracking_popup)) + { + desc = current_tracking_popup; + hash_tab = current_popup_hash_table; + } + else + { + desc = current_frame_menubar (f); + hash_tab = FRAME_MSWINDOWS_MENU_HASHTABLE(f); + } + + /* Find which guy is going to explode */ + path = Fgethash (hmenu_to_lisp_object (menu), hash_tab, Qunbound); + assert (!UNBOUNDP (path)); + + /* Now find a desc chunk for it. If none, then probably menu open + hook has played too much games around stuff */ + if (!NILP (path)) + { + desc = find_menu (desc, path); + if (NILP (desc)) + signal_simple_error ("this menu does not exist any more", path); + } + + /* Now, stuff it */ + /* DESC may be generated by filter, so we have to gcpro it */ + GCPRO1 (desc); + populate_menu (menu, path, desc, hash_tab, 0); + UNGCPRO; + return Qt; +} + +static Lisp_Object +unsafe_handle_wm_initmenu_1 (struct frame* f) +{ + /* This function can call lisp */ + /* #### - this menubar update mechanism is expensively anti-social and + the activate-menubar-hook is now mostly obsolete. */ + + /* We simply ignore return value. In any case, we construct the bar + on the fly */ + run_hook (Vactivate_menubar_hook); + + update_frame_menubar_maybe (f); + return Qt; +} + + +#ifdef KKM_DOES_NOT_LIKE_UNDOCS_SOMETIMES + +/* #### This may become wrong in future Windows */ + +static Lisp_Object +unsafe_handle_wm_exitmenuloop_1 (struct frame* f) +{ + if (!NILP (current_tracking_popup)) + prune_menubar (f); + return Qt; +} + +#endif + +/* + * Return value is Qt if we have dispatched the command, + * or Qnil if id has not been mapped to a callback. + * Window procedure may try other targets to route the + * command if we return nil + */ +Lisp_Object +mswindows_handle_wm_command (struct frame* f, WORD id) +{ + /* Try to map the command id through the proper hash table */ + Lisp_Object hash_tab, command, funcsym, frame; + struct gcpro gcpro1; + + if (!NILP (current_tracking_popup)) + hash_tab = current_popup_hash_table; + else + hash_tab = FRAME_MSWINDOWS_MENU_HASHTABLE(f); + + command = Fgethash (make_int (id), hash_tab, Qunbound); + if (UNBOUNDP (command)) + { + menu_cleanup (f); + return Qnil; + } + + /* Need to gcpro because the hashtable may get destroyed + by menu_cleanup(), and will not gcpro the command + any more */ + GCPRO1 (command); + menu_cleanup (f); + + /* Ok, this is our one. Enqueue it. */ +#if 0 + if (SYMBOLP (command)) + Fcall_interactively (command, Qnil, Qnil); + else if (CONSP (command)) + Feval (command); + else + signal_simple_error ("illegal callback", command); +#endif + if (SYMBOLP (command)) + funcsym = Qcall_interactively; + else if (CONSP (command)) + funcsym = Qeval; + else + signal_simple_error ("illegal callback", command); + + XSETFRAME (frame, f); + enqueue_misc_user_event (frame, funcsym, command); + + UNGCPRO; /* command */ + return Qt; +} + + +/*------------------------------------------------------------------------*/ +/* Message handling proxies */ +/*------------------------------------------------------------------------*/ + +static HMENU wm_initmenu_menu; +static struct frame* wm_initmenu_frame; + +static Lisp_Object +unsafe_handle_wm_initmenupopup (Lisp_Object u_n_u_s_e_d) +{ + return unsafe_handle_wm_initmenupopup_1 (wm_initmenu_menu, wm_initmenu_frame); +} + +static Lisp_Object +unsafe_handle_wm_initmenu (Lisp_Object u_n_u_s_e_d) +{ + return unsafe_handle_wm_initmenu_1 (wm_initmenu_frame); +} + +#ifdef KKM_DOES_NOT_LIKE_UNDOCS_SOMETIMES +static Lisp_Object +unsafe_handle_wm_exitmenuloop (Lisp_Object u_n_u_s_e_d) +{ + return unsafe_handle_wm_exitmenuloop_1 (wm_initmenu_frame); +} +#endif + +Lisp_Object +mswindows_handle_wm_initmenupopup (HMENU hmenu, struct frame* frm) +{ + /* We cannot pass hmenu as a lisp object. Use static var */ + wm_initmenu_menu = hmenu; + wm_initmenu_frame = frm; + return mswindows_protect_modal_loop (unsafe_handle_wm_initmenupopup, Qnil); +} + +Lisp_Object +mswindows_handle_wm_initmenu (struct frame* f) +{ + wm_initmenu_frame = f; + return mswindows_protect_modal_loop (unsafe_handle_wm_initmenu, Qnil); +} + +Lisp_Object +mswindows_handle_wm_exitmenuloop (struct frame* f) +{ +#ifdef KKM_DOES_NOT_LIKE_UNDOCS_SOMETIMES + wm_initmenu_frame = f; + return mswindows_protect_modal_loop (unsafe_handle_wm_exitmenuloop, Qnil); +#else + return Qt; +#endif +} + + +/*------------------------------------------------------------------------*/ +/* Methods */ +/*------------------------------------------------------------------------*/ + +static void +mswindows_update_frame_menubars (struct frame* f) +{ + update_frame_menubar_maybe (f); +} + +static void +mswindows_free_frame_menubars (struct frame* f) +{ + FRAME_MSWINDOWS_MENU_HASHTABLE(f) = Qnil; +} + +static void +mswindows_popup_menu (Lisp_Object menu_desc, Lisp_Object event) +{ + struct frame *f = selected_frame (); + struct Lisp_Event *eev = NULL; + HMENU menu; + POINT pt; + int ok; + + if (!NILP (event)) + { + CHECK_LIVE_EVENT (event); + eev = XEVENT (event); + if (eev->event_type != button_press_event + && eev->event_type != button_release_event) + wrong_type_argument (Qmouse_event_p, event); + } + else if (!NILP (Vthis_command_keys)) + { + /* if an event wasn't passed, use the last event of the event sequence + currently being executed, if that event is a mouse event */ + eev = XEVENT (Vthis_command_keys); /* last event first */ + if (eev->event_type != button_press_event + && eev->event_type != button_release_event) + eev = NULL; + } + + /* Default is to put the menu at the point (10, 10) in frame */ + if (eev) + { + pt.x = eev->event.button.x; + pt.y = eev->event.button.y; + ClientToScreen (FRAME_MSWINDOWS_HANDLE (f), &pt); + } + else + pt.x = pt.y = 10; + + if (SYMBOLP (menu_desc)) + menu_desc = Fsymbol_value (menu_desc); + + current_tracking_popup = menu_desc; + current_popup_hash_table = Fmake_hashtable (make_int(10), Qequal); + menu = create_empty_popup_menu(); + Fputhash (hmenu_to_lisp_object (menu), Qnil, current_popup_hash_table); + + ok = TrackPopupMenu (menu, TPM_LEFTALIGN | TPM_LEFTBUTTON, + pt.x, pt.y, 0, + FRAME_MSWINDOWS_HANDLE (f), NULL); + + DestroyMenu (menu); + + /* Signal a signal if caught by Track...() modal loop */ + mswindows_unmodalize_signal_maybe (); + + /* This is probably the only real reason for failure */ + if (!ok) { + menu_cleanup (f); + signal_simple_error ("cannot track popup menu while in menu", + menu_desc); + } +} + + +/*------------------------------------------------------------------------*/ +/* Initialization */ +/*------------------------------------------------------------------------*/ +void +syms_of_menubar_mswindows (void) +{ + defsymbol (&Qfind_menu_item, "find-menu-item"); +} + +void +console_type_create_menubar_mswindows (void) +{ + CONSOLE_HAS_METHOD (mswindows, update_frame_menubars); + CONSOLE_HAS_METHOD (mswindows, free_frame_menubars); + CONSOLE_HAS_METHOD (mswindows, popup_menu); +} + +void +vars_of_menubar_mswindows (void) +{ + current_tracking_popup = Qnil; + current_popup_hash_table = Qnil; + + staticpro (¤t_tracking_popup); + staticpro (¤t_popup_hash_table); + + Fprovide (intern ("mswindows-menubars")); +}