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>
49 int menu_trace_num = 0;
50 #define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \
51 __FILE__, __LINE__, ++menu_trace_num)
57 /* Include lisp -> C common menu parsing code */
58 #define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
59 #include "nsmenu_common.c"
62 extern long context_menu_value;
63 EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
65 /* Nonzero means a menu is currently active. */
66 static int popup_activated_flag;
68 /* Nonzero means we are tracking and updating menus. */
69 static int trackingMenu;
72 /* NOTE: toolbar implementation is at end,
73 following complete menu implementation. */
76 /* ==========================================================================
78 Menu: Externally-called functions
80 ========================================================================== */
83 /* Supposed to discard menubar and free storage. Since we share the
84 menubar among frames and update its context for the focused window,
85 there is nothing to do here. */
87 free_frame_menubar (struct frame *f)
94 popup_activated (void)
96 return popup_activated_flag;
100 /* --------------------------------------------------------------------------
101 Update menubar. Three cases:
102 1) ! deep_p, submenu = nil: Fresh switch onto a frame -- either set up
103 just top-level menu strings (OS X), or goto case (2) (GNUstep).
104 2) deep_p, submenu = nil: Recompute all submenus.
105 3) deep_p, submenu = non-nil: Update contents of a single submenu.
106 -------------------------------------------------------------------------- */
108 ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
110 NSAutoreleasePool *pool;
111 id menu = [NSApp mainMenu];
112 static EmacsMenu *last_submenu = nil;
116 widget_value *wv, *first_wv, *prev_wv = 0;
124 NSTRACE (ns_update_menubar);
126 if (f != SELECTED_FRAME ())
128 XSETFRAME (Vmenu_updating_frame, f);
129 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
132 pool = [[NSAutoreleasePool alloc] init];
134 /* Menu may have been created automatically; if so, discard it. */
135 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
143 menu = [[EmacsMenu alloc] initWithTitle: ns_app_name];
147 { /* close up anything on there */
148 id attMenu = [menu attachedMenu];
155 t = -(1000*tb.time+tb.millitm);
158 #ifdef NS_IMPL_GNUSTEP
159 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
164 /* Fully parse one or more of the submenus. */
166 int *submenu_start, *submenu_end;
167 bool *submenu_top_level_items;
168 int *submenu_n_panes;
169 struct buffer *prev = current_buffer;
171 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
172 int previous_menu_items_used = f->menu_bar_items_used;
173 Lisp_Object *previous_items
174 = alloca (previous_menu_items_used * sizeof *previous_items);
176 /* lisp preliminaries */
177 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
178 specbind (Qinhibit_quit, Qt);
179 specbind (Qdebug_on_next_call, Qnil);
180 record_unwind_save_match_data ();
181 if (NILP (Voverriding_local_map_menu_flag))
183 specbind (Qoverriding_terminal_local_map, Qnil);
184 specbind (Qoverriding_local_map, Qnil);
186 set_buffer_internal_1 (XBUFFER (buffer));
188 /* TODO: for some reason this is not needed in other terms,
189 but some menu updates call Info-extract-pointer which causes
190 abort-on-error if waiting-for-input. Needs further investigation. */
191 owfi = waiting_for_input;
192 waiting_for_input = 0;
194 /* lucid hook and possible reset */
195 safe_run_hooks (Qactivate_menubar_hook);
196 if (! NILP (Vlucid_menu_bar_dirty_flag))
197 call0 (Qrecompute_lucid_menubar);
198 safe_run_hooks (Qmenu_bar_update_hook);
199 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
201 /* Now ready to go */
202 items = FRAME_MENU_BAR_ITEMS (f);
204 /* Save the frame's previous menu bar contents data */
205 if (previous_menu_items_used)
206 memcpy (previous_items, aref_addr (f->menu_bar_vector, 0),
207 previous_menu_items_used * sizeof (Lisp_Object));
209 /* parse stage 1: extract from lisp */
212 menu_items = f->menu_bar_vector;
213 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
214 submenu_start = alloca (ASIZE (items) * sizeof *submenu_start);
215 submenu_end = alloca (ASIZE (items) * sizeof *submenu_end);
216 submenu_n_panes = alloca (ASIZE (items) * sizeof *submenu_n_panes);
217 submenu_top_level_items = alloca (ASIZE (items)
218 * sizeof *submenu_top_level_items);
220 for (i = 0; i < ASIZE (items); i += 4)
222 Lisp_Object key, string, maps;
224 key = AREF (items, i);
225 string = AREF (items, i + 1);
226 maps = AREF (items, i + 2);
230 /* FIXME: we'd like to only parse the needed submenu, but this
231 was causing crashes in the _common parsing code.. need to make
232 sure proper initialization done.. */
233 /* if (submenu && strcmp ([[submenu title] UTF8String], SSDATA (string)))
236 submenu_start[i] = menu_items_used;
238 menu_items_n_panes = 0;
239 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
240 submenu_n_panes[i] = menu_items_n_panes;
241 submenu_end[i] = menu_items_used;
245 finish_menu_items ();
246 waiting_for_input = owfi;
249 if (submenu && n == 0)
251 /* should have found a menu for this one but didn't */
252 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
253 [[submenu title] UTF8String]);
254 discard_menu_items ();
255 unbind_to (specpdl_count, Qnil);
261 /* parse stage 2: insert into lucid 'widget_value' structures
262 [comments in other terms say not to evaluate lisp code here] */
263 wv = make_widget_value ("menubar", NULL, true, Qnil);
264 wv->button_type = BUTTON_TYPE_NONE;
267 for (i = 0; i < 4*n; i += 4)
269 menu_items_n_panes = submenu_n_panes[i];
270 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
271 submenu_top_level_items[i]);
275 first_wv->contents = wv;
276 /* Don't set wv->name here; GC during the loop might relocate it. */
278 wv->button_type = BUTTON_TYPE_NONE;
282 set_buffer_internal_1 (prev);
284 /* Compare the new menu items with previous, and leave off if no change */
285 /* FIXME: following other terms here, but seems like this should be
286 done before parse stage 2 above, since its results aren't used */
287 if (previous_menu_items_used
288 && (!submenu || (submenu && submenu == last_submenu))
289 && menu_items_used == previous_menu_items_used)
291 for (i = 0; i < previous_menu_items_used; i++)
292 /* FIXME: this ALWAYS fails on Buffers menu items.. something
293 about their strings causes them to change every time, so we
294 double-check failures */
295 if (!EQ (previous_items[i], AREF (menu_items, i)))
296 if (!(STRINGP (previous_items[i])
297 && STRINGP (AREF (menu_items, i))
298 && !strcmp (SSDATA (previous_items[i]),
299 SSDATA (AREF (menu_items, i)))))
301 if (i == previous_menu_items_used)
307 t += 1000*tb.time+tb.millitm;
308 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
311 free_menubar_widget_value_tree (first_wv);
312 discard_menu_items ();
313 unbind_to (specpdl_count, Qnil);
319 /* The menu items are different, so store them in the frame */
320 /* FIXME: this is not correct for single-submenu case */
321 fset_menu_bar_vector (f, menu_items);
322 f->menu_bar_items_used = menu_items_used;
324 /* Calls restore_menu_items, etc., as they were outside */
325 unbind_to (specpdl_count, Qnil);
327 /* Parse stage 2a: now GC cannot happen during the lifetime of the
328 widget_value, so it's safe to store data from a Lisp_String */
329 wv = first_wv->contents;
330 for (i = 0; i < ASIZE (items); i += 4)
333 string = AREF (items, i + 1);
337 wv->name = SSDATA (string);
338 update_submenu_strings (wv->contents);
342 /* Now, update the NS menu; if we have a submenu, use that, otherwise
343 create a new menu for each sub and fill it. */
346 const char *submenuTitle = [[submenu title] UTF8String];
347 for (wv = first_wv->contents; wv; wv = wv->next)
349 if (!strcmp (submenuTitle, wv->name))
351 [submenu fillWithWidgetValue: wv->contents];
352 last_submenu = submenu;
359 [menu fillWithWidgetValue: first_wv->contents frame: f];
365 static int n_previous_strings = 0;
366 static char previous_strings[100][10];
367 static struct frame *last_f = NULL;
371 wv = make_widget_value ("menubar", NULL, true, Qnil);
372 wv->button_type = BUTTON_TYPE_NONE;
375 /* Make widget-value tree w/ just the top level menu bar strings */
376 items = FRAME_MENU_BAR_ITEMS (f);
379 free_menubar_widget_value_tree (first_wv);
386 /* check if no change.. this mechanism is a bit rough, but ready */
387 n = ASIZE (items) / 4;
388 if (f == last_f && n_previous_strings == n)
390 for (i = 0; i<n; i++)
392 string = AREF (items, 4*i+1);
394 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
398 if (previous_strings[i][0])
403 else if (memcmp (previous_strings[i], SDATA (string),
404 min (10, SBYTES (string) + 1)))
410 free_menubar_widget_value_tree (first_wv);
418 for (i = 0; i < ASIZE (items); i += 4)
420 string = AREF (items, i + 1);
425 memcpy (previous_strings[i/4], SDATA (string),
426 min (10, SBYTES (string) + 1));
428 wv = make_widget_value (SSDATA (string), NULL, true, Qnil);
429 wv->button_type = BUTTON_TYPE_NONE;
430 wv->call_data = (void *) (intptr_t) (-1);
433 /* we'll update the real copy under app menu when time comes */
434 if (!strcmp ("Services", wv->name))
436 /* but we need to make sure it will update on demand */
437 [svcsMenu setFrame: f];
441 [menu addSubmenuWithTitle: wv->name forFrame: f];
446 first_wv->contents = wv;
452 n_previous_strings = n;
454 n_previous_strings = 0;
457 free_menubar_widget_value_tree (first_wv);
462 t += 1000*tb.time+tb.millitm;
463 fprintf (stderr, "Menu update took %ld msec.\n", t);
468 [NSApp setMainMenu: menu];
476 /* Main emacs core entry point for menubar menus: called to indicate that the
477 frame's menus have changed, and the *step representation should be updated
480 set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
482 ns_update_menubar (f, deep_p, nil);
486 x_activate_menubar (struct frame *f)
489 ns_update_menubar (f, true, nil);
490 ns_check_pending_open_menu ();
497 /* ==========================================================================
499 Menu: class implementation
501 ========================================================================== */
504 /* Menu that can define itself from Emacs "widget_value"s and will lazily
505 update itself when user clicked. Based on Carbon/AppKit implementation
506 by Yamamoto Mitsuharu. */
507 @implementation EmacsMenu
509 /* override designated initializer */
510 - initWithTitle: (NSString *)title
513 if ((self = [super initWithTitle: title]))
514 [self setAutoenablesItems: NO];
519 /* used for top-level */
520 - initWithTitle: (NSString *)title frame: (struct frame *)f
522 [self initWithTitle: title];
525 [self setDelegate: self];
531 - (void)setFrame: (struct frame *)f
537 -(void)trackingNotification:(NSNotification *)notification
539 /* Update menu in menuNeedsUpdate only while tracking menus. */
540 trackingMenu = ([notification name] == NSMenuDidBeginTrackingNotification
542 if (! trackingMenu) ns_check_menu_open (nil);
545 - (void)menuWillOpen:(NSMenu *)menu
549 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
550 // On 10.6 we get repeated calls, only the one for NSSystemDefined is "real".
551 if ([[NSApp currentEvent] type] != NSSystemDefined) return;
554 /* When dragging from one menu to another, we get willOpen followed by didClose,
555 i.e. trackingMenu == 3 in willOpen and then 2 after didClose.
556 We have updated all menus, so avoid doing it when trackingMenu == 3. */
557 if (trackingMenu == 2)
558 ns_check_menu_open (menu);
561 - (void)menuDidClose:(NSMenu *)menu
566 #endif /* NS_IMPL_COCOA */
568 /* delegate method called when a submenu is being opened: run a 'deep' call
569 to set_frame_menubar */
570 - (void)menuNeedsUpdate: (NSMenu *)menu
572 if (!FRAME_LIVE_P (frame))
575 /* Cocoa/Carbon will request update on every keystroke
576 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
577 since key equivalents are handled through emacs.
578 On Leopard, even keystroke events generate SystemDefined event.
579 Third-party applications that enhance mouse / trackpad
580 interaction, or also VNC/Remote Desktop will send events
581 of type AppDefined rather than SysDefined.
582 Menus will fail to show up if they haven't been initialized.
583 AppDefined events may lack timing data.
585 Thus, we rely on the didBeginTrackingNotification notification
586 as above to indicate the need for updates.
587 From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
588 key press case, NSMenuPropertyItemImage (e.g.) won't be set.
590 if (trackingMenu == 0)
592 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
593 #ifdef NS_IMPL_GNUSTEP
594 /* Don't know how to do this for anything other than OSX >= 10.5
595 This is wrong, as it might run Lisp code in the event loop. */
596 ns_update_menubar (frame, true, self);
601 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
603 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
604 && FRAME_NS_VIEW (SELECTED_FRAME ()))
605 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
610 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
611 into an accelerator string. We are only able to display a single character
612 for an accelerator, together with an optional modifier combination. (Under
613 Carbon more control was possible, but in Cocoa multi-char strings passed to
614 NSMenuItem get ignored. For now we try to display a super-single letter
615 combo, and return the others as strings to be appended to the item title.
616 (This is signaled by setting keyEquivModMask to 0 for now.) */
617 -(NSString *)parseKeyEquiv: (const char *)key
619 const char *tpos = key;
620 keyEquivModMask = NSCommandKeyMask;
622 if (!key || !strlen (key))
625 while (*tpos == ' ' || *tpos == '(')
627 if ((*tpos == 's') && (*(tpos+1) == '-'))
629 return [NSString stringWithFormat: @"%c", tpos[2]];
631 keyEquivModMask = 0; /* signal */
632 return [NSString stringWithUTF8String: tpos];
636 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
639 widget_value *wv = (widget_value *)wvptr;
641 if (menu_separator_name_p (wv->name))
643 item = [NSMenuItem separatorItem];
644 [self addItem: item];
648 NSString *title, *keyEq;
649 title = [NSString stringWithUTF8String: wv->name];
651 title = @"< ? >"; /* (get out in the open so we know about it) */
653 keyEq = [self parseKeyEquiv: wv->key];
655 /* OS X just ignores modifier strings longer than one character */
656 if (keyEquivModMask == 0)
657 title = [title stringByAppendingFormat: @" (%@)", keyEq];
660 item = [self addItemWithTitle: (NSString *)title
661 action: @selector (menuDown:)
662 keyEquivalent: keyEq];
663 [item setKeyEquivalentModifierMask: keyEquivModMask];
665 [item setEnabled: wv->enabled];
667 /* Draw radio buttons and tickboxes */
668 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
669 wv->button_type == BUTTON_TYPE_RADIO))
670 [item setState: NSOnState];
672 [item setState: NSOffState];
674 [item setTag: (NSInteger)wv->call_data];
686 for (n = [self numberOfItems]-1; n >= 0; n--)
688 NSMenuItem *item = [self itemAtIndex: n];
689 NSString *title = [item title];
690 if ([ns_app_name isEqualToString: title]
691 && ![item isSeparatorItem])
693 [self removeItemAtIndex: n];
698 - (void)fillWithWidgetValue: (void *)wvptr
700 [self fillWithWidgetValue: wvptr frame: (struct frame *)nil];
703 - (void)fillWithWidgetValue: (void *)wvptr frame: (struct frame *)f
705 widget_value *wv = (widget_value *)wvptr;
707 /* clear existing contents */
708 [self setMenuChangedMessagesEnabled: NO];
711 /* add new contents */
712 for (; wv != NULL; wv = wv->next)
714 NSMenuItem *item = [self addItemWithWidgetValue: wv];
721 submenu = [[EmacsMenu alloc] initWithTitle: [item title] frame:f];
723 submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
725 [self setSubmenu: submenu forItem: item];
726 [submenu fillWithWidgetValue: wv->contents];
728 [item setAction: (SEL)nil];
732 [self setMenuChangedMessagesEnabled: YES];
733 #ifdef NS_IMPL_GNUSTEP
734 if ([[self window] isVisible])
740 /* adds an empty submenu and returns it */
741 - (EmacsMenu *)addSubmenuWithTitle: (const char *)title forFrame: (struct frame *)f
743 NSString *titleStr = [NSString stringWithUTF8String: title];
744 NSMenuItem *item = [self addItemWithTitle: titleStr
745 action: (SEL)nil /*@selector (menuDown:) */
747 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
748 [self setSubmenu: submenu forItem: item];
753 /* run a menu in popup mode */
754 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
755 keymaps: (bool)keymaps
757 EmacsView *view = FRAME_NS_VIEW (f);
761 /* p = [view convertPoint:p fromView: nil]; */
762 p.y = NSHeight ([view frame]) - p.y;
763 e = [[view window] currentEvent];
764 event = [NSEvent mouseEventWithType: NSRightMouseDown
767 timestamp: [e timestamp]
768 windowNumber: [[view window] windowNumber]
770 eventNumber: 0/*[e eventNumber] */
774 context_menu_value = -1;
775 [NSMenu popUpContextMenu: self withEvent: event forView: view];
776 retVal = context_menu_value;
777 context_menu_value = 0;
779 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
787 /* ==========================================================================
789 Context Menu: implementing functions
791 ========================================================================== */
794 ns_menu_show (struct frame *f, int x, int y, int menuflags,
795 Lisp_Object title, const char **error)
800 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
801 widget_value *wv, *first_wv = 0;
802 bool keymaps = (menuflags & MENU_KEYMAPS);
808 /* now parse stage 2 as in ns_update_menubar */
809 wv = make_widget_value ("contextmenu", NULL, true, Qnil);
810 wv->button_type = BUTTON_TYPE_NONE;
814 /* FIXME: a couple of one-line differences prevent reuse */
815 wv = digest_single_submenu (0, menu_items_used, 0);
818 widget_value *save_wv = 0, *prev_wv = 0;
819 widget_value **submenu_stack
820 = alloca (menu_items_used * sizeof *submenu_stack);
821 /* Lisp_Object *subprefix_stack
822 = alloca (menu_items_used * sizeof *subprefix_stack); */
823 int submenu_depth = 0;
827 /* Loop over all panes and items, filling in the tree. */
829 while (i < menu_items_used)
831 if (EQ (AREF (menu_items, i), Qnil))
833 submenu_stack[submenu_depth++] = save_wv;
839 else if (EQ (AREF (menu_items, i), Qlambda))
842 save_wv = submenu_stack[--submenu_depth];
846 else if (EQ (AREF (menu_items, i), Qt)
847 && submenu_depth != 0)
848 i += MENU_ITEMS_PANE_LENGTH;
849 /* Ignore a nil in the item list.
850 It's meaningful only for dialog boxes. */
851 else if (EQ (AREF (menu_items, i), Qquote))
853 else if (EQ (AREF (menu_items, i), Qt))
855 /* Create a new pane. */
856 Lisp_Object pane_name, prefix;
857 const char *pane_string;
859 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
860 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
862 #ifndef HAVE_MULTILINGUAL_MENU
863 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
865 pane_name = ENCODE_MENU_STRING (pane_name);
866 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
869 pane_string = (NILP (pane_name)
870 ? "" : SSDATA (pane_name));
871 /* If there is just one top-level pane, put all its items directly
872 under the top-level menu. */
873 if (menu_items_n_panes == 1)
876 /* If the pane has a meaningful name,
877 make the pane a top-level menu item
878 with its items as a submenu beneath it. */
879 if (!keymaps && strcmp (pane_string, ""))
881 wv = make_widget_value (pane_string, NULL, true, Qnil);
885 first_wv->contents = wv;
886 if (keymaps && !NILP (prefix))
888 wv->button_type = BUTTON_TYPE_NONE;
898 i += MENU_ITEMS_PANE_LENGTH;
902 /* Create a new item within current pane. */
903 Lisp_Object item_name, enable, descrip, def, type, selected, help;
904 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
905 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
906 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
907 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
908 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
909 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
910 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
912 #ifndef HAVE_MULTILINGUAL_MENU
913 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
915 item_name = ENCODE_MENU_STRING (item_name);
916 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
919 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
921 descrip = ENCODE_MENU_STRING (descrip);
922 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
924 #endif /* not HAVE_MULTILINGUAL_MENU */
926 wv = make_widget_value (SSDATA (item_name), NULL, !NILP (enable),
927 STRINGP (help) ? help : Qnil);
931 save_wv->contents = wv;
933 wv->key = SSDATA (descrip);
934 /* If this item has a null value,
935 make the call_data null so that it won't display a box
936 when the mouse is on it. */
937 wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
940 wv->button_type = BUTTON_TYPE_NONE;
941 else if (EQ (type, QCtoggle))
942 wv->button_type = BUTTON_TYPE_TOGGLE;
943 else if (EQ (type, QCradio))
944 wv->button_type = BUTTON_TYPE_RADIO;
948 wv->selected = !NILP (selected);
952 i += MENU_ITEMS_ITEM_LENGTH;
960 widget_value *wv_title;
961 widget_value *wv_sep = make_widget_value ("--", NULL, false, Qnil);
963 /* Maybe replace this separator with a bitmap or owner-draw item
964 so that it looks better. Having two separators looks odd. */
965 wv_sep->next = first_wv->contents;
967 #ifndef HAVE_MULTILINGUAL_MENU
968 if (STRING_MULTIBYTE (title))
969 title = ENCODE_MENU_STRING (title);
971 wv_title = make_widget_value (SSDATA (title), NULL, false, Qnil);
972 wv_title->button_type = BUTTON_TYPE_NONE;
973 wv_title->next = wv_sep;
974 first_wv->contents = wv_title;
977 pmenu = [[EmacsMenu alloc] initWithTitle:
978 [NSString stringWithUTF8String: SSDATA (title)]];
979 [pmenu fillWithWidgetValue: first_wv->contents];
980 free_menubar_widget_value_tree (first_wv);
981 unbind_to (specpdl_count, Qnil);
983 popup_activated_flag = 1;
984 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
985 popup_activated_flag = 0;
986 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
993 /* ==========================================================================
995 Toolbar: externally-called functions
997 ========================================================================== */
1000 free_frame_tool_bar (struct frame *f)
1001 /* --------------------------------------------------------------------------
1002 Under NS we just hide the toolbar until it might be needed again.
1003 -------------------------------------------------------------------------- */
1005 EmacsView *view = FRAME_NS_VIEW (f);
1007 view->wait_for_tool_bar = NO;
1008 [[view toolbar] setVisible: NO];
1009 FRAME_TOOLBAR_HEIGHT (f) = 0;
1014 update_frame_tool_bar (struct frame *f)
1015 /* --------------------------------------------------------------------------
1016 Update toolbar contents
1017 -------------------------------------------------------------------------- */
1020 EmacsView *view = FRAME_NS_VIEW (f);
1021 NSWindow *window = [view window];
1022 EmacsToolbar *toolbar = [view toolbar];
1025 if (view == nil || toolbar == nil) return;
1028 oldh = FRAME_TOOLBAR_HEIGHT (f);
1030 #ifdef NS_IMPL_COCOA
1031 [toolbar clearActive];
1036 /* update EmacsToolbar as in GtkUtils, build items list */
1037 for (i = 0; i < f->n_tool_bar_items; ++i)
1039 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1040 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1042 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1047 Lisp_Object helpObj;
1048 const char *helpText;
1050 /* Check if this is a separator. */
1051 if (EQ (TOOLPROP (TOOL_BAR_ITEM_TYPE), Qt))
1053 /* Skip separators. Newer OSX don't show them, and on GNUstep they
1054 are wide as a button, thus overflowing the toolbar most of
1059 /* If image is a vector, choose the image according to the
1061 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1062 if (VECTORP (image))
1064 /* NS toolbar auto-computes disabled and selected images */
1065 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1066 eassert (ASIZE (image) >= idx);
1067 image = AREF (image, idx);
1073 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1075 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1076 helpText = NILP (helpObj) ? "" : SSDATA (helpObj);
1078 /* Ignore invalid image specifications. */
1079 if (!valid_image_p (image))
1081 /* Don't log anything, GNUS makes invalid images all the time. */
1085 img_id = lookup_image (f, image);
1086 img = IMAGE_FROM_ID (f, img_id);
1087 prepare_image_for_display (f, img);
1089 if (img->load_failed_p || img->pixmap == nil)
1091 NSLog (@"Could not prepare toolbar image for display.");
1095 [toolbar addDisplayItemWithImage: img->pixmap
1099 enabled: enabled_p];
1103 if (![toolbar isVisible])
1104 [toolbar setVisible: YES];
1106 #ifdef NS_IMPL_COCOA
1107 if ([toolbar changed])
1109 /* inform app that toolbar has changed */
1110 NSDictionary *dict = [toolbar configurationDictionary];
1111 NSMutableDictionary *newDict = [dict mutableCopy];
1112 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1114 while ((key = [keys nextObject]) != nil)
1116 NSObject *val = [dict objectForKey: key];
1117 if ([val isKindOfClass: [NSArray class]])
1120 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1125 [toolbar setConfigurationFromDictionary: newDict];
1130 FRAME_TOOLBAR_HEIGHT (f) =
1131 NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1132 - FRAME_NS_TITLEBAR_HEIGHT (f);
1133 if (FRAME_TOOLBAR_HEIGHT (f) < 0) // happens if frame is fullscreen.
1134 FRAME_TOOLBAR_HEIGHT (f) = 0;
1136 if (oldh != FRAME_TOOLBAR_HEIGHT (f))
1137 [view updateFrameSize:YES];
1138 if (view->wait_for_tool_bar && FRAME_TOOLBAR_HEIGHT (f) > 0)
1140 view->wait_for_tool_bar = NO;
1141 [view setNeedsDisplay: YES];
1148 /* ==========================================================================
1150 Toolbar: class implementation
1152 ========================================================================== */
1154 @implementation EmacsToolbar
1156 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1158 self = [super initWithIdentifier: identifier];
1160 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1161 [self setSizeMode: NSToolbarSizeModeSmall];
1162 [self setDelegate: self];
1163 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1164 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1165 prevIdentifiers = nil;
1166 prevEnablement = enablement = 0L;
1172 [prevIdentifiers release];
1173 [activeIdentifiers release];
1174 [identifierToItem release];
1178 - (void) clearActive
1180 [prevIdentifiers release];
1181 prevIdentifiers = [activeIdentifiers copy];
1182 [activeIdentifiers removeAllObjects];
1183 prevEnablement = enablement;
1190 while ([[self items] count] > 0)
1191 [self removeItemAtIndex: 0];
1196 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1197 enablement == prevEnablement ? NO : YES;
1200 - (void) addDisplayItemWithImage: (EmacsImage *)img
1203 helpText: (const char *)help
1204 enabled: (BOOL)enabled
1206 /* 1) come up w/identifier */
1207 NSString *identifier
1208 = [NSString stringWithFormat: @"%lu", (unsigned long)[img hash]];
1209 [activeIdentifiers addObject: identifier];
1211 /* 2) create / reuse item */
1212 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1215 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1217 [item setImage: img];
1218 [item setToolTip: [NSString stringWithUTF8String: help]];
1219 [item setTarget: emacsView];
1220 [item setAction: @selector (toolbarClicked:)];
1221 [identifierToItem setObject: item forKey: identifier];
1224 #ifdef NS_IMPL_GNUSTEP
1225 [self insertItemWithItemIdentifier: identifier atIndex: idx];
1229 [item setEnabled: enabled];
1231 /* 3) update state */
1232 enablement = (enablement << 1) | (enabled == YES);
1235 /* This overrides super's implementation, which automatically sets
1236 all items to enabled state (for some reason). */
1237 - (void)validateVisibleItems
1242 /* delegate methods */
1244 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1245 itemForItemIdentifier: (NSString *)itemIdentifier
1246 willBeInsertedIntoToolbar: (BOOL)flag
1248 /* look up NSToolbarItem by identifier and return... */
1249 return [identifierToItem objectForKey: itemIdentifier];
1252 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1254 /* return entire set.. */
1255 return activeIdentifiers;
1258 /* for configuration palette (not yet supported) */
1259 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1261 /* return entire set... */
1262 return activeIdentifiers;
1263 //return [identifierToItem allKeys];
1266 /* optional and unneeded */
1267 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1268 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1269 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1271 @end /* EmacsToolbar */
1275 /* ==========================================================================
1277 Tooltip: class implementation
1279 ========================================================================== */
1281 /* Needed because NeXTstep does not provide enough control over tooltip
1283 @implementation EmacsTooltip
1287 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1288 blue: 0.792 alpha: 0.95];
1289 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1290 NSFont *sfont = [font screenFont];
1291 int height = [sfont ascender] - [sfont descender];
1292 /*[font boundingRectForFont].size.height; */
1293 NSRect r = NSMakeRect (0, 0, 100, height+6);
1295 textField = [[NSTextField alloc] initWithFrame: r];
1296 [textField setFont: font];
1297 [textField setBackgroundColor: col];
1299 [textField setEditable: NO];
1300 [textField setSelectable: NO];
1301 [textField setBordered: NO];
1302 [textField setBezeled: NO];
1303 [textField setDrawsBackground: YES];
1305 win = [[NSWindow alloc]
1306 initWithContentRect: [textField frame]
1308 backing: NSBackingStoreBuffered
1310 [win setHasShadow: YES];
1311 [win setReleasedWhenClosed: NO];
1312 [win setDelegate: self];
1313 [[win contentView] addSubview: textField];
1314 /* [win setBackgroundColor: col]; */
1315 [win setOpaque: NO];
1324 [textField release];
1328 - (void) setText: (char *)text
1330 NSString *str = [NSString stringWithUTF8String: text];
1331 NSRect r = [textField frame];
1334 [textField setStringValue: str];
1335 tooltipDims = [[textField cell] cellSize];
1337 r.size.width = tooltipDims.width;
1338 r.size.height = tooltipDims.height;
1339 [textField setFrame: r];
1342 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1344 NSRect wr = [win frame];
1346 wr.origin = NSMakePoint (x, y);
1347 wr.size = [textField frame].size;
1349 [win setFrame: wr display: YES];
1350 [win setLevel: NSPopUpMenuWindowLevel];
1351 [win orderFront: self];
1353 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1354 selector: @selector (hide)
1355 userInfo: nil repeats: NO];
1364 if ([timer isValid])
1373 return timer != nil;
1378 return [textField frame];
1381 @end /* EmacsTooltip */
1385 /* ==========================================================================
1387 Popup Dialog: implementing functions
1389 ========================================================================== */
1393 NSAutoreleasePool *pool;
1394 EmacsDialogPanel *dialog;
1398 pop_down_menu (void *arg)
1400 struct Popdown_data *unwind_data = arg;
1403 if (popup_activated_flag)
1405 EmacsDialogPanel *panel = unwind_data->dialog;
1406 popup_activated_flag = 0;
1408 [unwind_data->pool release];
1409 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1412 xfree (unwind_data);
1418 ns_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
1421 Lisp_Object window, tem, title;
1424 NSAutoreleasePool *pool;
1426 NSTRACE (x-popup-dialog);
1428 isQ = NILP (header);
1430 check_window_system (f);
1432 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1433 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1435 title = Fcar (contents);
1436 CHECK_STRING (title);
1438 if (NILP (Fcar (Fcdr (contents))))
1439 /* No buttons specified, add an "Ok" button so users can pop down
1441 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
1444 pool = [[NSAutoreleasePool alloc] init];
1445 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1449 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1450 struct Popdown_data *unwind_data = xmalloc (sizeof (*unwind_data));
1452 unwind_data->pool = pool;
1453 unwind_data->dialog = dialog;
1455 record_unwind_protect_ptr (pop_down_menu, unwind_data);
1456 popup_activated_flag = 1;
1457 tem = [dialog runDialogAt: p];
1458 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1467 /* ==========================================================================
1469 Popup Dialog: class implementation
1471 ========================================================================== */
1473 @interface FlippedView : NSView
1478 @implementation FlippedView
1485 @implementation EmacsDialogPanel
1488 #define ICONSIZE 64.0
1489 #define TEXTHEIGHT 20.0
1490 #define MINCELLWIDTH 90.0
1492 - initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
1493 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1495 NSSize spacing = {SPACER, SPACER};
1498 NSImageView *imgView;
1499 FlippedView *contentView;
1502 dialog_return = Qundefined;
1503 button_values = NULL;
1504 area.origin.x = 3*SPACER;
1505 area.origin.y = 2*SPACER;
1506 area.size.width = ICONSIZE;
1507 area.size.height= ICONSIZE;
1508 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1509 [img setScalesWhenResized: YES];
1510 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1511 imgView = [[NSImageView alloc] initWithFrame: area];
1512 [imgView setImage: img];
1513 [imgView setEditable: NO];
1515 [imgView autorelease];
1517 aStyle = NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask;
1521 [super initWithContentRect: contentRect styleMask: aStyle
1522 backing: backingType defer: flag];
1523 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1524 [contentView autorelease];
1526 [self setContentView: contentView];
1528 [[self contentView] setAutoresizesSubviews: YES];
1530 [[self contentView] addSubview: imgView];
1531 [self setTitle: @""];
1533 area.origin.x += ICONSIZE+2*SPACER;
1534 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1535 area.size.width = 400;
1536 area.size.height= TEXTHEIGHT;
1537 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1538 [[self contentView] addSubview: command];
1539 [command setStringValue: ns_app_name];
1540 [command setDrawsBackground: NO];
1541 [command setBezeled: NO];
1542 [command setSelectable: NO];
1543 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1545 /* area.origin.x = ICONSIZE+2*SPACER;
1546 area.origin.y = TEXTHEIGHT + 2*SPACER;
1547 area.size.width = 400;
1548 area.size.height= 2;
1549 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1550 [[self contentView] addSubview: tem];
1551 [tem setTitlePosition: NSNoTitle];
1552 [tem setAutoresizingMask: NSViewWidthSizable];*/
1554 /* area.origin.x = ICONSIZE+2*SPACER; */
1555 area.origin.y += TEXTHEIGHT+SPACER;
1556 area.size.width = 400;
1557 area.size.height= TEXTHEIGHT;
1558 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1559 [[self contentView] addSubview: title];
1560 [title setDrawsBackground: NO];
1561 [title setBezeled: NO];
1562 [title setSelectable: NO];
1563 [title setFont: [NSFont systemFontOfSize: 11.0]];
1565 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1566 [cell setBordered: NO];
1567 [cell setEnabled: NO];
1568 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1569 [cell setBezelStyle: NSRoundedBezelStyle];
1571 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1572 mode: NSHighlightModeMatrix
1575 numberOfColumns: 1];
1576 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1577 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1578 [matrix setIntercellSpacing: spacing];
1579 [matrix autorelease];
1581 [[self contentView] addSubview: matrix];
1582 [self setOneShot: YES];
1583 [self setReleasedWhenClosed: YES];
1584 [self setHidesOnDeactivate: YES];
1589 - (BOOL)windowShouldClose: (id)sender
1591 window_closed = YES;
1598 xfree (button_values);
1602 - (void)process_dialog: (Lisp_Object) list
1604 Lisp_Object item, lst = list;
1606 int buttons = 0, btnnr = 0;
1608 for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
1611 if (XTYPE (item) == Lisp_Cons)
1616 button_values = xmalloc (buttons * sizeof *button_values);
1618 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1621 if (XTYPE (item) == Lisp_String)
1623 [self addString: SSDATA (item) row: row++];
1625 else if (XTYPE (item) == Lisp_Cons)
1627 button_values[btnnr] = XCDR (item);
1628 [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
1631 else if (NILP (item))
1640 - (void)addButton: (char *)str value: (int)tag row: (int)row
1649 cell = [matrix cellAtRow: row column: cols-1];
1650 [cell setTarget: self];
1651 [cell setAction: @selector (clicked: )];
1652 [cell setTitle: [NSString stringWithUTF8String: str]];
1654 [cell setBordered: YES];
1655 [cell setEnabled: YES];
1659 - (void)addString: (char *)str row: (int)row
1668 cell = [matrix cellAtRow: row column: cols-1];
1669 [cell setTitle: [NSString stringWithUTF8String: str]];
1670 [cell setBordered: YES];
1671 [cell setEnabled: NO];
1682 - (void)clicked: sender
1684 NSArray *sellist = nil;
1687 sellist = [sender selectedCells];
1688 if ([sellist count] < 1)
1691 seltag = [[sellist objectAtIndex: 0] tag];
1692 dialog_return = button_values[seltag];
1697 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1702 if (XTYPE (contents) == Lisp_Cons)
1704 head = Fcar (contents);
1705 [self process_dialog: Fcdr (contents)];
1710 if (XTYPE (head) == Lisp_String)
1711 [title setStringValue:
1712 [NSString stringWithUTF8String: SSDATA (head)]];
1713 else if (isQ == YES)
1714 [title setStringValue: @"Question"];
1716 [title setStringValue: @"Information"];
1722 if (cols == 1 && rows > 1) /* Never told where to split */
1725 for (i = 0; i < rows/2; i++)
1727 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1728 atRow: i column: 1];
1729 [matrix removeRow: (rows+1)/2];
1735 NSSize csize = [matrix cellSize];
1736 if (csize.width < MINCELLWIDTH)
1738 csize.width = MINCELLWIDTH;
1739 [matrix setCellSize: csize];
1740 [matrix sizeToCells];
1745 [command sizeToFit];
1749 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1751 t.origin.x = r.origin.x;
1752 t.size.width = r.size.width;
1754 r = [command frame];
1755 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1757 t.origin.x = r.origin.x;
1758 t.size.width = r.size.width;
1762 s = [(NSView *)[self contentView] frame];
1763 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1764 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1765 [self setFrame: r display: NO];
1773 - (void)timeout_handler: (NSTimer *)timedEntry
1775 NSEvent *nxev = [NSEvent otherEventWithType: NSApplicationDefined
1776 location: NSMakePoint (0, 0)
1779 windowNumber: [[NSApp mainWindow] windowNumber]
1780 context: [NSApp context]
1786 /* We use sto because stopModal/abortModal out of the main loop does not
1787 seem to work in 10.6. But as we use stop we must send a real event so
1788 the stop is seen and acted upon. */
1790 [NSApp postEvent: nxev atStart: NO];
1793 - (Lisp_Object)runDialogAt: (NSPoint)p
1795 Lisp_Object ret = Qundefined;
1797 while (popup_activated_flag)
1800 struct timespec next_time = timer_check ();
1802 if (timespec_valid_p (next_time))
1804 double time = timespectod (next_time);
1805 tmo = [NSTimer timerWithTimeInterval: time
1807 selector: @selector (timeout_handler:)
1810 [[NSRunLoop currentRunLoop] addTimer: tmo
1811 forMode: NSModalPanelRunLoopMode];
1814 dialog_return = Qundefined;
1815 [NSApp runModalForWindow: self];
1816 ret = dialog_return;
1819 if (tmo != nil) [tmo invalidate]; /* Cancels timer */
1824 if (EQ (ret, Qundefined) && window_closed)
1825 /* Make close button pressed equivalent to C-g. */
1826 Fsignal (Qquit, Qnil);
1834 /* ==========================================================================
1838 ========================================================================== */
1840 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1841 doc: /* Cause the NS menu to be re-calculated. */)
1844 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1849 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1850 doc: /* Return t if a menu or popup dialog is active. */)
1853 return popup_activated () ? Qt : Qnil;
1856 /* ==========================================================================
1858 Lisp interface declaration
1860 ========================================================================== */
1863 syms_of_nsmenu (void)
1865 #ifndef NS_IMPL_COCOA
1866 /* Don't know how to keep track of this in Next/Open/GNUstep. Always
1867 update menus there. */
1870 defsubr (&Sns_reset_menu);
1871 defsubr (&Smenu_or_popup_active_p);
1873 DEFSYM (Qdebug_on_next_call, "debug-on-next-call");