Mercurial > hg > xemacs-beta
view 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 source
/* 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")); }