1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007-2013 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 = xmalloc_widget_value ();
270 wv->name = "menubar";
273 wv->button_type = BUTTON_TYPE_NONE;
277 for (i = 0; i < 4*n; i += 4)
279 menu_items_n_panes = submenu_n_panes[i];
280 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
281 submenu_top_level_items[i]);
285 first_wv->contents = wv;
286 /* Don't set wv->name here; GC during the loop might relocate it. */
288 wv->button_type = BUTTON_TYPE_NONE;
292 set_buffer_internal_1 (prev);
294 /* Compare the new menu items with previous, and leave off if no change */
295 /* FIXME: following other terms here, but seems like this should be
296 done before parse stage 2 above, since its results aren't used */
297 if (previous_menu_items_used
298 && (!submenu || (submenu && submenu == last_submenu))
299 && menu_items_used == previous_menu_items_used)
301 for (i = 0; i < previous_menu_items_used; i++)
302 /* FIXME: this ALWAYS fails on Buffers menu items.. something
303 about their strings causes them to change every time, so we
304 double-check failures */
305 if (!EQ (previous_items[i], AREF (menu_items, i)))
306 if (!(STRINGP (previous_items[i])
307 && STRINGP (AREF (menu_items, i))
308 && !strcmp (SSDATA (previous_items[i]),
309 SSDATA (AREF (menu_items, i)))))
311 if (i == previous_menu_items_used)
317 t += 1000*tb.time+tb.millitm;
318 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
321 free_menubar_widget_value_tree (first_wv);
322 discard_menu_items ();
323 unbind_to (specpdl_count, Qnil);
329 /* The menu items are different, so store them in the frame */
330 /* FIXME: this is not correct for single-submenu case */
331 fset_menu_bar_vector (f, menu_items);
332 f->menu_bar_items_used = menu_items_used;
334 /* Calls restore_menu_items, etc., as they were outside */
335 unbind_to (specpdl_count, Qnil);
337 /* Parse stage 2a: now GC cannot happen during the lifetime of the
338 widget_value, so it's safe to store data from a Lisp_String */
339 wv = first_wv->contents;
340 for (i = 0; i < ASIZE (items); i += 4)
343 string = AREF (items, i + 1);
347 wv->name = SSDATA (string);
348 update_submenu_strings (wv->contents);
352 /* Now, update the NS menu; if we have a submenu, use that, otherwise
353 create a new menu for each sub and fill it. */
356 const char *submenuTitle = [[submenu title] UTF8String];
357 for (wv = first_wv->contents; wv; wv = wv->next)
359 if (!strcmp (submenuTitle, wv->name))
361 [submenu fillWithWidgetValue: wv->contents];
362 last_submenu = submenu;
369 [menu fillWithWidgetValue: first_wv->contents setDelegate:YES];
375 static int n_previous_strings = 0;
376 static char previous_strings[100][10];
377 static struct frame *last_f = NULL;
381 wv = xmalloc_widget_value ();
382 wv->name = "menubar";
385 wv->button_type = BUTTON_TYPE_NONE;
389 /* Make widget-value tree w/ just the top level menu bar strings */
390 items = FRAME_MENU_BAR_ITEMS (f);
393 free_menubar_widget_value_tree (first_wv);
400 /* check if no change.. this mechanism is a bit rough, but ready */
401 n = ASIZE (items) / 4;
402 if (f == last_f && n_previous_strings == n)
404 for (i = 0; i<n; i++)
406 string = AREF (items, 4*i+1);
408 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
412 if (previous_strings[i][0])
417 else if (memcmp (previous_strings[i], SDATA (string),
418 min (10, SBYTES (string) + 1)))
424 free_menubar_widget_value_tree (first_wv);
432 for (i = 0; i < ASIZE (items); i += 4)
434 string = AREF (items, i + 1);
439 memcpy (previous_strings[i/4], SDATA (string),
440 min (10, SBYTES (string) + 1));
442 wv = xmalloc_widget_value ();
443 wv->name = SSDATA (string);
446 wv->button_type = BUTTON_TYPE_NONE;
448 wv->call_data = (void *) (intptr_t) (-1);
451 /* we'll update the real copy under app menu when time comes */
452 if (!strcmp ("Services", wv->name))
454 /* but we need to make sure it will update on demand */
455 [svcsMenu setFrame: f];
459 [menu addSubmenuWithTitle: wv->name forFrame: f];
464 first_wv->contents = wv;
470 n_previous_strings = n;
472 n_previous_strings = 0;
475 free_menubar_widget_value_tree (first_wv);
480 t += 1000*tb.time+tb.millitm;
481 fprintf (stderr, "Menu update took %ld msec.\n", t);
486 [NSApp setMainMenu: menu];
494 /* Main emacs core entry point for menubar menus: called to indicate that the
495 frame's menus have changed, and the *step representation should be updated
498 set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
500 ns_update_menubar (f, deep_p, nil);
504 x_activate_menubar (struct frame *f)
507 ns_update_menubar (f, true, nil);
508 ns_check_pending_open_menu ();
515 /* ==========================================================================
517 Menu: class implementation
519 ========================================================================== */
522 /* Menu that can define itself from Emacs "widget_value"s and will lazily
523 update itself when user clicked. Based on Carbon/AppKit implementation
524 by Yamamoto Mitsuharu. */
525 @implementation EmacsMenu
527 /* override designated initializer */
528 - initWithTitle: (NSString *)title
530 if ((self = [super initWithTitle: title]))
531 [self setAutoenablesItems: NO];
536 /* used for top-level */
537 - initWithTitle: (NSString *)title frame: (struct frame *)f
539 [self initWithTitle: title];
542 [self setDelegate: self];
548 - (void)setFrame: (struct frame *)f
554 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
555 extern NSString *NSMenuDidBeginTrackingNotification;
560 -(void)trackingNotification:(NSNotification *)notification
562 /* Update menu in menuNeedsUpdate only while tracking menus. */
563 trackingMenu = ([notification name] == NSMenuDidBeginTrackingNotification
565 if (! trackingMenu) ns_check_menu_open (nil);
568 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
569 - (void)menuWillOpen:(NSMenu *)menu
573 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_6
574 // On 10.6 we get repeated calls, only the one for NSSystemDefined is "real".
575 if ([[NSApp currentEvent] type] != NSSystemDefined) return;
578 /* When dragging from one menu to another, we get willOpen followed by didClose,
579 i.e. trackingMenu == 3 in willOpen and then 2 after didClose.
580 We have updated all menus, so avoid doing it when trackingMenu == 3. */
581 if (trackingMenu == 2)
582 ns_check_menu_open (menu);
585 - (void)menuDidClose:(NSMenu *)menu
589 #endif /* OSX >= 10.5 */
591 #endif /* NS_IMPL_COCOA */
593 /* delegate method called when a submenu is being opened: run a 'deep' call
594 to set_frame_menubar */
595 - (void)menuNeedsUpdate: (NSMenu *)menu
597 if (!FRAME_LIVE_P (frame))
600 /* Cocoa/Carbon will request update on every keystroke
601 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
602 since key equivalents are handled through emacs.
603 On Leopard, even keystroke events generate SystemDefined event.
604 Third-party applications that enhance mouse / trackpad
605 interaction, or also VNC/Remote Desktop will send events
606 of type AppDefined rather than SysDefined.
607 Menus will fail to show up if they haven't been initialized.
608 AppDefined events may lack timing data.
610 Thus, we rely on the didBeginTrackingNotification notification
611 as above to indicate the need for updates.
612 From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
613 key press case, NSMenuPropertyItemImage (e.g.) won't be set.
615 if (trackingMenu == 0)
617 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
618 #if (! defined (NS_IMPL_COCOA) \
619 || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
620 /* Don't know how to do this for anything other than OSX >= 10.5
621 This is wrong, as it might run Lisp code in the event loop. */
622 ns_update_menubar (frame, true, self);
627 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
629 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
630 && FRAME_NS_VIEW (SELECTED_FRAME ()))
631 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
636 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
637 into an accelerator string. We are only able to display a single character
638 for an accelerator, together with an optional modifier combination. (Under
639 Carbon more control was possible, but in Cocoa multi-char strings passed to
640 NSMenuItem get ignored. For now we try to display a super-single letter
641 combo, and return the others as strings to be appended to the item title.
642 (This is signaled by setting keyEquivModMask to 0 for now.) */
643 -(NSString *)parseKeyEquiv: (const char *)key
645 const char *tpos = key;
646 keyEquivModMask = NSCommandKeyMask;
648 if (!key || !strlen (key))
651 while (*tpos == ' ' || *tpos == '(')
653 if ((*tpos == 's') && (*(tpos+1) == '-'))
655 return [NSString stringWithFormat: @"%c", tpos[2]];
657 keyEquivModMask = 0; /* signal */
658 return [NSString stringWithUTF8String: tpos];
662 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
665 widget_value *wv = (widget_value *)wvptr;
667 if (menu_separator_name_p (wv->name))
669 item = [NSMenuItem separatorItem];
670 [self addItem: item];
674 NSString *title, *keyEq;
675 title = [NSString stringWithUTF8String: wv->name];
677 title = @"< ? >"; /* (get out in the open so we know about it) */
679 keyEq = [self parseKeyEquiv: wv->key];
681 /* OS X just ignores modifier strings longer than one character */
682 if (keyEquivModMask == 0)
683 title = [title stringByAppendingFormat: @" (%@)", keyEq];
686 item = [self addItemWithTitle: (NSString *)title
687 action: @selector (menuDown:)
688 keyEquivalent: keyEq];
689 [item setKeyEquivalentModifierMask: keyEquivModMask];
691 [item setEnabled: wv->enabled];
693 /* Draw radio buttons and tickboxes */
694 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
695 wv->button_type == BUTTON_TYPE_RADIO))
696 [item setState: NSOnState];
698 [item setState: NSOffState];
700 [item setTag: (NSInteger)wv->call_data];
712 for (n = [self numberOfItems]-1; n >= 0; n--)
714 NSMenuItem *item = [self itemAtIndex: n];
715 NSString *title = [item title];
716 if (([title length] == 0 /* OSX 10.5 */
717 || [ns_app_name isEqualToString: title] /* from 10.6 on */
718 || [@"Apple" isEqualToString: title]) /* older */
719 && ![item isSeparatorItem])
721 [self removeItemAtIndex: n];
726 - (void)fillWithWidgetValue: (void *)wvptr
728 [self fillWithWidgetValue: wvptr setDelegate:NO];
731 - (void)fillWithWidgetValue: (void *)wvptr setDelegate: (BOOL)set
733 widget_value *wv = (widget_value *)wvptr;
735 /* clear existing contents */
736 [self setMenuChangedMessagesEnabled: NO];
739 /* add new contents */
740 for (; wv != NULL; wv = wv->next)
742 NSMenuItem *item = [self addItemWithWidgetValue: wv];
746 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
749 if (set) [submenu setDelegate: submenu];
751 [self setSubmenu: submenu forItem: item];
752 [submenu fillWithWidgetValue: wv->contents];
754 [item setAction: (SEL)nil];
758 [self setMenuChangedMessagesEnabled: YES];
759 #ifdef NS_IMPL_GNUSTEP
760 if ([[self window] isVisible])
766 /* adds an empty submenu and returns it */
767 - (EmacsMenu *)addSubmenuWithTitle: (const char *)title forFrame: (struct frame *)f
769 NSString *titleStr = [NSString stringWithUTF8String: title];
770 NSMenuItem *item = [self addItemWithTitle: titleStr
771 action: (SEL)nil /*@selector (menuDown:) */
773 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
774 [self setSubmenu: submenu forItem: item];
779 /* run a menu in popup mode */
780 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
781 keymaps: (bool)keymaps
783 EmacsView *view = FRAME_NS_VIEW (f);
787 /* p = [view convertPoint:p fromView: nil]; */
788 p.y = NSHeight ([view frame]) - p.y;
789 e = [[view window] currentEvent];
790 event = [NSEvent mouseEventWithType: NSRightMouseDown
793 timestamp: [e timestamp]
794 windowNumber: [[view window] windowNumber]
796 eventNumber: 0/*[e eventNumber] */
800 context_menu_value = -1;
801 [NSMenu popUpContextMenu: self withEvent: event forView: view];
802 retVal = context_menu_value;
803 context_menu_value = 0;
805 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
813 /* ==========================================================================
815 Context Menu: implementing functions
817 ========================================================================== */
820 ns_menu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
821 Lisp_Object title, const char **error)
826 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
827 widget_value *wv, *first_wv = 0;
831 /* now parse stage 2 as in ns_update_menubar */
832 wv = xmalloc_widget_value ();
833 wv->name = "contextmenu";
836 wv->button_type = BUTTON_TYPE_NONE;
841 /* FIXME: a couple of one-line differences prevent reuse */
842 wv = digest_single_submenu (0, menu_items_used, 0);
845 widget_value *save_wv = 0, *prev_wv = 0;
846 widget_value **submenu_stack
847 = alloca (menu_items_used * sizeof *submenu_stack);
848 /* Lisp_Object *subprefix_stack
849 = alloca (menu_items_used * sizeof *subprefix_stack); */
850 int submenu_depth = 0;
854 /* Loop over all panes and items, filling in the tree. */
856 while (i < menu_items_used)
858 if (EQ (AREF (menu_items, i), Qnil))
860 submenu_stack[submenu_depth++] = save_wv;
866 else if (EQ (AREF (menu_items, i), Qlambda))
869 save_wv = submenu_stack[--submenu_depth];
873 else if (EQ (AREF (menu_items, i), Qt)
874 && submenu_depth != 0)
875 i += MENU_ITEMS_PANE_LENGTH;
876 /* Ignore a nil in the item list.
877 It's meaningful only for dialog boxes. */
878 else if (EQ (AREF (menu_items, i), Qquote))
880 else if (EQ (AREF (menu_items, i), Qt))
882 /* Create a new pane. */
883 Lisp_Object pane_name, prefix;
884 const char *pane_string;
886 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
887 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
889 #ifndef HAVE_MULTILINGUAL_MENU
890 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
892 pane_name = ENCODE_MENU_STRING (pane_name);
893 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
896 pane_string = (NILP (pane_name)
897 ? "" : SSDATA (pane_name));
898 /* If there is just one top-level pane, put all its items directly
899 under the top-level menu. */
900 if (menu_items_n_panes == 1)
903 /* If the pane has a meaningful name,
904 make the pane a top-level menu item
905 with its items as a submenu beneath it. */
906 if (!keymaps && strcmp (pane_string, ""))
908 wv = xmalloc_widget_value ();
912 first_wv->contents = wv;
913 wv->name = pane_string;
914 if (keymaps && !NILP (prefix))
918 wv->button_type = BUTTON_TYPE_NONE;
929 i += MENU_ITEMS_PANE_LENGTH;
933 /* Create a new item within current pane. */
934 Lisp_Object item_name, enable, descrip, def, type, selected, help;
935 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
936 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
937 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
938 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
939 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
940 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
941 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
943 #ifndef HAVE_MULTILINGUAL_MENU
944 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
946 item_name = ENCODE_MENU_STRING (item_name);
947 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
950 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
952 descrip = ENCODE_MENU_STRING (descrip);
953 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
955 #endif /* not HAVE_MULTILINGUAL_MENU */
957 wv = xmalloc_widget_value ();
961 save_wv->contents = wv;
962 wv->name = SSDATA (item_name);
964 wv->key = SSDATA (descrip);
966 /* If this item has a null value,
967 make the call_data null so that it won't display a box
968 when the mouse is on it. */
969 wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
970 wv->enabled = !NILP (enable);
973 wv->button_type = BUTTON_TYPE_NONE;
974 else if (EQ (type, QCtoggle))
975 wv->button_type = BUTTON_TYPE_TOGGLE;
976 else if (EQ (type, QCradio))
977 wv->button_type = BUTTON_TYPE_RADIO;
981 wv->selected = !NILP (selected);
983 if (! STRINGP (help))
990 i += MENU_ITEMS_ITEM_LENGTH;
998 widget_value *wv_title = xmalloc_widget_value ();
999 widget_value *wv_sep = xmalloc_widget_value ();
1001 /* Maybe replace this separator with a bitmap or owner-draw item
1002 so that it looks better. Having two separators looks odd. */
1003 wv_sep->name = "--";
1004 wv_sep->next = first_wv->contents;
1005 wv_sep->help = Qnil;
1007 #ifndef HAVE_MULTILINGUAL_MENU
1008 if (STRING_MULTIBYTE (title))
1009 title = ENCODE_MENU_STRING (title);
1012 wv_title->name = SSDATA (title);
1013 wv_title->enabled = NO;
1014 wv_title->button_type = BUTTON_TYPE_NONE;
1015 wv_title->help = Qnil;
1016 wv_title->next = wv_sep;
1017 first_wv->contents = wv_title;
1020 pmenu = [[EmacsMenu alloc] initWithTitle:
1021 [NSString stringWithUTF8String: SSDATA (title)]];
1022 [pmenu fillWithWidgetValue: first_wv->contents];
1023 free_menubar_widget_value_tree (first_wv);
1024 unbind_to (specpdl_count, Qnil);
1026 popup_activated_flag = 1;
1027 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
1028 popup_activated_flag = 0;
1029 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1035 /* ==========================================================================
1037 Toolbar: externally-called functions
1039 ========================================================================== */
1042 free_frame_tool_bar (struct frame *f)
1043 /* --------------------------------------------------------------------------
1044 Under NS we just hide the toolbar until it might be needed again.
1045 -------------------------------------------------------------------------- */
1048 [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
1049 FRAME_TOOLBAR_HEIGHT (f) = 0;
1054 update_frame_tool_bar (struct frame *f)
1055 /* --------------------------------------------------------------------------
1056 Update toolbar contents
1057 -------------------------------------------------------------------------- */
1060 EmacsView *view = FRAME_NS_VIEW (f);
1061 NSWindow *window = [view window];
1062 EmacsToolbar *toolbar = [view toolbar];
1066 #ifdef NS_IMPL_COCOA
1067 [toolbar clearActive];
1072 /* update EmacsToolbar as in GtkUtils, build items list */
1073 for (i = 0; i < f->n_tool_bar_items; ++i)
1075 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1076 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1078 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1083 Lisp_Object helpObj;
1084 const char *helpText;
1086 /* Check if this is a separator. */
1087 if (EQ (TOOLPROP (TOOL_BAR_ITEM_TYPE), Qt))
1089 /* Skip separators. Newer OSX don't show them, and on GNUStep they
1090 are wide as a button, thus overflowing the toolbar most of
1095 /* If image is a vector, choose the image according to the
1097 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1098 if (VECTORP (image))
1100 /* NS toolbar auto-computes disabled and selected images */
1101 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1102 eassert (ASIZE (image) >= idx);
1103 image = AREF (image, idx);
1109 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1111 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1112 helpText = NILP (helpObj) ? "" : SSDATA (helpObj);
1114 /* Ignore invalid image specifications. */
1115 if (!valid_image_p (image))
1117 /* Don't log anything, GNUS makes invalid images all the time. */
1121 img_id = lookup_image (f, image);
1122 img = IMAGE_FROM_ID (f, img_id);
1123 prepare_image_for_display (f, img);
1125 if (img->load_failed_p || img->pixmap == nil)
1127 NSLog (@"Could not prepare toolbar image for display.");
1131 [toolbar addDisplayItemWithImage: img->pixmap
1135 enabled: enabled_p];
1139 if (![toolbar isVisible])
1140 [toolbar setVisible: YES];
1142 #ifdef NS_IMPL_COCOA
1143 if ([toolbar changed])
1145 /* inform app that toolbar has changed */
1146 NSDictionary *dict = [toolbar configurationDictionary];
1147 NSMutableDictionary *newDict = [dict mutableCopy];
1148 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1150 while ((key = [keys nextObject]) != nil)
1152 NSObject *val = [dict objectForKey: key];
1153 if ([val isKindOfClass: [NSArray class]])
1156 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1161 [toolbar setConfigurationFromDictionary: newDict];
1166 FRAME_TOOLBAR_HEIGHT (f) =
1167 NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1168 - FRAME_NS_TITLEBAR_HEIGHT (f);
1169 if (FRAME_TOOLBAR_HEIGHT (f) < 0) // happens if frame is fullscreen.
1170 FRAME_TOOLBAR_HEIGHT (f) = 0;
1175 /* ==========================================================================
1177 Toolbar: class implementation
1179 ========================================================================== */
1181 @implementation EmacsToolbar
1183 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1185 self = [super initWithIdentifier: identifier];
1187 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1188 [self setSizeMode: NSToolbarSizeModeSmall];
1189 [self setDelegate: self];
1190 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1191 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1192 prevIdentifiers = nil;
1193 prevEnablement = enablement = 0L;
1199 [prevIdentifiers release];
1200 [activeIdentifiers release];
1201 [identifierToItem release];
1205 - (void) clearActive
1207 [prevIdentifiers release];
1208 prevIdentifiers = [activeIdentifiers copy];
1209 [activeIdentifiers removeAllObjects];
1210 prevEnablement = enablement;
1217 while ([[self items] count] > 0)
1218 [self removeItemAtIndex: 0];
1223 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1224 enablement == prevEnablement ? NO : YES;
1227 - (void) addDisplayItemWithImage: (EmacsImage *)img
1230 helpText: (const char *)help
1231 enabled: (BOOL)enabled
1233 /* 1) come up w/identifier */
1234 NSString *identifier
1235 = [NSString stringWithFormat: @"%u", [img hash]];
1236 [activeIdentifiers addObject: identifier];
1238 /* 2) create / reuse item */
1239 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1242 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1244 [item setImage: img];
1245 [item setToolTip: [NSString stringWithUTF8String: help]];
1246 [item setTarget: emacsView];
1247 [item setAction: @selector (toolbarClicked:)];
1248 [identifierToItem setObject: item forKey: identifier];
1251 #ifdef NS_IMPL_GNUSTEP
1252 [self insertItemWithItemIdentifier: identifier atIndex: idx];
1256 [item setEnabled: enabled];
1258 /* 3) update state */
1259 enablement = (enablement << 1) | (enabled == YES);
1262 /* This overrides super's implementation, which automatically sets
1263 all items to enabled state (for some reason). */
1264 - (void)validateVisibleItems
1269 /* delegate methods */
1271 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1272 itemForItemIdentifier: (NSString *)itemIdentifier
1273 willBeInsertedIntoToolbar: (BOOL)flag
1275 /* look up NSToolbarItem by identifier and return... */
1276 return [identifierToItem objectForKey: itemIdentifier];
1279 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1281 /* return entire set.. */
1282 return activeIdentifiers;
1285 /* for configuration palette (not yet supported) */
1286 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1288 /* return entire set... */
1289 return activeIdentifiers;
1290 //return [identifierToItem allKeys];
1293 /* optional and unneeded */
1294 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1295 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1296 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1298 @end /* EmacsToolbar */
1302 /* ==========================================================================
1304 Tooltip: class implementation
1306 ========================================================================== */
1308 /* Needed because NeXTstep does not provide enough control over tooltip
1310 @implementation EmacsTooltip
1314 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1315 blue: 0.792 alpha: 0.95];
1316 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1317 NSFont *sfont = [font screenFont];
1318 int height = [sfont ascender] - [sfont descender];
1319 /*[font boundingRectForFont].size.height; */
1320 NSRect r = NSMakeRect (0, 0, 100, height+6);
1322 textField = [[NSTextField alloc] initWithFrame: r];
1323 [textField setFont: font];
1324 [textField setBackgroundColor: col];
1326 [textField setEditable: NO];
1327 [textField setSelectable: NO];
1328 [textField setBordered: NO];
1329 [textField setBezeled: NO];
1330 [textField setDrawsBackground: YES];
1332 win = [[NSWindow alloc]
1333 initWithContentRect: [textField frame]
1335 backing: NSBackingStoreBuffered
1337 [win setHasShadow: YES];
1338 [win setReleasedWhenClosed: NO];
1339 [win setDelegate: self];
1340 [[win contentView] addSubview: textField];
1341 /* [win setBackgroundColor: col]; */
1342 [win setOpaque: NO];
1351 [textField release];
1355 - (void) setText: (char *)text
1357 NSString *str = [NSString stringWithUTF8String: text];
1358 NSRect r = [textField frame];
1361 [textField setStringValue: str];
1362 tooltipDims = [[textField cell] cellSize];
1364 r.size.width = tooltipDims.width;
1365 r.size.height = tooltipDims.height;
1366 [textField setFrame: r];
1369 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1371 NSRect wr = [win frame];
1373 wr.origin = NSMakePoint (x, y);
1374 wr.size = [textField frame].size;
1376 [win setFrame: wr display: YES];
1377 [win setLevel: NSPopUpMenuWindowLevel];
1378 [win orderFront: self];
1380 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1381 selector: @selector (hide)
1382 userInfo: nil repeats: NO];
1391 if ([timer isValid])
1400 return timer != nil;
1405 return [textField frame];
1408 @end /* EmacsTooltip */
1412 /* ==========================================================================
1414 Popup Dialog: implementing functions
1416 ========================================================================== */
1420 NSAutoreleasePool *pool;
1421 EmacsDialogPanel *dialog;
1425 pop_down_menu (void *arg)
1427 struct Popdown_data *unwind_data = arg;
1430 if (popup_activated_flag)
1432 EmacsDialogPanel *panel = unwind_data->dialog;
1433 popup_activated_flag = 0;
1435 [unwind_data->pool release];
1436 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1439 xfree (unwind_data);
1445 ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1448 Lisp_Object window, tem, title;
1452 NSAutoreleasePool *pool;
1454 NSTRACE (x-popup-dialog);
1456 isQ = NILP (header);
1458 if (EQ (position, Qt)
1459 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1460 || EQ (XCAR (position), Qtool_bar))))
1462 window = selected_window;
1464 else if (CONSP (position))
1467 tem = Fcar (position);
1468 if (XTYPE (tem) == Lisp_Cons)
1469 window = Fcar (Fcdr (position));
1472 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1473 window = Fcar (tem); /* POSN_WINDOW (tem) */
1476 else if (WINDOWP (position) || FRAMEP (position))
1483 if (FRAMEP (window))
1484 f = XFRAME (window);
1485 else if (WINDOWP (window))
1487 CHECK_LIVE_WINDOW (window);
1488 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1491 CHECK_WINDOW (window);
1493 check_window_system (f);
1495 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1496 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1498 title = Fcar (contents);
1499 CHECK_STRING (title);
1501 if (NILP (Fcar (Fcdr (contents))))
1502 /* No buttons specified, add an "Ok" button so users can pop down
1504 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
1507 pool = [[NSAutoreleasePool alloc] init];
1508 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1512 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1513 struct Popdown_data *unwind_data = xmalloc (sizeof (*unwind_data));
1515 unwind_data->pool = pool;
1516 unwind_data->dialog = dialog;
1518 record_unwind_protect_ptr (pop_down_menu, unwind_data);
1519 popup_activated_flag = 1;
1520 tem = [dialog runDialogAt: p];
1521 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1530 /* ==========================================================================
1532 Popup Dialog: class implementation
1534 ========================================================================== */
1536 @interface FlippedView : NSView
1541 @implementation FlippedView
1548 @implementation EmacsDialogPanel
1551 #define ICONSIZE 64.0
1552 #define TEXTHEIGHT 20.0
1553 #define MINCELLWIDTH 90.0
1555 - initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
1556 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1558 NSSize spacing = {SPACER, SPACER};
1561 NSImageView *imgView;
1562 FlippedView *contentView;
1565 dialog_return = Qundefined;
1566 button_values = NULL;
1567 area.origin.x = 3*SPACER;
1568 area.origin.y = 2*SPACER;
1569 area.size.width = ICONSIZE;
1570 area.size.height= ICONSIZE;
1571 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1572 [img setScalesWhenResized: YES];
1573 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1574 imgView = [[NSImageView alloc] initWithFrame: area];
1575 [imgView setImage: img];
1576 [imgView setEditable: NO];
1578 [imgView autorelease];
1580 aStyle = NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask;
1584 [super initWithContentRect: contentRect styleMask: aStyle
1585 backing: backingType defer: flag];
1586 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1587 [contentView autorelease];
1589 [self setContentView: contentView];
1591 [[self contentView] setAutoresizesSubviews: YES];
1593 [[self contentView] addSubview: imgView];
1594 [self setTitle: @""];
1596 area.origin.x += ICONSIZE+2*SPACER;
1597 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1598 area.size.width = 400;
1599 area.size.height= TEXTHEIGHT;
1600 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1601 [[self contentView] addSubview: command];
1602 [command setStringValue: ns_app_name];
1603 [command setDrawsBackground: NO];
1604 [command setBezeled: NO];
1605 [command setSelectable: NO];
1606 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1608 /* area.origin.x = ICONSIZE+2*SPACER;
1609 area.origin.y = TEXTHEIGHT + 2*SPACER;
1610 area.size.width = 400;
1611 area.size.height= 2;
1612 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1613 [[self contentView] addSubview: tem];
1614 [tem setTitlePosition: NSNoTitle];
1615 [tem setAutoresizingMask: NSViewWidthSizable];*/
1617 /* area.origin.x = ICONSIZE+2*SPACER; */
1618 area.origin.y += TEXTHEIGHT+SPACER;
1619 area.size.width = 400;
1620 area.size.height= TEXTHEIGHT;
1621 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1622 [[self contentView] addSubview: title];
1623 [title setDrawsBackground: NO];
1624 [title setBezeled: NO];
1625 [title setSelectable: NO];
1626 [title setFont: [NSFont systemFontOfSize: 11.0]];
1628 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1629 [cell setBordered: NO];
1630 [cell setEnabled: NO];
1631 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1632 [cell setBezelStyle: NSRoundedBezelStyle];
1634 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1635 mode: NSHighlightModeMatrix
1638 numberOfColumns: 1];
1639 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1640 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1641 [matrix setIntercellSpacing: spacing];
1642 [matrix autorelease];
1644 [[self contentView] addSubview: matrix];
1645 [self setOneShot: YES];
1646 [self setReleasedWhenClosed: YES];
1647 [self setHidesOnDeactivate: YES];
1652 - (BOOL)windowShouldClose: (id)sender
1654 window_closed = YES;
1661 xfree (button_values);
1665 - (void)process_dialog: (Lisp_Object) list
1667 Lisp_Object item, lst = list;
1669 int buttons = 0, btnnr = 0;
1671 for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
1674 if (XTYPE (item) == Lisp_Cons)
1679 button_values = xmalloc (buttons * sizeof *button_values);
1681 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1684 if (XTYPE (item) == Lisp_String)
1686 [self addString: SSDATA (item) row: row++];
1688 else if (XTYPE (item) == Lisp_Cons)
1690 button_values[btnnr] = XCDR (item);
1691 [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
1694 else if (NILP (item))
1703 - (void)addButton: (char *)str value: (int)tag row: (int)row
1712 cell = [matrix cellAtRow: row column: cols-1];
1713 [cell setTarget: self];
1714 [cell setAction: @selector (clicked: )];
1715 [cell setTitle: [NSString stringWithUTF8String: str]];
1717 [cell setBordered: YES];
1718 [cell setEnabled: YES];
1722 - (void)addString: (char *)str row: (int)row
1731 cell = [matrix cellAtRow: row column: cols-1];
1732 [cell setTitle: [NSString stringWithUTF8String: str]];
1733 [cell setBordered: YES];
1734 [cell setEnabled: NO];
1745 - (void)clicked: sender
1747 NSArray *sellist = nil;
1750 sellist = [sender selectedCells];
1751 if ([sellist count] < 1)
1754 seltag = [[sellist objectAtIndex: 0] tag];
1755 dialog_return = button_values[seltag];
1760 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1765 if (XTYPE (contents) == Lisp_Cons)
1767 head = Fcar (contents);
1768 [self process_dialog: Fcdr (contents)];
1773 if (XTYPE (head) == Lisp_String)
1774 [title setStringValue:
1775 [NSString stringWithUTF8String: SSDATA (head)]];
1776 else if (isQ == YES)
1777 [title setStringValue: @"Question"];
1779 [title setStringValue: @"Information"];
1785 if (cols == 1 && rows > 1) /* Never told where to split */
1788 for (i = 0; i < rows/2; i++)
1790 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1791 atRow: i column: 1];
1792 [matrix removeRow: (rows+1)/2];
1798 NSSize csize = [matrix cellSize];
1799 if (csize.width < MINCELLWIDTH)
1801 csize.width = MINCELLWIDTH;
1802 [matrix setCellSize: csize];
1803 [matrix sizeToCells];
1808 [command sizeToFit];
1812 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1814 t.origin.x = r.origin.x;
1815 t.size.width = r.size.width;
1817 r = [command frame];
1818 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1820 t.origin.x = r.origin.x;
1821 t.size.width = r.size.width;
1825 s = [(NSView *)[self contentView] frame];
1826 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1827 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1828 [self setFrame: r display: NO];
1836 - (void)timeout_handler: (NSTimer *)timedEntry
1838 NSEvent *nxev = [NSEvent otherEventWithType: NSApplicationDefined
1839 location: NSMakePoint (0, 0)
1842 windowNumber: [[NSApp mainWindow] windowNumber]
1843 context: [NSApp context]
1849 /* We use sto because stopModal/abortModal out of the main loop does not
1850 seem to work in 10.6. But as we use stop we must send a real event so
1851 the stop is seen and acted upon. */
1853 [NSApp postEvent: nxev atStart: NO];
1856 - (Lisp_Object)runDialogAt: (NSPoint)p
1858 Lisp_Object ret = Qundefined;
1860 while (popup_activated_flag)
1863 EMACS_TIME next_time = timer_check ();
1865 if (EMACS_TIME_VALID_P (next_time))
1867 double time = EMACS_TIME_TO_DOUBLE (next_time);
1868 tmo = [NSTimer timerWithTimeInterval: time
1870 selector: @selector (timeout_handler:)
1873 [[NSRunLoop currentRunLoop] addTimer: tmo
1874 forMode: NSModalPanelRunLoopMode];
1877 dialog_return = Qundefined;
1878 [NSApp runModalForWindow: self];
1879 ret = dialog_return;
1882 if (tmo != nil) [tmo invalidate]; /* Cancels timer */
1887 if (EQ (ret, Qundefined) && window_closed)
1888 /* Make close button pressed equivalent to C-g. */
1889 Fsignal (Qquit, Qnil);
1897 /* ==========================================================================
1901 ========================================================================== */
1903 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1904 doc: /* Cause the NS menu to be re-calculated. */)
1907 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1912 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1913 doc: /* Pop up a dialog box and return user's selection.
1914 POSITION specifies which frame to use.
1915 This is normally a mouse button event or a window or frame.
1916 If POSITION is t, it means to use the frame the mouse is on.
1917 The dialog box appears in the middle of the specified frame.
1919 CONTENTS specifies the alternatives to display in the dialog box.
1920 It is a list of the form (DIALOG ITEM1 ITEM2...).
1921 Each ITEM is a cons cell (STRING . VALUE).
1922 The return value is VALUE from the chosen item.
1924 An ITEM may also be just a string--that makes a nonselectable item.
1925 An ITEM may also be nil--that means to put all preceding items
1926 on the left of the dialog box and all following items on the right.
1927 \(By default, approximately half appear on each side.)
1929 If HEADER is non-nil, the frame title for the box is "Information",
1930 otherwise it is "Question".
1932 If the user gets rid of the dialog box without making a valid choice,
1933 for instance using the window manager, then this produces a quit and
1934 `x-popup-dialog' does not return. */)
1935 (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1937 return ns_popup_dialog (position, contents, header);
1940 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1941 doc: /* Return t if a menu or popup dialog is active. */)
1944 return popup_activated () ? Qt : Qnil;
1947 /* ==========================================================================
1949 Lisp interface declaration
1951 ========================================================================== */
1954 syms_of_nsmenu (void)
1956 #ifndef NS_IMPL_COCOA
1957 /* Don't know how to keep track of this in Next/Open/Gnustep. Always
1958 update menus there. */
1961 defsubr (&Sx_popup_dialog);
1962 defsubr (&Sns_reset_menu);
1963 defsubr (&Smenu_or_popup_active_p);
1965 Qdebug_on_next_call = intern_c_string ("debug-on-next-call");
1966 staticpro (&Qdebug_on_next_call);