1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007-2015 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
20 By Adrian Robert, based on code from original nsmenu.m (Carl Edman,
21 Christian Limpach, Scott Bender, Christophe de Dinechin) and code in the
22 Carbon version by Yamamoto Mitsuharu. */
24 /* This should be the first include, as it may set up #defines affecting
25 interpretation of even the system includes. */
30 #include "character.h"
35 #include "blockinput.h"
37 #include "termhooks.h"
41 #define NSMENUPROFILE 0
44 #include <sys/timeb.h>
45 #include <sys/types.h>
50 /* Include lisp -> C common menu parsing code */
51 #define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
52 #include "nsmenu_common.c"
55 extern long context_menu_value;
56 EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
58 /* Nonzero means a menu is currently active. */
59 static int popup_activated_flag;
61 /* Nonzero means we are tracking and updating menus. */
62 static int trackingMenu;
65 /* NOTE: toolbar implementation is at end,
66 following complete menu implementation. */
69 /* ==========================================================================
71 Menu: Externally-called functions
73 ========================================================================== */
76 /* Supposed to discard menubar and free storage. Since we share the
77 menubar among frames and update its context for the focused window,
78 there is nothing to do here. */
80 free_frame_menubar (struct frame *f)
87 popup_activated (void)
89 return popup_activated_flag;
93 /* --------------------------------------------------------------------------
94 Update menubar. Three cases:
95 1) ! deep_p, submenu = nil: Fresh switch onto a frame -- either set up
96 just top-level menu strings (OS X), or goto case (2) (GNUstep).
97 2) deep_p, submenu = nil: Recompute all submenus.
98 3) deep_p, submenu = non-nil: Update contents of a single submenu.
99 -------------------------------------------------------------------------- */
101 ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
103 NSAutoreleasePool *pool;
104 id menu = [NSApp mainMenu];
105 static EmacsMenu *last_submenu = nil;
109 widget_value *wv, *first_wv, *prev_wv = 0;
117 NSTRACE ("ns_update_menubar");
119 if (f != SELECTED_FRAME ())
121 XSETFRAME (Vmenu_updating_frame, f);
122 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
125 pool = [[NSAutoreleasePool alloc] init];
127 /* Menu may have been created automatically; if so, discard it. */
128 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
136 menu = [[EmacsMenu alloc] initWithTitle: ns_app_name];
140 { /* close up anything on there */
141 id attMenu = [menu attachedMenu];
148 t = -(1000*tb.time+tb.millitm);
151 #ifdef NS_IMPL_GNUSTEP
152 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
157 /* Fully parse one or more of the submenus. */
159 int *submenu_start, *submenu_end;
160 bool *submenu_top_level_items;
161 int *submenu_n_panes;
162 struct buffer *prev = current_buffer;
164 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
165 int previous_menu_items_used = f->menu_bar_items_used;
166 Lisp_Object *previous_items
167 = alloca (previous_menu_items_used * sizeof *previous_items);
169 /* lisp preliminaries */
170 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
171 specbind (Qinhibit_quit, Qt);
172 specbind (Qdebug_on_next_call, Qnil);
173 record_unwind_save_match_data ();
174 if (NILP (Voverriding_local_map_menu_flag))
176 specbind (Qoverriding_terminal_local_map, Qnil);
177 specbind (Qoverriding_local_map, Qnil);
179 set_buffer_internal_1 (XBUFFER (buffer));
181 /* TODO: for some reason this is not needed in other terms,
182 but some menu updates call Info-extract-pointer which causes
183 abort-on-error if waiting-for-input. Needs further investigation. */
184 owfi = waiting_for_input;
185 waiting_for_input = 0;
187 /* lucid hook and possible reset */
188 safe_run_hooks (Qactivate_menubar_hook);
189 if (! NILP (Vlucid_menu_bar_dirty_flag))
190 call0 (Qrecompute_lucid_menubar);
191 safe_run_hooks (Qmenu_bar_update_hook);
192 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
194 /* Now ready to go */
195 items = FRAME_MENU_BAR_ITEMS (f);
197 /* Save the frame's previous menu bar contents data */
198 if (previous_menu_items_used)
199 memcpy (previous_items, aref_addr (f->menu_bar_vector, 0),
200 previous_menu_items_used * sizeof (Lisp_Object));
202 /* parse stage 1: extract from lisp */
205 menu_items = f->menu_bar_vector;
206 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
207 submenu_start = alloca (ASIZE (items) * sizeof *submenu_start);
208 submenu_end = alloca (ASIZE (items) * sizeof *submenu_end);
209 submenu_n_panes = alloca (ASIZE (items) * sizeof *submenu_n_panes);
210 submenu_top_level_items = alloca (ASIZE (items)
211 * sizeof *submenu_top_level_items);
213 for (i = 0; i < ASIZE (items); i += 4)
215 Lisp_Object key, string, maps;
217 key = AREF (items, i);
218 string = AREF (items, i + 1);
219 maps = AREF (items, i + 2);
223 /* FIXME: we'd like to only parse the needed submenu, but this
224 was causing crashes in the _common parsing code.. need to make
225 sure proper initialization done.. */
226 /* if (submenu && strcmp ([[submenu title] UTF8String], SSDATA (string)))
229 submenu_start[i] = menu_items_used;
231 menu_items_n_panes = 0;
232 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
233 submenu_n_panes[i] = menu_items_n_panes;
234 submenu_end[i] = menu_items_used;
238 finish_menu_items ();
239 waiting_for_input = owfi;
242 if (submenu && n == 0)
244 /* should have found a menu for this one but didn't */
245 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
246 [[submenu title] UTF8String]);
247 discard_menu_items ();
248 unbind_to (specpdl_count, Qnil);
254 /* parse stage 2: insert into lucid 'widget_value' structures
255 [comments in other terms say not to evaluate lisp code here] */
256 wv = make_widget_value ("menubar", NULL, true, Qnil);
257 wv->button_type = BUTTON_TYPE_NONE;
260 for (i = 0; i < 4*n; i += 4)
262 menu_items_n_panes = submenu_n_panes[i];
263 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
264 submenu_top_level_items[i]);
268 first_wv->contents = wv;
269 /* Don't set wv->name here; GC during the loop might relocate it. */
271 wv->button_type = BUTTON_TYPE_NONE;
275 set_buffer_internal_1 (prev);
277 /* Compare the new menu items with previous, and leave off if no change */
278 /* FIXME: following other terms here, but seems like this should be
279 done before parse stage 2 above, since its results aren't used */
280 if (previous_menu_items_used
281 && (!submenu || (submenu && submenu == last_submenu))
282 && menu_items_used == previous_menu_items_used)
284 for (i = 0; i < previous_menu_items_used; i++)
285 /* FIXME: this ALWAYS fails on Buffers menu items.. something
286 about their strings causes them to change every time, so we
287 double-check failures */
288 if (!EQ (previous_items[i], AREF (menu_items, i)))
289 if (!(STRINGP (previous_items[i])
290 && STRINGP (AREF (menu_items, i))
291 && !strcmp (SSDATA (previous_items[i]),
292 SSDATA (AREF (menu_items, i)))))
294 if (i == previous_menu_items_used)
300 t += 1000*tb.time+tb.millitm;
301 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
304 free_menubar_widget_value_tree (first_wv);
305 discard_menu_items ();
306 unbind_to (specpdl_count, Qnil);
312 /* The menu items are different, so store them in the frame */
313 /* FIXME: this is not correct for single-submenu case */
314 fset_menu_bar_vector (f, menu_items);
315 f->menu_bar_items_used = menu_items_used;
317 /* Calls restore_menu_items, etc., as they were outside */
318 unbind_to (specpdl_count, Qnil);
320 /* Parse stage 2a: now GC cannot happen during the lifetime of the
321 widget_value, so it's safe to store data from a Lisp_String */
322 wv = first_wv->contents;
323 for (i = 0; i < ASIZE (items); i += 4)
326 string = AREF (items, i + 1);
330 wv->name = SSDATA (string);
331 update_submenu_strings (wv->contents);
335 /* Now, update the NS menu; if we have a submenu, use that, otherwise
336 create a new menu for each sub and fill it. */
339 const char *submenuTitle = [[submenu title] UTF8String];
340 for (wv = first_wv->contents; wv; wv = wv->next)
342 if (!strcmp (submenuTitle, wv->name))
344 [submenu fillWithWidgetValue: wv->contents];
345 last_submenu = submenu;
352 [menu fillWithWidgetValue: first_wv->contents frame: f];
358 static int n_previous_strings = 0;
359 static char previous_strings[100][10];
360 static struct frame *last_f = NULL;
364 wv = make_widget_value ("menubar", NULL, true, Qnil);
365 wv->button_type = BUTTON_TYPE_NONE;
368 /* Make widget-value tree w/ just the top level menu bar strings */
369 items = FRAME_MENU_BAR_ITEMS (f);
372 free_menubar_widget_value_tree (first_wv);
379 /* check if no change.. this mechanism is a bit rough, but ready */
380 n = ASIZE (items) / 4;
381 if (f == last_f && n_previous_strings == n)
383 for (i = 0; i<n; i++)
385 string = AREF (items, 4*i+1);
387 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
391 if (previous_strings[i][0])
396 else if (memcmp (previous_strings[i], SDATA (string),
397 min (10, SBYTES (string) + 1)))
403 free_menubar_widget_value_tree (first_wv);
411 for (i = 0; i < ASIZE (items); i += 4)
413 string = AREF (items, i + 1);
418 memcpy (previous_strings[i/4], SDATA (string),
419 min (10, SBYTES (string) + 1));
421 wv = make_widget_value (SSDATA (string), NULL, true, Qnil);
422 wv->button_type = BUTTON_TYPE_NONE;
423 wv->call_data = (void *) (intptr_t) (-1);
426 /* we'll update the real copy under app menu when time comes */
427 if (!strcmp ("Services", wv->name))
429 /* but we need to make sure it will update on demand */
430 [svcsMenu setFrame: f];
434 [menu addSubmenuWithTitle: wv->name forFrame: f];
439 first_wv->contents = wv;
445 n_previous_strings = n;
447 n_previous_strings = 0;
450 free_menubar_widget_value_tree (first_wv);
455 t += 1000*tb.time+tb.millitm;
456 fprintf (stderr, "Menu update took %ld msec.\n", t);
461 [NSApp setMainMenu: menu];
469 /* Main emacs core entry point for menubar menus: called to indicate that the
470 frame's menus have changed, and the *step representation should be updated
473 set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
475 ns_update_menubar (f, deep_p, nil);
479 x_activate_menubar (struct frame *f)
482 ns_update_menubar (f, true, nil);
483 ns_check_pending_open_menu ();
490 /* ==========================================================================
492 Menu: class implementation
494 ========================================================================== */
497 /* Menu that can define itself from Emacs "widget_value"s and will lazily
498 update itself when user clicked. Based on Carbon/AppKit implementation
499 by Yamamoto Mitsuharu. */
500 @implementation EmacsMenu
502 /* override designated initializer */
503 - initWithTitle: (NSString *)title
506 if ((self = [super initWithTitle: title]))
507 [self setAutoenablesItems: NO];
512 /* used for top-level */
513 - initWithTitle: (NSString *)title frame: (struct frame *)f
515 [self initWithTitle: title];
518 [self setDelegate: self];
524 - (void)setFrame: (struct frame *)f
530 -(void)trackingNotification:(NSNotification *)notification
532 /* Update menu in menuNeedsUpdate only while tracking menus. */
533 trackingMenu = ([notification name] == NSMenuDidBeginTrackingNotification
535 if (! trackingMenu) ns_check_menu_open (nil);
538 - (void)menuWillOpen:(NSMenu *)menu
542 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
543 // On 10.6 we get repeated calls, only the one for NSSystemDefined is "real".
544 if ([[NSApp currentEvent] type] != NSSystemDefined) return;
547 /* When dragging from one menu to another, we get willOpen followed by didClose,
548 i.e. trackingMenu == 3 in willOpen and then 2 after didClose.
549 We have updated all menus, so avoid doing it when trackingMenu == 3. */
550 if (trackingMenu == 2)
551 ns_check_menu_open (menu);
554 - (void)menuDidClose:(NSMenu *)menu
559 #endif /* NS_IMPL_COCOA */
561 /* delegate method called when a submenu is being opened: run a 'deep' call
562 to set_frame_menubar */
563 - (void)menuNeedsUpdate: (NSMenu *)menu
565 if (!FRAME_LIVE_P (frame))
568 /* Cocoa/Carbon will request update on every keystroke
569 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
570 since key equivalents are handled through emacs.
571 On Leopard, even keystroke events generate SystemDefined event.
572 Third-party applications that enhance mouse / trackpad
573 interaction, or also VNC/Remote Desktop will send events
574 of type AppDefined rather than SysDefined.
575 Menus will fail to show up if they haven't been initialized.
576 AppDefined events may lack timing data.
578 Thus, we rely on the didBeginTrackingNotification notification
579 as above to indicate the need for updates.
580 From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
581 key press case, NSMenuPropertyItemImage (e.g.) won't be set.
583 if (trackingMenu == 0)
585 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
586 #ifdef NS_IMPL_GNUSTEP
587 /* Don't know how to do this for anything other than OSX >= 10.5
588 This is wrong, as it might run Lisp code in the event loop. */
589 ns_update_menubar (frame, true, self);
594 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
596 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
597 && FRAME_NS_VIEW (SELECTED_FRAME ()))
598 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
603 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
604 into an accelerator string. We are only able to display a single character
605 for an accelerator, together with an optional modifier combination. (Under
606 Carbon more control was possible, but in Cocoa multi-char strings passed to
607 NSMenuItem get ignored. For now we try to display a super-single letter
608 combo, and return the others as strings to be appended to the item title.
609 (This is signaled by setting keyEquivModMask to 0 for now.) */
610 -(NSString *)parseKeyEquiv: (const char *)key
612 const char *tpos = key;
613 keyEquivModMask = NSCommandKeyMask;
615 if (!key || !strlen (key))
618 while (*tpos == ' ' || *tpos == '(')
620 if ((*tpos == 's') && (*(tpos+1) == '-'))
622 return [NSString stringWithFormat: @"%c", tpos[2]];
624 keyEquivModMask = 0; /* signal */
625 return [NSString stringWithUTF8String: tpos];
629 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
632 widget_value *wv = (widget_value *)wvptr;
634 if (menu_separator_name_p (wv->name))
636 item = [NSMenuItem separatorItem];
637 [self addItem: item];
641 NSString *title, *keyEq;
642 title = [NSString stringWithUTF8String: wv->name];
644 title = @"< ? >"; /* (get out in the open so we know about it) */
646 keyEq = [self parseKeyEquiv: wv->key];
648 /* OS X just ignores modifier strings longer than one character */
649 if (keyEquivModMask == 0)
650 title = [title stringByAppendingFormat: @" (%@)", keyEq];
653 item = [self addItemWithTitle: (NSString *)title
654 action: @selector (menuDown:)
655 keyEquivalent: keyEq];
656 [item setKeyEquivalentModifierMask: keyEquivModMask];
658 [item setEnabled: wv->enabled];
660 /* Draw radio buttons and tickboxes */
661 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
662 wv->button_type == BUTTON_TYPE_RADIO))
663 [item setState: NSOnState];
665 [item setState: NSOffState];
667 [item setTag: (NSInteger)wv->call_data];
679 for (n = [self numberOfItems]-1; n >= 0; n--)
681 NSMenuItem *item = [self itemAtIndex: n];
682 NSString *title = [item title];
683 if ([ns_app_name isEqualToString: title]
684 && ![item isSeparatorItem])
686 [self removeItemAtIndex: n];
691 - (void)fillWithWidgetValue: (void *)wvptr
693 [self fillWithWidgetValue: wvptr frame: (struct frame *)nil];
696 - (void)fillWithWidgetValue: (void *)wvptr frame: (struct frame *)f
698 widget_value *wv = (widget_value *)wvptr;
700 /* clear existing contents */
701 [self setMenuChangedMessagesEnabled: NO];
704 /* add new contents */
705 for (; wv != NULL; wv = wv->next)
707 NSMenuItem *item = [self addItemWithWidgetValue: wv];
714 submenu = [[EmacsMenu alloc] initWithTitle: [item title] frame:f];
716 submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
718 [self setSubmenu: submenu forItem: item];
719 [submenu fillWithWidgetValue: wv->contents];
721 [item setAction: (SEL)nil];
725 [self setMenuChangedMessagesEnabled: YES];
726 #ifdef NS_IMPL_GNUSTEP
727 if ([[self window] isVisible])
733 /* adds an empty submenu and returns it */
734 - (EmacsMenu *)addSubmenuWithTitle: (const char *)title forFrame: (struct frame *)f
736 NSString *titleStr = [NSString stringWithUTF8String: title];
737 NSMenuItem *item = [self addItemWithTitle: titleStr
738 action: (SEL)nil /*@selector (menuDown:) */
740 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
741 [self setSubmenu: submenu forItem: item];
746 /* run a menu in popup mode */
747 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
748 keymaps: (bool)keymaps
750 EmacsView *view = FRAME_NS_VIEW (f);
754 /* p = [view convertPoint:p fromView: nil]; */
755 p.y = NSHeight ([view frame]) - p.y;
756 e = [[view window] currentEvent];
757 event = [NSEvent mouseEventWithType: NSRightMouseDown
760 timestamp: [e timestamp]
761 windowNumber: [[view window] windowNumber]
763 eventNumber: 0/*[e eventNumber] */
767 context_menu_value = -1;
768 [NSMenu popUpContextMenu: self withEvent: event forView: view];
769 retVal = context_menu_value;
770 context_menu_value = 0;
772 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
780 /* ==========================================================================
782 Context Menu: implementing functions
784 ========================================================================== */
787 ns_menu_show (struct frame *f, int x, int y, int menuflags,
788 Lisp_Object title, const char **error)
793 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
794 widget_value *wv, *first_wv = 0;
795 bool keymaps = (menuflags & MENU_KEYMAPS);
797 NSTRACE ("ns_menu_show");
803 /* now parse stage 2 as in ns_update_menubar */
804 wv = make_widget_value ("contextmenu", NULL, true, Qnil);
805 wv->button_type = BUTTON_TYPE_NONE;
809 /* FIXME: a couple of one-line differences prevent reuse */
810 wv = digest_single_submenu (0, menu_items_used, 0);
813 widget_value *save_wv = 0, *prev_wv = 0;
814 widget_value **submenu_stack
815 = alloca (menu_items_used * sizeof *submenu_stack);
816 /* Lisp_Object *subprefix_stack
817 = alloca (menu_items_used * sizeof *subprefix_stack); */
818 int submenu_depth = 0;
822 /* Loop over all panes and items, filling in the tree. */
824 while (i < menu_items_used)
826 if (EQ (AREF (menu_items, i), Qnil))
828 submenu_stack[submenu_depth++] = save_wv;
834 else if (EQ (AREF (menu_items, i), Qlambda))
837 save_wv = submenu_stack[--submenu_depth];
841 else if (EQ (AREF (menu_items, i), Qt)
842 && submenu_depth != 0)
843 i += MENU_ITEMS_PANE_LENGTH;
844 /* Ignore a nil in the item list.
845 It's meaningful only for dialog boxes. */
846 else if (EQ (AREF (menu_items, i), Qquote))
848 else if (EQ (AREF (menu_items, i), Qt))
850 /* Create a new pane. */
851 Lisp_Object pane_name, prefix;
852 const char *pane_string;
854 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
855 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
857 #ifndef HAVE_MULTILINGUAL_MENU
858 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
860 pane_name = ENCODE_MENU_STRING (pane_name);
861 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
864 pane_string = (NILP (pane_name)
865 ? "" : SSDATA (pane_name));
866 /* If there is just one top-level pane, put all its items directly
867 under the top-level menu. */
868 if (menu_items_n_panes == 1)
871 /* If the pane has a meaningful name,
872 make the pane a top-level menu item
873 with its items as a submenu beneath it. */
874 if (!keymaps && strcmp (pane_string, ""))
876 wv = make_widget_value (pane_string, NULL, true, Qnil);
880 first_wv->contents = wv;
881 if (keymaps && !NILP (prefix))
883 wv->button_type = BUTTON_TYPE_NONE;
893 i += MENU_ITEMS_PANE_LENGTH;
897 /* Create a new item within current pane. */
898 Lisp_Object item_name, enable, descrip, def, type, selected, help;
899 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
900 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
901 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
902 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
903 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
904 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
905 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
907 #ifndef HAVE_MULTILINGUAL_MENU
908 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
910 item_name = ENCODE_MENU_STRING (item_name);
911 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
914 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
916 descrip = ENCODE_MENU_STRING (descrip);
917 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
919 #endif /* not HAVE_MULTILINGUAL_MENU */
921 wv = make_widget_value (SSDATA (item_name), NULL, !NILP (enable),
922 STRINGP (help) ? help : Qnil);
926 save_wv->contents = wv;
928 wv->key = SSDATA (descrip);
929 /* If this item has a null value,
930 make the call_data null so that it won't display a box
931 when the mouse is on it. */
932 wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
935 wv->button_type = BUTTON_TYPE_NONE;
936 else if (EQ (type, QCtoggle))
937 wv->button_type = BUTTON_TYPE_TOGGLE;
938 else if (EQ (type, QCradio))
939 wv->button_type = BUTTON_TYPE_RADIO;
943 wv->selected = !NILP (selected);
947 i += MENU_ITEMS_ITEM_LENGTH;
955 widget_value *wv_title;
956 widget_value *wv_sep = make_widget_value ("--", NULL, false, Qnil);
958 /* Maybe replace this separator with a bitmap or owner-draw item
959 so that it looks better. Having two separators looks odd. */
960 wv_sep->next = first_wv->contents;
962 #ifndef HAVE_MULTILINGUAL_MENU
963 if (STRING_MULTIBYTE (title))
964 title = ENCODE_MENU_STRING (title);
966 wv_title = make_widget_value (SSDATA (title), NULL, false, Qnil);
967 wv_title->button_type = BUTTON_TYPE_NONE;
968 wv_title->next = wv_sep;
969 first_wv->contents = wv_title;
972 pmenu = [[EmacsMenu alloc] initWithTitle:
973 [NSString stringWithUTF8String: SSDATA (title)]];
974 [pmenu fillWithWidgetValue: first_wv->contents];
975 free_menubar_widget_value_tree (first_wv);
976 unbind_to (specpdl_count, Qnil);
978 popup_activated_flag = 1;
979 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
980 popup_activated_flag = 0;
981 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
988 /* ==========================================================================
990 Toolbar: externally-called functions
992 ========================================================================== */
995 free_frame_tool_bar (struct frame *f)
996 /* --------------------------------------------------------------------------
997 Under NS we just hide the toolbar until it might be needed again.
998 -------------------------------------------------------------------------- */
1000 EmacsView *view = FRAME_NS_VIEW (f);
1002 view->wait_for_tool_bar = NO;
1003 [[view toolbar] setVisible: NO];
1004 FRAME_TOOLBAR_HEIGHT (f) = 0;
1009 update_frame_tool_bar (struct frame *f)
1010 /* --------------------------------------------------------------------------
1011 Update toolbar contents
1012 -------------------------------------------------------------------------- */
1015 EmacsView *view = FRAME_NS_VIEW (f);
1016 NSWindow *window = [view window];
1017 EmacsToolbar *toolbar = [view toolbar];
1020 if (view == nil || toolbar == nil) return;
1023 oldh = FRAME_TOOLBAR_HEIGHT (f);
1025 #ifdef NS_IMPL_COCOA
1026 [toolbar clearActive];
1031 /* update EmacsToolbar as in GtkUtils, build items list */
1032 for (i = 0; i < f->n_tool_bar_items; ++i)
1034 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1035 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1037 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1042 Lisp_Object helpObj;
1043 const char *helpText;
1045 /* Check if this is a separator. */
1046 if (EQ (TOOLPROP (TOOL_BAR_ITEM_TYPE), Qt))
1048 /* Skip separators. Newer OSX don't show them, and on GNUstep they
1049 are wide as a button, thus overflowing the toolbar most of
1054 /* If image is a vector, choose the image according to the
1056 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1057 if (VECTORP (image))
1059 /* NS toolbar auto-computes disabled and selected images */
1060 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1061 eassert (ASIZE (image) >= idx);
1062 image = AREF (image, idx);
1068 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1070 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1071 helpText = NILP (helpObj) ? "" : SSDATA (helpObj);
1073 /* Ignore invalid image specifications. */
1074 if (!valid_image_p (image))
1076 /* Don't log anything, GNUS makes invalid images all the time. */
1080 img_id = lookup_image (f, image);
1081 img = IMAGE_FROM_ID (f, img_id);
1082 prepare_image_for_display (f, img);
1084 if (img->load_failed_p || img->pixmap == nil)
1086 NSLog (@"Could not prepare toolbar image for display.");
1090 [toolbar addDisplayItemWithImage: img->pixmap
1094 enabled: enabled_p];
1098 if (![toolbar isVisible])
1099 [toolbar setVisible: YES];
1101 #ifdef NS_IMPL_COCOA
1102 if ([toolbar changed])
1104 /* inform app that toolbar has changed */
1105 NSDictionary *dict = [toolbar configurationDictionary];
1106 NSMutableDictionary *newDict = [dict mutableCopy];
1107 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1109 while ((key = [keys nextObject]) != nil)
1111 NSObject *val = [dict objectForKey: key];
1112 if ([val isKindOfClass: [NSArray class]])
1115 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1120 [toolbar setConfigurationFromDictionary: newDict];
1125 FRAME_TOOLBAR_HEIGHT (f) =
1126 NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1127 - FRAME_NS_TITLEBAR_HEIGHT (f);
1128 if (FRAME_TOOLBAR_HEIGHT (f) < 0) // happens if frame is fullscreen.
1129 FRAME_TOOLBAR_HEIGHT (f) = 0;
1131 if (oldh != FRAME_TOOLBAR_HEIGHT (f))
1132 [view updateFrameSize:YES];
1133 if (view->wait_for_tool_bar && FRAME_TOOLBAR_HEIGHT (f) > 0)
1135 view->wait_for_tool_bar = NO;
1136 [view setNeedsDisplay: YES];
1143 /* ==========================================================================
1145 Toolbar: class implementation
1147 ========================================================================== */
1149 @implementation EmacsToolbar
1151 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1153 self = [super initWithIdentifier: identifier];
1155 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1156 [self setSizeMode: NSToolbarSizeModeSmall];
1157 [self setDelegate: self];
1158 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1159 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1160 prevIdentifiers = nil;
1161 prevEnablement = enablement = 0L;
1167 [prevIdentifiers release];
1168 [activeIdentifiers release];
1169 [identifierToItem release];
1173 - (void) clearActive
1175 [prevIdentifiers release];
1176 prevIdentifiers = [activeIdentifiers copy];
1177 [activeIdentifiers removeAllObjects];
1178 prevEnablement = enablement;
1185 while ([[self items] count] > 0)
1186 [self removeItemAtIndex: 0];
1191 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1192 enablement == prevEnablement ? NO : YES;
1195 - (void) addDisplayItemWithImage: (EmacsImage *)img
1198 helpText: (const char *)help
1199 enabled: (BOOL)enabled
1201 /* 1) come up w/identifier */
1202 NSString *identifier
1203 = [NSString stringWithFormat: @"%lu", (unsigned long)[img hash]];
1204 [activeIdentifiers addObject: identifier];
1206 /* 2) create / reuse item */
1207 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1210 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1212 [item setImage: img];
1213 [item setToolTip: [NSString stringWithUTF8String: help]];
1214 [item setTarget: emacsView];
1215 [item setAction: @selector (toolbarClicked:)];
1216 [identifierToItem setObject: item forKey: identifier];
1219 #ifdef NS_IMPL_GNUSTEP
1220 [self insertItemWithItemIdentifier: identifier atIndex: idx];
1224 [item setEnabled: enabled];
1226 /* 3) update state */
1227 enablement = (enablement << 1) | (enabled == YES);
1230 /* This overrides super's implementation, which automatically sets
1231 all items to enabled state (for some reason). */
1232 - (void)validateVisibleItems
1237 /* delegate methods */
1239 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1240 itemForItemIdentifier: (NSString *)itemIdentifier
1241 willBeInsertedIntoToolbar: (BOOL)flag
1243 /* look up NSToolbarItem by identifier and return... */
1244 return [identifierToItem objectForKey: itemIdentifier];
1247 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1249 /* return entire set.. */
1250 return activeIdentifiers;
1253 /* for configuration palette (not yet supported) */
1254 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1256 /* return entire set... */
1257 return activeIdentifiers;
1258 //return [identifierToItem allKeys];
1261 /* optional and unneeded */
1262 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1263 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1264 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1266 @end /* EmacsToolbar */
1270 /* ==========================================================================
1272 Tooltip: class implementation
1274 ========================================================================== */
1276 /* Needed because NeXTstep does not provide enough control over tooltip
1278 @implementation EmacsTooltip
1282 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1283 blue: 0.792 alpha: 0.95];
1284 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1285 NSFont *sfont = [font screenFont];
1286 int height = [sfont ascender] - [sfont descender];
1287 /*[font boundingRectForFont].size.height; */
1288 NSRect r = NSMakeRect (0, 0, 100, height+6);
1290 textField = [[NSTextField alloc] initWithFrame: r];
1291 [textField setFont: font];
1292 [textField setBackgroundColor: col];
1294 [textField setEditable: NO];
1295 [textField setSelectable: NO];
1296 [textField setBordered: NO];
1297 [textField setBezeled: NO];
1298 [textField setDrawsBackground: YES];
1300 win = [[NSWindow alloc]
1301 initWithContentRect: [textField frame]
1303 backing: NSBackingStoreBuffered
1305 [win setHasShadow: YES];
1306 [win setReleasedWhenClosed: NO];
1307 [win setDelegate: self];
1308 [[win contentView] addSubview: textField];
1309 /* [win setBackgroundColor: col]; */
1310 [win setOpaque: NO];
1319 [textField release];
1323 - (void) setText: (char *)text
1325 NSString *str = [NSString stringWithUTF8String: text];
1326 NSRect r = [textField frame];
1329 [textField setStringValue: str];
1330 tooltipDims = [[textField cell] cellSize];
1332 r.size.width = tooltipDims.width;
1333 r.size.height = tooltipDims.height;
1334 [textField setFrame: r];
1337 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1339 NSRect wr = [win frame];
1341 wr.origin = NSMakePoint (x, y);
1342 wr.size = [textField frame].size;
1344 [win setFrame: wr display: YES];
1345 [win setLevel: NSPopUpMenuWindowLevel];
1346 [win orderFront: self];
1348 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1349 selector: @selector (hide)
1350 userInfo: nil repeats: NO];
1359 if ([timer isValid])
1368 return timer != nil;
1373 return [textField frame];
1376 @end /* EmacsTooltip */
1380 /* ==========================================================================
1382 Popup Dialog: implementing functions
1384 ========================================================================== */
1388 NSAutoreleasePool *pool;
1389 EmacsDialogPanel *dialog;
1393 pop_down_menu (void *arg)
1395 struct Popdown_data *unwind_data = arg;
1398 if (popup_activated_flag)
1400 EmacsDialogPanel *panel = unwind_data->dialog;
1401 popup_activated_flag = 0;
1403 [unwind_data->pool release];
1404 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1407 xfree (unwind_data);
1413 ns_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
1416 Lisp_Object tem, title;
1419 NSAutoreleasePool *pool;
1421 NSTRACE ("ns_popup_dialog");
1423 isQ = NILP (header);
1425 check_window_system (f);
1427 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1428 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1430 title = Fcar (contents);
1431 CHECK_STRING (title);
1433 if (NILP (Fcar (Fcdr (contents))))
1434 /* No buttons specified, add an "Ok" button so users can pop down
1436 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
1439 pool = [[NSAutoreleasePool alloc] init];
1440 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1444 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1445 struct Popdown_data *unwind_data = xmalloc (sizeof (*unwind_data));
1447 unwind_data->pool = pool;
1448 unwind_data->dialog = dialog;
1450 record_unwind_protect_ptr (pop_down_menu, unwind_data);
1451 popup_activated_flag = 1;
1452 tem = [dialog runDialogAt: p];
1453 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1462 /* ==========================================================================
1464 Popup Dialog: class implementation
1466 ========================================================================== */
1468 @interface FlippedView : NSView
1473 @implementation FlippedView
1480 @implementation EmacsDialogPanel
1483 #define ICONSIZE 64.0
1484 #define TEXTHEIGHT 20.0
1485 #define MINCELLWIDTH 90.0
1487 - initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
1488 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1490 NSSize spacing = {SPACER, SPACER};
1493 NSImageView *imgView;
1494 FlippedView *contentView;
1497 dialog_return = Qundefined;
1498 button_values = NULL;
1499 area.origin.x = 3*SPACER;
1500 area.origin.y = 2*SPACER;
1501 area.size.width = ICONSIZE;
1502 area.size.height= ICONSIZE;
1503 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1504 #ifdef NS_IMPL_COCOA
1505 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
1506 [img setScalesWhenResized: YES];
1509 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1510 imgView = [[NSImageView alloc] initWithFrame: area];
1511 [imgView setImage: img];
1512 [imgView setEditable: NO];
1514 [imgView autorelease];
1516 aStyle = NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask;
1520 [super initWithContentRect: contentRect styleMask: aStyle
1521 backing: backingType defer: flag];
1522 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1523 [contentView autorelease];
1525 [self setContentView: contentView];
1527 [[self contentView] setAutoresizesSubviews: YES];
1529 [[self contentView] addSubview: imgView];
1530 [self setTitle: @""];
1532 area.origin.x += ICONSIZE+2*SPACER;
1533 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1534 area.size.width = 400;
1535 area.size.height= TEXTHEIGHT;
1536 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1537 [[self contentView] addSubview: command];
1538 [command setStringValue: ns_app_name];
1539 [command setDrawsBackground: NO];
1540 [command setBezeled: NO];
1541 [command setSelectable: NO];
1542 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1544 /* area.origin.x = ICONSIZE+2*SPACER;
1545 area.origin.y = TEXTHEIGHT + 2*SPACER;
1546 area.size.width = 400;
1547 area.size.height= 2;
1548 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1549 [[self contentView] addSubview: tem];
1550 [tem setTitlePosition: NSNoTitle];
1551 [tem setAutoresizingMask: NSViewWidthSizable];*/
1553 /* area.origin.x = ICONSIZE+2*SPACER; */
1554 area.origin.y += TEXTHEIGHT+SPACER;
1555 area.size.width = 400;
1556 area.size.height= TEXTHEIGHT;
1557 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1558 [[self contentView] addSubview: title];
1559 [title setDrawsBackground: NO];
1560 [title setBezeled: NO];
1561 [title setSelectable: NO];
1562 [title setFont: [NSFont systemFontOfSize: 11.0]];
1564 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1565 [cell setBordered: NO];
1566 [cell setEnabled: NO];
1567 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1568 [cell setBezelStyle: NSRoundedBezelStyle];
1570 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1571 mode: NSHighlightModeMatrix
1574 numberOfColumns: 1];
1575 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1576 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1577 [matrix setIntercellSpacing: spacing];
1578 [matrix autorelease];
1580 [[self contentView] addSubview: matrix];
1581 [self setOneShot: YES];
1582 [self setReleasedWhenClosed: YES];
1583 [self setHidesOnDeactivate: YES];
1588 - (BOOL)windowShouldClose: (id)sender
1590 window_closed = YES;
1597 xfree (button_values);
1601 - (void)process_dialog: (Lisp_Object) list
1603 Lisp_Object item, lst = list;
1605 int buttons = 0, btnnr = 0;
1607 for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
1610 if (XTYPE (item) == Lisp_Cons)
1615 button_values = xmalloc (buttons * sizeof *button_values);
1617 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1620 if (XTYPE (item) == Lisp_String)
1622 [self addString: SSDATA (item) row: row++];
1624 else if (XTYPE (item) == Lisp_Cons)
1626 button_values[btnnr] = XCDR (item);
1627 [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
1630 else if (NILP (item))
1639 - (void)addButton: (char *)str value: (int)tag row: (int)row
1648 cell = [matrix cellAtRow: row column: cols-1];
1649 [cell setTarget: self];
1650 [cell setAction: @selector (clicked: )];
1651 [cell setTitle: [NSString stringWithUTF8String: str]];
1653 [cell setBordered: YES];
1654 [cell setEnabled: YES];
1658 - (void)addString: (char *)str row: (int)row
1667 cell = [matrix cellAtRow: row column: cols-1];
1668 [cell setTitle: [NSString stringWithUTF8String: str]];
1669 [cell setBordered: YES];
1670 [cell setEnabled: NO];
1681 - (void)clicked: sender
1683 NSArray *sellist = nil;
1686 sellist = [sender selectedCells];
1687 if ([sellist count] < 1)
1690 seltag = [[sellist objectAtIndex: 0] tag];
1691 dialog_return = button_values[seltag];
1696 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1701 if (XTYPE (contents) == Lisp_Cons)
1703 head = Fcar (contents);
1704 [self process_dialog: Fcdr (contents)];
1709 if (XTYPE (head) == Lisp_String)
1710 [title setStringValue:
1711 [NSString stringWithUTF8String: SSDATA (head)]];
1712 else if (isQ == YES)
1713 [title setStringValue: @"Question"];
1715 [title setStringValue: @"Information"];
1721 if (cols == 1 && rows > 1) /* Never told where to split */
1724 for (i = 0; i < rows/2; i++)
1726 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1727 atRow: i column: 1];
1728 [matrix removeRow: (rows+1)/2];
1734 NSSize csize = [matrix cellSize];
1735 if (csize.width < MINCELLWIDTH)
1737 csize.width = MINCELLWIDTH;
1738 [matrix setCellSize: csize];
1739 [matrix sizeToCells];
1744 [command sizeToFit];
1748 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1750 t.origin.x = r.origin.x;
1751 t.size.width = r.size.width;
1753 r = [command frame];
1754 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1756 t.origin.x = r.origin.x;
1757 t.size.width = r.size.width;
1761 s = [(NSView *)[self contentView] frame];
1762 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1763 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1764 [self setFrame: r display: NO];
1772 - (void)timeout_handler: (NSTimer *)timedEntry
1774 NSEvent *nxev = [NSEvent otherEventWithType: NSApplicationDefined
1775 location: NSMakePoint (0, 0)
1778 windowNumber: [[NSApp mainWindow] windowNumber]
1779 context: [NSApp context]
1785 /* We use sto because stopModal/abortModal out of the main loop does not
1786 seem to work in 10.6. But as we use stop we must send a real event so
1787 the stop is seen and acted upon. */
1789 [NSApp postEvent: nxev atStart: NO];
1792 - (Lisp_Object)runDialogAt: (NSPoint)p
1794 Lisp_Object ret = Qundefined;
1796 while (popup_activated_flag)
1799 struct timespec next_time = timer_check ();
1801 if (timespec_valid_p (next_time))
1803 double time = timespectod (next_time);
1804 tmo = [NSTimer timerWithTimeInterval: time
1806 selector: @selector (timeout_handler:)
1809 [[NSRunLoop currentRunLoop] addTimer: tmo
1810 forMode: NSModalPanelRunLoopMode];
1813 dialog_return = Qundefined;
1814 [NSApp runModalForWindow: self];
1815 ret = dialog_return;
1818 if (tmo != nil) [tmo invalidate]; /* Cancels timer */
1823 if (EQ (ret, Qundefined) && window_closed)
1824 /* Make close button pressed equivalent to C-g. */
1825 Fsignal (Qquit, Qnil);
1833 /* ==========================================================================
1837 ========================================================================== */
1839 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1840 doc: /* Cause the NS menu to be re-calculated. */)
1843 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1848 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1849 doc: /* Return t if a menu or popup dialog is active. */)
1852 return popup_activated () ? Qt : Qnil;
1855 /* ==========================================================================
1857 Lisp interface declaration
1859 ========================================================================== */
1862 syms_of_nsmenu (void)
1864 #ifndef NS_IMPL_COCOA
1865 /* Don't know how to keep track of this in Next/Open/GNUstep. Always
1866 update menus there. */
1869 defsubr (&Sns_reset_menu);
1870 defsubr (&Smenu_or_popup_active_p);
1872 DEFSYM (Qdebug_on_next_call, "debug-on-next-call");