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