Well here it is, finally: popup-menu support for NTEmacs. I finally managed to root out and destroy the last (known) bug (beeping after selecting from or aborting a popup menu called on a mouse-down event). Here is a summary of the user-visible changes: * x-popup-menu now works * x-popup-dialog now works by pulling up a popup menu * menus now show keybindings * C-mouse-down-1 mouse-buffer-menu and C-mouse-down-3 mouse-major-mode-menu now work See the Changelog for what changed specifically. It might be nice to release the diffs to the NTEmacs using public, as these patches address a couple of common complaints in the NT version of Emacs. Also, please note that Nico (nico.francois@scala.nl) found out why the TrackPopupMenu call was not working (which I very much doubt I would have figured out by myself), and created a set of patches from which much of these are based. Here is a set of diffs based on the 19.34.2 sources: *** src/Changelog.orig Tue May 06 15:08:43 1997 --- src/Changelog Tue May 06 15:15:15 1997 *************** *** 1,3 **** --- 1,43 ---- + Tue May 06 14:57:55 1997 Michael Welsh Duggan + + * w32menu.c (win32menu_show): Call eat_mouse_events in order to + get rid of any extraneous mouse events. + + * lisp.h (eat_mouse_events): Declare here. + + * keyboard.c (eat_mouse_events): New function. Changes kind of + mouse events in the keayboard buffer to no_event. + + Thu Apr 24 19:44:13 1997 Michael Welsh Duggan + + * w32fns.c (win32_wnd_proc): Allow a dragged selection from a + popup menu started up by a mouse down event. + + * w32menu.c (list_of_panes): Only bring up one pane if the length + of the list of panes is one. + + Tue Apr 22 14:01:10 1997 Michael Welsh Duggan + + * w32fns.c (win32_wnd_proc): Capture and handle + WM_EMACS_TRACKPOPUPMENU events. + + * w32term.h (WM_EMACS_TRACKPOPUPMENU): New Macro. + + * w32menu.c (add_menu_item): equiv parameter send and paid + attention to. + (keymap_panes): Use CreatePopupMenu. + (single_keymap_panes): Use CreatePopupMenu. Send key descriptions + to add_menu_item. + (list_of_panes): Use CreatePopupMenu. + (list_of_items): Use CreatePopupMenu. Send nil description to + add_menu_item. + (get_menu_event): Send keymap instead of menu to + get_keymap_event. + (Fx_popup_menu): Extra parameter to mouse_position_hook. Don't + send address of menu to win32menu_show. + (win32menu_show): Send message to call popup menu rather than + trying directly. get_menu_event should take an address. + Wed Aug 21 03:51:42 1996 Richard Stallman * Version 19.34 released. *** src/keyboard.c.orig Tue May 06 15:08:54 1997 --- src/keyboard.c Tue May 06 15:15:23 1997 *************** *** 2535,2540 **** --- 2535,2562 ---- } } + /* Discard any mouse events in the event buffer by setting them to + no_event. */ + void + eat_mouse_events () + { + struct input_event *sp; + for (sp = kbd_fetch_ptr; sp != kbd_store_ptr; sp++) + { + if (sp == kbd_buffer + KBD_BUFFER_SIZE) + sp = kbd_buffer; + + if (sp->kind == mouse_click || + #ifdef WINDOWSNT + sp->kind == win32_scroll_bar_click || + #endif + sp->kind == scroll_bar_click) + { + sp->kind = no_event; + } + } + } + /* Read one event from the event buffer, waiting if necessary. The value is a Lisp object representing the event. The value is nil for an event that should be ignored, *************** *** 2707,2712 **** --- 2729,2736 ---- (They shouldn't otherwise be found in the buffer, but on some machines it appears they do show up even without MULTI_KBOARD.) */ + /* The Windows NT version now uses no_event to delete extraneous + mouse events during a popup-menu call. */ else if (event->kind == no_event) kbd_fetch_ptr = event + 1; *** src/lisp.h.orig Tue May 06 15:16:22 1997 --- src/lisp.h Tue May 06 15:16:47 1997 *************** *** 1664,1669 **** --- 1664,1670 ---- extern Lisp_Object Fcommand_execute (), Finput_pending_p (); extern Lisp_Object menu_bar_items (); extern Lisp_Object Qvertical_scroll_bar; + extern void eat_mouse_events (); #ifdef MULTI_KBOARD extern void delete_kboard (); #endif *** src/w32fns.c.orig Tue May 06 15:09:08 1997 --- src/w32fns.c Tue May 06 15:15:33 1997 *************** *** 3203,3213 **** handle_plain_button: { BOOL up; ! if (parse_button (msg, NULL, &up)) { if (up) ReleaseCapture (); else SetCapture (hwnd); } } --- 3203,3220 ---- handle_plain_button: { BOOL up; + int button; ! if (parse_button (msg, &button, &up)) { if (up) ReleaseCapture (); else SetCapture (hwnd); + button = (button == 0) ? LMOUSE : + ((button == 1) ? MMOUSE : RMOUSE); + if (up) + button_state &= ~button; + else + button_state |= button; } } *************** *** 3371,3376 **** --- 3378,3421 ---- case WM_EMACS_DESTROYWINDOW: DestroyWindow ((HWND) wParam); break; + + case WM_EMACS_TRACKPOPUPMENU: + { + UINT flags; + POINT *pos; + int retval; + pos = (POINT *)lParam; + flags = TPM_CENTERALIGN; + if (button_state & LMOUSE) + flags |= TPM_LEFTBUTTON; + else if (button_state & RMOUSE) + flags |= TPM_RIGHTBUTTON; + + if (TrackPopupMenu ((HMENU)wParam, flags, pos->x, pos->y, + 0, hwnd, NULL)) + { + MSG amsg; + /* Eat any mouse messages during popupmenu */ + while (PeekMessage(&amsg, hwnd, WM_MOUSEFIRST, WM_MOUSELAST, + PM_REMOVE)); + /* Get the menu selection, if any */ + if (PeekMessage(&amsg, hwnd, WM_COMMAND, WM_COMMAND, PM_REMOVE)) + { + retval = LOWORD(amsg.wParam); + } + else + { + retval = 0; + } + button_state = 0; + } + else + { + retval = -1; + } + + return retval; + } default: dflt: *** src/w32menu.c.orig Tue May 06 15:09:08 1997 --- src/w32menu.c Tue May 06 15:15:33 1997 *************** *** 79,84 **** --- 79,85 ---- /* Initialize the menu_items structure if we haven't already done so. Also mark it as currently empty. */ + #if 0 static void init_menu_items (lpmm) menu_map * lpmm; *************** *** 93,109 **** lpmm->menu_items_used = 0; } - /* Call when finished using the data for the current menu - in menu_items. */ - - static void - discard_menu_items (lpmm) - menu_map * lpmm; - { - lpmm->menu_items = Qnil; - lpmm->menu_items_allocated = lpmm->menu_items_used = 0; - } - /* Make the menu_items vector twice as large. */ static void --- 94,99 ---- *************** *** 120,125 **** --- 110,129 ---- lpmm->menu_items = new; } + #endif + + /* Call when finished using the data for the current menu + in menu_items. */ + + static void + discard_menu_items (lpmm) + menu_map * lpmm; + { + #if 0 + lpmm->menu_items = Qnil; + #endif + lpmm->menu_items_allocated = lpmm->menu_items_used = 0; + } /* Indicate boundary between left and right. */ *************** *** 137,164 **** of the keyboard equivalent for this item (or nil if none). */ static void ! add_menu_item (lpmm, hmenu, name, enable, key) menu_map * lpmm; HMENU hmenu; Lisp_Object name; UINT enable; Lisp_Object key; { UINT fuFlags; if (NILP (name) || ((char *) XSTRING (name)->data)[0] == 0 || strcmp ((char *) XSTRING (name)->data, "--") == 0) fuFlags = MF_SEPARATOR; ! else if (enable) ! fuFlags = MF_STRING; ! else ! fuFlags = MF_STRING | MF_GRAYED; ! AppendMenu (hmenu, fuFlags, lpmm->menu_items_used + 1, ! (fuFlags == MF_SEPARATOR)?NULL: (char *) XSTRING (name)->data); lpmm->menu_items_used++; #if 0 --- 141,182 ---- of the keyboard equivalent for this item (or nil if none). */ static void ! add_menu_item (lpmm, hmenu, name, enable, key, equiv) menu_map * lpmm; HMENU hmenu; Lisp_Object name; UINT enable; Lisp_Object key; + Lisp_Object equiv; { UINT fuFlags; + Lisp_Object out_string; if (NILP (name) || ((char *) XSTRING (name)->data)[0] == 0 || strcmp ((char *) XSTRING (name)->data, "--") == 0) fuFlags = MF_SEPARATOR; ! else ! { ! if (enable) ! fuFlags = MF_STRING; ! else ! fuFlags = MF_STRING | MF_GRAYED; ! ! if (!NILP(equiv)) ! { ! out_string = concat2 (name, make_string("\t", 1)); ! out_string = concat2 (out_string, equiv); ! } ! else ! out_string = name; ! } ! AppendMenu (hmenu, fuFlags, lpmm->menu_items_used + 1, ! (fuFlags == MF_SEPARATOR)?NULL: ! (char *) XSTRING (out_string)->data); lpmm->menu_items_used++; #if 0 *************** *** 316,331 **** int notreal; { int mapno; ! ! // init_menu_items (lpmm); ! if (nmaps > 1) { HMENU hmenu; if (!notreal) { ! hmenu = CreateMenu (); if (!hmenu) return (NULL); } --- 334,351 ---- int notreal; { int mapno; ! ! #if 0 ! init_menu_items (lpmm); ! #endif ! if (nmaps > 1) { HMENU hmenu; if (!notreal) { ! hmenu = CreatePopupMenu (); if (!hmenu) return (NULL); } *************** *** 380,386 **** if (!notreal) { ! hmenu = CreateMenu (); if (hmenu == NULL) return NULL; } else --- 400,406 ---- if (!notreal) { ! hmenu = CreatePopupMenu (); if (hmenu == NULL) return NULL; } else *************** *** 459,465 **** hmenu, item_string, !NILP (enabled), ! Fcons (XCONS (item)->car, prefix)); } } else --- 479,486 ---- hmenu, item_string, !NILP (enabled), ! Fcons (XCONS (item)->car, prefix), ! descrip); } } else *************** *** 542,548 **** hmenu, item_string, !NILP (enabled), ! character); } } else --- 563,570 ---- hmenu, item_string, !NILP (enabled), ! character, ! descrip); } } else *************** *** 609,637 **** Lisp_Object tail; HMENU hmenu; ! hmenu = CreateMenu (); ! if (hmenu == NULL) return NULL; ! // init_menu_items (lpmm); ! for (tail = menu; !NILP (tail); tail = Fcdr (tail)) { Lisp_Object elt, pane_name, pane_data; ! HMENU new_hmenu; ! ! elt = Fcar (tail); pane_name = Fcar (elt); CHECK_STRING (pane_name, 0); pane_data = Fcdr (elt); CHECK_CONS (pane_data, 0); ! ! new_hmenu = list_of_items (lpmm, pane_data); ! if (new_hmenu == NULL) goto error; ! ! AppendMenu (hmenu, MF_POPUP, (UINT)new_hmenu, ! (char *) XSTRING (pane_name)->data); } - return (hmenu); error: --- 631,672 ---- Lisp_Object tail; HMENU hmenu; ! if (XFASTINT (Flength (menu)) > 1) ! { ! hmenu = CreatePopupMenu (); ! if (hmenu == NULL) return NULL; ! /* init_menu_items (lpmm); */ ! for (tail = menu; !NILP (tail); tail = Fcdr (tail)) ! { ! Lisp_Object elt, pane_name, pane_data; ! HMENU new_hmenu; ! ! elt = Fcar (tail); ! pane_name = Fcar (elt); ! CHECK_STRING (pane_name, 0); ! pane_data = Fcdr (elt); ! CHECK_CONS (pane_data, 0); ! ! new_hmenu = list_of_items (lpmm, pane_data); ! if (new_hmenu == NULL) goto error; ! ! AppendMenu (hmenu, MF_POPUP, (UINT)new_hmenu, ! (char *) XSTRING (pane_name)->data); ! } ! } ! else { Lisp_Object elt, pane_name, pane_data; ! ! elt = Fcar (menu); pane_name = Fcar (elt); CHECK_STRING (pane_name, 0); pane_data = Fcdr (elt); CHECK_CONS (pane_data, 0); ! hmenu = list_of_items (lpmm, pane_data); } return (hmenu); error: *************** *** 650,663 **** Lisp_Object tail, item, item1; HMENU hmenu; ! hmenu = CreateMenu (); if (hmenu == NULL) return NULL; for (tail = pane; !NILP (tail); tail = Fcdr (tail)) { item = Fcar (tail); if (STRINGP (item)) ! add_menu_item (lpmm, hmenu, item, Qnil, Qnil); else if (NILP (item)) add_left_right_boundary (); else --- 685,698 ---- Lisp_Object tail, item, item1; HMENU hmenu; ! hmenu = CreatePopupMenu (); if (hmenu == NULL) return NULL; for (tail = pane; !NILP (tail); tail = Fcdr (tail)) { item = Fcar (tail); if (STRINGP (item)) ! add_menu_item (lpmm, hmenu, item, Qnil, Qnil, Qnil); else if (NILP (item)) add_left_right_boundary (); else *************** *** 665,671 **** CHECK_CONS (item, 0); item1 = Fcar (item); CHECK_STRING (item1, 1); ! add_menu_item (lpmm, hmenu, item1, Qt, Fcdr (item)); } } --- 700,706 ---- CHECK_CONS (item, 0); item1 = Fcar (item); CHECK_STRING (item1, 1); ! add_menu_item (lpmm, hmenu, item1, Qt, Fcdr (item), Qnil); } } *************** *** 1062,1068 **** Lisp_Object event; /* Decode the menu items from what was specified. */ ! keymap = Fkeymapp (menu); tem = Qnil; if (XTYPE (menu) == Lisp_Cons) --- 1097,1104 ---- Lisp_Object event; /* Decode the menu items from what was specified. */ ! ! keymap = Fkeymapp (menu); tem = Qnil; if (XTYPE (menu) == Lisp_Cons) *************** *** 1072,1078 **** { keymap = get_keymap (menu); ! event = get_keymap_event (menu, 1, lpnum); } else if (!NILP (tem)) { --- 1108,1114 ---- { keymap = get_keymap (menu); ! event = get_keymap_event (&keymap, 1, lpnum); } else if (!NILP (tem)) { *************** *** 1161,1167 **** unsigned long time; if (mouse_position_hook) ! (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time); if (new_f != 0) XSETFRAME (window, new_f); else --- 1197,1204 ---- unsigned long time; if (mouse_position_hook) ! (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, ! &time); if (new_f != 0) XSETFRAME (window, new_f); else *************** *** 1240,1246 **** /* Display them in a menu. */ BLOCK_INPUT; ! selection = win32menu_show (f, xpos, ypos, menu, &hmenu, &error_name); UNBLOCK_INPUT; --- 1277,1283 ---- /* Display them in a menu. */ BLOCK_INPUT; ! selection = win32menu_show (f, xpos, ypos, menu, hmenu, &error_name); UNBLOCK_INPUT; *************** *** 1517,1523 **** Lisp_Object x, y; unsigned long time; ! (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time); if (f == new_f && other_menu_bar_item_p (f, x, y)) { --- 1554,1560 ---- Lisp_Object x, y; unsigned long time; ! (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time); if (f == new_f && other_menu_bar_item_p (f, x, y)) { *************** *** 1588,1594 **** if (!hmenu || strcmp (pane_string, "")) { ! HMENU new_hmenu = CreateMenu (); if (!new_hmenu) { --- 1625,1631 ---- if (!hmenu || strcmp (pane_string, "")) { ! HMENU new_hmenu = CreatePopupMenu (); if (!new_hmenu) { *************** *** 1699,1725 **** return Qnil; } #endif ! /* Display the menu. */ ! menu_selection = TrackPopupMenu (hmenu, ! 0x10, ! pos.x, pos.y, ! 0, ! FRAME_WIN32_WINDOW (f), ! NULL); if (menu_selection == -1) { *error = "Invalid menu specification"; return Qnil; } ! /* Find the selected item, and its pane, to return the proper value. */ #if 1 if (menu_selection > 0) { ! return get_menu_event (menu, menu_selection); } #else if (menu_selection > 0 && menu_selection <= lpmm->menu_items_used) --- 1736,1765 ---- return Qnil; } #endif ! /* Display the menu. */ ! ! menu_selection = SendMessage (FRAME_WIN32_WINDOW (f), ! WM_EMACS_TRACKPOPUPMENU, ! (WPARAM)hmenu, (LPARAM)&pos); ! ! /* Clean up extraneous mouse events which might have been generated ! during the call. */ ! eat_mouse_events(); ! if (menu_selection == -1) { *error = "Invalid menu specification"; return Qnil; } ! /* Find the selected item, and its pane, to return the proper value. */ #if 1 if (menu_selection > 0) { ! return get_menu_event (menu, &menu_selection); } #else if (menu_selection > 0 && menu_selection <= lpmm->menu_items_used) *************** *** 1727,1733 **** return (XVECTOR (lpmm->menu_items)->contents[menu_selection - 1]); } #endif ! return Qnil; } --- 1767,1773 ---- return (XVECTOR (lpmm->menu_items)->contents[menu_selection - 1]); } #endif ! return Qnil; } *** src/w32term.h.orig Tue May 06 15:09:09 1997 --- src/w32term.h Tue May 06 15:15:34 1997 *************** *** 585,590 **** --- 585,591 ---- #define WM_EMACS_SHOWWINDOW (WM_EMACS_START + 0x04) #define WM_EMACS_SETWINDOWPOS (WM_EMACS_START + 0x05) #define WM_EMACS_DESTROYWINDOW (WM_EMACS_START + 0x06) + #define WM_EMACS_TRACKPOPUPMENU (WM_EMACS_START + 0x07) #define WM_EMACS_END (WM_EMACS_START + 0x10) #define WND_X_UNITS_INDEX (0) *** lisp/winnt.el.orig Tue May 06 15:19:50 1997 --- lisp/winnt.el Tue May 06 15:20:04 1997 *************** *** 190,199 **** (or type (setq type 'PRIMARY)) (get 'x-selections type)) ! (fmakunbound 'font-menu-add-default) ! (global-unset-key [C-down-mouse-1]) ! (global-unset-key [C-down-mouse-2]) ! (global-unset-key [C-down-mouse-3]) ;;; Set to a system sound if you want a fancy bell. (set-message-beep nil) --- 190,199 ---- (or type (setq type 'PRIMARY)) (get 'x-selections type)) ! ;(fmakunbound 'font-menu-add-default) ! ;(global-unset-key [C-down-mouse-1]) ! ;(global-unset-key [C-down-mouse-2]) ! ;(global-unset-key [C-down-mouse-3]) ;;; Set to a system sound if you want a fancy bell. (set-message-beep nil) *** lisp/Changelog.orig Wed Aug 21 04:52:21 1996 --- lisp/Changelog Tue May 06 15:21:49 1997 *************** *** 1,3 **** --- 1,7 ---- + Tue May 06 15:20:43 1997 Michael Welsh Duggan + + * winnt.el: Delete popupmenu-based global-unsets. + Wed Aug 21 03:51:42 1996 Richard Stallman * Version 19.34 released. -- Michael Duggan (md5i@schenley.com)