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;
122 const char *submenuTitle = [[submenu title] UTF8String];
125 widget_value *wv, *first_wv, *prev_wv = 0;
133 NSTRACE (ns_update_menubar);
135 if (f != SELECTED_FRAME ())
137 XSETFRAME (Vmenu_updating_frame, f);
138 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
141 pool = [[NSAutoreleasePool alloc] init];
143 /* Menu may have been created automatically; if so, discard it. */
144 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
152 menu = [[EmacsMenu alloc] initWithTitle: ns_app_name];
156 { /* close up anything on there */
157 id attMenu = [menu attachedMenu];
164 t = -(1000*tb.time+tb.millitm);
167 #ifdef NS_IMPL_GNUSTEP
168 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
173 /* Fully parse one or more of the submenus. */
175 int *submenu_start, *submenu_end;
176 bool *submenu_top_level_items;
177 int *submenu_n_panes;
178 struct buffer *prev = current_buffer;
180 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
181 int previous_menu_items_used = f->menu_bar_items_used;
182 Lisp_Object *previous_items
183 = alloca (previous_menu_items_used * sizeof *previous_items);
185 /* lisp preliminaries */
186 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
187 specbind (Qinhibit_quit, Qt);
188 specbind (Qdebug_on_next_call, Qnil);
189 record_unwind_save_match_data ();
190 if (NILP (Voverriding_local_map_menu_flag))
192 specbind (Qoverriding_terminal_local_map, Qnil);
193 specbind (Qoverriding_local_map, Qnil);
195 set_buffer_internal_1 (XBUFFER (buffer));
197 /* TODO: for some reason this is not needed in other terms,
198 but some menu updates call Info-extract-pointer which causes
199 abort-on-error if waiting-for-input. Needs further investigation. */
200 owfi = waiting_for_input;
201 waiting_for_input = 0;
203 /* lucid hook and possible reset */
204 safe_run_hooks (Qactivate_menubar_hook);
205 if (! NILP (Vlucid_menu_bar_dirty_flag))
206 call0 (Qrecompute_lucid_menubar);
207 safe_run_hooks (Qmenu_bar_update_hook);
208 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
210 /* Now ready to go */
211 items = FRAME_MENU_BAR_ITEMS (f);
213 /* Save the frame's previous menu bar contents data */
214 if (previous_menu_items_used)
215 memcpy (previous_items, aref_addr (f->menu_bar_vector, 0),
216 previous_menu_items_used * sizeof (Lisp_Object));
218 /* parse stage 1: extract from lisp */
221 menu_items = f->menu_bar_vector;
222 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
223 submenu_start = alloca (ASIZE (items) * sizeof *submenu_start);
224 submenu_end = alloca (ASIZE (items) * sizeof *submenu_end);
225 submenu_n_panes = alloca (ASIZE (items) * sizeof *submenu_n_panes);
226 submenu_top_level_items = alloca (ASIZE (items)
227 * sizeof *submenu_top_level_items);
229 for (i = 0; i < ASIZE (items); i += 4)
231 Lisp_Object key, string, maps;
233 key = AREF (items, i);
234 string = AREF (items, i + 1);
235 maps = AREF (items, i + 2);
239 /* FIXME: we'd like to only parse the needed submenu, but this
240 was causing crashes in the _common parsing code.. need to make
241 sure proper initialization done.. */
242 /* if (submenu && strcmp (submenuTitle, SSDATA (string)))
245 submenu_start[i] = menu_items_used;
247 menu_items_n_panes = 0;
248 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
249 submenu_n_panes[i] = menu_items_n_panes;
250 submenu_end[i] = menu_items_used;
254 finish_menu_items ();
255 waiting_for_input = owfi;
258 if (submenu && n == 0)
260 /* should have found a menu for this one but didn't */
261 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
263 discard_menu_items ();
264 unbind_to (specpdl_count, Qnil);
270 /* parse stage 2: insert into lucid 'widget_value' structures
271 [comments in other terms say not to evaluate lisp code here] */
272 wv = xmalloc_widget_value ();
273 wv->name = "menubar";
276 wv->button_type = BUTTON_TYPE_NONE;
280 for (i = 0; i < 4*n; i += 4)
282 menu_items_n_panes = submenu_n_panes[i];
283 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
284 submenu_top_level_items[i]);
288 first_wv->contents = wv;
289 /* Don't set wv->name here; GC during the loop might relocate it. */
291 wv->button_type = BUTTON_TYPE_NONE;
295 set_buffer_internal_1 (prev);
297 /* Compare the new menu items with previous, and leave off if no change */
298 /* FIXME: following other terms here, but seems like this should be
299 done before parse stage 2 above, since its results aren't used */
300 if (previous_menu_items_used
301 && (!submenu || (submenu && submenu == last_submenu))
302 && menu_items_used == previous_menu_items_used)
304 for (i = 0; i < previous_menu_items_used; i++)
305 /* FIXME: this ALWAYS fails on Buffers menu items.. something
306 about their strings causes them to change every time, so we
307 double-check failures */
308 if (!EQ (previous_items[i], AREF (menu_items, i)))
309 if (!(STRINGP (previous_items[i])
310 && STRINGP (AREF (menu_items, i))
311 && !strcmp (SSDATA (previous_items[i]),
312 SSDATA (AREF (menu_items, i)))))
314 if (i == previous_menu_items_used)
320 t += 1000*tb.time+tb.millitm;
321 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
324 free_menubar_widget_value_tree (first_wv);
325 discard_menu_items ();
326 unbind_to (specpdl_count, Qnil);
332 /* The menu items are different, so store them in the frame */
333 /* FIXME: this is not correct for single-submenu case */
334 fset_menu_bar_vector (f, menu_items);
335 f->menu_bar_items_used = menu_items_used;
337 /* Calls restore_menu_items, etc., as they were outside */
338 unbind_to (specpdl_count, Qnil);
340 /* Parse stage 2a: now GC cannot happen during the lifetime of the
341 widget_value, so it's safe to store data from a Lisp_String */
342 wv = first_wv->contents;
343 for (i = 0; i < ASIZE (items); i += 4)
346 string = AREF (items, i + 1);
349 /* if (submenu && strcmp (submenuTitle, SSDATA (string)))
352 wv->name = SSDATA (string);
353 update_submenu_strings (wv->contents);
357 /* Now, update the NS menu; if we have a submenu, use that, otherwise
358 create a new menu for each sub and fill it. */
361 for (wv = first_wv->contents; wv; wv = wv->next)
363 if (!strcmp (submenuTitle, wv->name))
365 [submenu fillWithWidgetValue: wv->contents];
366 last_submenu = submenu;
373 [menu fillWithWidgetValue: first_wv->contents];
379 static int n_previous_strings = 0;
380 static char previous_strings[100][10];
381 static struct frame *last_f = NULL;
385 wv = xmalloc_widget_value ();
386 wv->name = "menubar";
389 wv->button_type = BUTTON_TYPE_NONE;
393 /* Make widget-value tree w/ just the top level menu bar strings */
394 items = FRAME_MENU_BAR_ITEMS (f);
397 free_menubar_widget_value_tree (first_wv);
404 /* check if no change.. this mechanism is a bit rough, but ready */
405 n = ASIZE (items) / 4;
406 if (f == last_f && n_previous_strings == n)
408 for (i = 0; i<n; i++)
410 string = AREF (items, 4*i+1);
412 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
416 if (previous_strings[i][0])
421 else if (memcmp (previous_strings[i], SDATA (string),
422 min (10, SBYTES (string) + 1)))
428 free_menubar_widget_value_tree (first_wv);
436 for (i = 0; i < ASIZE (items); i += 4)
438 string = AREF (items, i + 1);
443 memcpy (previous_strings[i/4], SDATA (string),
444 min (10, SBYTES (string) + 1));
446 wv = xmalloc_widget_value ();
447 wv->name = SSDATA (string);
450 wv->button_type = BUTTON_TYPE_NONE;
452 wv->call_data = (void *) (intptr_t) (-1);
455 /* we'll update the real copy under app menu when time comes */
456 if (!strcmp ("Services", wv->name))
458 /* but we need to make sure it will update on demand */
459 [svcsMenu setFrame: f];
463 [menu addSubmenuWithTitle: wv->name forFrame: f];
468 first_wv->contents = wv;
474 n_previous_strings = n;
476 n_previous_strings = 0;
479 free_menubar_widget_value_tree (first_wv);
484 t += 1000*tb.time+tb.millitm;
485 fprintf (stderr, "Menu update took %ld msec.\n", t);
490 [NSApp setMainMenu: menu];
498 /* Main emacs core entry point for menubar menus: called to indicate that the
499 frame's menus have changed, and the *step representation should be updated
502 set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
504 ns_update_menubar (f, deep_p, nil);
508 x_activate_menubar (struct frame *f)
510 NSArray *a = [[NSApp mainMenu] itemArray];
511 /* Update each submenu separately so ns_update_menubar doesn't reset
514 while (i < [a count])
516 EmacsMenu *menu = (EmacsMenu *)[[a objectAtIndex:i] submenu];
517 const char *title = [[menu title] UTF8String];
518 if (strcmp (title, ns_get_pending_menu_title ()) == 0)
520 ns_update_menubar (f, true, menu);
525 ns_check_pending_open_menu ();
531 /* ==========================================================================
533 Menu: class implementation
535 ========================================================================== */
538 /* Menu that can define itself from Emacs "widget_value"s and will lazily
539 update itself when user clicked. Based on Carbon/AppKit implementation
540 by Yamamoto Mitsuharu. */
541 @implementation EmacsMenu
543 /* override designated initializer */
544 - initWithTitle: (NSString *)title
546 if ((self = [super initWithTitle: title]))
547 [self setAutoenablesItems: NO];
552 /* used for top-level */
553 - initWithTitle: (NSString *)title frame: (struct frame *)f
555 [self initWithTitle: title];
558 [self setDelegate: self];
564 - (void)setFrame: (struct frame *)f
570 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
571 extern NSString *NSMenuDidBeginTrackingNotification;
576 -(void)trackingNotification:(NSNotification *)notification
578 /* Update menu in menuNeedsUpdate only while tracking menus. */
579 trackingMenu = ([notification name] == NSMenuDidBeginTrackingNotification
583 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
584 - (void)menuWillOpen:(NSMenu *)menu
586 ns_check_menu_open (menu);
592 /* delegate method called when a submenu is being opened: run a 'deep' call
593 to set_frame_menubar */
594 - (void)menuNeedsUpdate: (NSMenu *)menu
596 if (!FRAME_LIVE_P (frame))
599 /* Cocoa/Carbon will request update on every keystroke
600 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
601 since key equivalents are handled through emacs.
602 On Leopard, even keystroke events generate SystemDefined event.
603 Third-party applications that enhance mouse / trackpad
604 interaction, or also VNC/Remote Desktop will send events
605 of type AppDefined rather than SysDefined.
606 Menus will fail to show up if they haven't been initialized.
607 AppDefined events may lack timing data.
609 Thus, we rely on the didBeginTrackingNotification notification
610 as above to indicate the need for updates.
611 From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
612 key press case, NSMenuPropertyItemImage (e.g.) won't be set.
614 if (trackingMenu == 0)
616 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
617 #if ! defined(NS_IMPL_COCOA) || \
618 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
619 /* Don't know how to do this for anything other than OSX >= 10.5
620 This is wrong, as it might run Lisp code in the event loop. */
621 ns_update_menubar (frame, true, self);
626 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
628 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
629 && FRAME_NS_VIEW (SELECTED_FRAME ()))
630 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
635 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
636 into an accelerator string. We are only able to display a single character
637 for an accelerator, together with an optional modifier combination. (Under
638 Carbon more control was possible, but in Cocoa multi-char strings passed to
639 NSMenuItem get ignored. For now we try to display a super-single letter
640 combo, and return the others as strings to be appended to the item title.
641 (This is signaled by setting keyEquivModMask to 0 for now.) */
642 -(NSString *)parseKeyEquiv: (const char *)key
644 const char *tpos = key;
645 keyEquivModMask = NSCommandKeyMask;
647 if (!key || !strlen (key))
650 while (*tpos == ' ' || *tpos == '(')
652 if ((*tpos == 's') && (*(tpos+1) == '-'))
654 return [NSString stringWithFormat: @"%c", tpos[2]];
656 keyEquivModMask = 0; /* signal */
657 return [NSString stringWithUTF8String: tpos];
661 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
664 widget_value *wv = (widget_value *)wvptr;
666 if (menu_separator_name_p (wv->name))
668 item = [NSMenuItem separatorItem];
669 [self addItem: item];
673 NSString *title, *keyEq;
674 title = [NSString stringWithUTF8String: wv->name];
676 title = @"< ? >"; /* (get out in the open so we know about it) */
678 keyEq = [self parseKeyEquiv: wv->key];
680 /* OS X just ignores modifier strings longer than one character */
681 if (keyEquivModMask == 0)
682 title = [title stringByAppendingFormat: @" (%@)", keyEq];
685 item = [self addItemWithTitle: (NSString *)title
686 action: @selector (menuDown:)
687 keyEquivalent: keyEq];
688 [item setKeyEquivalentModifierMask: keyEquivModMask];
690 [item setEnabled: wv->enabled];
692 /* Draw radio buttons and tickboxes */
693 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
694 wv->button_type == BUTTON_TYPE_RADIO))
695 [item setState: NSOnState];
697 [item setState: NSOffState];
699 [item setTag: (NSInteger)wv->call_data];
711 for (n = [self numberOfItems]-1; n >= 0; n--)
713 NSMenuItem *item = [self itemAtIndex: n];
714 NSString *title = [item title];
715 if (([title length] == 0 /* OSX 10.5 */
716 || [ns_app_name isEqualToString: title] /* from 10.6 on */
717 || [@"Apple" isEqualToString: title]) /* older */
718 && ![item isSeparatorItem])
720 [self removeItemAtIndex: n];
725 - (void)fillWithWidgetValue: (void *)wvptr
727 widget_value *wv = (widget_value *)wvptr;
729 /* clear existing contents */
730 [self setMenuChangedMessagesEnabled: NO];
733 /* add new contents */
734 for (; wv != NULL; wv = wv->next)
736 NSMenuItem *item = [self addItemWithWidgetValue: wv];
740 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
742 [self setSubmenu: submenu forItem: item];
743 [submenu fillWithWidgetValue: wv->contents];
745 [item setAction: nil];
749 [self setMenuChangedMessagesEnabled: YES];
750 #ifdef NS_IMPL_GNUSTEP
751 if ([[self window] isVisible])
757 /* adds an empty submenu and returns it */
758 - (EmacsMenu *)addSubmenuWithTitle: (const char *)title forFrame: (struct frame *)f
760 NSString *titleStr = [NSString stringWithUTF8String: title];
761 NSMenuItem *item = [self addItemWithTitle: titleStr
762 action: nil /*@selector (menuDown:) */
764 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
765 [self setSubmenu: submenu forItem: item];
770 /* run a menu in popup mode */
771 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
772 keymaps: (bool)keymaps
774 EmacsView *view = FRAME_NS_VIEW (f);
778 /* p = [view convertPoint:p fromView: nil]; */
779 p.y = NSHeight ([view frame]) - p.y;
780 e = [[view window] currentEvent];
781 event = [NSEvent mouseEventWithType: NSRightMouseDown
784 timestamp: [e timestamp]
785 windowNumber: [[view window] windowNumber]
787 eventNumber: 0/*[e eventNumber] */
791 context_menu_value = -1;
792 [NSMenu popUpContextMenu: self withEvent: event forView: view];
793 retVal = context_menu_value;
794 context_menu_value = 0;
796 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
804 /* ==========================================================================
806 Context Menu: implementing functions
808 ========================================================================== */
811 ns_menu_show (FRAME_PTR f, int x, int y, bool for_click, bool keymaps,
812 Lisp_Object title, const char **error)
817 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
818 widget_value *wv, *first_wv = 0;
822 /* now parse stage 2 as in ns_update_menubar */
823 wv = xmalloc_widget_value ();
824 wv->name = "contextmenu";
827 wv->button_type = BUTTON_TYPE_NONE;
832 /* FIXME: a couple of one-line differences prevent reuse */
833 wv = digest_single_submenu (0, menu_items_used, 0);
836 widget_value *save_wv = 0, *prev_wv = 0;
837 widget_value **submenu_stack
838 = alloca (menu_items_used * sizeof *submenu_stack);
839 /* Lisp_Object *subprefix_stack
840 = alloca (menu_items_used * sizeof *subprefix_stack); */
841 int submenu_depth = 0;
845 /* Loop over all panes and items, filling in the tree. */
847 while (i < menu_items_used)
849 if (EQ (AREF (menu_items, i), Qnil))
851 submenu_stack[submenu_depth++] = save_wv;
857 else if (EQ (AREF (menu_items, i), Qlambda))
860 save_wv = submenu_stack[--submenu_depth];
864 else if (EQ (AREF (menu_items, i), Qt)
865 && submenu_depth != 0)
866 i += MENU_ITEMS_PANE_LENGTH;
867 /* Ignore a nil in the item list.
868 It's meaningful only for dialog boxes. */
869 else if (EQ (AREF (menu_items, i), Qquote))
871 else if (EQ (AREF (menu_items, i), Qt))
873 /* Create a new pane. */
874 Lisp_Object pane_name, prefix;
875 const char *pane_string;
877 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
878 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
880 #ifndef HAVE_MULTILINGUAL_MENU
881 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
883 pane_name = ENCODE_MENU_STRING (pane_name);
884 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
887 pane_string = (NILP (pane_name)
888 ? "" : SSDATA (pane_name));
889 /* If there is just one top-level pane, put all its items directly
890 under the top-level menu. */
891 if (menu_items_n_panes == 1)
894 /* If the pane has a meaningful name,
895 make the pane a top-level menu item
896 with its items as a submenu beneath it. */
897 if (!keymaps && strcmp (pane_string, ""))
899 wv = xmalloc_widget_value ();
903 first_wv->contents = wv;
904 wv->name = pane_string;
905 if (keymaps && !NILP (prefix))
909 wv->button_type = BUTTON_TYPE_NONE;
920 i += MENU_ITEMS_PANE_LENGTH;
924 /* Create a new item within current pane. */
925 Lisp_Object item_name, enable, descrip, def, type, selected, help;
926 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
927 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
928 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
929 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
930 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
931 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
932 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
934 #ifndef HAVE_MULTILINGUAL_MENU
935 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
937 item_name = ENCODE_MENU_STRING (item_name);
938 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
941 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
943 descrip = ENCODE_MENU_STRING (descrip);
944 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
946 #endif /* not HAVE_MULTILINGUAL_MENU */
948 wv = xmalloc_widget_value ();
952 save_wv->contents = wv;
953 wv->name = SSDATA (item_name);
955 wv->key = SSDATA (descrip);
957 /* If this item has a null value,
958 make the call_data null so that it won't display a box
959 when the mouse is on it. */
960 wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
961 wv->enabled = !NILP (enable);
964 wv->button_type = BUTTON_TYPE_NONE;
965 else if (EQ (type, QCtoggle))
966 wv->button_type = BUTTON_TYPE_TOGGLE;
967 else if (EQ (type, QCradio))
968 wv->button_type = BUTTON_TYPE_RADIO;
972 wv->selected = !NILP (selected);
974 if (! STRINGP (help))
981 i += MENU_ITEMS_ITEM_LENGTH;
989 widget_value *wv_title = xmalloc_widget_value ();
990 widget_value *wv_sep = xmalloc_widget_value ();
992 /* Maybe replace this separator with a bitmap or owner-draw item
993 so that it looks better. Having two separators looks odd. */
995 wv_sep->next = first_wv->contents;
998 #ifndef HAVE_MULTILINGUAL_MENU
999 if (STRING_MULTIBYTE (title))
1000 title = ENCODE_MENU_STRING (title);
1003 wv_title->name = SSDATA (title);
1004 wv_title->enabled = NO;
1005 wv_title->button_type = BUTTON_TYPE_NONE;
1006 wv_title->help = Qnil;
1007 wv_title->next = wv_sep;
1008 first_wv->contents = wv_title;
1011 pmenu = [[EmacsMenu alloc] initWithTitle:
1012 [NSString stringWithUTF8String: SSDATA (title)]];
1013 [pmenu fillWithWidgetValue: first_wv->contents];
1014 free_menubar_widget_value_tree (first_wv);
1015 unbind_to (specpdl_count, Qnil);
1017 popup_activated_flag = 1;
1018 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
1019 popup_activated_flag = 0;
1020 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1026 /* ==========================================================================
1028 Toolbar: externally-called functions
1030 ========================================================================== */
1033 free_frame_tool_bar (FRAME_PTR f)
1034 /* --------------------------------------------------------------------------
1035 Under NS we just hide the toolbar until it might be needed again.
1036 -------------------------------------------------------------------------- */
1039 [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
1040 FRAME_TOOLBAR_HEIGHT (f) = 0;
1045 update_frame_tool_bar (FRAME_PTR f)
1046 /* --------------------------------------------------------------------------
1047 Update toolbar contents
1048 -------------------------------------------------------------------------- */
1051 EmacsView *view = FRAME_NS_VIEW (f);
1052 NSWindow *window = [view window];
1053 EmacsToolbar *toolbar = [view toolbar];
1056 [toolbar clearActive];
1058 /* update EmacsToolbar as in GtkUtils, build items list */
1059 for (i = 0; i < f->n_tool_bar_items; ++i)
1061 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1062 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1064 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1069 Lisp_Object helpObj;
1070 const char *helpText;
1072 /* If image is a vector, choose the image according to the
1074 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1075 if (VECTORP (image))
1077 /* NS toolbar auto-computes disabled and selected images */
1078 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1079 eassert (ASIZE (image) >= idx);
1080 image = AREF (image, idx);
1086 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1088 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1089 helpText = NILP (helpObj) ? "" : SSDATA (helpObj);
1091 /* Ignore invalid image specifications. */
1092 if (!valid_image_p (image))
1094 /* Don't log anything, GNUS makes invalid images all the time. */
1098 img_id = lookup_image (f, image);
1099 img = IMAGE_FROM_ID (f, img_id);
1100 prepare_image_for_display (f, img);
1102 if (img->load_failed_p || img->pixmap == nil)
1104 NSLog (@"Could not prepare toolbar image for display.");
1108 [toolbar addDisplayItemWithImage: img->pixmap idx: i helpText: helpText
1109 enabled: enabled_p];
1113 if (![toolbar isVisible])
1114 [toolbar setVisible: YES];
1116 if ([toolbar changed])
1118 /* inform app that toolbar has changed */
1119 NSDictionary *dict = [toolbar configurationDictionary];
1120 NSMutableDictionary *newDict = [dict mutableCopy];
1121 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1123 while ((key = [keys nextObject]) != nil)
1125 NSObject *val = [dict objectForKey: key];
1126 if ([val isKindOfClass: [NSArray class]])
1129 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1134 [toolbar setConfigurationFromDictionary: newDict];
1138 FRAME_TOOLBAR_HEIGHT (f) =
1139 NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1140 - FRAME_NS_TITLEBAR_HEIGHT (f);
1141 if (FRAME_TOOLBAR_HEIGHT (f) < 0) // happens if frame is fullscreen.
1142 FRAME_TOOLBAR_HEIGHT (f) = 0;
1147 /* ==========================================================================
1149 Toolbar: class implementation
1151 ========================================================================== */
1153 @implementation EmacsToolbar
1155 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1157 self = [super initWithIdentifier: identifier];
1159 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1160 [self setSizeMode: NSToolbarSizeModeSmall];
1161 [self setDelegate: self];
1162 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1163 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1164 prevEnablement = enablement = 0L;
1170 [prevIdentifiers release];
1171 [activeIdentifiers release];
1172 [identifierToItem release];
1176 - (void) clearActive
1178 [prevIdentifiers release];
1179 prevIdentifiers = [activeIdentifiers copy];
1180 [activeIdentifiers removeAllObjects];
1181 prevEnablement = enablement;
1187 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1188 enablement == prevEnablement ? NO : YES;
1191 - (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx
1192 helpText: (const char *)help enabled: (BOOL)enabled
1194 /* 1) come up w/identifier */
1195 NSString *identifier
1196 = [NSString stringWithFormat: @"%u", [img hash]];
1198 /* 2) create / reuse item */
1199 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1202 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1204 [item setImage: img];
1205 [item setToolTip: [NSString stringWithUTF8String: help]];
1206 [item setTarget: emacsView];
1207 [item setAction: @selector (toolbarClicked:)];
1211 [item setEnabled: enabled];
1213 /* 3) update state */
1214 [identifierToItem setObject: item forKey: identifier];
1215 [activeIdentifiers addObject: identifier];
1216 enablement = (enablement << 1) | (enabled == YES);
1219 /* This overrides super's implementation, which automatically sets
1220 all items to enabled state (for some reason). */
1221 - (void)validateVisibleItems { }
1224 /* delegate methods */
1226 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1227 itemForItemIdentifier: (NSString *)itemIdentifier
1228 willBeInsertedIntoToolbar: (BOOL)flag
1230 /* look up NSToolbarItem by identifier and return... */
1231 return [identifierToItem objectForKey: itemIdentifier];
1234 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1236 /* return entire set.. */
1237 return activeIdentifiers;
1240 /* for configuration palette (not yet supported) */
1241 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1243 /* return entire set... */
1244 return [identifierToItem allKeys];
1247 /* optional and unneeded */
1248 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1249 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1250 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1252 @end /* EmacsToolbar */
1256 /* ==========================================================================
1258 Tooltip: class implementation
1260 ========================================================================== */
1262 /* Needed because NeXTstep does not provide enough control over tooltip
1264 @implementation EmacsTooltip
1268 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1269 blue: 0.792 alpha: 0.95];
1270 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1271 NSFont *sfont = [font screenFont];
1272 int height = [sfont ascender] - [sfont descender];
1273 /*[font boundingRectForFont].size.height; */
1274 NSRect r = NSMakeRect (0, 0, 100, height+6);
1276 textField = [[NSTextField alloc] initWithFrame: r];
1277 [textField setFont: font];
1278 [textField setBackgroundColor: col];
1280 [textField setEditable: NO];
1281 [textField setSelectable: NO];
1282 [textField setBordered: NO];
1283 [textField setBezeled: NO];
1284 [textField setDrawsBackground: YES];
1286 win = [[NSWindow alloc]
1287 initWithContentRect: [textField frame]
1289 backing: NSBackingStoreBuffered
1291 [win setHasShadow: YES];
1292 [win setReleasedWhenClosed: NO];
1293 [win setDelegate: self];
1294 [[win contentView] addSubview: textField];
1295 /* [win setBackgroundColor: col]; */
1296 [win setOpaque: NO];
1305 [textField release];
1309 - (void) setText: (char *)text
1311 NSString *str = [NSString stringWithUTF8String: text];
1312 NSRect r = [textField frame];
1315 [textField setStringValue: str];
1316 tooltipDims = [[textField cell] cellSize];
1318 r.size.width = tooltipDims.width;
1319 r.size.height = tooltipDims.height;
1320 [textField setFrame: r];
1323 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1325 NSRect wr = [win frame];
1327 wr.origin = NSMakePoint (x, y);
1328 wr.size = [textField frame].size;
1330 [win setFrame: wr display: YES];
1331 [win setLevel: NSPopUpMenuWindowLevel];
1332 [win orderFront: self];
1334 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1335 selector: @selector (hide)
1336 userInfo: nil repeats: NO];
1345 if ([timer isValid])
1354 return timer != nil;
1359 return [textField frame];
1362 @end /* EmacsTooltip */
1366 /* ==========================================================================
1368 Popup Dialog: implementing functions
1370 ========================================================================== */
1374 NSAutoreleasePool *pool;
1375 EmacsDialogPanel *dialog;
1379 pop_down_menu (Lisp_Object arg)
1381 struct Popdown_data *unwind_data = XSAVE_POINTER (arg, 0);
1384 if (popup_activated_flag)
1386 EmacsDialogPanel *panel = unwind_data->dialog;
1387 popup_activated_flag = 0;
1389 [unwind_data->pool release];
1390 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1393 xfree (unwind_data);
1401 ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1404 Lisp_Object window, tem, title;
1408 NSAutoreleasePool *pool;
1410 NSTRACE (x-popup-dialog);
1412 isQ = NILP (header);
1414 if (EQ (position, Qt)
1415 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1416 || EQ (XCAR (position), Qtool_bar))))
1418 window = selected_window;
1420 else if (CONSP (position))
1423 tem = Fcar (position);
1424 if (XTYPE (tem) == Lisp_Cons)
1425 window = Fcar (Fcdr (position));
1428 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1429 window = Fcar (tem); /* POSN_WINDOW (tem) */
1432 else if (WINDOWP (position) || FRAMEP (position))
1439 if (FRAMEP (window))
1440 f = XFRAME (window);
1441 else if (WINDOWP (window))
1443 CHECK_LIVE_WINDOW (window);
1444 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1447 CHECK_WINDOW (window);
1449 check_window_system (f);
1451 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1452 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1454 title = Fcar (contents);
1455 CHECK_STRING (title);
1457 if (NILP (Fcar (Fcdr (contents))))
1458 /* No buttons specified, add an "Ok" button so users can pop down
1460 contents = Fcons (title, Fcons (Fcons (build_string ("Ok"), Qt), Qnil));
1463 pool = [[NSAutoreleasePool alloc] init];
1464 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1468 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1469 struct Popdown_data *unwind_data = xmalloc (sizeof (*unwind_data));
1471 unwind_data->pool = pool;
1472 unwind_data->dialog = dialog;
1474 record_unwind_protect (pop_down_menu, make_save_pointer (unwind_data));
1475 popup_activated_flag = 1;
1476 tem = [dialog runDialogAt: p];
1477 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1486 /* ==========================================================================
1488 Popup Dialog: class implementation
1490 ========================================================================== */
1492 @interface FlippedView : NSView
1497 @implementation FlippedView
1504 @implementation EmacsDialogPanel
1507 #define ICONSIZE 64.0
1508 #define TEXTHEIGHT 20.0
1509 #define MINCELLWIDTH 90.0
1511 - initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
1512 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1514 NSSize spacing = {SPACER, SPACER};
1517 NSImageView *imgView;
1518 FlippedView *contentView;
1521 dialog_return = Qundefined;
1522 button_values = NULL;
1523 area.origin.x = 3*SPACER;
1524 area.origin.y = 2*SPACER;
1525 area.size.width = ICONSIZE;
1526 area.size.height= ICONSIZE;
1527 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1528 [img setScalesWhenResized: YES];
1529 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1530 imgView = [[NSImageView alloc] initWithFrame: area];
1531 [imgView setImage: img];
1532 [imgView setEditable: NO];
1534 [imgView autorelease];
1536 aStyle = NSTitledWindowMask;
1540 [super initWithContentRect: contentRect styleMask: aStyle
1541 backing: backingType defer: flag];
1542 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1543 [contentView autorelease];
1545 [self setContentView: contentView];
1547 [[self contentView] setAutoresizesSubviews: YES];
1549 [[self contentView] addSubview: imgView];
1550 [self setTitle: @""];
1552 area.origin.x += ICONSIZE+2*SPACER;
1553 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1554 area.size.width = 400;
1555 area.size.height= TEXTHEIGHT;
1556 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1557 [[self contentView] addSubview: command];
1558 [command setStringValue: ns_app_name];
1559 [command setDrawsBackground: NO];
1560 [command setBezeled: NO];
1561 [command setSelectable: NO];
1562 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1564 /* area.origin.x = ICONSIZE+2*SPACER;
1565 area.origin.y = TEXTHEIGHT + 2*SPACER;
1566 area.size.width = 400;
1567 area.size.height= 2;
1568 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1569 [[self contentView] addSubview: tem];
1570 [tem setTitlePosition: NSNoTitle];
1571 [tem setAutoresizingMask: NSViewWidthSizable];*/
1573 /* area.origin.x = ICONSIZE+2*SPACER; */
1574 area.origin.y += TEXTHEIGHT+SPACER;
1575 area.size.width = 400;
1576 area.size.height= TEXTHEIGHT;
1577 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1578 [[self contentView] addSubview: title];
1579 [title setDrawsBackground: NO];
1580 [title setBezeled: NO];
1581 [title setSelectable: NO];
1582 [title setFont: [NSFont systemFontOfSize: 11.0]];
1584 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1585 [cell setBordered: NO];
1586 [cell setEnabled: NO];
1587 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1588 [cell setBezelStyle: NSRoundedBezelStyle];
1590 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1591 mode: NSHighlightModeMatrix
1594 numberOfColumns: 1];
1595 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1596 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1597 [matrix setIntercellSpacing: spacing];
1598 [matrix autorelease];
1600 [[self contentView] addSubview: matrix];
1601 [self setOneShot: YES];
1602 [self setReleasedWhenClosed: YES];
1603 [self setHidesOnDeactivate: YES];
1605 NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask];
1611 - (BOOL)windowShouldClose: (id)sender
1613 window_closed = YES;
1620 xfree (button_values);
1624 - (void)process_dialog: (Lisp_Object) list
1626 Lisp_Object item, lst = list;
1628 int buttons = 0, btnnr = 0;
1630 for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
1633 if (XTYPE (item) == Lisp_Cons)
1638 button_values = (Lisp_Object *) xmalloc (buttons * sizeof (*button_values));
1640 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1643 if (XTYPE (item) == Lisp_String)
1645 [self addString: SSDATA (item) row: row++];
1647 else if (XTYPE (item) == Lisp_Cons)
1649 button_values[btnnr] = XCDR (item);
1650 [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
1653 else if (NILP (item))
1662 - (void)addButton: (char *)str value: (int)tag row: (int)row
1671 cell = [matrix cellAtRow: row column: cols-1];
1672 [cell setTarget: self];
1673 [cell setAction: @selector (clicked: )];
1674 [cell setTitle: [NSString stringWithUTF8String: str]];
1676 [cell setBordered: YES];
1677 [cell setEnabled: YES];
1681 - (void)addString: (char *)str row: (int)row
1690 cell = [matrix cellAtRow: row column: cols-1];
1691 [cell setTitle: [NSString stringWithUTF8String: str]];
1692 [cell setBordered: YES];
1693 [cell setEnabled: NO];
1704 - (void)clicked: sender
1706 NSArray *sellist = nil;
1709 sellist = [sender selectedCells];
1710 if ([sellist count] < 1)
1713 seltag = [[sellist objectAtIndex: 0] tag];
1714 dialog_return = button_values[seltag];
1719 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1724 if (XTYPE (contents) == Lisp_Cons)
1726 head = Fcar (contents);
1727 [self process_dialog: Fcdr (contents)];
1732 if (XTYPE (head) == Lisp_String)
1733 [title setStringValue:
1734 [NSString stringWithUTF8String: SSDATA (head)]];
1735 else if (isQ == YES)
1736 [title setStringValue: @"Question"];
1738 [title setStringValue: @"Information"];
1744 if (cols == 1 && rows > 1) /* Never told where to split */
1747 for (i = 0; i < rows/2; i++)
1749 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1750 atRow: i column: 1];
1751 [matrix removeRow: (rows+1)/2];
1757 NSSize csize = [matrix cellSize];
1758 if (csize.width < MINCELLWIDTH)
1760 csize.width = MINCELLWIDTH;
1761 [matrix setCellSize: csize];
1762 [matrix sizeToCells];
1767 [command sizeToFit];
1771 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1773 t.origin.x = r.origin.x;
1774 t.size.width = r.size.width;
1776 r = [command frame];
1777 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1779 t.origin.x = r.origin.x;
1780 t.size.width = r.size.width;
1784 s = [(NSView *)[self contentView] frame];
1785 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1786 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1787 [self setFrame: r display: NO];
1795 - (void)timeout_handler: (NSTimer *)timedEntry
1797 NSEvent *nxev = [NSEvent otherEventWithType: NSApplicationDefined
1798 location: NSMakePoint (0, 0)
1801 windowNumber: [[NSApp mainWindow] windowNumber]
1802 context: [NSApp context]
1808 /* We use sto because stopModal/abortModal out of the main loop does not
1809 seem to work in 10.6. But as we use stop we must send a real event so
1810 the stop is seen and acted upon. */
1812 [NSApp postEvent: nxev atStart: NO];
1815 - (Lisp_Object)runDialogAt: (NSPoint)p
1817 Lisp_Object ret = Qundefined;
1819 while (popup_activated_flag)
1822 EMACS_TIME next_time = timer_check ();
1824 if (EMACS_TIME_VALID_P (next_time))
1826 double time = EMACS_TIME_TO_DOUBLE (next_time);
1827 tmo = [NSTimer timerWithTimeInterval: time
1829 selector: @selector (timeout_handler:)
1832 [[NSRunLoop currentRunLoop] addTimer: tmo
1833 forMode: NSModalPanelRunLoopMode];
1836 dialog_return = Qundefined;
1837 [NSApp runModalForWindow: self];
1838 ret = dialog_return;
1841 if (tmo != nil) [tmo invalidate]; /* Cancels timer */
1846 if (EQ (ret, Qundefined) && window_closed)
1847 /* Make close button pressed equivalent to C-g. */
1848 Fsignal (Qquit, Qnil);
1856 /* ==========================================================================
1860 ========================================================================== */
1862 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1863 doc: /* Cause the NS menu to be re-calculated. */)
1866 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1871 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1872 doc: /* Pop up a dialog box and return user's selection.
1873 POSITION specifies which frame to use.
1874 This is normally a mouse button event or a window or frame.
1875 If POSITION is t, it means to use the frame the mouse is on.
1876 The dialog box appears in the middle of the specified frame.
1878 CONTENTS specifies the alternatives to display in the dialog box.
1879 It is a list of the form (DIALOG ITEM1 ITEM2...).
1880 Each ITEM is a cons cell (STRING . VALUE).
1881 The return value is VALUE from the chosen item.
1883 An ITEM may also be just a string--that makes a nonselectable item.
1884 An ITEM may also be nil--that means to put all preceding items
1885 on the left of the dialog box and all following items on the right.
1886 \(By default, approximately half appear on each side.)
1888 If HEADER is non-nil, the frame title for the box is "Information",
1889 otherwise it is "Question".
1891 If the user gets rid of the dialog box without making a valid choice,
1892 for instance using the window manager, then this produces a quit and
1893 `x-popup-dialog' does not return. */)
1894 (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1896 return ns_popup_dialog (position, contents, header);
1899 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1900 doc: /* Return t if a menu or popup dialog is active. */)
1903 return popup_activated () ? Qt : Qnil;
1906 /* ==========================================================================
1908 Lisp interface declaration
1910 ========================================================================== */
1913 syms_of_nsmenu (void)
1915 #ifndef NS_IMPL_COCOA
1916 /* Don't know how to keep track of this in Next/Open/Gnustep. Always
1917 update menus there. */
1920 defsubr (&Sx_popup_dialog);
1921 defsubr (&Sns_reset_menu);
1922 defsubr (&Smenu_or_popup_active_p);
1924 Qdebug_on_next_call = intern_c_string ("debug-on-next-call");
1925 staticpro (&Qdebug_on_next_call);