1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007-2014 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 Lisp_Object Qundefined, Qmenu_enable, Qmenu_bar_update_hook;
63 extern Lisp_Object QCtoggle, QCradio;
65 Lisp_Object Qdebug_on_next_call;
66 extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
68 extern long context_menu_value;
69 EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
71 /* Nonzero means a menu is currently active. */
72 static int popup_activated_flag;
74 /* Nonzero means we are tracking and updating menus. */
75 static int trackingMenu;
78 /* NOTE: toolbar implementation is at end,
79 following complete menu implementation. */
82 /* ==========================================================================
84 Menu: Externally-called functions
86 ========================================================================== */
89 /* Supposed to discard menubar and free storage. Since we share the
90 menubar among frames and update its context for the focused window,
91 there is nothing to do here. */
93 free_frame_menubar (struct frame *f)
100 popup_activated (void)
102 return popup_activated_flag;
106 /* --------------------------------------------------------------------------
107 Update menubar. Three cases:
108 1) ! deep_p, submenu = nil: Fresh switch onto a frame -- either set up
109 just top-level menu strings (OS X), or goto case (2) (GNUstep).
110 2) deep_p, submenu = nil: Recompute all submenus.
111 3) deep_p, submenu = non-nil: Update contents of a single submenu.
112 -------------------------------------------------------------------------- */
114 ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
116 NSAutoreleasePool *pool;
117 id menu = [NSApp mainMenu];
118 static EmacsMenu *last_submenu = nil;
122 widget_value *wv, *first_wv, *prev_wv = 0;
130 NSTRACE (ns_update_menubar);
132 if (f != SELECTED_FRAME ())
134 XSETFRAME (Vmenu_updating_frame, f);
135 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
138 pool = [[NSAutoreleasePool alloc] init];
140 /* Menu may have been created automatically; if so, discard it. */
141 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
149 menu = [[EmacsMenu alloc] initWithTitle: ns_app_name];
153 { /* close up anything on there */
154 id attMenu = [menu attachedMenu];
161 t = -(1000*tb.time+tb.millitm);
164 #ifdef NS_IMPL_GNUSTEP
165 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
170 /* Fully parse one or more of the submenus. */
172 int *submenu_start, *submenu_end;
173 bool *submenu_top_level_items;
174 int *submenu_n_panes;
175 struct buffer *prev = current_buffer;
177 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
178 int previous_menu_items_used = f->menu_bar_items_used;
179 Lisp_Object *previous_items
180 = alloca (previous_menu_items_used * sizeof *previous_items);
182 /* lisp preliminaries */
183 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
184 specbind (Qinhibit_quit, Qt);
185 specbind (Qdebug_on_next_call, Qnil);
186 record_unwind_save_match_data ();
187 if (NILP (Voverriding_local_map_menu_flag))
189 specbind (Qoverriding_terminal_local_map, Qnil);
190 specbind (Qoverriding_local_map, Qnil);
192 set_buffer_internal_1 (XBUFFER (buffer));
194 /* TODO: for some reason this is not needed in other terms,
195 but some menu updates call Info-extract-pointer which causes
196 abort-on-error if waiting-for-input. Needs further investigation. */
197 owfi = waiting_for_input;
198 waiting_for_input = 0;
200 /* lucid hook and possible reset */
201 safe_run_hooks (Qactivate_menubar_hook);
202 if (! NILP (Vlucid_menu_bar_dirty_flag))
203 call0 (Qrecompute_lucid_menubar);
204 safe_run_hooks (Qmenu_bar_update_hook);
205 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
207 /* Now ready to go */
208 items = FRAME_MENU_BAR_ITEMS (f);
210 /* Save the frame's previous menu bar contents data */
211 if (previous_menu_items_used)
212 memcpy (previous_items, aref_addr (f->menu_bar_vector, 0),
213 previous_menu_items_used * sizeof (Lisp_Object));
215 /* parse stage 1: extract from lisp */
218 menu_items = f->menu_bar_vector;
219 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
220 submenu_start = alloca (ASIZE (items) * sizeof *submenu_start);
221 submenu_end = alloca (ASIZE (items) * sizeof *submenu_end);
222 submenu_n_panes = alloca (ASIZE (items) * sizeof *submenu_n_panes);
223 submenu_top_level_items = alloca (ASIZE (items)
224 * sizeof *submenu_top_level_items);
226 for (i = 0; i < ASIZE (items); i += 4)
228 Lisp_Object key, string, maps;
230 key = AREF (items, i);
231 string = AREF (items, i + 1);
232 maps = AREF (items, i + 2);
236 /* FIXME: we'd like to only parse the needed submenu, but this
237 was causing crashes in the _common parsing code.. need to make
238 sure proper initialization done.. */
239 /* if (submenu && strcmp ([[submenu title] UTF8String], SSDATA (string)))
242 submenu_start[i] = menu_items_used;
244 menu_items_n_panes = 0;
245 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
246 submenu_n_panes[i] = menu_items_n_panes;
247 submenu_end[i] = menu_items_used;
251 finish_menu_items ();
252 waiting_for_input = owfi;
255 if (submenu && n == 0)
257 /* should have found a menu for this one but didn't */
258 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
259 [[submenu title] UTF8String]);
260 discard_menu_items ();
261 unbind_to (specpdl_count, Qnil);
267 /* parse stage 2: insert into lucid 'widget_value' structures
268 [comments in other terms say not to evaluate lisp code here] */
269 wv = make_widget_value ("menubar", NULL, true, Qnil);
270 wv->button_type = BUTTON_TYPE_NONE;
273 for (i = 0; i < 4*n; i += 4)
275 menu_items_n_panes = submenu_n_panes[i];
276 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
277 submenu_top_level_items[i]);
281 first_wv->contents = wv;
282 /* Don't set wv->name here; GC during the loop might relocate it. */
284 wv->button_type = BUTTON_TYPE_NONE;
288 set_buffer_internal_1 (prev);
290 /* Compare the new menu items with previous, and leave off if no change */
291 /* FIXME: following other terms here, but seems like this should be
292 done before parse stage 2 above, since its results aren't used */
293 if (previous_menu_items_used
294 && (!submenu || (submenu && submenu == last_submenu))
295 && menu_items_used == previous_menu_items_used)
297 for (i = 0; i < previous_menu_items_used; i++)
298 /* FIXME: this ALWAYS fails on Buffers menu items.. something
299 about their strings causes them to change every time, so we
300 double-check failures */
301 if (!EQ (previous_items[i], AREF (menu_items, i)))
302 if (!(STRINGP (previous_items[i])
303 && STRINGP (AREF (menu_items, i))
304 && !strcmp (SSDATA (previous_items[i]),
305 SSDATA (AREF (menu_items, i)))))
307 if (i == previous_menu_items_used)
313 t += 1000*tb.time+tb.millitm;
314 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
317 free_menubar_widget_value_tree (first_wv);
318 discard_menu_items ();
319 unbind_to (specpdl_count, Qnil);
325 /* The menu items are different, so store them in the frame */
326 /* FIXME: this is not correct for single-submenu case */
327 fset_menu_bar_vector (f, menu_items);
328 f->menu_bar_items_used = menu_items_used;
330 /* Calls restore_menu_items, etc., as they were outside */
331 unbind_to (specpdl_count, Qnil);
333 /* Parse stage 2a: now GC cannot happen during the lifetime of the
334 widget_value, so it's safe to store data from a Lisp_String */
335 wv = first_wv->contents;
336 for (i = 0; i < ASIZE (items); i += 4)
339 string = AREF (items, i + 1);
343 wv->name = SSDATA (string);
344 update_submenu_strings (wv->contents);
348 /* Now, update the NS menu; if we have a submenu, use that, otherwise
349 create a new menu for each sub and fill it. */
352 const char *submenuTitle = [[submenu title] UTF8String];
353 for (wv = first_wv->contents; wv; wv = wv->next)
355 if (!strcmp (submenuTitle, wv->name))
357 [submenu fillWithWidgetValue: wv->contents];
358 last_submenu = submenu;
365 [menu fillWithWidgetValue: first_wv->contents frame: f];
371 static int n_previous_strings = 0;
372 static char previous_strings[100][10];
373 static struct frame *last_f = NULL;
377 wv = make_widget_value ("menubar", NULL, true, Qnil);
378 wv->button_type = BUTTON_TYPE_NONE;
381 /* Make widget-value tree w/ just the top level menu bar strings */
382 items = FRAME_MENU_BAR_ITEMS (f);
385 free_menubar_widget_value_tree (first_wv);
392 /* check if no change.. this mechanism is a bit rough, but ready */
393 n = ASIZE (items) / 4;
394 if (f == last_f && n_previous_strings == n)
396 for (i = 0; i<n; i++)
398 string = AREF (items, 4*i+1);
400 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
404 if (previous_strings[i][0])
409 else if (memcmp (previous_strings[i], SDATA (string),
410 min (10, SBYTES (string) + 1)))
416 free_menubar_widget_value_tree (first_wv);
424 for (i = 0; i < ASIZE (items); i += 4)
426 string = AREF (items, i + 1);
431 memcpy (previous_strings[i/4], SDATA (string),
432 min (10, SBYTES (string) + 1));
434 wv = make_widget_value (SSDATA (string), NULL, true, Qnil);
435 wv->button_type = BUTTON_TYPE_NONE;
436 wv->call_data = (void *) (intptr_t) (-1);
439 /* we'll update the real copy under app menu when time comes */
440 if (!strcmp ("Services", wv->name))
442 /* but we need to make sure it will update on demand */
443 [svcsMenu setFrame: f];
447 [menu addSubmenuWithTitle: wv->name forFrame: f];
452 first_wv->contents = wv;
458 n_previous_strings = n;
460 n_previous_strings = 0;
463 free_menubar_widget_value_tree (first_wv);
468 t += 1000*tb.time+tb.millitm;
469 fprintf (stderr, "Menu update took %ld msec.\n", t);
474 [NSApp setMainMenu: menu];
482 /* Main emacs core entry point for menubar menus: called to indicate that the
483 frame's menus have changed, and the *step representation should be updated
486 set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
488 ns_update_menubar (f, deep_p, nil);
492 x_activate_menubar (struct frame *f)
495 ns_update_menubar (f, true, nil);
496 ns_check_pending_open_menu ();
503 /* ==========================================================================
505 Menu: class implementation
507 ========================================================================== */
510 /* Menu that can define itself from Emacs "widget_value"s and will lazily
511 update itself when user clicked. Based on Carbon/AppKit implementation
512 by Yamamoto Mitsuharu. */
513 @implementation EmacsMenu
515 /* override designated initializer */
516 - initWithTitle: (NSString *)title
519 if ((self = [super initWithTitle: title]))
520 [self setAutoenablesItems: NO];
525 /* used for top-level */
526 - initWithTitle: (NSString *)title frame: (struct frame *)f
528 [self initWithTitle: title];
531 [self setDelegate: self];
537 - (void)setFrame: (struct frame *)f
543 -(void)trackingNotification:(NSNotification *)notification
545 /* Update menu in menuNeedsUpdate only while tracking menus. */
546 trackingMenu = ([notification name] == NSMenuDidBeginTrackingNotification
548 if (! trackingMenu) ns_check_menu_open (nil);
551 - (void)menuWillOpen:(NSMenu *)menu
555 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
556 // On 10.6 we get repeated calls, only the one for NSSystemDefined is "real".
557 if ([[NSApp currentEvent] type] != NSSystemDefined) return;
560 /* When dragging from one menu to another, we get willOpen followed by didClose,
561 i.e. trackingMenu == 3 in willOpen and then 2 after didClose.
562 We have updated all menus, so avoid doing it when trackingMenu == 3. */
563 if (trackingMenu == 2)
564 ns_check_menu_open (menu);
567 - (void)menuDidClose:(NSMenu *)menu
572 #endif /* NS_IMPL_COCOA */
574 /* delegate method called when a submenu is being opened: run a 'deep' call
575 to set_frame_menubar */
576 - (void)menuNeedsUpdate: (NSMenu *)menu
578 if (!FRAME_LIVE_P (frame))
581 /* Cocoa/Carbon will request update on every keystroke
582 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
583 since key equivalents are handled through emacs.
584 On Leopard, even keystroke events generate SystemDefined event.
585 Third-party applications that enhance mouse / trackpad
586 interaction, or also VNC/Remote Desktop will send events
587 of type AppDefined rather than SysDefined.
588 Menus will fail to show up if they haven't been initialized.
589 AppDefined events may lack timing data.
591 Thus, we rely on the didBeginTrackingNotification notification
592 as above to indicate the need for updates.
593 From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
594 key press case, NSMenuPropertyItemImage (e.g.) won't be set.
596 if (trackingMenu == 0)
598 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
599 #ifdef NS_IMPL_GNUSTEP
600 /* Don't know how to do this for anything other than OSX >= 10.5
601 This is wrong, as it might run Lisp code in the event loop. */
602 ns_update_menubar (frame, true, self);
607 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
609 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
610 && FRAME_NS_VIEW (SELECTED_FRAME ()))
611 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
616 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
617 into an accelerator string. We are only able to display a single character
618 for an accelerator, together with an optional modifier combination. (Under
619 Carbon more control was possible, but in Cocoa multi-char strings passed to
620 NSMenuItem get ignored. For now we try to display a super-single letter
621 combo, and return the others as strings to be appended to the item title.
622 (This is signaled by setting keyEquivModMask to 0 for now.) */
623 -(NSString *)parseKeyEquiv: (const char *)key
625 const char *tpos = key;
626 keyEquivModMask = NSCommandKeyMask;
628 if (!key || !strlen (key))
631 while (*tpos == ' ' || *tpos == '(')
633 if ((*tpos == 's') && (*(tpos+1) == '-'))
635 return [NSString stringWithFormat: @"%c", tpos[2]];
637 keyEquivModMask = 0; /* signal */
638 return [NSString stringWithUTF8String: tpos];
642 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
645 widget_value *wv = (widget_value *)wvptr;
647 if (menu_separator_name_p (wv->name))
649 item = [NSMenuItem separatorItem];
650 [self addItem: item];
654 NSString *title, *keyEq;
655 title = [NSString stringWithUTF8String: wv->name];
657 title = @"< ? >"; /* (get out in the open so we know about it) */
659 keyEq = [self parseKeyEquiv: wv->key];
661 /* OS X just ignores modifier strings longer than one character */
662 if (keyEquivModMask == 0)
663 title = [title stringByAppendingFormat: @" (%@)", keyEq];
666 item = [self addItemWithTitle: (NSString *)title
667 action: @selector (menuDown:)
668 keyEquivalent: keyEq];
669 [item setKeyEquivalentModifierMask: keyEquivModMask];
671 [item setEnabled: wv->enabled];
673 /* Draw radio buttons and tickboxes */
674 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
675 wv->button_type == BUTTON_TYPE_RADIO))
676 [item setState: NSOnState];
678 [item setState: NSOffState];
680 [item setTag: (NSInteger)wv->call_data];
692 for (n = [self numberOfItems]-1; n >= 0; n--)
694 NSMenuItem *item = [self itemAtIndex: n];
695 NSString *title = [item title];
696 if (([title length] == 0 /* OSX 10.5 */
697 || [ns_app_name isEqualToString: title] /* from 10.6 on */
698 || [@"Apple" isEqualToString: title]) /* older */
699 && ![item isSeparatorItem])
701 [self removeItemAtIndex: n];
706 - (void)fillWithWidgetValue: (void *)wvptr
708 [self fillWithWidgetValue: wvptr frame: (struct frame *)nil];
711 - (void)fillWithWidgetValue: (void *)wvptr frame: (struct frame *)f
713 widget_value *wv = (widget_value *)wvptr;
715 /* clear existing contents */
716 [self setMenuChangedMessagesEnabled: NO];
719 /* add new contents */
720 for (; wv != NULL; wv = wv->next)
722 NSMenuItem *item = [self addItemWithWidgetValue: wv];
729 submenu = [[EmacsMenu alloc] initWithTitle: [item title] frame:f];
731 submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
733 [self setSubmenu: submenu forItem: item];
734 [submenu fillWithWidgetValue: wv->contents];
736 [item setAction: (SEL)nil];
740 [self setMenuChangedMessagesEnabled: YES];
741 #ifdef NS_IMPL_GNUSTEP
742 if ([[self window] isVisible])
748 /* adds an empty submenu and returns it */
749 - (EmacsMenu *)addSubmenuWithTitle: (const char *)title forFrame: (struct frame *)f
751 NSString *titleStr = [NSString stringWithUTF8String: title];
752 NSMenuItem *item = [self addItemWithTitle: titleStr
753 action: (SEL)nil /*@selector (menuDown:) */
755 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
756 [self setSubmenu: submenu forItem: item];
761 /* run a menu in popup mode */
762 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
763 keymaps: (bool)keymaps
765 EmacsView *view = FRAME_NS_VIEW (f);
769 /* p = [view convertPoint:p fromView: nil]; */
770 p.y = NSHeight ([view frame]) - p.y;
771 e = [[view window] currentEvent];
772 event = [NSEvent mouseEventWithType: NSRightMouseDown
775 timestamp: [e timestamp]
776 windowNumber: [[view window] windowNumber]
778 eventNumber: 0/*[e eventNumber] */
782 context_menu_value = -1;
783 [NSMenu popUpContextMenu: self withEvent: event forView: view];
784 retVal = context_menu_value;
785 context_menu_value = 0;
787 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
795 /* ==========================================================================
797 Context Menu: implementing functions
799 ========================================================================== */
802 ns_menu_show (struct frame *f, int x, int y, int menuflags,
803 Lisp_Object title, const char **error)
808 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
809 widget_value *wv, *first_wv = 0;
810 bool keymaps = (menuflags & MENU_KEYMAPS);
816 /* now parse stage 2 as in ns_update_menubar */
817 wv = make_widget_value ("contextmenu", NULL, true, Qnil);
818 wv->button_type = BUTTON_TYPE_NONE;
822 /* FIXME: a couple of one-line differences prevent reuse */
823 wv = digest_single_submenu (0, menu_items_used, 0);
826 widget_value *save_wv = 0, *prev_wv = 0;
827 widget_value **submenu_stack
828 = alloca (menu_items_used * sizeof *submenu_stack);
829 /* Lisp_Object *subprefix_stack
830 = alloca (menu_items_used * sizeof *subprefix_stack); */
831 int submenu_depth = 0;
835 /* Loop over all panes and items, filling in the tree. */
837 while (i < menu_items_used)
839 if (EQ (AREF (menu_items, i), Qnil))
841 submenu_stack[submenu_depth++] = save_wv;
847 else if (EQ (AREF (menu_items, i), Qlambda))
850 save_wv = submenu_stack[--submenu_depth];
854 else if (EQ (AREF (menu_items, i), Qt)
855 && submenu_depth != 0)
856 i += MENU_ITEMS_PANE_LENGTH;
857 /* Ignore a nil in the item list.
858 It's meaningful only for dialog boxes. */
859 else if (EQ (AREF (menu_items, i), Qquote))
861 else if (EQ (AREF (menu_items, i), Qt))
863 /* Create a new pane. */
864 Lisp_Object pane_name, prefix;
865 const char *pane_string;
867 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
868 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
870 #ifndef HAVE_MULTILINGUAL_MENU
871 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
873 pane_name = ENCODE_MENU_STRING (pane_name);
874 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
877 pane_string = (NILP (pane_name)
878 ? "" : SSDATA (pane_name));
879 /* If there is just one top-level pane, put all its items directly
880 under the top-level menu. */
881 if (menu_items_n_panes == 1)
884 /* If the pane has a meaningful name,
885 make the pane a top-level menu item
886 with its items as a submenu beneath it. */
887 if (!keymaps && strcmp (pane_string, ""))
889 wv = make_widget_value (pane_string, NULL, true, Qnil);
893 first_wv->contents = wv;
894 if (keymaps && !NILP (prefix))
896 wv->button_type = BUTTON_TYPE_NONE;
906 i += MENU_ITEMS_PANE_LENGTH;
910 /* Create a new item within current pane. */
911 Lisp_Object item_name, enable, descrip, def, type, selected, help;
912 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
913 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
914 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
915 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
916 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
917 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
918 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
920 #ifndef HAVE_MULTILINGUAL_MENU
921 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
923 item_name = ENCODE_MENU_STRING (item_name);
924 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
927 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
929 descrip = ENCODE_MENU_STRING (descrip);
930 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
932 #endif /* not HAVE_MULTILINGUAL_MENU */
934 wv = make_widget_value (SSDATA (item_name), NULL, !NILP (enable),
935 STRINGP (help) ? help : Qnil);
939 save_wv->contents = wv;
941 wv->key = SSDATA (descrip);
942 /* If this item has a null value,
943 make the call_data null so that it won't display a box
944 when the mouse is on it. */
945 wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
948 wv->button_type = BUTTON_TYPE_NONE;
949 else if (EQ (type, QCtoggle))
950 wv->button_type = BUTTON_TYPE_TOGGLE;
951 else if (EQ (type, QCradio))
952 wv->button_type = BUTTON_TYPE_RADIO;
956 wv->selected = !NILP (selected);
960 i += MENU_ITEMS_ITEM_LENGTH;
968 widget_value *wv_title;
969 widget_value *wv_sep = make_widget_value ("--", NULL, false, Qnil);
971 /* Maybe replace this separator with a bitmap or owner-draw item
972 so that it looks better. Having two separators looks odd. */
973 wv_sep->next = first_wv->contents;
975 #ifndef HAVE_MULTILINGUAL_MENU
976 if (STRING_MULTIBYTE (title))
977 title = ENCODE_MENU_STRING (title);
979 wv_title = make_widget_value (SSDATA (title), NULL, false, Qnil);
980 wv_title->button_type = BUTTON_TYPE_NONE;
981 wv_title->next = wv_sep;
982 first_wv->contents = wv_title;
985 pmenu = [[EmacsMenu alloc] initWithTitle:
986 [NSString stringWithUTF8String: SSDATA (title)]];
987 [pmenu fillWithWidgetValue: first_wv->contents];
988 free_menubar_widget_value_tree (first_wv);
989 unbind_to (specpdl_count, Qnil);
991 popup_activated_flag = 1;
992 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
993 popup_activated_flag = 0;
994 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1001 /* ==========================================================================
1003 Toolbar: externally-called functions
1005 ========================================================================== */
1008 free_frame_tool_bar (struct frame *f)
1009 /* --------------------------------------------------------------------------
1010 Under NS we just hide the toolbar until it might be needed again.
1011 -------------------------------------------------------------------------- */
1013 EmacsView *view = FRAME_NS_VIEW (f);
1015 view->wait_for_tool_bar = NO;
1016 [[view toolbar] setVisible: NO];
1017 FRAME_TOOLBAR_HEIGHT (f) = 0;
1022 update_frame_tool_bar (struct frame *f)
1023 /* --------------------------------------------------------------------------
1024 Update toolbar contents
1025 -------------------------------------------------------------------------- */
1028 EmacsView *view = FRAME_NS_VIEW (f);
1029 NSWindow *window = [view window];
1030 EmacsToolbar *toolbar = [view toolbar];
1032 if (view == nil || toolbar == nil) return;
1035 #ifdef NS_IMPL_COCOA
1036 [toolbar clearActive];
1041 /* update EmacsToolbar as in GtkUtils, build items list */
1042 for (i = 0; i < f->n_tool_bar_items; ++i)
1044 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1045 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1047 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1052 Lisp_Object helpObj;
1053 const char *helpText;
1055 /* Check if this is a separator. */
1056 if (EQ (TOOLPROP (TOOL_BAR_ITEM_TYPE), Qt))
1058 /* Skip separators. Newer OSX don't show them, and on GNUstep they
1059 are wide as a button, thus overflowing the toolbar most of
1064 /* If image is a vector, choose the image according to the
1066 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1067 if (VECTORP (image))
1069 /* NS toolbar auto-computes disabled and selected images */
1070 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1071 eassert (ASIZE (image) >= idx);
1072 image = AREF (image, idx);
1078 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1080 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1081 helpText = NILP (helpObj) ? "" : SSDATA (helpObj);
1083 /* Ignore invalid image specifications. */
1084 if (!valid_image_p (image))
1086 /* Don't log anything, GNUS makes invalid images all the time. */
1090 img_id = lookup_image (f, image);
1091 img = IMAGE_FROM_ID (f, img_id);
1092 prepare_image_for_display (f, img);
1094 if (img->load_failed_p || img->pixmap == nil)
1096 NSLog (@"Could not prepare toolbar image for display.");
1100 [toolbar addDisplayItemWithImage: img->pixmap
1104 enabled: enabled_p];
1108 if (![toolbar isVisible])
1109 [toolbar setVisible: YES];
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 (view->wait_for_tool_bar && FRAME_TOOLBAR_HEIGHT (f) > 0)
1143 view->wait_for_tool_bar = NO;
1144 [view setNeedsDisplay: YES];
1151 /* ==========================================================================
1153 Toolbar: class implementation
1155 ========================================================================== */
1157 @implementation EmacsToolbar
1159 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1161 self = [super initWithIdentifier: identifier];
1163 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1164 [self setSizeMode: NSToolbarSizeModeSmall];
1165 [self setDelegate: self];
1166 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1167 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1168 prevIdentifiers = nil;
1169 prevEnablement = enablement = 0L;
1175 [prevIdentifiers release];
1176 [activeIdentifiers release];
1177 [identifierToItem release];
1181 - (void) clearActive
1183 [prevIdentifiers release];
1184 prevIdentifiers = [activeIdentifiers copy];
1185 [activeIdentifiers removeAllObjects];
1186 prevEnablement = enablement;
1193 while ([[self items] count] > 0)
1194 [self removeItemAtIndex: 0];
1199 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1200 enablement == prevEnablement ? NO : YES;
1203 - (void) addDisplayItemWithImage: (EmacsImage *)img
1206 helpText: (const char *)help
1207 enabled: (BOOL)enabled
1209 /* 1) come up w/identifier */
1210 NSString *identifier
1211 = [NSString stringWithFormat: @"%lu", (unsigned long)[img hash]];
1212 [activeIdentifiers addObject: identifier];
1214 /* 2) create / reuse item */
1215 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1218 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1220 [item setImage: img];
1221 [item setToolTip: [NSString stringWithUTF8String: help]];
1222 [item setTarget: emacsView];
1223 [item setAction: @selector (toolbarClicked:)];
1224 [identifierToItem setObject: item forKey: identifier];
1227 #ifdef NS_IMPL_GNUSTEP
1228 [self insertItemWithItemIdentifier: identifier atIndex: idx];
1232 [item setEnabled: enabled];
1234 /* 3) update state */
1235 enablement = (enablement << 1) | (enabled == YES);
1238 /* This overrides super's implementation, which automatically sets
1239 all items to enabled state (for some reason). */
1240 - (void)validateVisibleItems
1245 /* delegate methods */
1247 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1248 itemForItemIdentifier: (NSString *)itemIdentifier
1249 willBeInsertedIntoToolbar: (BOOL)flag
1251 /* look up NSToolbarItem by identifier and return... */
1252 return [identifierToItem objectForKey: itemIdentifier];
1255 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1257 /* return entire set.. */
1258 return activeIdentifiers;
1261 /* for configuration palette (not yet supported) */
1262 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1264 /* return entire set... */
1265 return activeIdentifiers;
1266 //return [identifierToItem allKeys];
1269 /* optional and unneeded */
1270 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1271 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1272 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1274 @end /* EmacsToolbar */
1278 /* ==========================================================================
1280 Tooltip: class implementation
1282 ========================================================================== */
1284 /* Needed because NeXTstep does not provide enough control over tooltip
1286 @implementation EmacsTooltip
1290 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1291 blue: 0.792 alpha: 0.95];
1292 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1293 NSFont *sfont = [font screenFont];
1294 int height = [sfont ascender] - [sfont descender];
1295 /*[font boundingRectForFont].size.height; */
1296 NSRect r = NSMakeRect (0, 0, 100, height+6);
1298 textField = [[NSTextField alloc] initWithFrame: r];
1299 [textField setFont: font];
1300 [textField setBackgroundColor: col];
1302 [textField setEditable: NO];
1303 [textField setSelectable: NO];
1304 [textField setBordered: NO];
1305 [textField setBezeled: NO];
1306 [textField setDrawsBackground: YES];
1308 win = [[NSWindow alloc]
1309 initWithContentRect: [textField frame]
1311 backing: NSBackingStoreBuffered
1313 [win setHasShadow: YES];
1314 [win setReleasedWhenClosed: NO];
1315 [win setDelegate: self];
1316 [[win contentView] addSubview: textField];
1317 /* [win setBackgroundColor: col]; */
1318 [win setOpaque: NO];
1327 [textField release];
1331 - (void) setText: (char *)text
1333 NSString *str = [NSString stringWithUTF8String: text];
1334 NSRect r = [textField frame];
1337 [textField setStringValue: str];
1338 tooltipDims = [[textField cell] cellSize];
1340 r.size.width = tooltipDims.width;
1341 r.size.height = tooltipDims.height;
1342 [textField setFrame: r];
1345 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1347 NSRect wr = [win frame];
1349 wr.origin = NSMakePoint (x, y);
1350 wr.size = [textField frame].size;
1352 [win setFrame: wr display: YES];
1353 [win setLevel: NSPopUpMenuWindowLevel];
1354 [win orderFront: self];
1356 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1357 selector: @selector (hide)
1358 userInfo: nil repeats: NO];
1367 if ([timer isValid])
1376 return timer != nil;
1381 return [textField frame];
1384 @end /* EmacsTooltip */
1388 /* ==========================================================================
1390 Popup Dialog: implementing functions
1392 ========================================================================== */
1396 NSAutoreleasePool *pool;
1397 EmacsDialogPanel *dialog;
1401 pop_down_menu (void *arg)
1403 struct Popdown_data *unwind_data = arg;
1406 if (popup_activated_flag)
1408 EmacsDialogPanel *panel = unwind_data->dialog;
1409 popup_activated_flag = 0;
1411 [unwind_data->pool release];
1412 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1415 xfree (unwind_data);
1421 ns_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
1424 Lisp_Object window, tem, title;
1427 NSAutoreleasePool *pool;
1429 NSTRACE (x-popup-dialog);
1431 isQ = NILP (header);
1433 check_window_system (f);
1435 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1436 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1438 title = Fcar (contents);
1439 CHECK_STRING (title);
1441 if (NILP (Fcar (Fcdr (contents))))
1442 /* No buttons specified, add an "Ok" button so users can pop down
1444 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
1447 pool = [[NSAutoreleasePool alloc] init];
1448 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1452 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1453 struct Popdown_data *unwind_data = xmalloc (sizeof (*unwind_data));
1455 unwind_data->pool = pool;
1456 unwind_data->dialog = dialog;
1458 record_unwind_protect_ptr (pop_down_menu, unwind_data);
1459 popup_activated_flag = 1;
1460 tem = [dialog runDialogAt: p];
1461 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1470 /* ==========================================================================
1472 Popup Dialog: class implementation
1474 ========================================================================== */
1476 @interface FlippedView : NSView
1481 @implementation FlippedView
1488 @implementation EmacsDialogPanel
1491 #define ICONSIZE 64.0
1492 #define TEXTHEIGHT 20.0
1493 #define MINCELLWIDTH 90.0
1495 - initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
1496 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1498 NSSize spacing = {SPACER, SPACER};
1501 NSImageView *imgView;
1502 FlippedView *contentView;
1505 dialog_return = Qundefined;
1506 button_values = NULL;
1507 area.origin.x = 3*SPACER;
1508 area.origin.y = 2*SPACER;
1509 area.size.width = ICONSIZE;
1510 area.size.height= ICONSIZE;
1511 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1512 [img setScalesWhenResized: YES];
1513 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1514 imgView = [[NSImageView alloc] initWithFrame: area];
1515 [imgView setImage: img];
1516 [imgView setEditable: NO];
1518 [imgView autorelease];
1520 aStyle = NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask;
1524 [super initWithContentRect: contentRect styleMask: aStyle
1525 backing: backingType defer: flag];
1526 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1527 [contentView autorelease];
1529 [self setContentView: contentView];
1531 [[self contentView] setAutoresizesSubviews: YES];
1533 [[self contentView] addSubview: imgView];
1534 [self setTitle: @""];
1536 area.origin.x += ICONSIZE+2*SPACER;
1537 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1538 area.size.width = 400;
1539 area.size.height= TEXTHEIGHT;
1540 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1541 [[self contentView] addSubview: command];
1542 [command setStringValue: ns_app_name];
1543 [command setDrawsBackground: NO];
1544 [command setBezeled: NO];
1545 [command setSelectable: NO];
1546 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1548 /* area.origin.x = ICONSIZE+2*SPACER;
1549 area.origin.y = TEXTHEIGHT + 2*SPACER;
1550 area.size.width = 400;
1551 area.size.height= 2;
1552 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1553 [[self contentView] addSubview: tem];
1554 [tem setTitlePosition: NSNoTitle];
1555 [tem setAutoresizingMask: NSViewWidthSizable];*/
1557 /* area.origin.x = ICONSIZE+2*SPACER; */
1558 area.origin.y += TEXTHEIGHT+SPACER;
1559 area.size.width = 400;
1560 area.size.height= TEXTHEIGHT;
1561 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1562 [[self contentView] addSubview: title];
1563 [title setDrawsBackground: NO];
1564 [title setBezeled: NO];
1565 [title setSelectable: NO];
1566 [title setFont: [NSFont systemFontOfSize: 11.0]];
1568 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1569 [cell setBordered: NO];
1570 [cell setEnabled: NO];
1571 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1572 [cell setBezelStyle: NSRoundedBezelStyle];
1574 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1575 mode: NSHighlightModeMatrix
1578 numberOfColumns: 1];
1579 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1580 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1581 [matrix setIntercellSpacing: spacing];
1582 [matrix autorelease];
1584 [[self contentView] addSubview: matrix];
1585 [self setOneShot: YES];
1586 [self setReleasedWhenClosed: YES];
1587 [self setHidesOnDeactivate: YES];
1592 - (BOOL)windowShouldClose: (id)sender
1594 window_closed = YES;
1601 xfree (button_values);
1605 - (void)process_dialog: (Lisp_Object) list
1607 Lisp_Object item, lst = list;
1609 int buttons = 0, btnnr = 0;
1611 for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
1614 if (XTYPE (item) == Lisp_Cons)
1619 button_values = xmalloc (buttons * sizeof *button_values);
1621 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1624 if (XTYPE (item) == Lisp_String)
1626 [self addString: SSDATA (item) row: row++];
1628 else if (XTYPE (item) == Lisp_Cons)
1630 button_values[btnnr] = XCDR (item);
1631 [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
1634 else if (NILP (item))
1643 - (void)addButton: (char *)str value: (int)tag row: (int)row
1652 cell = [matrix cellAtRow: row column: cols-1];
1653 [cell setTarget: self];
1654 [cell setAction: @selector (clicked: )];
1655 [cell setTitle: [NSString stringWithUTF8String: str]];
1657 [cell setBordered: YES];
1658 [cell setEnabled: YES];
1662 - (void)addString: (char *)str row: (int)row
1671 cell = [matrix cellAtRow: row column: cols-1];
1672 [cell setTitle: [NSString stringWithUTF8String: str]];
1673 [cell setBordered: YES];
1674 [cell setEnabled: NO];
1685 - (void)clicked: sender
1687 NSArray *sellist = nil;
1690 sellist = [sender selectedCells];
1691 if ([sellist count] < 1)
1694 seltag = [[sellist objectAtIndex: 0] tag];
1695 dialog_return = button_values[seltag];
1700 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1705 if (XTYPE (contents) == Lisp_Cons)
1707 head = Fcar (contents);
1708 [self process_dialog: Fcdr (contents)];
1713 if (XTYPE (head) == Lisp_String)
1714 [title setStringValue:
1715 [NSString stringWithUTF8String: SSDATA (head)]];
1716 else if (isQ == YES)
1717 [title setStringValue: @"Question"];
1719 [title setStringValue: @"Information"];
1725 if (cols == 1 && rows > 1) /* Never told where to split */
1728 for (i = 0; i < rows/2; i++)
1730 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1731 atRow: i column: 1];
1732 [matrix removeRow: (rows+1)/2];
1738 NSSize csize = [matrix cellSize];
1739 if (csize.width < MINCELLWIDTH)
1741 csize.width = MINCELLWIDTH;
1742 [matrix setCellSize: csize];
1743 [matrix sizeToCells];
1748 [command sizeToFit];
1752 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1754 t.origin.x = r.origin.x;
1755 t.size.width = r.size.width;
1757 r = [command frame];
1758 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1760 t.origin.x = r.origin.x;
1761 t.size.width = r.size.width;
1765 s = [(NSView *)[self contentView] frame];
1766 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1767 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1768 [self setFrame: r display: NO];
1776 - (void)timeout_handler: (NSTimer *)timedEntry
1778 NSEvent *nxev = [NSEvent otherEventWithType: NSApplicationDefined
1779 location: NSMakePoint (0, 0)
1782 windowNumber: [[NSApp mainWindow] windowNumber]
1783 context: [NSApp context]
1789 /* We use sto because stopModal/abortModal out of the main loop does not
1790 seem to work in 10.6. But as we use stop we must send a real event so
1791 the stop is seen and acted upon. */
1793 [NSApp postEvent: nxev atStart: NO];
1796 - (Lisp_Object)runDialogAt: (NSPoint)p
1798 Lisp_Object ret = Qundefined;
1800 while (popup_activated_flag)
1803 struct timespec next_time = timer_check ();
1805 if (timespec_valid_p (next_time))
1807 double time = timespectod (next_time);
1808 tmo = [NSTimer timerWithTimeInterval: time
1810 selector: @selector (timeout_handler:)
1813 [[NSRunLoop currentRunLoop] addTimer: tmo
1814 forMode: NSModalPanelRunLoopMode];
1817 dialog_return = Qundefined;
1818 [NSApp runModalForWindow: self];
1819 ret = dialog_return;
1822 if (tmo != nil) [tmo invalidate]; /* Cancels timer */
1827 if (EQ (ret, Qundefined) && window_closed)
1828 /* Make close button pressed equivalent to C-g. */
1829 Fsignal (Qquit, Qnil);
1837 /* ==========================================================================
1841 ========================================================================== */
1843 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1844 doc: /* Cause the NS menu to be re-calculated. */)
1847 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1852 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1853 doc: /* Return t if a menu or popup dialog is active. */)
1856 return popup_activated () ? Qt : Qnil;
1859 /* ==========================================================================
1861 Lisp interface declaration
1863 ========================================================================== */
1866 syms_of_nsmenu (void)
1868 #ifndef NS_IMPL_COCOA
1869 /* Don't know how to keep track of this in Next/Open/GNUstep. Always
1870 update menus there. */
1873 defsubr (&Sns_reset_menu);
1874 defsubr (&Smenu_or_popup_active_p);
1876 Qdebug_on_next_call = intern_c_string ("debug-on-next-call");
1877 staticpro (&Qdebug_on_next_call);