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];
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 - (void)setVisible:(BOOL)shown
1298 NSTRACE ("[EmacsToolbar setVisible:%d]", shown);
1300 [super setVisible:shown];
1304 /* optional and unneeded */
1305 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1306 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1307 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1309 @end /* EmacsToolbar */
1313 /* ==========================================================================
1315 Tooltip: class implementation
1317 ========================================================================== */
1319 /* Needed because NeXTstep does not provide enough control over tooltip
1321 @implementation EmacsTooltip
1325 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1326 blue: 0.792 alpha: 0.95];
1327 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1328 NSFont *sfont = [font screenFont];
1329 int height = [sfont ascender] - [sfont descender];
1330 /*[font boundingRectForFont].size.height; */
1331 NSRect r = NSMakeRect (0, 0, 100, height+6);
1333 textField = [[NSTextField alloc] initWithFrame: r];
1334 [textField setFont: font];
1335 [textField setBackgroundColor: col];
1337 [textField setEditable: NO];
1338 [textField setSelectable: NO];
1339 [textField setBordered: NO];
1340 [textField setBezeled: NO];
1341 [textField setDrawsBackground: YES];
1343 win = [[NSWindow alloc]
1344 initWithContentRect: [textField frame]
1346 backing: NSBackingStoreBuffered
1348 [win setHasShadow: YES];
1349 [win setReleasedWhenClosed: NO];
1350 [win setDelegate: self];
1351 [[win contentView] addSubview: textField];
1352 /* [win setBackgroundColor: col]; */
1353 [win setOpaque: NO];
1362 [textField release];
1366 - (void) setText: (char *)text
1368 NSString *str = [NSString stringWithUTF8String: text];
1369 NSRect r = [textField frame];
1372 [textField setStringValue: str];
1373 tooltipDims = [[textField cell] cellSize];
1375 r.size.width = tooltipDims.width;
1376 r.size.height = tooltipDims.height;
1377 [textField setFrame: r];
1380 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1382 NSRect wr = [win frame];
1384 wr.origin = NSMakePoint (x, y);
1385 wr.size = [textField frame].size;
1387 [win setFrame: wr display: YES];
1388 [win setLevel: NSPopUpMenuWindowLevel];
1389 [win orderFront: self];
1391 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1392 selector: @selector (hide)
1393 userInfo: nil repeats: NO];
1402 if ([timer isValid])
1411 return timer != nil;
1416 return [textField frame];
1419 @end /* EmacsTooltip */
1423 /* ==========================================================================
1425 Popup Dialog: implementing functions
1427 ========================================================================== */
1431 NSAutoreleasePool *pool;
1432 EmacsDialogPanel *dialog;
1436 pop_down_menu (void *arg)
1438 struct Popdown_data *unwind_data = arg;
1441 if (popup_activated_flag)
1443 EmacsDialogPanel *panel = unwind_data->dialog;
1444 popup_activated_flag = 0;
1446 [unwind_data->pool release];
1447 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1450 xfree (unwind_data);
1456 ns_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
1459 Lisp_Object tem, title;
1462 NSAutoreleasePool *pool;
1464 NSTRACE ("ns_popup_dialog");
1466 isQ = NILP (header);
1468 check_window_system (f);
1470 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1471 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1473 title = Fcar (contents);
1474 CHECK_STRING (title);
1476 if (NILP (Fcar (Fcdr (contents))))
1477 /* No buttons specified, add an "Ok" button so users can pop down
1479 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
1482 pool = [[NSAutoreleasePool alloc] init];
1483 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1487 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1488 struct Popdown_data *unwind_data = xmalloc (sizeof (*unwind_data));
1490 unwind_data->pool = pool;
1491 unwind_data->dialog = dialog;
1493 record_unwind_protect_ptr (pop_down_menu, unwind_data);
1494 popup_activated_flag = 1;
1495 tem = [dialog runDialogAt: p];
1496 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1505 /* ==========================================================================
1507 Popup Dialog: class implementation
1509 ========================================================================== */
1511 @interface FlippedView : NSView
1516 @implementation FlippedView
1523 @implementation EmacsDialogPanel
1526 #define ICONSIZE 64.0
1527 #define TEXTHEIGHT 20.0
1528 #define MINCELLWIDTH 90.0
1530 - initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
1531 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1533 NSSize spacing = {SPACER, SPACER};
1536 NSImageView *imgView;
1537 FlippedView *contentView;
1540 dialog_return = Qundefined;
1541 button_values = NULL;
1542 area.origin.x = 3*SPACER;
1543 area.origin.y = 2*SPACER;
1544 area.size.width = ICONSIZE;
1545 area.size.height= ICONSIZE;
1546 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1547 #ifdef NS_IMPL_COCOA
1548 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
1549 [img setScalesWhenResized: YES];
1552 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1553 imgView = [[NSImageView alloc] initWithFrame: area];
1554 [imgView setImage: img];
1555 [imgView setEditable: NO];
1557 [imgView autorelease];
1559 aStyle = NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask;
1563 [super initWithContentRect: contentRect styleMask: aStyle
1564 backing: backingType defer: flag];
1565 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1566 [contentView autorelease];
1568 [self setContentView: contentView];
1570 [[self contentView] setAutoresizesSubviews: YES];
1572 [[self contentView] addSubview: imgView];
1573 [self setTitle: @""];
1575 area.origin.x += ICONSIZE+2*SPACER;
1576 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1577 area.size.width = 400;
1578 area.size.height= TEXTHEIGHT;
1579 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1580 [[self contentView] addSubview: command];
1581 [command setStringValue: ns_app_name];
1582 [command setDrawsBackground: NO];
1583 [command setBezeled: NO];
1584 [command setSelectable: NO];
1585 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1587 /* area.origin.x = ICONSIZE+2*SPACER;
1588 area.origin.y = TEXTHEIGHT + 2*SPACER;
1589 area.size.width = 400;
1590 area.size.height= 2;
1591 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1592 [[self contentView] addSubview: tem];
1593 [tem setTitlePosition: NSNoTitle];
1594 [tem setAutoresizingMask: NSViewWidthSizable];*/
1596 /* area.origin.x = ICONSIZE+2*SPACER; */
1597 area.origin.y += TEXTHEIGHT+SPACER;
1598 area.size.width = 400;
1599 area.size.height= TEXTHEIGHT;
1600 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1601 [[self contentView] addSubview: title];
1602 [title setDrawsBackground: NO];
1603 [title setBezeled: NO];
1604 [title setSelectable: NO];
1605 [title setFont: [NSFont systemFontOfSize: 11.0]];
1607 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1608 [cell setBordered: NO];
1609 [cell setEnabled: NO];
1610 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1611 [cell setBezelStyle: NSRoundedBezelStyle];
1613 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1614 mode: NSHighlightModeMatrix
1617 numberOfColumns: 1];
1618 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1619 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1620 [matrix setIntercellSpacing: spacing];
1621 [matrix autorelease];
1623 [[self contentView] addSubview: matrix];
1624 [self setOneShot: YES];
1625 [self setReleasedWhenClosed: YES];
1626 [self setHidesOnDeactivate: YES];
1631 - (BOOL)windowShouldClose: (id)sender
1633 window_closed = YES;
1640 xfree (button_values);
1644 - (void)process_dialog: (Lisp_Object) list
1646 Lisp_Object item, lst = list;
1648 int buttons = 0, btnnr = 0;
1650 for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
1653 if (XTYPE (item) == Lisp_Cons)
1658 button_values = xmalloc (buttons * sizeof *button_values);
1660 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1663 if (XTYPE (item) == Lisp_String)
1665 [self addString: SSDATA (item) row: row++];
1667 else if (XTYPE (item) == Lisp_Cons)
1669 button_values[btnnr] = XCDR (item);
1670 [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
1673 else if (NILP (item))
1682 - (void)addButton: (char *)str value: (int)tag row: (int)row
1691 cell = [matrix cellAtRow: row column: cols-1];
1692 [cell setTarget: self];
1693 [cell setAction: @selector (clicked: )];
1694 [cell setTitle: [NSString stringWithUTF8String: str]];
1696 [cell setBordered: YES];
1697 [cell setEnabled: YES];
1701 - (void)addString: (char *)str row: (int)row
1710 cell = [matrix cellAtRow: row column: cols-1];
1711 [cell setTitle: [NSString stringWithUTF8String: str]];
1712 [cell setBordered: YES];
1713 [cell setEnabled: NO];
1724 - (void)clicked: sender
1726 NSArray *sellist = nil;
1729 sellist = [sender selectedCells];
1730 if ([sellist count] < 1)
1733 seltag = [[sellist objectAtIndex: 0] tag];
1734 dialog_return = button_values[seltag];
1739 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1744 if (XTYPE (contents) == Lisp_Cons)
1746 head = Fcar (contents);
1747 [self process_dialog: Fcdr (contents)];
1752 if (XTYPE (head) == Lisp_String)
1753 [title setStringValue:
1754 [NSString stringWithUTF8String: SSDATA (head)]];
1755 else if (isQ == YES)
1756 [title setStringValue: @"Question"];
1758 [title setStringValue: @"Information"];
1764 if (cols == 1 && rows > 1) /* Never told where to split */
1767 for (i = 0; i < rows/2; i++)
1769 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1770 atRow: i column: 1];
1771 [matrix removeRow: (rows+1)/2];
1777 NSSize csize = [matrix cellSize];
1778 if (csize.width < MINCELLWIDTH)
1780 csize.width = MINCELLWIDTH;
1781 [matrix setCellSize: csize];
1782 [matrix sizeToCells];
1787 [command sizeToFit];
1791 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1793 t.origin.x = r.origin.x;
1794 t.size.width = r.size.width;
1796 r = [command frame];
1797 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1799 t.origin.x = r.origin.x;
1800 t.size.width = r.size.width;
1804 s = [(NSView *)[self contentView] frame];
1805 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1806 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1807 [self setFrame: r display: NO];
1815 - (void)timeout_handler: (NSTimer *)timedEntry
1817 NSEvent *nxev = [NSEvent otherEventWithType: NSApplicationDefined
1818 location: NSMakePoint (0, 0)
1821 windowNumber: [[NSApp mainWindow] windowNumber]
1822 context: [NSApp context]
1828 /* We use sto because stopModal/abortModal out of the main loop does not
1829 seem to work in 10.6. But as we use stop we must send a real event so
1830 the stop is seen and acted upon. */
1832 [NSApp postEvent: nxev atStart: NO];
1835 - (Lisp_Object)runDialogAt: (NSPoint)p
1837 Lisp_Object ret = Qundefined;
1839 while (popup_activated_flag)
1842 struct timespec next_time = timer_check ();
1844 if (timespec_valid_p (next_time))
1846 double time = timespectod (next_time);
1847 tmo = [NSTimer timerWithTimeInterval: time
1849 selector: @selector (timeout_handler:)
1852 [[NSRunLoop currentRunLoop] addTimer: tmo
1853 forMode: NSModalPanelRunLoopMode];
1856 dialog_return = Qundefined;
1857 [NSApp runModalForWindow: self];
1858 ret = dialog_return;
1861 if (tmo != nil) [tmo invalidate]; /* Cancels timer */
1866 if (EQ (ret, Qundefined) && window_closed)
1867 /* Make close button pressed equivalent to C-g. */
1868 Fsignal (Qquit, Qnil);
1876 /* ==========================================================================
1880 ========================================================================== */
1882 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1883 doc: /* Cause the NS menu to be re-calculated. */)
1886 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1891 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1892 doc: /* Return t if a menu or popup dialog is active. */)
1895 return popup_activated () ? Qt : Qnil;
1898 /* ==========================================================================
1900 Lisp interface declaration
1902 ========================================================================== */
1905 syms_of_nsmenu (void)
1907 #ifndef NS_IMPL_COCOA
1908 /* Don't know how to keep track of this in Next/Open/GNUstep. Always
1909 update menus there. */
1912 defsubr (&Sns_reset_menu);
1913 defsubr (&Smenu_or_popup_active_p);
1915 DEFSYM (Qdebug_on_next_call, "debug-on-next-call");