1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007-2016 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 (at
9 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];
142 t = -(1000*tb.time+tb.millitm);
145 #ifdef NS_IMPL_GNUSTEP
146 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
151 /* Fully parse one or more of the submenus. */
153 int *submenu_start, *submenu_end;
154 bool *submenu_top_level_items;
155 int *submenu_n_panes;
156 struct buffer *prev = current_buffer;
158 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
159 int previous_menu_items_used = f->menu_bar_items_used;
160 Lisp_Object *previous_items
161 = alloca (previous_menu_items_used * sizeof *previous_items);
163 /* lisp preliminaries */
164 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
165 specbind (Qinhibit_quit, Qt);
166 specbind (Qdebug_on_next_call, Qnil);
167 record_unwind_save_match_data ();
168 if (NILP (Voverriding_local_map_menu_flag))
170 specbind (Qoverriding_terminal_local_map, Qnil);
171 specbind (Qoverriding_local_map, Qnil);
173 set_buffer_internal_1 (XBUFFER (buffer));
175 /* TODO: for some reason this is not needed in other terms,
176 but some menu updates call Info-extract-pointer which causes
177 abort-on-error if waiting-for-input. Needs further investigation. */
178 owfi = waiting_for_input;
179 waiting_for_input = 0;
181 /* lucid hook and possible reset */
182 safe_run_hooks (Qactivate_menubar_hook);
183 if (! NILP (Vlucid_menu_bar_dirty_flag))
184 call0 (Qrecompute_lucid_menubar);
185 safe_run_hooks (Qmenu_bar_update_hook);
186 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
188 /* Now ready to go */
189 items = FRAME_MENU_BAR_ITEMS (f);
191 /* Save the frame's previous menu bar contents data */
192 if (previous_menu_items_used)
193 memcpy (previous_items, aref_addr (f->menu_bar_vector, 0),
194 previous_menu_items_used * sizeof (Lisp_Object));
196 /* parse stage 1: extract from lisp */
199 menu_items = f->menu_bar_vector;
200 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
201 submenu_start = alloca (ASIZE (items) * sizeof *submenu_start);
202 submenu_end = alloca (ASIZE (items) * sizeof *submenu_end);
203 submenu_n_panes = alloca (ASIZE (items) * sizeof *submenu_n_panes);
204 submenu_top_level_items = alloca (ASIZE (items)
205 * sizeof *submenu_top_level_items);
207 for (i = 0; i < ASIZE (items); i += 4)
209 Lisp_Object key, string, maps;
211 key = AREF (items, i);
212 string = AREF (items, i + 1);
213 maps = AREF (items, i + 2);
217 /* FIXME: we'd like to only parse the needed submenu, but this
218 was causing crashes in the _common parsing code.. need to make
219 sure proper initialization done.. */
220 /* if (submenu && strcmp ([[submenu title] UTF8String], SSDATA (string)))
223 submenu_start[i] = menu_items_used;
225 menu_items_n_panes = 0;
226 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
227 submenu_n_panes[i] = menu_items_n_panes;
228 submenu_end[i] = menu_items_used;
232 finish_menu_items ();
233 waiting_for_input = owfi;
236 if (submenu && n == 0)
238 /* should have found a menu for this one but didn't */
239 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
240 [[submenu title] UTF8String]);
241 discard_menu_items ();
242 unbind_to (specpdl_count, Qnil);
248 /* parse stage 2: insert into lucid 'widget_value' structures
249 [comments in other terms say not to evaluate lisp code here] */
250 wv = make_widget_value ("menubar", NULL, true, Qnil);
251 wv->button_type = BUTTON_TYPE_NONE;
254 for (i = 0; i < 4*n; i += 4)
256 menu_items_n_panes = submenu_n_panes[i];
257 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
258 submenu_top_level_items[i]);
262 first_wv->contents = wv;
263 /* Don't set wv->name here; GC during the loop might relocate it. */
265 wv->button_type = BUTTON_TYPE_NONE;
269 set_buffer_internal_1 (prev);
271 /* Compare the new menu items with previous, and leave off if no change */
272 /* FIXME: following other terms here, but seems like this should be
273 done before parse stage 2 above, since its results aren't used */
274 if (previous_menu_items_used
275 && (!submenu || (submenu && submenu == last_submenu))
276 && menu_items_used == previous_menu_items_used)
278 for (i = 0; i < previous_menu_items_used; i++)
279 /* FIXME: this ALWAYS fails on Buffers menu items.. something
280 about their strings causes them to change every time, so we
281 double-check failures */
282 if (!EQ (previous_items[i], AREF (menu_items, i)))
283 if (!(STRINGP (previous_items[i])
284 && STRINGP (AREF (menu_items, i))
285 && !strcmp (SSDATA (previous_items[i]),
286 SSDATA (AREF (menu_items, i)))))
288 if (i == previous_menu_items_used)
294 t += 1000*tb.time+tb.millitm;
295 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
298 free_menubar_widget_value_tree (first_wv);
299 discard_menu_items ();
300 unbind_to (specpdl_count, Qnil);
306 /* The menu items are different, so store them in the frame */
307 /* FIXME: this is not correct for single-submenu case */
308 fset_menu_bar_vector (f, menu_items);
309 f->menu_bar_items_used = menu_items_used;
311 /* Calls restore_menu_items, etc., as they were outside */
312 unbind_to (specpdl_count, Qnil);
314 /* Parse stage 2a: now GC cannot happen during the lifetime of the
315 widget_value, so it's safe to store data from a Lisp_String */
316 wv = first_wv->contents;
317 for (i = 0; i < ASIZE (items); i += 4)
320 string = AREF (items, i + 1);
324 wv->name = SSDATA (string);
325 update_submenu_strings (wv->contents);
329 /* Now, update the NS menu; if we have a submenu, use that, otherwise
330 create a new menu for each sub and fill it. */
333 const char *submenuTitle = [[submenu title] UTF8String];
334 for (wv = first_wv->contents; wv; wv = wv->next)
336 if (!strcmp (submenuTitle, wv->name))
338 [submenu fillWithWidgetValue: wv->contents];
339 last_submenu = submenu;
346 [menu fillWithWidgetValue: first_wv->contents frame: f];
352 static int n_previous_strings = 0;
353 static char previous_strings[100][10];
354 static struct frame *last_f = NULL;
358 wv = make_widget_value ("menubar", NULL, true, Qnil);
359 wv->button_type = BUTTON_TYPE_NONE;
362 /* Make widget-value tree w/ just the top level menu bar strings */
363 items = FRAME_MENU_BAR_ITEMS (f);
366 free_menubar_widget_value_tree (first_wv);
373 /* check if no change.. this mechanism is a bit rough, but ready */
374 n = ASIZE (items) / 4;
375 if (f == last_f && n_previous_strings == n)
377 for (i = 0; i<n; i++)
379 string = AREF (items, 4*i+1);
381 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
385 if (previous_strings[i][0])
390 else if (memcmp (previous_strings[i], SDATA (string),
391 min (10, SBYTES (string) + 1)))
397 free_menubar_widget_value_tree (first_wv);
405 for (i = 0; i < ASIZE (items); i += 4)
407 string = AREF (items, i + 1);
412 memcpy (previous_strings[i/4], SDATA (string),
413 min (10, SBYTES (string) + 1));
415 wv = make_widget_value (SSDATA (string), NULL, true, Qnil);
416 wv->button_type = BUTTON_TYPE_NONE;
417 wv->call_data = (void *) (intptr_t) (-1);
420 /* we'll update the real copy under app menu when time comes */
421 if (!strcmp ("Services", wv->name))
423 /* but we need to make sure it will update on demand */
424 [svcsMenu setFrame: f];
428 [menu addSubmenuWithTitle: wv->name forFrame: f];
433 first_wv->contents = wv;
439 n_previous_strings = n;
441 n_previous_strings = 0;
444 free_menubar_widget_value_tree (first_wv);
449 t += 1000*tb.time+tb.millitm;
450 fprintf (stderr, "Menu update took %ld msec.\n", t);
455 [NSApp setMainMenu: menu];
463 /* Main emacs core entry point for menubar menus: called to indicate that the
464 frame's menus have changed, and the *step representation should be updated
467 set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
469 ns_update_menubar (f, deep_p, nil);
473 x_activate_menubar (struct frame *f)
476 ns_update_menubar (f, true, nil);
477 ns_check_pending_open_menu ();
484 /* ==========================================================================
486 Menu: class implementation
488 ========================================================================== */
491 /* Menu that can define itself from Emacs "widget_value"s and will lazily
492 update itself when user clicked. Based on Carbon/AppKit implementation
493 by Yamamoto Mitsuharu. */
494 @implementation EmacsMenu
496 /* override designated initializer */
497 - initWithTitle: (NSString *)title
500 if ((self = [super initWithTitle: title]))
501 [self setAutoenablesItems: NO];
506 /* used for top-level */
507 - initWithTitle: (NSString *)title frame: (struct frame *)f
509 [self initWithTitle: title];
512 [self setDelegate: self];
518 - (void)setFrame: (struct frame *)f
524 -(void)trackingNotification:(NSNotification *)notification
526 /* Update menu in menuNeedsUpdate only while tracking menus. */
527 trackingMenu = ([notification name] == NSMenuDidBeginTrackingNotification
529 if (! trackingMenu) ns_check_menu_open (nil);
532 - (void)menuWillOpen:(NSMenu *)menu
536 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
537 // On 10.6 we get repeated calls, only the one for NSSystemDefined is "real".
538 if ([[NSApp currentEvent] type] != NSSystemDefined) return;
541 /* When dragging from one menu to another, we get willOpen followed by didClose,
542 i.e. trackingMenu == 3 in willOpen and then 2 after didClose.
543 We have updated all menus, so avoid doing it when trackingMenu == 3. */
544 if (trackingMenu == 2)
545 ns_check_menu_open (menu);
548 - (void)menuDidClose:(NSMenu *)menu
553 #endif /* NS_IMPL_COCOA */
555 /* delegate method called when a submenu is being opened: run a 'deep' call
556 to set_frame_menubar */
557 - (void)menuNeedsUpdate: (NSMenu *)menu
559 if (!FRAME_LIVE_P (frame))
562 /* Cocoa/Carbon will request update on every keystroke
563 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
564 since key equivalents are handled through emacs.
565 On Leopard, even keystroke events generate SystemDefined event.
566 Third-party applications that enhance mouse / trackpad
567 interaction, or also VNC/Remote Desktop will send events
568 of type AppDefined rather than SysDefined.
569 Menus will fail to show up if they haven't been initialized.
570 AppDefined events may lack timing data.
572 Thus, we rely on the didBeginTrackingNotification notification
573 as above to indicate the need for updates.
574 From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
575 key press case, NSMenuPropertyItemImage (e.g.) won't be set.
577 if (trackingMenu == 0)
579 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
580 #ifdef NS_IMPL_GNUSTEP
581 /* Don't know how to do this for anything other than OSX >= 10.5
582 This is wrong, as it might run Lisp code in the event loop. */
583 ns_update_menubar (frame, true, self);
588 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
590 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
591 && FRAME_NS_VIEW (SELECTED_FRAME ()))
592 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
597 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
598 into an accelerator string. We are only able to display a single character
599 for an accelerator, together with an optional modifier combination. (Under
600 Carbon more control was possible, but in Cocoa multi-char strings passed to
601 NSMenuItem get ignored. For now we try to display a super-single letter
602 combo, and return the others as strings to be appended to the item title.
603 (This is signaled by setting keyEquivModMask to 0 for now.) */
604 -(NSString *)parseKeyEquiv: (const char *)key
606 const char *tpos = key;
607 keyEquivModMask = NSCommandKeyMask;
609 if (!key || !strlen (key))
612 while (*tpos == ' ' || *tpos == '(')
614 if ((*tpos == 's') && (*(tpos+1) == '-'))
616 return [NSString stringWithFormat: @"%c", tpos[2]];
618 keyEquivModMask = 0; /* signal */
619 return [NSString stringWithUTF8String: tpos];
623 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
626 widget_value *wv = (widget_value *)wvptr;
628 if (menu_separator_name_p (wv->name))
630 item = [NSMenuItem separatorItem];
631 [self addItem: item];
635 NSString *title, *keyEq;
636 title = [NSString stringWithUTF8String: wv->name];
638 title = @"< ? >"; /* (get out in the open so we know about it) */
640 keyEq = [self parseKeyEquiv: wv->key];
642 /* OS X just ignores modifier strings longer than one character */
643 if (keyEquivModMask == 0)
644 title = [title stringByAppendingFormat: @" (%@)", keyEq];
647 item = [self addItemWithTitle: (NSString *)title
648 action: @selector (menuDown:)
649 keyEquivalent: keyEq];
650 [item setKeyEquivalentModifierMask: keyEquivModMask];
652 [item setEnabled: wv->enabled];
654 /* Draw radio buttons and tickboxes */
655 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
656 wv->button_type == BUTTON_TYPE_RADIO))
657 [item setState: NSOnState];
659 [item setState: NSOffState];
661 [item setTag: (NSInteger)wv->call_data];
673 for (n = [self numberOfItems]-1; n >= 0; n--)
675 NSMenuItem *item = [self itemAtIndex: n];
676 NSString *title = [item title];
677 if ([ns_app_name isEqualToString: title]
678 && ![item isSeparatorItem])
680 [self removeItemAtIndex: n];
685 - (void)fillWithWidgetValue: (void *)wvptr
687 [self fillWithWidgetValue: wvptr frame: (struct frame *)nil];
690 - (void)fillWithWidgetValue: (void *)wvptr frame: (struct frame *)f
692 widget_value *wv = (widget_value *)wvptr;
694 /* clear existing contents */
695 [self setMenuChangedMessagesEnabled: NO];
698 /* add new contents */
699 for (; wv != NULL; wv = wv->next)
701 NSMenuItem *item = [self addItemWithWidgetValue: wv];
708 submenu = [[EmacsMenu alloc] initWithTitle: [item title] frame:f];
710 submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
712 [self setSubmenu: submenu forItem: item];
713 [submenu fillWithWidgetValue: wv->contents];
715 [item setAction: (SEL)nil];
719 [self setMenuChangedMessagesEnabled: YES];
720 #ifdef NS_IMPL_GNUSTEP
721 if ([[self window] isVisible])
727 /* adds an empty submenu and returns it */
728 - (EmacsMenu *)addSubmenuWithTitle: (const char *)title forFrame: (struct frame *)f
730 NSString *titleStr = [NSString stringWithUTF8String: title];
731 NSMenuItem *item = [self addItemWithTitle: titleStr
732 action: (SEL)nil /*@selector (menuDown:) */
734 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
735 [self setSubmenu: submenu forItem: item];
740 /* run a menu in popup mode */
741 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
742 keymaps: (bool)keymaps
744 EmacsView *view = FRAME_NS_VIEW (f);
748 /* p = [view convertPoint:p fromView: nil]; */
749 p.y = NSHeight ([view frame]) - p.y;
750 e = [[view window] currentEvent];
751 event = [NSEvent mouseEventWithType: NSRightMouseDown
754 timestamp: [e timestamp]
755 windowNumber: [[view window] windowNumber]
757 eventNumber: 0/*[e eventNumber] */
761 context_menu_value = -1;
762 [NSMenu popUpContextMenu: self withEvent: event forView: view];
763 retVal = context_menu_value;
764 context_menu_value = 0;
766 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
774 /* ==========================================================================
776 Context Menu: implementing functions
778 ========================================================================== */
781 ns_menu_show (struct frame *f, int x, int y, int menuflags,
782 Lisp_Object title, const char **error)
787 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
788 widget_value *wv, *first_wv = 0;
789 bool keymaps = (menuflags & MENU_KEYMAPS);
791 NSTRACE ("ns_menu_show");
797 /* now parse stage 2 as in ns_update_menubar */
798 wv = make_widget_value ("contextmenu", NULL, true, Qnil);
799 wv->button_type = BUTTON_TYPE_NONE;
803 /* FIXME: a couple of one-line differences prevent reuse */
804 wv = digest_single_submenu (0, menu_items_used, 0);
807 widget_value *save_wv = 0, *prev_wv = 0;
808 widget_value **submenu_stack
809 = alloca (menu_items_used * sizeof *submenu_stack);
810 /* Lisp_Object *subprefix_stack
811 = alloca (menu_items_used * sizeof *subprefix_stack); */
812 int submenu_depth = 0;
816 /* Loop over all panes and items, filling in the tree. */
818 while (i < menu_items_used)
820 if (EQ (AREF (menu_items, i), Qnil))
822 submenu_stack[submenu_depth++] = save_wv;
828 else if (EQ (AREF (menu_items, i), Qlambda))
831 save_wv = submenu_stack[--submenu_depth];
835 else if (EQ (AREF (menu_items, i), Qt)
836 && submenu_depth != 0)
837 i += MENU_ITEMS_PANE_LENGTH;
838 /* Ignore a nil in the item list.
839 It's meaningful only for dialog boxes. */
840 else if (EQ (AREF (menu_items, i), Qquote))
842 else if (EQ (AREF (menu_items, i), Qt))
844 /* Create a new pane. */
845 Lisp_Object pane_name, prefix;
846 const char *pane_string;
848 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
849 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
851 #ifndef HAVE_MULTILINGUAL_MENU
852 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
854 pane_name = ENCODE_MENU_STRING (pane_name);
855 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
858 pane_string = (NILP (pane_name)
859 ? "" : SSDATA (pane_name));
860 /* If there is just one top-level pane, put all its items directly
861 under the top-level menu. */
862 if (menu_items_n_panes == 1)
865 /* If the pane has a meaningful name,
866 make the pane a top-level menu item
867 with its items as a submenu beneath it. */
868 if (!keymaps && strcmp (pane_string, ""))
870 wv = make_widget_value (pane_string, NULL, true, Qnil);
874 first_wv->contents = wv;
875 if (keymaps && !NILP (prefix))
877 wv->button_type = BUTTON_TYPE_NONE;
887 i += MENU_ITEMS_PANE_LENGTH;
891 /* Create a new item within current pane. */
892 Lisp_Object item_name, enable, descrip, def, type, selected, help;
893 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
894 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
895 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
896 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
897 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
898 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
899 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
901 #ifndef HAVE_MULTILINGUAL_MENU
902 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
904 item_name = ENCODE_MENU_STRING (item_name);
905 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
908 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
910 descrip = ENCODE_MENU_STRING (descrip);
911 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
913 #endif /* not HAVE_MULTILINGUAL_MENU */
915 wv = make_widget_value (SSDATA (item_name), NULL, !NILP (enable),
916 STRINGP (help) ? help : Qnil);
920 save_wv->contents = wv;
922 wv->key = SSDATA (descrip);
923 /* If this item has a null value,
924 make the call_data null so that it won't display a box
925 when the mouse is on it. */
926 wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
929 wv->button_type = BUTTON_TYPE_NONE;
930 else if (EQ (type, QCtoggle))
931 wv->button_type = BUTTON_TYPE_TOGGLE;
932 else if (EQ (type, QCradio))
933 wv->button_type = BUTTON_TYPE_RADIO;
937 wv->selected = !NILP (selected);
941 i += MENU_ITEMS_ITEM_LENGTH;
949 widget_value *wv_title;
950 widget_value *wv_sep = make_widget_value ("--", NULL, false, Qnil);
952 /* Maybe replace this separator with a bitmap or owner-draw item
953 so that it looks better. Having two separators looks odd. */
954 wv_sep->next = first_wv->contents;
956 #ifndef HAVE_MULTILINGUAL_MENU
957 if (STRING_MULTIBYTE (title))
958 title = ENCODE_MENU_STRING (title);
960 wv_title = make_widget_value (SSDATA (title), NULL, false, Qnil);
961 wv_title->button_type = BUTTON_TYPE_NONE;
962 wv_title->next = wv_sep;
963 first_wv->contents = wv_title;
966 pmenu = [[EmacsMenu alloc] initWithTitle:
967 [NSString stringWithUTF8String: SSDATA (title)]];
968 [pmenu fillWithWidgetValue: first_wv->contents];
969 free_menubar_widget_value_tree (first_wv);
970 unbind_to (specpdl_count, Qnil);
972 popup_activated_flag = 1;
973 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
974 popup_activated_flag = 0;
975 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
982 /* ==========================================================================
984 Toolbar: externally-called functions
986 ========================================================================== */
989 free_frame_tool_bar (struct frame *f)
990 /* --------------------------------------------------------------------------
991 Under NS we just hide the toolbar until it might be needed again.
992 -------------------------------------------------------------------------- */
994 EmacsView *view = FRAME_NS_VIEW (f);
996 NSTRACE ("free_frame_tool_bar");
999 view->wait_for_tool_bar = NO;
1001 FRAME_TOOLBAR_HEIGHT (f) = 0;
1003 /* Note: This trigger an animation, which calls windowDidResize
1005 f->output_data.ns->in_animation = 1;
1006 [[view toolbar] setVisible: NO];
1007 f->output_data.ns->in_animation = 0;
1013 update_frame_tool_bar (struct frame *f)
1014 /* --------------------------------------------------------------------------
1015 Update toolbar contents
1016 -------------------------------------------------------------------------- */
1019 EmacsView *view = FRAME_NS_VIEW (f);
1020 NSWindow *window = [view window];
1021 EmacsToolbar *toolbar = [view toolbar];
1024 NSTRACE ("update_frame_tool_bar");
1026 if (view == nil || toolbar == nil) return;
1029 oldh = FRAME_TOOLBAR_HEIGHT (f);
1031 #ifdef NS_IMPL_COCOA
1032 [toolbar clearActive];
1037 /* update EmacsToolbar as in GtkUtils, build items list */
1038 for (i = 0; i < f->n_tool_bar_items; ++i)
1040 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1041 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1043 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1048 Lisp_Object helpObj;
1049 const char *helpText;
1051 /* Check if this is a separator. */
1052 if (EQ (TOOLPROP (TOOL_BAR_ITEM_TYPE), Qt))
1054 /* Skip separators. Newer OSX don't show them, and on GNUstep they
1055 are wide as a button, thus overflowing the toolbar most of
1060 /* If image is a vector, choose the image according to the
1062 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1063 if (VECTORP (image))
1065 /* NS toolbar auto-computes disabled and selected images */
1066 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1067 eassert (ASIZE (image) >= idx);
1068 image = AREF (image, idx);
1074 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1076 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1077 helpText = NILP (helpObj) ? "" : SSDATA (helpObj);
1079 /* Ignore invalid image specifications. */
1080 if (!valid_image_p (image))
1082 /* Don't log anything, GNUS makes invalid images all the time. */
1086 img_id = lookup_image (f, image);
1087 img = IMAGE_FROM_ID (f, img_id);
1088 prepare_image_for_display (f, img);
1090 if (img->load_failed_p || img->pixmap == nil)
1092 NSLog (@"Could not prepare toolbar image for display.");
1096 [toolbar addDisplayItemWithImage: img->pixmap
1100 enabled: enabled_p];
1104 if (![toolbar isVisible])
1106 f->output_data.ns->in_animation = 1;
1107 [toolbar setVisible: YES];
1108 f->output_data.ns->in_animation = 0;
1111 #ifdef NS_IMPL_COCOA
1112 if ([toolbar changed])
1114 /* inform app that toolbar has changed */
1115 NSDictionary *dict = [toolbar configurationDictionary];
1116 NSMutableDictionary *newDict = [dict mutableCopy];
1117 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1119 while ((key = [keys nextObject]) != nil)
1121 NSObject *val = [dict objectForKey: key];
1122 if ([val isKindOfClass: [NSArray class]])
1125 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1130 [toolbar setConfigurationFromDictionary: newDict];
1135 FRAME_TOOLBAR_HEIGHT (f) =
1136 NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1137 - FRAME_NS_TITLEBAR_HEIGHT (f);
1138 if (FRAME_TOOLBAR_HEIGHT (f) < 0) // happens if frame is fullscreen.
1139 FRAME_TOOLBAR_HEIGHT (f) = 0;
1141 if (oldh != FRAME_TOOLBAR_HEIGHT (f))
1142 [view updateFrameSize:YES];
1143 if (view->wait_for_tool_bar && FRAME_TOOLBAR_HEIGHT (f) > 0)
1145 view->wait_for_tool_bar = NO;
1146 [view setNeedsDisplay: YES];
1153 /* ==========================================================================
1155 Toolbar: class implementation
1157 ========================================================================== */
1159 @implementation EmacsToolbar
1161 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1163 NSTRACE ("[EmacsToolbar initForView: withIdentifier:]");
1165 self = [super initWithIdentifier: identifier];
1167 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1168 [self setSizeMode: NSToolbarSizeModeSmall];
1169 [self setDelegate: self];
1170 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1171 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1172 prevIdentifiers = nil;
1173 prevEnablement = enablement = 0L;
1179 NSTRACE ("[EmacsToolbar dealloc]");
1181 [prevIdentifiers release];
1182 [activeIdentifiers release];
1183 [identifierToItem release];
1187 - (void) clearActive
1189 NSTRACE ("[EmacsToolbar clearActive]");
1191 [prevIdentifiers release];
1192 prevIdentifiers = [activeIdentifiers copy];
1193 [activeIdentifiers removeAllObjects];
1194 prevEnablement = enablement;
1200 NSTRACE ("[EmacsToolbar clearAll]");
1203 while ([[self items] count] > 0)
1204 [self removeItemAtIndex: 0];
1209 NSTRACE ("[EmacsToolbar changed]");
1211 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1212 enablement == prevEnablement ? NO : YES;
1215 - (void) addDisplayItemWithImage: (EmacsImage *)img
1218 helpText: (const char *)help
1219 enabled: (BOOL)enabled
1221 NSTRACE ("[EmacsToolbar addDisplayItemWithImage: ...]");
1223 /* 1) come up w/identifier */
1224 NSString *identifier
1225 = [NSString stringWithFormat: @"%lu", (unsigned long)[img hash]];
1226 [activeIdentifiers addObject: identifier];
1228 /* 2) create / reuse item */
1229 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1232 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1234 [item setImage: img];
1235 [item setToolTip: [NSString stringWithUTF8String: help]];
1236 [item setTarget: emacsView];
1237 [item setAction: @selector (toolbarClicked:)];
1238 [identifierToItem setObject: item forKey: identifier];
1241 #ifdef NS_IMPL_GNUSTEP
1242 [self insertItemWithItemIdentifier: identifier atIndex: idx];
1246 [item setEnabled: enabled];
1248 /* 3) update state */
1249 enablement = (enablement << 1) | (enabled == YES);
1252 /* This overrides super's implementation, which automatically sets
1253 all items to enabled state (for some reason). */
1254 - (void)validateVisibleItems
1256 NSTRACE ("[EmacsToolbar validateVisibleItems]");
1260 /* delegate methods */
1262 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1263 itemForItemIdentifier: (NSString *)itemIdentifier
1264 willBeInsertedIntoToolbar: (BOOL)flag
1266 NSTRACE ("[EmacsToolbar toolbar: ...]");
1268 /* look up NSToolbarItem by identifier and return... */
1269 return [identifierToItem objectForKey: itemIdentifier];
1272 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1274 NSTRACE ("[EmacsToolbar toolbarDefaultItemIdentifiers:]");
1276 /* return entire set.. */
1277 return activeIdentifiers;
1280 /* for configuration palette (not yet supported) */
1281 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1283 NSTRACE ("[EmacsToolbar toolbarAllowedItemIdentifiers:]");
1285 /* return entire set... */
1286 return activeIdentifiers;
1287 //return [identifierToItem allKeys];
1290 - (void)setVisible:(BOOL)shown
1292 NSTRACE ("[EmacsToolbar setVisible:%d]", shown);
1294 [super setVisible:shown];
1298 /* optional and unneeded */
1299 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1300 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1301 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1303 @end /* EmacsToolbar */
1307 /* ==========================================================================
1309 Tooltip: class implementation
1311 ========================================================================== */
1313 /* Needed because NeXTstep does not provide enough control over tooltip
1315 @implementation EmacsTooltip
1319 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1320 blue: 0.792 alpha: 0.95];
1321 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1322 NSFont *sfont = [font screenFont];
1323 int height = [sfont ascender] - [sfont descender];
1324 /*[font boundingRectForFont].size.height; */
1325 NSRect r = NSMakeRect (0, 0, 100, height+6);
1327 textField = [[NSTextField alloc] initWithFrame: r];
1328 [textField setFont: font];
1329 [textField setBackgroundColor: col];
1331 [textField setEditable: NO];
1332 [textField setSelectable: NO];
1333 [textField setBordered: NO];
1334 [textField setBezeled: NO];
1335 [textField setDrawsBackground: YES];
1337 win = [[NSWindow alloc]
1338 initWithContentRect: [textField frame]
1340 backing: NSBackingStoreBuffered
1342 [win setHasShadow: YES];
1343 [win setReleasedWhenClosed: NO];
1344 [win setDelegate: self];
1345 [[win contentView] addSubview: textField];
1346 /* [win setBackgroundColor: col]; */
1347 [win setOpaque: NO];
1356 [textField release];
1360 - (void) setText: (char *)text
1362 NSString *str = [NSString stringWithUTF8String: text];
1363 NSRect r = [textField frame];
1366 [textField setStringValue: str];
1367 tooltipDims = [[textField cell] cellSize];
1369 r.size.width = tooltipDims.width;
1370 r.size.height = tooltipDims.height;
1371 [textField setFrame: r];
1374 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1376 NSRect wr = [win frame];
1378 wr.origin = NSMakePoint (x, y);
1379 wr.size = [textField frame].size;
1381 [win setFrame: wr display: YES];
1382 [win setLevel: NSPopUpMenuWindowLevel];
1383 [win orderFront: self];
1385 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1386 selector: @selector (hide)
1387 userInfo: nil repeats: NO];
1396 if ([timer isValid])
1405 return timer != nil;
1410 return [textField frame];
1413 @end /* EmacsTooltip */
1417 /* ==========================================================================
1419 Popup Dialog: implementing functions
1421 ========================================================================== */
1424 pop_down_menu (void *arg)
1426 EmacsDialogPanel *panel = arg;
1428 if (popup_activated_flag)
1431 popup_activated_flag = 0;
1433 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1440 ns_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
1443 Lisp_Object tem, title;
1447 NSTRACE ("ns_popup_dialog");
1449 isQ = NILP (header);
1451 check_window_system (f);
1453 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1454 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1456 title = Fcar (contents);
1457 CHECK_STRING (title);
1459 if (NILP (Fcar (Fcdr (contents))))
1460 /* No buttons specified, add an "Ok" button so users can pop down
1462 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
1465 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1469 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1471 record_unwind_protect_ptr (pop_down_menu, dialog);
1472 popup_activated_flag = 1;
1473 tem = [dialog runDialogAt: p];
1474 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1483 /* ==========================================================================
1485 Popup Dialog: class implementation
1487 ========================================================================== */
1489 @interface FlippedView : NSView
1494 @implementation FlippedView
1501 @implementation EmacsDialogPanel
1504 #define ICONSIZE 64.0
1505 #define TEXTHEIGHT 20.0
1506 #define MINCELLWIDTH 90.0
1508 - initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
1509 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1511 NSSize spacing = {SPACER, SPACER};
1514 NSImageView *imgView;
1515 FlippedView *contentView;
1518 dialog_return = Qundefined;
1519 button_values = NULL;
1520 area.origin.x = 3*SPACER;
1521 area.origin.y = 2*SPACER;
1522 area.size.width = ICONSIZE;
1523 area.size.height= ICONSIZE;
1524 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1525 #ifdef NS_IMPL_COCOA
1526 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
1527 [img setScalesWhenResized: YES];
1530 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1531 imgView = [[NSImageView alloc] initWithFrame: area];
1532 [imgView setImage: img];
1533 [imgView setEditable: NO];
1535 [imgView autorelease];
1537 aStyle = NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask;
1541 [super initWithContentRect: contentRect styleMask: aStyle
1542 backing: backingType defer: flag];
1543 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1544 [contentView autorelease];
1546 [self setContentView: contentView];
1548 [[self contentView] setAutoresizesSubviews: YES];
1550 [[self contentView] addSubview: imgView];
1551 [self setTitle: @""];
1553 area.origin.x += ICONSIZE+2*SPACER;
1554 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1555 area.size.width = 400;
1556 area.size.height= TEXTHEIGHT;
1557 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1558 [[self contentView] addSubview: command];
1559 [command setStringValue: ns_app_name];
1560 [command setDrawsBackground: NO];
1561 [command setBezeled: NO];
1562 [command setSelectable: NO];
1563 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1565 /* area.origin.x = ICONSIZE+2*SPACER;
1566 area.origin.y = TEXTHEIGHT + 2*SPACER;
1567 area.size.width = 400;
1568 area.size.height= 2;
1569 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1570 [[self contentView] addSubview: tem];
1571 [tem setTitlePosition: NSNoTitle];
1572 [tem setAutoresizingMask: NSViewWidthSizable];*/
1574 /* area.origin.x = ICONSIZE+2*SPACER; */
1575 area.origin.y += TEXTHEIGHT+SPACER;
1576 area.size.width = 400;
1577 area.size.height= TEXTHEIGHT;
1578 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1579 [[self contentView] addSubview: title];
1580 [title setDrawsBackground: NO];
1581 [title setBezeled: NO];
1582 [title setSelectable: NO];
1583 [title setFont: [NSFont systemFontOfSize: 11.0]];
1585 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1586 [cell setBordered: NO];
1587 [cell setEnabled: NO];
1588 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1589 [cell setBezelStyle: NSRoundedBezelStyle];
1591 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1592 mode: NSHighlightModeMatrix
1595 numberOfColumns: 1];
1596 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1597 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1598 [matrix setIntercellSpacing: spacing];
1599 [matrix autorelease];
1601 [[self contentView] addSubview: matrix];
1602 [self setOneShot: YES];
1603 [self setReleasedWhenClosed: YES];
1604 [self setHidesOnDeactivate: YES];
1609 - (BOOL)windowShouldClose: (id)sender
1611 window_closed = YES;
1618 xfree (button_values);
1622 - (void)process_dialog: (Lisp_Object) list
1624 Lisp_Object item, lst = list;
1626 int buttons = 0, btnnr = 0;
1628 for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
1631 if (XTYPE (item) == Lisp_Cons)
1636 button_values = xmalloc (buttons * sizeof *button_values);
1638 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1641 if (XTYPE (item) == Lisp_String)
1643 [self addString: SSDATA (item) row: row++];
1645 else if (XTYPE (item) == Lisp_Cons)
1647 button_values[btnnr] = XCDR (item);
1648 [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
1651 else if (NILP (item))
1660 - (void)addButton: (char *)str value: (int)tag row: (int)row
1669 cell = [matrix cellAtRow: row column: cols-1];
1670 [cell setTarget: self];
1671 [cell setAction: @selector (clicked: )];
1672 [cell setTitle: [NSString stringWithUTF8String: str]];
1674 [cell setBordered: YES];
1675 [cell setEnabled: YES];
1679 - (void)addString: (char *)str row: (int)row
1688 cell = [matrix cellAtRow: row column: cols-1];
1689 [cell setTitle: [NSString stringWithUTF8String: str]];
1690 [cell setBordered: YES];
1691 [cell setEnabled: NO];
1702 - (void)clicked: sender
1704 NSArray *sellist = nil;
1707 sellist = [sender selectedCells];
1708 if ([sellist count] < 1)
1711 seltag = [[sellist objectAtIndex: 0] tag];
1712 dialog_return = button_values[seltag];
1717 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1722 if (XTYPE (contents) == Lisp_Cons)
1724 head = Fcar (contents);
1725 [self process_dialog: Fcdr (contents)];
1730 if (XTYPE (head) == Lisp_String)
1731 [title setStringValue:
1732 [NSString stringWithUTF8String: SSDATA (head)]];
1733 else if (isQ == YES)
1734 [title setStringValue: @"Question"];
1736 [title setStringValue: @"Information"];
1742 if (cols == 1 && rows > 1) /* Never told where to split */
1745 for (i = 0; i < rows/2; i++)
1747 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1748 atRow: i column: 1];
1749 [matrix removeRow: (rows+1)/2];
1755 NSSize csize = [matrix cellSize];
1756 if (csize.width < MINCELLWIDTH)
1758 csize.width = MINCELLWIDTH;
1759 [matrix setCellSize: csize];
1760 [matrix sizeToCells];
1765 [command sizeToFit];
1769 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1771 t.origin.x = r.origin.x;
1772 t.size.width = r.size.width;
1774 r = [command frame];
1775 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1777 t.origin.x = r.origin.x;
1778 t.size.width = r.size.width;
1782 s = [(NSView *)[self contentView] frame];
1783 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1784 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1785 [self setFrame: r display: NO];
1793 - (void)timeout_handler: (NSTimer *)timedEntry
1795 NSEvent *nxev = [NSEvent otherEventWithType: NSApplicationDefined
1796 location: NSMakePoint (0, 0)
1799 windowNumber: [[NSApp mainWindow] windowNumber]
1800 context: [NSApp context]
1806 /* We use sto because stopModal/abortModal out of the main loop does not
1807 seem to work in 10.6. But as we use stop we must send a real event so
1808 the stop is seen and acted upon. */
1810 [NSApp postEvent: nxev atStart: NO];
1813 - (Lisp_Object)runDialogAt: (NSPoint)p
1815 Lisp_Object ret = Qundefined;
1817 while (popup_activated_flag)
1820 struct timespec next_time = timer_check ();
1822 if (timespec_valid_p (next_time))
1824 double time = timespectod (next_time);
1825 tmo = [NSTimer timerWithTimeInterval: time
1827 selector: @selector (timeout_handler:)
1830 [[NSRunLoop currentRunLoop] addTimer: tmo
1831 forMode: NSModalPanelRunLoopMode];
1834 dialog_return = Qundefined;
1835 [NSApp runModalForWindow: self];
1836 ret = dialog_return;
1839 if (tmo != nil) [tmo invalidate]; /* Cancels timer */
1844 if (EQ (ret, Qundefined) && window_closed)
1845 /* Make close button pressed equivalent to C-g. */
1854 /* ==========================================================================
1858 ========================================================================== */
1860 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1861 doc: /* Cause the NS menu to be re-calculated. */)
1864 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1869 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1870 doc: /* Return t if a menu or popup dialog is active. */)
1873 return popup_activated () ? Qt : Qnil;
1876 /* ==========================================================================
1878 Lisp interface declaration
1880 ========================================================================== */
1883 syms_of_nsmenu (void)
1885 #ifndef NS_IMPL_COCOA
1886 /* Don't know how to keep track of this in Next/Open/GNUstep. Always
1887 update menus there. */
1890 defsubr (&Sns_reset_menu);
1891 defsubr (&Smenu_or_popup_active_p);
1893 DEFSYM (Qdebug_on_next_call, "debug-on-next-call");