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>
48 #define MenuStagger 10.0
51 int menu_trace_num = 0;
52 #define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \
53 __FILE__, __LINE__, ++menu_trace_num)
59 /* Include lisp -> C common menu parsing code */
60 #define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
61 #include "nsmenu_common.c"
64 extern Lisp_Object Qundefined, Qmenu_enable, Qmenu_bar_update_hook;
65 extern Lisp_Object QCtoggle, QCradio;
67 Lisp_Object Qdebug_on_next_call;
68 extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
70 extern long context_menu_value;
71 EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
73 /* Nonzero means a menu is currently active. */
74 static int popup_activated_flag;
76 /* Nonzero means we are tracking and updating menus. */
77 static int trackingMenu;
80 /* NOTE: toolbar implementation is at end,
81 following complete menu implementation. */
84 /* ==========================================================================
86 Menu: Externally-called functions
88 ========================================================================== */
91 /* Supposed to discard menubar and free storage. Since we share the
92 menubar among frames and update its context for the focused window,
93 there is nothing to do here. */
95 free_frame_menubar (struct frame *f)
102 popup_activated (void)
104 return popup_activated_flag;
108 /* --------------------------------------------------------------------------
109 Update menubar. Three cases:
110 1) ! deep_p, submenu = nil: Fresh switch onto a frame -- either set up
111 just top-level menu strings (OS X), or goto case (2) (GNUstep).
112 2) deep_p, submenu = nil: Recompute all submenus.
113 3) deep_p, submenu = non-nil: Update contents of a single submenu.
114 -------------------------------------------------------------------------- */
116 ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
118 NSAutoreleasePool *pool;
119 id menu = [NSApp mainMenu];
120 static EmacsMenu *last_submenu = nil;
124 widget_value *wv, *first_wv, *prev_wv = 0;
132 NSTRACE (ns_update_menubar);
134 if (f != SELECTED_FRAME ())
136 XSETFRAME (Vmenu_updating_frame, f);
137 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
140 pool = [[NSAutoreleasePool alloc] init];
142 /* Menu may have been created automatically; if so, discard it. */
143 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
151 menu = [[EmacsMenu alloc] initWithTitle: ns_app_name];
155 { /* close up anything on there */
156 id attMenu = [menu attachedMenu];
163 t = -(1000*tb.time+tb.millitm);
166 #ifdef NS_IMPL_GNUSTEP
167 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
172 /* Fully parse one or more of the submenus. */
174 int *submenu_start, *submenu_end;
175 bool *submenu_top_level_items;
176 int *submenu_n_panes;
177 struct buffer *prev = current_buffer;
179 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
180 int previous_menu_items_used = f->menu_bar_items_used;
181 Lisp_Object *previous_items
182 = alloca (previous_menu_items_used * sizeof *previous_items);
184 /* lisp preliminaries */
185 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
186 specbind (Qinhibit_quit, Qt);
187 specbind (Qdebug_on_next_call, Qnil);
188 record_unwind_save_match_data ();
189 if (NILP (Voverriding_local_map_menu_flag))
191 specbind (Qoverriding_terminal_local_map, Qnil);
192 specbind (Qoverriding_local_map, Qnil);
194 set_buffer_internal_1 (XBUFFER (buffer));
196 /* TODO: for some reason this is not needed in other terms,
197 but some menu updates call Info-extract-pointer which causes
198 abort-on-error if waiting-for-input. Needs further investigation. */
199 owfi = waiting_for_input;
200 waiting_for_input = 0;
202 /* lucid hook and possible reset */
203 safe_run_hooks (Qactivate_menubar_hook);
204 if (! NILP (Vlucid_menu_bar_dirty_flag))
205 call0 (Qrecompute_lucid_menubar);
206 safe_run_hooks (Qmenu_bar_update_hook);
207 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
209 /* Now ready to go */
210 items = FRAME_MENU_BAR_ITEMS (f);
212 /* Save the frame's previous menu bar contents data */
213 if (previous_menu_items_used)
214 memcpy (previous_items, aref_addr (f->menu_bar_vector, 0),
215 previous_menu_items_used * sizeof (Lisp_Object));
217 /* parse stage 1: extract from lisp */
220 menu_items = f->menu_bar_vector;
221 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
222 submenu_start = alloca (ASIZE (items) * sizeof *submenu_start);
223 submenu_end = alloca (ASIZE (items) * sizeof *submenu_end);
224 submenu_n_panes = alloca (ASIZE (items) * sizeof *submenu_n_panes);
225 submenu_top_level_items = alloca (ASIZE (items)
226 * sizeof *submenu_top_level_items);
228 for (i = 0; i < ASIZE (items); i += 4)
230 Lisp_Object key, string, maps;
232 key = AREF (items, i);
233 string = AREF (items, i + 1);
234 maps = AREF (items, i + 2);
238 /* FIXME: we'd like to only parse the needed submenu, but this
239 was causing crashes in the _common parsing code.. need to make
240 sure proper initialization done.. */
241 /* if (submenu && strcmp ([[submenu title] UTF8String], SSDATA (string)))
244 submenu_start[i] = menu_items_used;
246 menu_items_n_panes = 0;
247 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
248 submenu_n_panes[i] = menu_items_n_panes;
249 submenu_end[i] = menu_items_used;
253 finish_menu_items ();
254 waiting_for_input = owfi;
257 if (submenu && n == 0)
259 /* should have found a menu for this one but didn't */
260 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
261 [[submenu title] UTF8String]);
262 discard_menu_items ();
263 unbind_to (specpdl_count, Qnil);
269 /* parse stage 2: insert into lucid 'widget_value' structures
270 [comments in other terms say not to evaluate lisp code here] */
271 wv = xmalloc_widget_value ();
272 wv->name = "menubar";
275 wv->button_type = BUTTON_TYPE_NONE;
279 for (i = 0; i < 4*n; i += 4)
281 menu_items_n_panes = submenu_n_panes[i];
282 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
283 submenu_top_level_items[i]);
287 first_wv->contents = wv;
288 /* Don't set wv->name here; GC during the loop might relocate it. */
290 wv->button_type = BUTTON_TYPE_NONE;
294 set_buffer_internal_1 (prev);
296 /* Compare the new menu items with previous, and leave off if no change */
297 /* FIXME: following other terms here, but seems like this should be
298 done before parse stage 2 above, since its results aren't used */
299 if (previous_menu_items_used
300 && (!submenu || (submenu && submenu == last_submenu))
301 && menu_items_used == previous_menu_items_used)
303 for (i = 0; i < previous_menu_items_used; i++)
304 /* FIXME: this ALWAYS fails on Buffers menu items.. something
305 about their strings causes them to change every time, so we
306 double-check failures */
307 if (!EQ (previous_items[i], AREF (menu_items, i)))
308 if (!(STRINGP (previous_items[i])
309 && STRINGP (AREF (menu_items, i))
310 && !strcmp (SSDATA (previous_items[i]),
311 SSDATA (AREF (menu_items, i)))))
313 if (i == previous_menu_items_used)
319 t += 1000*tb.time+tb.millitm;
320 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
323 free_menubar_widget_value_tree (first_wv);
324 discard_menu_items ();
325 unbind_to (specpdl_count, Qnil);
331 /* The menu items are different, so store them in the frame */
332 /* FIXME: this is not correct for single-submenu case */
333 fset_menu_bar_vector (f, menu_items);
334 f->menu_bar_items_used = menu_items_used;
336 /* Calls restore_menu_items, etc., as they were outside */
337 unbind_to (specpdl_count, Qnil);
339 /* Parse stage 2a: now GC cannot happen during the lifetime of the
340 widget_value, so it's safe to store data from a Lisp_String */
341 wv = first_wv->contents;
342 for (i = 0; i < ASIZE (items); i += 4)
345 string = AREF (items, i + 1);
349 wv->name = SSDATA (string);
350 update_submenu_strings (wv->contents);
354 /* Now, update the NS menu; if we have a submenu, use that, otherwise
355 create a new menu for each sub and fill it. */
358 const char *submenuTitle = [[submenu title] UTF8String];
359 for (wv = first_wv->contents; wv; wv = wv->next)
361 if (!strcmp (submenuTitle, wv->name))
363 [submenu fillWithWidgetValue: wv->contents];
364 last_submenu = submenu;
371 [menu fillWithWidgetValue: first_wv->contents];
377 static int n_previous_strings = 0;
378 static char previous_strings[100][10];
379 static struct frame *last_f = NULL;
383 wv = xmalloc_widget_value ();
384 wv->name = "menubar";
387 wv->button_type = BUTTON_TYPE_NONE;
391 /* Make widget-value tree w/ just the top level menu bar strings */
392 items = FRAME_MENU_BAR_ITEMS (f);
395 free_menubar_widget_value_tree (first_wv);
402 /* check if no change.. this mechanism is a bit rough, but ready */
403 n = ASIZE (items) / 4;
404 if (f == last_f && n_previous_strings == n)
406 for (i = 0; i<n; i++)
408 string = AREF (items, 4*i+1);
410 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
414 if (previous_strings[i][0])
419 else if (memcmp (previous_strings[i], SDATA (string),
420 min (10, SBYTES (string) + 1)))
426 free_menubar_widget_value_tree (first_wv);
434 for (i = 0; i < ASIZE (items); i += 4)
436 string = AREF (items, i + 1);
441 memcpy (previous_strings[i/4], SDATA (string),
442 min (10, SBYTES (string) + 1));
444 wv = xmalloc_widget_value ();
445 wv->name = SSDATA (string);
448 wv->button_type = BUTTON_TYPE_NONE;
450 wv->call_data = (void *) (intptr_t) (-1);
453 /* we'll update the real copy under app menu when time comes */
454 if (!strcmp ("Services", wv->name))
456 /* but we need to make sure it will update on demand */
457 [svcsMenu setFrame: f];
461 [menu addSubmenuWithTitle: wv->name forFrame: f];
466 first_wv->contents = wv;
472 n_previous_strings = n;
474 n_previous_strings = 0;
477 free_menubar_widget_value_tree (first_wv);
482 t += 1000*tb.time+tb.millitm;
483 fprintf (stderr, "Menu update took %ld msec.\n", t);
488 [NSApp setMainMenu: menu];
496 /* Main emacs core entry point for menubar menus: called to indicate that the
497 frame's menus have changed, and the *step representation should be updated
500 set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
502 ns_update_menubar (f, deep_p, nil);
506 x_activate_menubar (struct frame *f)
508 NSArray *a = [[NSApp mainMenu] itemArray];
509 /* Update each submenu separately so ns_update_menubar doesn't reset
512 while (i < [a count])
514 EmacsMenu *menu = (EmacsMenu *)[[a objectAtIndex:i] submenu];
515 const char *title = [[menu title] UTF8String];
516 if (strcmp (title, ns_get_pending_menu_title ()) == 0)
518 ns_update_menubar (f, true, menu);
523 ns_check_pending_open_menu ();
529 /* ==========================================================================
531 Menu: class implementation
533 ========================================================================== */
536 /* Menu that can define itself from Emacs "widget_value"s and will lazily
537 update itself when user clicked. Based on Carbon/AppKit implementation
538 by Yamamoto Mitsuharu. */
539 @implementation EmacsMenu
541 /* override designated initializer */
542 - initWithTitle: (NSString *)title
544 if ((self = [super initWithTitle: title]))
545 [self setAutoenablesItems: NO];
550 /* used for top-level */
551 - initWithTitle: (NSString *)title frame: (struct frame *)f
553 [self initWithTitle: title];
556 [self setDelegate: self];
562 - (void)setFrame: (struct frame *)f
568 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
569 extern NSString *NSMenuDidBeginTrackingNotification;
574 -(void)trackingNotification:(NSNotification *)notification
576 /* Update menu in menuNeedsUpdate only while tracking menus. */
577 trackingMenu = ([notification name] == NSMenuDidBeginTrackingNotification
581 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
582 - (void)menuWillOpen:(NSMenu *)menu
584 ns_check_menu_open (menu);
590 /* delegate method called when a submenu is being opened: run a 'deep' call
591 to set_frame_menubar */
592 - (void)menuNeedsUpdate: (NSMenu *)menu
594 if (!FRAME_LIVE_P (frame))
597 /* Cocoa/Carbon will request update on every keystroke
598 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
599 since key equivalents are handled through emacs.
600 On Leopard, even keystroke events generate SystemDefined event.
601 Third-party applications that enhance mouse / trackpad
602 interaction, or also VNC/Remote Desktop will send events
603 of type AppDefined rather than SysDefined.
604 Menus will fail to show up if they haven't been initialized.
605 AppDefined events may lack timing data.
607 Thus, we rely on the didBeginTrackingNotification notification
608 as above to indicate the need for updates.
609 From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
610 key press case, NSMenuPropertyItemImage (e.g.) won't be set.
612 if (trackingMenu == 0)
614 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
615 #if ! defined(NS_IMPL_COCOA) || \
616 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
617 /* Don't know how to do this for anything other than OSX >= 10.5
618 This is wrong, as it might run Lisp code in the event loop. */
619 ns_update_menubar (frame, true, self);
624 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
626 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
627 && FRAME_NS_VIEW (SELECTED_FRAME ()))
628 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
633 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
634 into an accelerator string. We are only able to display a single character
635 for an accelerator, together with an optional modifier combination. (Under
636 Carbon more control was possible, but in Cocoa multi-char strings passed to
637 NSMenuItem get ignored. For now we try to display a super-single letter
638 combo, and return the others as strings to be appended to the item title.
639 (This is signaled by setting keyEquivModMask to 0 for now.) */
640 -(NSString *)parseKeyEquiv: (const char *)key
642 const char *tpos = key;
643 keyEquivModMask = NSCommandKeyMask;
645 if (!key || !strlen (key))
648 while (*tpos == ' ' || *tpos == '(')
650 if ((*tpos == 's') && (*(tpos+1) == '-'))
652 return [NSString stringWithFormat: @"%c", tpos[2]];
654 keyEquivModMask = 0; /* signal */
655 return [NSString stringWithUTF8String: tpos];
659 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
662 widget_value *wv = (widget_value *)wvptr;
664 if (menu_separator_name_p (wv->name))
666 item = [NSMenuItem separatorItem];
667 [self addItem: item];
671 NSString *title, *keyEq;
672 title = [NSString stringWithUTF8String: wv->name];
674 title = @"< ? >"; /* (get out in the open so we know about it) */
676 keyEq = [self parseKeyEquiv: wv->key];
678 /* OS X just ignores modifier strings longer than one character */
679 if (keyEquivModMask == 0)
680 title = [title stringByAppendingFormat: @" (%@)", keyEq];
683 item = [self addItemWithTitle: (NSString *)title
684 action: @selector (menuDown:)
685 keyEquivalent: keyEq];
686 [item setKeyEquivalentModifierMask: keyEquivModMask];
688 [item setEnabled: wv->enabled];
690 /* Draw radio buttons and tickboxes */
691 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
692 wv->button_type == BUTTON_TYPE_RADIO))
693 [item setState: NSOnState];
695 [item setState: NSOffState];
697 [item setTag: (NSInteger)wv->call_data];
709 for (n = [self numberOfItems]-1; n >= 0; n--)
711 NSMenuItem *item = [self itemAtIndex: n];
712 NSString *title = [item title];
713 if (([title length] == 0 /* OSX 10.5 */
714 || [ns_app_name isEqualToString: title] /* from 10.6 on */
715 || [@"Apple" isEqualToString: title]) /* older */
716 && ![item isSeparatorItem])
718 [self removeItemAtIndex: n];
723 - (void)fillWithWidgetValue: (void *)wvptr
725 widget_value *wv = (widget_value *)wvptr;
727 /* clear existing contents */
728 [self setMenuChangedMessagesEnabled: NO];
731 /* add new contents */
732 for (; wv != NULL; wv = wv->next)
734 NSMenuItem *item = [self addItemWithWidgetValue: wv];
738 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
740 [self setSubmenu: submenu forItem: item];
741 [submenu fillWithWidgetValue: wv->contents];
743 [item setAction: nil];
747 [self setMenuChangedMessagesEnabled: YES];
748 #ifdef NS_IMPL_GNUSTEP
749 if ([[self window] isVisible])
755 /* adds an empty submenu and returns it */
756 - (EmacsMenu *)addSubmenuWithTitle: (const char *)title forFrame: (struct frame *)f
758 NSString *titleStr = [NSString stringWithUTF8String: title];
759 NSMenuItem *item = [self addItemWithTitle: titleStr
760 action: nil /*@selector (menuDown:) */
762 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
763 [self setSubmenu: submenu forItem: item];
768 /* run a menu in popup mode */
769 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
770 keymaps: (bool)keymaps
772 EmacsView *view = FRAME_NS_VIEW (f);
776 /* p = [view convertPoint:p fromView: nil]; */
777 p.y = NSHeight ([view frame]) - p.y;
778 e = [[view window] currentEvent];
779 event = [NSEvent mouseEventWithType: NSRightMouseDown
782 timestamp: [e timestamp]
783 windowNumber: [[view window] windowNumber]
785 eventNumber: 0/*[e eventNumber] */
789 context_menu_value = -1;
790 [NSMenu popUpContextMenu: self withEvent: event forView: view];
791 retVal = context_menu_value;
792 context_menu_value = 0;
794 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
802 /* ==========================================================================
804 Context Menu: implementing functions
806 ========================================================================== */
809 ns_menu_show (FRAME_PTR f, int x, int y, bool for_click, bool keymaps,
810 Lisp_Object title, const char **error)
815 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
816 widget_value *wv, *first_wv = 0;
820 /* now parse stage 2 as in ns_update_menubar */
821 wv = xmalloc_widget_value ();
822 wv->name = "contextmenu";
825 wv->button_type = BUTTON_TYPE_NONE;
830 /* FIXME: a couple of one-line differences prevent reuse */
831 wv = digest_single_submenu (0, menu_items_used, 0);
834 widget_value *save_wv = 0, *prev_wv = 0;
835 widget_value **submenu_stack
836 = alloca (menu_items_used * sizeof *submenu_stack);
837 /* Lisp_Object *subprefix_stack
838 = alloca (menu_items_used * sizeof *subprefix_stack); */
839 int submenu_depth = 0;
843 /* Loop over all panes and items, filling in the tree. */
845 while (i < menu_items_used)
847 if (EQ (AREF (menu_items, i), Qnil))
849 submenu_stack[submenu_depth++] = save_wv;
855 else if (EQ (AREF (menu_items, i), Qlambda))
858 save_wv = submenu_stack[--submenu_depth];
862 else if (EQ (AREF (menu_items, i), Qt)
863 && submenu_depth != 0)
864 i += MENU_ITEMS_PANE_LENGTH;
865 /* Ignore a nil in the item list.
866 It's meaningful only for dialog boxes. */
867 else if (EQ (AREF (menu_items, i), Qquote))
869 else if (EQ (AREF (menu_items, i), Qt))
871 /* Create a new pane. */
872 Lisp_Object pane_name, prefix;
873 const char *pane_string;
875 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
876 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
878 #ifndef HAVE_MULTILINGUAL_MENU
879 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
881 pane_name = ENCODE_MENU_STRING (pane_name);
882 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
885 pane_string = (NILP (pane_name)
886 ? "" : SSDATA (pane_name));
887 /* If there is just one top-level pane, put all its items directly
888 under the top-level menu. */
889 if (menu_items_n_panes == 1)
892 /* If the pane has a meaningful name,
893 make the pane a top-level menu item
894 with its items as a submenu beneath it. */
895 if (!keymaps && strcmp (pane_string, ""))
897 wv = xmalloc_widget_value ();
901 first_wv->contents = wv;
902 wv->name = pane_string;
903 if (keymaps && !NILP (prefix))
907 wv->button_type = BUTTON_TYPE_NONE;
918 i += MENU_ITEMS_PANE_LENGTH;
922 /* Create a new item within current pane. */
923 Lisp_Object item_name, enable, descrip, def, type, selected, help;
924 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
925 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
926 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
927 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
928 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
929 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
930 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
932 #ifndef HAVE_MULTILINGUAL_MENU
933 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
935 item_name = ENCODE_MENU_STRING (item_name);
936 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
939 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
941 descrip = ENCODE_MENU_STRING (descrip);
942 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
944 #endif /* not HAVE_MULTILINGUAL_MENU */
946 wv = xmalloc_widget_value ();
950 save_wv->contents = wv;
951 wv->name = SSDATA (item_name);
953 wv->key = SSDATA (descrip);
955 /* If this item has a null value,
956 make the call_data null so that it won't display a box
957 when the mouse is on it. */
958 wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
959 wv->enabled = !NILP (enable);
962 wv->button_type = BUTTON_TYPE_NONE;
963 else if (EQ (type, QCtoggle))
964 wv->button_type = BUTTON_TYPE_TOGGLE;
965 else if (EQ (type, QCradio))
966 wv->button_type = BUTTON_TYPE_RADIO;
970 wv->selected = !NILP (selected);
972 if (! STRINGP (help))
979 i += MENU_ITEMS_ITEM_LENGTH;
987 widget_value *wv_title = xmalloc_widget_value ();
988 widget_value *wv_sep = xmalloc_widget_value ();
990 /* Maybe replace this separator with a bitmap or owner-draw item
991 so that it looks better. Having two separators looks odd. */
993 wv_sep->next = first_wv->contents;
996 #ifndef HAVE_MULTILINGUAL_MENU
997 if (STRING_MULTIBYTE (title))
998 title = ENCODE_MENU_STRING (title);
1001 wv_title->name = SSDATA (title);
1002 wv_title->enabled = NO;
1003 wv_title->button_type = BUTTON_TYPE_NONE;
1004 wv_title->help = Qnil;
1005 wv_title->next = wv_sep;
1006 first_wv->contents = wv_title;
1009 pmenu = [[EmacsMenu alloc] initWithTitle:
1010 [NSString stringWithUTF8String: SSDATA (title)]];
1011 [pmenu fillWithWidgetValue: first_wv->contents];
1012 free_menubar_widget_value_tree (first_wv);
1013 unbind_to (specpdl_count, Qnil);
1015 popup_activated_flag = 1;
1016 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
1017 popup_activated_flag = 0;
1018 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1024 /* ==========================================================================
1026 Toolbar: externally-called functions
1028 ========================================================================== */
1031 free_frame_tool_bar (FRAME_PTR f)
1032 /* --------------------------------------------------------------------------
1033 Under NS we just hide the toolbar until it might be needed again.
1034 -------------------------------------------------------------------------- */
1037 [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
1038 FRAME_TOOLBAR_HEIGHT (f) = 0;
1043 update_frame_tool_bar (FRAME_PTR f)
1044 /* --------------------------------------------------------------------------
1045 Update toolbar contents
1046 -------------------------------------------------------------------------- */
1049 EmacsView *view = FRAME_NS_VIEW (f);
1050 NSWindow *window = [view window];
1051 EmacsToolbar *toolbar = [view toolbar];
1054 [toolbar clearActive];
1056 /* update EmacsToolbar as in GtkUtils, build items list */
1057 for (i = 0; i < f->n_tool_bar_items; ++i)
1059 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1060 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1062 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1067 Lisp_Object helpObj;
1068 const char *helpText;
1070 /* If image is a vector, choose the image according to the
1072 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1073 if (VECTORP (image))
1075 /* NS toolbar auto-computes disabled and selected images */
1076 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1077 eassert (ASIZE (image) >= idx);
1078 image = AREF (image, idx);
1084 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1086 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1087 helpText = NILP (helpObj) ? "" : SSDATA (helpObj);
1089 /* Ignore invalid image specifications. */
1090 if (!valid_image_p (image))
1092 /* Don't log anything, GNUS makes invalid images all the time. */
1096 img_id = lookup_image (f, image);
1097 img = IMAGE_FROM_ID (f, img_id);
1098 prepare_image_for_display (f, img);
1100 if (img->load_failed_p || img->pixmap == nil)
1102 NSLog (@"Could not prepare toolbar image for display.");
1106 [toolbar addDisplayItemWithImage: img->pixmap idx: i helpText: helpText
1107 enabled: enabled_p];
1111 if (![toolbar isVisible])
1112 [toolbar setVisible: YES];
1114 if ([toolbar changed])
1116 /* inform app that toolbar has changed */
1117 NSDictionary *dict = [toolbar configurationDictionary];
1118 NSMutableDictionary *newDict = [dict mutableCopy];
1119 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1121 while ((key = [keys nextObject]) != nil)
1123 NSObject *val = [dict objectForKey: key];
1124 if ([val isKindOfClass: [NSArray class]])
1127 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1132 [toolbar setConfigurationFromDictionary: newDict];
1136 FRAME_TOOLBAR_HEIGHT (f) =
1137 NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1138 - FRAME_NS_TITLEBAR_HEIGHT (f);
1139 if (FRAME_TOOLBAR_HEIGHT (f) < 0) // happens if frame is fullscreen.
1140 FRAME_TOOLBAR_HEIGHT (f) = 0;
1145 /* ==========================================================================
1147 Toolbar: class implementation
1149 ========================================================================== */
1151 @implementation EmacsToolbar
1153 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1155 self = [super initWithIdentifier: identifier];
1157 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1158 [self setSizeMode: NSToolbarSizeModeSmall];
1159 [self setDelegate: self];
1160 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1161 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1162 prevEnablement = enablement = 0L;
1168 [prevIdentifiers release];
1169 [activeIdentifiers release];
1170 [identifierToItem release];
1174 - (void) clearActive
1176 [prevIdentifiers release];
1177 prevIdentifiers = [activeIdentifiers copy];
1178 [activeIdentifiers removeAllObjects];
1179 prevEnablement = enablement;
1185 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1186 enablement == prevEnablement ? NO : YES;
1189 - (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx
1190 helpText: (const char *)help enabled: (BOOL)enabled
1192 /* 1) come up w/identifier */
1193 NSString *identifier
1194 = [NSString stringWithFormat: @"%u", [img hash]];
1196 /* 2) create / reuse item */
1197 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1200 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1202 [item setImage: img];
1203 [item setToolTip: [NSString stringWithUTF8String: help]];
1204 [item setTarget: emacsView];
1205 [item setAction: @selector (toolbarClicked:)];
1209 [item setEnabled: enabled];
1211 /* 3) update state */
1212 [identifierToItem setObject: item forKey: identifier];
1213 [activeIdentifiers addObject: identifier];
1214 enablement = (enablement << 1) | (enabled == YES);
1217 /* This overrides super's implementation, which automatically sets
1218 all items to enabled state (for some reason). */
1219 - (void)validateVisibleItems { }
1222 /* delegate methods */
1224 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1225 itemForItemIdentifier: (NSString *)itemIdentifier
1226 willBeInsertedIntoToolbar: (BOOL)flag
1228 /* look up NSToolbarItem by identifier and return... */
1229 return [identifierToItem objectForKey: itemIdentifier];
1232 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1234 /* return entire set.. */
1235 return activeIdentifiers;
1238 /* for configuration palette (not yet supported) */
1239 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1241 /* return entire set... */
1242 return [identifierToItem allKeys];
1245 /* optional and unneeded */
1246 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1247 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1248 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1250 @end /* EmacsToolbar */
1254 /* ==========================================================================
1256 Tooltip: class implementation
1258 ========================================================================== */
1260 /* Needed because NeXTstep does not provide enough control over tooltip
1262 @implementation EmacsTooltip
1266 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1267 blue: 0.792 alpha: 0.95];
1268 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1269 NSFont *sfont = [font screenFont];
1270 int height = [sfont ascender] - [sfont descender];
1271 /*[font boundingRectForFont].size.height; */
1272 NSRect r = NSMakeRect (0, 0, 100, height+6);
1274 textField = [[NSTextField alloc] initWithFrame: r];
1275 [textField setFont: font];
1276 [textField setBackgroundColor: col];
1278 [textField setEditable: NO];
1279 [textField setSelectable: NO];
1280 [textField setBordered: NO];
1281 [textField setBezeled: NO];
1282 [textField setDrawsBackground: YES];
1284 win = [[NSWindow alloc]
1285 initWithContentRect: [textField frame]
1287 backing: NSBackingStoreBuffered
1289 [win setHasShadow: YES];
1290 [win setReleasedWhenClosed: NO];
1291 [win setDelegate: self];
1292 [[win contentView] addSubview: textField];
1293 /* [win setBackgroundColor: col]; */
1294 [win setOpaque: NO];
1303 [textField release];
1307 - (void) setText: (char *)text
1309 NSString *str = [NSString stringWithUTF8String: text];
1310 NSRect r = [textField frame];
1313 [textField setStringValue: str];
1314 tooltipDims = [[textField cell] cellSize];
1316 r.size.width = tooltipDims.width;
1317 r.size.height = tooltipDims.height;
1318 [textField setFrame: r];
1321 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1323 NSRect wr = [win frame];
1325 wr.origin = NSMakePoint (x, y);
1326 wr.size = [textField frame].size;
1328 [win setFrame: wr display: YES];
1329 [win setLevel: NSPopUpMenuWindowLevel];
1330 [win orderFront: self];
1332 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1333 selector: @selector (hide)
1334 userInfo: nil repeats: NO];
1343 if ([timer isValid])
1352 return timer != nil;
1357 return [textField frame];
1360 @end /* EmacsTooltip */
1364 /* ==========================================================================
1366 Popup Dialog: implementing functions
1368 ========================================================================== */
1372 NSAutoreleasePool *pool;
1373 EmacsDialogPanel *dialog;
1377 pop_down_menu (Lisp_Object arg)
1379 struct Popdown_data *unwind_data = XSAVE_POINTER (arg, 0);
1382 if (popup_activated_flag)
1384 EmacsDialogPanel *panel = unwind_data->dialog;
1385 popup_activated_flag = 0;
1387 [unwind_data->pool release];
1388 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1391 xfree (unwind_data);
1399 ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1402 Lisp_Object window, tem, title;
1406 NSAutoreleasePool *pool;
1408 NSTRACE (x-popup-dialog);
1410 isQ = NILP (header);
1412 if (EQ (position, Qt)
1413 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1414 || EQ (XCAR (position), Qtool_bar))))
1416 window = selected_window;
1418 else if (CONSP (position))
1421 tem = Fcar (position);
1422 if (XTYPE (tem) == Lisp_Cons)
1423 window = Fcar (Fcdr (position));
1426 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1427 window = Fcar (tem); /* POSN_WINDOW (tem) */
1430 else if (WINDOWP (position) || FRAMEP (position))
1437 if (FRAMEP (window))
1438 f = XFRAME (window);
1439 else if (WINDOWP (window))
1441 CHECK_LIVE_WINDOW (window);
1442 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1445 CHECK_WINDOW (window);
1447 check_window_system (f);
1449 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1450 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1452 title = Fcar (contents);
1453 CHECK_STRING (title);
1455 if (NILP (Fcar (Fcdr (contents))))
1456 /* No buttons specified, add an "Ok" button so users can pop down
1458 contents = Fcons (title, Fcons (Fcons (build_string ("Ok"), Qt), Qnil));
1461 pool = [[NSAutoreleasePool alloc] init];
1462 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1466 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1467 struct Popdown_data *unwind_data = xmalloc (sizeof (*unwind_data));
1469 unwind_data->pool = pool;
1470 unwind_data->dialog = dialog;
1472 record_unwind_protect (pop_down_menu, make_save_pointer (unwind_data));
1473 popup_activated_flag = 1;
1474 tem = [dialog runDialogAt: p];
1475 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1484 /* ==========================================================================
1486 Popup Dialog: class implementation
1488 ========================================================================== */
1490 @interface FlippedView : NSView
1495 @implementation FlippedView
1502 @implementation EmacsDialogPanel
1505 #define ICONSIZE 64.0
1506 #define TEXTHEIGHT 20.0
1507 #define MINCELLWIDTH 90.0
1509 - initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
1510 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1512 NSSize spacing = {SPACER, SPACER};
1515 NSImageView *imgView;
1516 FlippedView *contentView;
1519 dialog_return = Qundefined;
1520 button_values = NULL;
1521 area.origin.x = 3*SPACER;
1522 area.origin.y = 2*SPACER;
1523 area.size.width = ICONSIZE;
1524 area.size.height= ICONSIZE;
1525 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1526 [img setScalesWhenResized: YES];
1527 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1528 imgView = [[NSImageView alloc] initWithFrame: area];
1529 [imgView setImage: img];
1530 [imgView setEditable: NO];
1532 [imgView autorelease];
1534 aStyle = NSTitledWindowMask;
1538 [super initWithContentRect: contentRect styleMask: aStyle
1539 backing: backingType defer: flag];
1540 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1541 [contentView autorelease];
1543 [self setContentView: contentView];
1545 [[self contentView] setAutoresizesSubviews: YES];
1547 [[self contentView] addSubview: imgView];
1548 [self setTitle: @""];
1550 area.origin.x += ICONSIZE+2*SPACER;
1551 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1552 area.size.width = 400;
1553 area.size.height= TEXTHEIGHT;
1554 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1555 [[self contentView] addSubview: command];
1556 [command setStringValue: ns_app_name];
1557 [command setDrawsBackground: NO];
1558 [command setBezeled: NO];
1559 [command setSelectable: NO];
1560 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1562 /* area.origin.x = ICONSIZE+2*SPACER;
1563 area.origin.y = TEXTHEIGHT + 2*SPACER;
1564 area.size.width = 400;
1565 area.size.height= 2;
1566 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1567 [[self contentView] addSubview: tem];
1568 [tem setTitlePosition: NSNoTitle];
1569 [tem setAutoresizingMask: NSViewWidthSizable];*/
1571 /* area.origin.x = ICONSIZE+2*SPACER; */
1572 area.origin.y += TEXTHEIGHT+SPACER;
1573 area.size.width = 400;
1574 area.size.height= TEXTHEIGHT;
1575 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1576 [[self contentView] addSubview: title];
1577 [title setDrawsBackground: NO];
1578 [title setBezeled: NO];
1579 [title setSelectable: NO];
1580 [title setFont: [NSFont systemFontOfSize: 11.0]];
1582 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1583 [cell setBordered: NO];
1584 [cell setEnabled: NO];
1585 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1586 [cell setBezelStyle: NSRoundedBezelStyle];
1588 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1589 mode: NSHighlightModeMatrix
1592 numberOfColumns: 1];
1593 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1594 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1595 [matrix setIntercellSpacing: spacing];
1596 [matrix autorelease];
1598 [[self contentView] addSubview: matrix];
1599 [self setOneShot: YES];
1600 [self setReleasedWhenClosed: YES];
1601 [self setHidesOnDeactivate: YES];
1603 NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask];
1609 - (BOOL)windowShouldClose: (id)sender
1611 window_closed = YES;
1618 xfree (button_values);
1622 - (void)process_dialog: (Lisp_Object) list
1624 Lisp_Object item, lst = list;
1626 int buttons = 0, btnnr = 0;
1628 for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
1631 if (XTYPE (item) == Lisp_Cons)
1636 button_values = (Lisp_Object *) xmalloc (buttons * sizeof (*button_values));
1638 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1641 if (XTYPE (item) == Lisp_String)
1643 [self addString: SSDATA (item) row: row++];
1645 else if (XTYPE (item) == Lisp_Cons)
1647 button_values[btnnr] = XCDR (item);
1648 [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
1651 else if (NILP (item))
1660 - (void)addButton: (char *)str value: (int)tag row: (int)row
1669 cell = [matrix cellAtRow: row column: cols-1];
1670 [cell setTarget: self];
1671 [cell setAction: @selector (clicked: )];
1672 [cell setTitle: [NSString stringWithUTF8String: str]];
1674 [cell setBordered: YES];
1675 [cell setEnabled: YES];
1679 - (void)addString: (char *)str row: (int)row
1688 cell = [matrix cellAtRow: row column: cols-1];
1689 [cell setTitle: [NSString stringWithUTF8String: str]];
1690 [cell setBordered: YES];
1691 [cell setEnabled: NO];
1702 - (void)clicked: sender
1704 NSArray *sellist = nil;
1707 sellist = [sender selectedCells];
1708 if ([sellist count] < 1)
1711 seltag = [[sellist objectAtIndex: 0] tag];
1712 dialog_return = button_values[seltag];
1717 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1722 if (XTYPE (contents) == Lisp_Cons)
1724 head = Fcar (contents);
1725 [self process_dialog: Fcdr (contents)];
1730 if (XTYPE (head) == Lisp_String)
1731 [title setStringValue:
1732 [NSString stringWithUTF8String: SSDATA (head)]];
1733 else if (isQ == YES)
1734 [title setStringValue: @"Question"];
1736 [title setStringValue: @"Information"];
1742 if (cols == 1 && rows > 1) /* Never told where to split */
1745 for (i = 0; i < rows/2; i++)
1747 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1748 atRow: i column: 1];
1749 [matrix removeRow: (rows+1)/2];
1755 NSSize csize = [matrix cellSize];
1756 if (csize.width < MINCELLWIDTH)
1758 csize.width = MINCELLWIDTH;
1759 [matrix setCellSize: csize];
1760 [matrix sizeToCells];
1765 [command sizeToFit];
1769 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1771 t.origin.x = r.origin.x;
1772 t.size.width = r.size.width;
1774 r = [command frame];
1775 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1777 t.origin.x = r.origin.x;
1778 t.size.width = r.size.width;
1782 s = [(NSView *)[self contentView] frame];
1783 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1784 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1785 [self setFrame: r display: NO];
1793 - (void)timeout_handler: (NSTimer *)timedEntry
1795 NSEvent *nxev = [NSEvent otherEventWithType: NSApplicationDefined
1796 location: NSMakePoint (0, 0)
1799 windowNumber: [[NSApp mainWindow] windowNumber]
1800 context: [NSApp context]
1806 /* We use sto because stopModal/abortModal out of the main loop does not
1807 seem to work in 10.6. But as we use stop we must send a real event so
1808 the stop is seen and acted upon. */
1810 [NSApp postEvent: nxev atStart: NO];
1813 - (Lisp_Object)runDialogAt: (NSPoint)p
1815 Lisp_Object ret = Qundefined;
1817 while (popup_activated_flag)
1820 EMACS_TIME next_time = timer_check ();
1822 if (EMACS_TIME_VALID_P (next_time))
1824 double time = EMACS_TIME_TO_DOUBLE (next_time);
1825 tmo = [NSTimer timerWithTimeInterval: time
1827 selector: @selector (timeout_handler:)
1830 [[NSRunLoop currentRunLoop] addTimer: tmo
1831 forMode: NSModalPanelRunLoopMode];
1834 dialog_return = Qundefined;
1835 [NSApp runModalForWindow: self];
1836 ret = dialog_return;
1839 if (tmo != nil) [tmo invalidate]; /* Cancels timer */
1844 if (EQ (ret, Qundefined) && window_closed)
1845 /* Make close button pressed equivalent to C-g. */
1846 Fsignal (Qquit, Qnil);
1854 /* ==========================================================================
1858 ========================================================================== */
1860 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1861 doc: /* Cause the NS menu to be re-calculated. */)
1864 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1869 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1870 doc: /* Pop up a dialog box and return user's selection.
1871 POSITION specifies which frame to use.
1872 This is normally a mouse button event or a window or frame.
1873 If POSITION is t, it means to use the frame the mouse is on.
1874 The dialog box appears in the middle of the specified frame.
1876 CONTENTS specifies the alternatives to display in the dialog box.
1877 It is a list of the form (DIALOG ITEM1 ITEM2...).
1878 Each ITEM is a cons cell (STRING . VALUE).
1879 The return value is VALUE from the chosen item.
1881 An ITEM may also be just a string--that makes a nonselectable item.
1882 An ITEM may also be nil--that means to put all preceding items
1883 on the left of the dialog box and all following items on the right.
1884 \(By default, approximately half appear on each side.)
1886 If HEADER is non-nil, the frame title for the box is "Information",
1887 otherwise it is "Question".
1889 If the user gets rid of the dialog box without making a valid choice,
1890 for instance using the window manager, then this produces a quit and
1891 `x-popup-dialog' does not return. */)
1892 (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1894 return ns_popup_dialog (position, contents, header);
1897 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1898 doc: /* Return t if a menu or popup dialog is active. */)
1901 return popup_activated () ? Qt : Qnil;
1904 /* ==========================================================================
1906 Lisp interface declaration
1908 ========================================================================== */
1911 syms_of_nsmenu (void)
1913 #ifndef NS_IMPL_COCOA
1914 /* Don't know how to keep track of this in Next/Open/Gnustep. Always
1915 update menus there. */
1918 defsubr (&Sx_popup_dialog);
1919 defsubr (&Sns_reset_menu);
1920 defsubr (&Smenu_or_popup_active_p);
1922 Qdebug_on_next_call = intern_c_string ("debug-on-next-call");
1923 staticpro (&Qdebug_on_next_call);