1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007-2012 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. */
31 #include "character.h"
36 #include "blockinput.h"
38 #include "termhooks.h"
42 #define NSMENUPROFILE 0
45 #include <sys/timeb.h>
46 #include <sys/types.h>
49 #define MenuStagger 10.0
52 int menu_trace_num = 0;
53 #define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \
54 __FILE__, __LINE__, ++menu_trace_num)
60 /* Include lisp -> C common menu parsing code */
61 #define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
62 #include "nsmenu_common.c"
65 extern Lisp_Object Qundefined, Qmenu_enable, Qmenu_bar_update_hook;
66 extern Lisp_Object QCtoggle, QCradio;
68 Lisp_Object Qdebug_on_next_call;
69 extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
71 extern long context_menu_value;
72 EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
74 /* Nonzero means a menu is currently active. */
75 static int popup_activated_flag;
77 /* Nonzero means we are tracking and updating menus. */
78 static int trackingMenu;
81 /* NOTE: toolbar implementation is at end,
82 following complete menu implementation. */
85 /* ==========================================================================
87 Menu: Externally-called functions
89 ========================================================================== */
92 /* FIXME: not currently used, but should normalize with other terms. */
94 x_activate_menubar (struct frame *f)
96 fprintf (stderr, "XXX: Received x_activate_menubar event.\n");
100 /* Supposed to discard menubar and free storage. Since we share the
101 menubar among frames and update its context for the focused window,
102 there is nothing to do here. */
104 free_frame_menubar (struct frame *f)
111 popup_activated (void)
113 return popup_activated_flag;
117 /* --------------------------------------------------------------------------
118 Update menubar. Three cases:
119 1) deep_p = 0, submenu = nil: Fresh switch onto a frame -- either set up
120 just top-level menu strings (OS X), or goto case (2) (GNUstep).
121 2) deep_p = 1, submenu = nil: Recompute all submenus.
122 3) deep_p = 1, submenu = non-nil: Update contents of a single submenu.
123 -------------------------------------------------------------------------- */
125 ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
127 NSAutoreleasePool *pool;
128 id menu = [NSApp mainMenu];
129 static EmacsMenu *last_submenu = nil;
131 const char *submenuTitle = [[submenu title] UTF8String];
132 extern int waiting_for_input;
135 widget_value *wv, *first_wv, *prev_wv = 0;
143 NSTRACE (set_frame_menubar);
145 if (f != SELECTED_FRAME ())
147 XSETFRAME (Vmenu_updating_frame, f);
148 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
151 pool = [[NSAutoreleasePool alloc] init];
153 /* Menu may have been created automatically; if so, discard it. */
154 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
162 menu = [[EmacsMenu alloc] initWithTitle: ns_app_name];
166 { /* close up anything on there */
167 id attMenu = [menu attachedMenu];
174 t = -(1000*tb.time+tb.millitm);
177 #ifdef NS_IMPL_GNUSTEP
178 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
183 /* Fully parse one or more of the submenus. */
185 int *submenu_start, *submenu_end;
186 int *submenu_top_level_items, *submenu_n_panes;
187 struct buffer *prev = current_buffer;
189 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
190 int previous_menu_items_used = f->menu_bar_items_used;
191 Lisp_Object *previous_items
192 = alloca (previous_menu_items_used * sizeof *previous_items);
194 /* lisp preliminaries */
195 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
196 specbind (Qinhibit_quit, Qt);
197 specbind (Qdebug_on_next_call, Qnil);
198 record_unwind_save_match_data ();
199 if (NILP (Voverriding_local_map_menu_flag))
201 specbind (Qoverriding_terminal_local_map, Qnil);
202 specbind (Qoverriding_local_map, Qnil);
204 set_buffer_internal_1 (XBUFFER (buffer));
206 /* TODO: for some reason this is not needed in other terms,
207 but some menu updates call Info-extract-pointer which causes
208 abort-on-error if waiting-for-input. Needs further investigation. */
209 owfi = waiting_for_input;
210 waiting_for_input = 0;
212 /* lucid hook and possible reset */
213 safe_run_hooks (Qactivate_menubar_hook);
214 if (! NILP (Vlucid_menu_bar_dirty_flag))
215 call0 (Qrecompute_lucid_menubar);
216 safe_run_hooks (Qmenu_bar_update_hook);
217 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
219 /* Now ready to go */
220 items = FRAME_MENU_BAR_ITEMS (f);
222 /* Save the frame's previous menu bar contents data */
223 if (previous_menu_items_used)
224 memcpy (previous_items, aref_addr (f->menu_bar_vector, 0),
225 previous_menu_items_used * sizeof (Lisp_Object));
227 /* parse stage 1: extract from lisp */
230 menu_items = f->menu_bar_vector;
231 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
232 submenu_start = alloca (ASIZE (items) * sizeof *submenu_start);
233 submenu_end = alloca (ASIZE (items) * sizeof *submenu_end);
234 submenu_n_panes = alloca (ASIZE (items) * sizeof *submenu_n_panes);
235 submenu_top_level_items = alloca (ASIZE (items)
236 * sizeof *submenu_top_level_items);
238 for (i = 0; i < ASIZE (items); i += 4)
240 Lisp_Object key, string, maps;
242 key = AREF (items, i);
243 string = AREF (items, i + 1);
244 maps = AREF (items, i + 2);
248 /* FIXME: we'd like to only parse the needed submenu, but this
249 was causing crashes in the _common parsing code.. need to make
250 sure proper initialization done.. */
251 /* if (submenu && strcmp (submenuTitle, SSDATA (string)))
254 submenu_start[i] = menu_items_used;
256 menu_items_n_panes = 0;
257 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
258 submenu_n_panes[i] = menu_items_n_panes;
259 submenu_end[i] = menu_items_used;
263 finish_menu_items ();
264 waiting_for_input = owfi;
267 if (submenu && n == 0)
269 /* should have found a menu for this one but didn't */
270 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
272 discard_menu_items ();
273 unbind_to (specpdl_count, Qnil);
279 /* parse stage 2: insert into lucid 'widget_value' structures
280 [comments in other terms say not to evaluate lisp code here] */
281 wv = xmalloc_widget_value ();
282 wv->name = "menubar";
285 wv->button_type = BUTTON_TYPE_NONE;
289 for (i = 0; i < 4*n; i += 4)
291 menu_items_n_panes = submenu_n_panes[i];
292 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
293 submenu_top_level_items[i]);
297 first_wv->contents = wv;
298 /* Don't set wv->name here; GC during the loop might relocate it. */
300 wv->button_type = BUTTON_TYPE_NONE;
304 set_buffer_internal_1 (prev);
306 /* Compare the new menu items with previous, and leave off if no change */
307 /* FIXME: following other terms here, but seems like this should be
308 done before parse stage 2 above, since its results aren't used */
309 if (previous_menu_items_used
310 && (!submenu || (submenu && submenu == last_submenu))
311 && menu_items_used == previous_menu_items_used)
313 for (i = 0; i < previous_menu_items_used; i++)
314 /* FIXME: this ALWAYS fails on Buffers menu items.. something
315 about their strings causes them to change every time, so we
316 double-check failures */
317 if (!EQ (previous_items[i], AREF (menu_items, i)))
318 if (!(STRINGP (previous_items[i])
319 && STRINGP (AREF (menu_items, i))
320 && !strcmp (SSDATA (previous_items[i]),
321 SSDATA (AREF (menu_items, i)))))
323 if (i == previous_menu_items_used)
329 t += 1000*tb.time+tb.millitm;
330 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
333 free_menubar_widget_value_tree (first_wv);
334 discard_menu_items ();
335 unbind_to (specpdl_count, Qnil);
341 /* The menu items are different, so store them in the frame */
342 /* FIXME: this is not correct for single-submenu case */
343 fset_menu_bar_vector (f, menu_items);
344 f->menu_bar_items_used = menu_items_used;
346 /* Calls restore_menu_items, etc., as they were outside */
347 unbind_to (specpdl_count, Qnil);
349 /* Parse stage 2a: now GC cannot happen during the lifetime of the
350 widget_value, so it's safe to store data from a Lisp_String */
351 wv = first_wv->contents;
352 for (i = 0; i < ASIZE (items); i += 4)
355 string = AREF (items, i + 1);
358 /* if (submenu && strcmp (submenuTitle, SSDATA (string)))
361 wv->name = SSDATA (string);
362 update_submenu_strings (wv->contents);
366 /* Now, update the NS menu; if we have a submenu, use that, otherwise
367 create a new menu for each sub and fill it. */
370 for (wv = first_wv->contents; wv; wv = wv->next)
372 if (!strcmp (submenuTitle, wv->name))
374 [submenu fillWithWidgetValue: wv->contents];
375 last_submenu = submenu;
382 [menu fillWithWidgetValue: first_wv->contents];
388 static int n_previous_strings = 0;
389 static char previous_strings[100][10];
390 static struct frame *last_f = NULL;
394 wv = xmalloc_widget_value ();
395 wv->name = "menubar";
398 wv->button_type = BUTTON_TYPE_NONE;
402 /* Make widget-value tree w/ just the top level menu bar strings */
403 items = FRAME_MENU_BAR_ITEMS (f);
406 free_menubar_widget_value_tree (first_wv);
413 /* check if no change.. this mechanism is a bit rough, but ready */
414 n = ASIZE (items) / 4;
415 if (f == last_f && n_previous_strings == n)
417 for (i = 0; i<n; i++)
419 string = AREF (items, 4*i+1);
421 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
425 if (previous_strings[i][0])
430 else if (memcmp (previous_strings[i], SDATA (string),
431 min (10, SBYTES (string) + 1)))
437 free_menubar_widget_value_tree (first_wv);
445 for (i = 0; i < ASIZE (items); i += 4)
447 string = AREF (items, i + 1);
452 memcpy (previous_strings[i/4], SDATA (string),
453 min (10, SBYTES (string) + 1));
455 wv = xmalloc_widget_value ();
456 wv->name = SSDATA (string);
459 wv->button_type = BUTTON_TYPE_NONE;
461 wv->call_data = (void *) (intptr_t) (-1);
464 /* we'll update the real copy under app menu when time comes */
465 if (!strcmp ("Services", wv->name))
467 /* but we need to make sure it will update on demand */
468 [svcsMenu setFrame: f];
472 [menu addSubmenuWithTitle: wv->name forFrame: f];
477 first_wv->contents = wv;
483 n_previous_strings = n;
485 n_previous_strings = 0;
488 free_menubar_widget_value_tree (first_wv);
493 t += 1000*tb.time+tb.millitm;
494 fprintf (stderr, "Menu update took %ld msec.\n", t);
499 [NSApp setMainMenu: menu];
507 /* Main emacs core entry point for menubar menus: called to indicate that the
508 frame's menus have changed, and the *step representation should be updated
511 set_frame_menubar (struct frame *f, int first_time, int deep_p)
513 ns_update_menubar (f, deep_p, nil);
517 /* ==========================================================================
519 Menu: class implementation
521 ========================================================================== */
524 /* Menu that can define itself from Emacs "widget_value"s and will lazily
525 update itself when user clicked. Based on Carbon/AppKit implementation
526 by Yamamoto Mitsuharu. */
527 @implementation EmacsMenu
529 /* override designated initializer */
530 - initWithTitle: (NSString *)title
532 if ((self = [super initWithTitle: title]))
533 [self setAutoenablesItems: NO];
538 /* used for top-level */
539 - initWithTitle: (NSString *)title frame: (struct frame *)f
541 [self initWithTitle: title];
544 [self setDelegate: self];
550 - (void)setFrame: (struct frame *)f
556 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
557 extern NSString *NSMenuDidBeginTrackingNotification;
562 -(void)trackingNotification:(NSNotification *)notification
564 /* Update menu in menuNeedsUpdate only while tracking menus. */
565 trackingMenu = ([notification name] == NSMenuDidBeginTrackingNotification
570 /* delegate method called when a submenu is being opened: run a 'deep' call
571 to set_frame_menubar */
572 - (void)menuNeedsUpdate: (NSMenu *)menu
574 if (!FRAME_LIVE_P (frame))
577 /* Cocoa/Carbon will request update on every keystroke
578 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
579 since key equivalents are handled through emacs.
580 On Leopard, even keystroke events generate SystemDefined event.
581 Third-party applications that enhance mouse / trackpad
582 interaction, or also VNC/Remote Desktop will send events
583 of type AppDefined rather than SysDefined.
584 Menus will fail to show up if they haven't been initialized.
585 AppDefined events may lack timing data.
587 Thus, we rely on the didBeginTrackingNotification notification
588 as above to indicate the need for updates.
589 From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
590 key press case, NSMenuPropertyItemImage (e.g.) won't be set.
592 if (trackingMenu == 0
593 /* Also, don't try this if from an event picked up asynchronously,
594 as lots of lisp evaluation happens in ns_update_menubar. */
595 || handling_signal != 0)
597 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
598 ns_update_menubar (frame, 1, self);
602 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
604 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
605 && FRAME_NS_VIEW (SELECTED_FRAME ()))
606 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
611 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
612 into an accelerator string. We are only able to display a single character
613 for an accelerator, together with an optional modifier combination. (Under
614 Carbon more control was possible, but in Cocoa multi-char strings passed to
615 NSMenuItem get ignored. For now we try to display a super-single letter
616 combo, and return the others as strings to be appended to the item title.
617 (This is signaled by setting keyEquivModMask to 0 for now.) */
618 -(NSString *)parseKeyEquiv: (const char *)key
620 const char *tpos = key;
621 keyEquivModMask = NSCommandKeyMask;
623 if (!key || !strlen (key))
626 while (*tpos == ' ' || *tpos == '(')
628 if ((*tpos == 's') && (*(tpos+1) == '-'))
630 return [NSString stringWithFormat: @"%c", tpos[2]];
632 keyEquivModMask = 0; /* signal */
633 return [NSString stringWithUTF8String: tpos];
637 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
640 widget_value *wv = (widget_value *)wvptr;
642 if (menu_separator_name_p (wv->name))
644 item = [NSMenuItem separatorItem];
645 [self addItem: item];
649 NSString *title, *keyEq;
650 title = [NSString stringWithUTF8String: wv->name];
652 title = @"< ? >"; /* (get out in the open so we know about it) */
654 keyEq = [self parseKeyEquiv: wv->key];
656 /* OS X just ignores modifier strings longer than one character */
657 if (keyEquivModMask == 0)
658 title = [title stringByAppendingFormat: @" (%@)", keyEq];
661 item = [self addItemWithTitle: (NSString *)title
662 action: @selector (menuDown:)
663 keyEquivalent: keyEq];
664 [item setKeyEquivalentModifierMask: keyEquivModMask];
666 [item setEnabled: wv->enabled];
668 /* Draw radio buttons and tickboxes */
669 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
670 wv->button_type == BUTTON_TYPE_RADIO))
671 [item setState: NSOnState];
673 [item setState: NSOffState];
675 [item setTag: (NSInteger)wv->call_data];
687 for (n = [self numberOfItems]-1; n >= 0; n--)
689 NSMenuItem *item = [self itemAtIndex: n];
690 NSString *title = [item title];
691 if (([title length] == 0 /* OSX 10.5 */
692 || [ns_app_name isEqualToString: title] /* from 10.6 on */
693 || [@"Apple" isEqualToString: title]) /* older */
694 && ![item isSeparatorItem])
696 [self removeItemAtIndex: n];
701 - (void)fillWithWidgetValue: (void *)wvptr
703 widget_value *wv = (widget_value *)wvptr;
705 /* clear existing contents */
706 [self setMenuChangedMessagesEnabled: NO];
709 /* add new contents */
710 for (; wv != NULL; wv = wv->next)
712 NSMenuItem *item = [self addItemWithWidgetValue: wv];
716 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
718 [self setSubmenu: submenu forItem: item];
719 [submenu fillWithWidgetValue: wv->contents];
721 [item setAction: nil];
725 [self setMenuChangedMessagesEnabled: YES];
726 #ifdef NS_IMPL_GNUSTEP
727 if ([[self window] isVisible])
730 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_2
731 if ([self supermenu] == nil)
738 /* adds an empty submenu and returns it */
739 - (EmacsMenu *)addSubmenuWithTitle: (const char *)title forFrame: (struct frame *)f
741 NSString *titleStr = [NSString stringWithUTF8String: title];
742 NSMenuItem *item = [self addItemWithTitle: titleStr
743 action: nil /*@selector (menuDown:) */
745 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
746 [self setSubmenu: submenu forItem: item];
751 /* run a menu in popup mode */
752 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
753 keymaps: (int)keymaps
755 EmacsView *view = FRAME_NS_VIEW (f);
759 /* p = [view convertPoint:p fromView: nil]; */
760 p.y = NSHeight ([view frame]) - p.y;
761 e = [[view window] currentEvent];
762 event = [NSEvent mouseEventWithType: NSRightMouseDown
765 timestamp: [e timestamp]
766 windowNumber: [[view window] windowNumber]
768 eventNumber: 0/*[e eventNumber] */
772 context_menu_value = -1;
773 [NSMenu popUpContextMenu: self withEvent: event forView: view];
774 retVal = context_menu_value;
775 context_menu_value = 0;
777 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
785 /* ==========================================================================
787 Context Menu: implementing functions
789 ========================================================================== */
792 ns_menu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps,
793 Lisp_Object title, const char **error)
798 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
799 widget_value *wv, *first_wv = 0;
803 /* now parse stage 2 as in ns_update_menubar */
804 wv = xmalloc_widget_value ();
805 wv->name = "contextmenu";
808 wv->button_type = BUTTON_TYPE_NONE;
813 /* FIXME: a couple of one-line differences prevent reuse */
814 wv = digest_single_submenu (0, menu_items_used, Qnil);
817 widget_value *save_wv = 0, *prev_wv = 0;
818 widget_value **submenu_stack
819 = alloca (menu_items_used * sizeof *submenu_stack);
820 /* Lisp_Object *subprefix_stack
821 = alloca (menu_items_used * sizeof *subprefix_stack); */
822 int submenu_depth = 0;
826 /* Loop over all panes and items, filling in the tree. */
828 while (i < menu_items_used)
830 if (EQ (AREF (menu_items, i), Qnil))
832 submenu_stack[submenu_depth++] = save_wv;
838 else if (EQ (AREF (menu_items, i), Qlambda))
841 save_wv = submenu_stack[--submenu_depth];
845 else if (EQ (AREF (menu_items, i), Qt)
846 && submenu_depth != 0)
847 i += MENU_ITEMS_PANE_LENGTH;
848 /* Ignore a nil in the item list.
849 It's meaningful only for dialog boxes. */
850 else if (EQ (AREF (menu_items, i), Qquote))
852 else if (EQ (AREF (menu_items, i), Qt))
854 /* Create a new pane. */
855 Lisp_Object pane_name, prefix;
856 const char *pane_string;
858 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
859 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
861 #ifndef HAVE_MULTILINGUAL_MENU
862 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
864 pane_name = ENCODE_MENU_STRING (pane_name);
865 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
868 pane_string = (NILP (pane_name)
869 ? "" : SSDATA (pane_name));
870 /* If there is just one top-level pane, put all its items directly
871 under the top-level menu. */
872 if (menu_items_n_panes == 1)
875 /* If the pane has a meaningful name,
876 make the pane a top-level menu item
877 with its items as a submenu beneath it. */
878 if (!keymaps && strcmp (pane_string, ""))
880 wv = xmalloc_widget_value ();
884 first_wv->contents = wv;
885 wv->name = pane_string;
886 if (keymaps && !NILP (prefix))
890 wv->button_type = BUTTON_TYPE_NONE;
901 i += MENU_ITEMS_PANE_LENGTH;
905 /* Create a new item within current pane. */
906 Lisp_Object item_name, enable, descrip, def, type, selected, help;
907 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
908 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
909 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
910 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
911 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
912 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
913 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
915 #ifndef HAVE_MULTILINGUAL_MENU
916 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
918 item_name = ENCODE_MENU_STRING (item_name);
919 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
922 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
924 descrip = ENCODE_MENU_STRING (descrip);
925 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
927 #endif /* not HAVE_MULTILINGUAL_MENU */
929 wv = xmalloc_widget_value ();
933 save_wv->contents = wv;
934 wv->name = SSDATA (item_name);
936 wv->key = SSDATA (descrip);
938 /* If this item has a null value,
939 make the call_data null so that it won't display a box
940 when the mouse is on it. */
941 wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
942 wv->enabled = !NILP (enable);
945 wv->button_type = BUTTON_TYPE_NONE;
946 else if (EQ (type, QCtoggle))
947 wv->button_type = BUTTON_TYPE_TOGGLE;
948 else if (EQ (type, QCradio))
949 wv->button_type = BUTTON_TYPE_RADIO;
953 wv->selected = !NILP (selected);
955 if (! STRINGP (help))
962 i += MENU_ITEMS_ITEM_LENGTH;
970 widget_value *wv_title = xmalloc_widget_value ();
971 widget_value *wv_sep = xmalloc_widget_value ();
973 /* Maybe replace this separator with a bitmap or owner-draw item
974 so that it looks better. Having two separators looks odd. */
976 wv_sep->next = first_wv->contents;
979 #ifndef HAVE_MULTILINGUAL_MENU
980 if (STRING_MULTIBYTE (title))
981 title = ENCODE_MENU_STRING (title);
984 wv_title->name = SSDATA (title);
985 wv_title->enabled = NO;
986 wv_title->button_type = BUTTON_TYPE_NONE;
987 wv_title->help = Qnil;
988 wv_title->next = wv_sep;
989 first_wv->contents = wv_title;
992 pmenu = [[EmacsMenu alloc] initWithTitle:
993 [NSString stringWithUTF8String: SSDATA (title)]];
994 [pmenu fillWithWidgetValue: first_wv->contents];
995 free_menubar_widget_value_tree (first_wv);
996 unbind_to (specpdl_count, Qnil);
998 popup_activated_flag = 1;
999 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
1000 popup_activated_flag = 0;
1001 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1007 /* ==========================================================================
1009 Toolbar: externally-called functions
1011 ========================================================================== */
1014 free_frame_tool_bar (FRAME_PTR f)
1015 /* --------------------------------------------------------------------------
1016 Under NS we just hide the toolbar until it might be needed again.
1017 -------------------------------------------------------------------------- */
1020 [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
1021 FRAME_TOOLBAR_HEIGHT (f) = 0;
1026 update_frame_tool_bar (FRAME_PTR f)
1027 /* --------------------------------------------------------------------------
1028 Update toolbar contents
1029 -------------------------------------------------------------------------- */
1032 EmacsView *view = FRAME_NS_VIEW (f);
1033 NSWindow *window = [view window];
1034 EmacsToolbar *toolbar = [view toolbar];
1037 [toolbar clearActive];
1039 /* update EmacsToolbar as in GtkUtils, build items list */
1040 for (i = 0; i < f->n_tool_bar_items; ++i)
1042 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1043 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1045 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1050 Lisp_Object helpObj;
1051 const char *helpText;
1053 /* If image is a vector, choose the image according to the
1055 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1056 if (VECTORP (image))
1058 /* NS toolbar auto-computes disabled and selected images */
1059 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1060 eassert (ASIZE (image) >= idx);
1061 image = AREF (image, idx);
1067 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1069 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1070 helpText = NILP (helpObj) ? "" : SSDATA (helpObj);
1072 /* Ignore invalid image specifications. */
1073 if (!valid_image_p (image))
1075 /* Don't log anything, GNUS makes invalid images all the time. */
1079 img_id = lookup_image (f, image);
1080 img = IMAGE_FROM_ID (f, img_id);
1081 prepare_image_for_display (f, img);
1083 if (img->load_failed_p || img->pixmap == nil)
1085 NSLog (@"Could not prepare toolbar image for display.");
1089 [toolbar addDisplayItemWithImage: img->pixmap idx: i helpText: helpText
1090 enabled: enabled_p];
1094 if (![toolbar isVisible])
1095 [toolbar setVisible: YES];
1097 if ([toolbar changed])
1099 /* inform app that toolbar has changed */
1100 NSDictionary *dict = [toolbar configurationDictionary];
1101 NSMutableDictionary *newDict = [dict mutableCopy];
1102 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1104 while ((key = [keys nextObject]) != nil)
1106 NSObject *val = [dict objectForKey: key];
1107 if ([val isKindOfClass: [NSArray class]])
1110 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1115 [toolbar setConfigurationFromDictionary: newDict];
1119 FRAME_TOOLBAR_HEIGHT (f) =
1120 NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1121 - FRAME_NS_TITLEBAR_HEIGHT (f);
1126 /* ==========================================================================
1128 Toolbar: class implementation
1130 ========================================================================== */
1132 @implementation EmacsToolbar
1134 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1136 self = [super initWithIdentifier: identifier];
1138 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1139 [self setSizeMode: NSToolbarSizeModeSmall];
1140 [self setDelegate: self];
1141 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1142 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1143 prevEnablement = enablement = 0L;
1149 [prevIdentifiers release];
1150 [activeIdentifiers release];
1151 [identifierToItem release];
1155 - (void) clearActive
1157 [prevIdentifiers release];
1158 prevIdentifiers = [activeIdentifiers copy];
1159 [activeIdentifiers removeAllObjects];
1160 prevEnablement = enablement;
1166 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1167 enablement == prevEnablement ? NO : YES;
1170 - (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx
1171 helpText: (const char *)help enabled: (BOOL)enabled
1173 /* 1) come up w/identifier */
1174 NSString *identifier
1175 = [NSString stringWithFormat: @"%u", [img hash]];
1177 /* 2) create / reuse item */
1178 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1181 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1183 [item setImage: img];
1184 [item setToolTip: [NSString stringWithUTF8String: help]];
1185 [item setTarget: emacsView];
1186 [item setAction: @selector (toolbarClicked:)];
1190 [item setEnabled: enabled];
1192 /* 3) update state */
1193 [identifierToItem setObject: item forKey: identifier];
1194 [activeIdentifiers addObject: identifier];
1195 enablement = (enablement << 1) | (enabled == YES);
1198 /* This overrides super's implementation, which automatically sets
1199 all items to enabled state (for some reason). */
1200 - (void)validateVisibleItems { }
1203 /* delegate methods */
1205 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1206 itemForItemIdentifier: (NSString *)itemIdentifier
1207 willBeInsertedIntoToolbar: (BOOL)flag
1209 /* look up NSToolbarItem by identifier and return... */
1210 return [identifierToItem objectForKey: itemIdentifier];
1213 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1215 /* return entire set.. */
1216 return activeIdentifiers;
1219 /* for configuration palette (not yet supported) */
1220 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1222 /* return entire set... */
1223 return [identifierToItem allKeys];
1226 /* optional and unneeded */
1227 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1228 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1229 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1231 @end /* EmacsToolbar */
1235 /* ==========================================================================
1237 Tooltip: class implementation
1239 ========================================================================== */
1241 /* Needed because NeXTstep does not provide enough control over tooltip
1243 @implementation EmacsTooltip
1247 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1248 blue: 0.792 alpha: 0.95];
1249 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1250 NSFont *sfont = [font screenFont];
1251 int height = [sfont ascender] - [sfont descender];
1252 /*[font boundingRectForFont].size.height; */
1253 NSRect r = NSMakeRect (0, 0, 100, height+6);
1255 textField = [[NSTextField alloc] initWithFrame: r];
1256 [textField setFont: font];
1257 [textField setBackgroundColor: col];
1259 [textField setEditable: NO];
1260 [textField setSelectable: NO];
1261 [textField setBordered: NO];
1262 [textField setBezeled: NO];
1263 [textField setDrawsBackground: YES];
1265 win = [[NSWindow alloc]
1266 initWithContentRect: [textField frame]
1268 backing: NSBackingStoreBuffered
1270 [win setHasShadow: YES];
1271 [win setReleasedWhenClosed: NO];
1272 [win setDelegate: self];
1273 [[win contentView] addSubview: textField];
1274 /* [win setBackgroundColor: col]; */
1275 [win setOpaque: NO];
1284 [textField release];
1288 - (void) setText: (char *)text
1290 NSString *str = [NSString stringWithUTF8String: text];
1291 NSRect r = [textField frame];
1294 [textField setStringValue: str];
1295 tooltipDims = [[textField cell] cellSize];
1297 r.size.width = tooltipDims.width;
1298 r.size.height = tooltipDims.height;
1299 [textField setFrame: r];
1302 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1304 NSRect wr = [win frame];
1306 wr.origin = NSMakePoint (x, y);
1307 wr.size = [textField frame].size;
1309 [win setFrame: wr display: YES];
1310 [win orderFront: self];
1312 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1313 selector: @selector (hide)
1314 userInfo: nil repeats: NO];
1323 if ([timer isValid])
1332 return timer != nil;
1337 return [textField frame];
1340 @end /* EmacsTooltip */
1344 /* ==========================================================================
1346 Popup Dialog: implementing functions
1348 ========================================================================== */
1352 NSAutoreleasePool *pool;
1353 EmacsDialogPanel *dialog;
1357 pop_down_menu (Lisp_Object arg)
1359 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
1360 struct Popdown_data *unwind_data = (struct Popdown_data *) p->pointer;
1363 if (popup_activated_flag)
1365 EmacsDialogPanel *panel = unwind_data->dialog;
1366 popup_activated_flag = 0;
1368 [unwind_data->pool release];
1369 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1372 xfree (unwind_data);
1380 ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1383 Lisp_Object window, tem, title;
1387 NSAutoreleasePool *pool;
1389 NSTRACE (x-popup-dialog);
1393 isQ = NILP (header);
1395 if (EQ (position, Qt)
1396 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1397 || EQ (XCAR (position), Qtool_bar))))
1399 window = selected_window;
1401 else if (CONSP (position))
1404 tem = Fcar (position);
1405 if (XTYPE (tem) == Lisp_Cons)
1406 window = Fcar (Fcdr (position));
1409 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1410 window = Fcar (tem); /* POSN_WINDOW (tem) */
1413 else if (WINDOWP (position) || FRAMEP (position))
1420 if (FRAMEP (window))
1421 f = XFRAME (window);
1422 else if (WINDOWP (window))
1424 CHECK_LIVE_WINDOW (window);
1425 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1428 CHECK_WINDOW (window);
1430 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1431 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1433 title = Fcar (contents);
1434 CHECK_STRING (title);
1436 if (NILP (Fcar (Fcdr (contents))))
1437 /* No buttons specified, add an "Ok" button so users can pop down
1439 contents = Fcons (title, Fcons (Fcons (build_string ("Ok"), Qt), Qnil));
1442 pool = [[NSAutoreleasePool alloc] init];
1443 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1447 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1448 struct Popdown_data *unwind_data = xmalloc (sizeof (*unwind_data));
1450 unwind_data->pool = pool;
1451 unwind_data->dialog = dialog;
1453 record_unwind_protect (pop_down_menu, make_save_value (unwind_data, 0));
1454 popup_activated_flag = 1;
1455 tem = [dialog runDialogAt: p];
1456 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1465 /* ==========================================================================
1467 Popup Dialog: class implementation
1469 ========================================================================== */
1471 @interface FlippedView : NSView
1476 @implementation FlippedView
1483 @implementation EmacsDialogPanel
1486 #define ICONSIZE 64.0
1487 #define TEXTHEIGHT 20.0
1488 #define MINCELLWIDTH 90.0
1490 - initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
1491 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1493 NSSize spacing = {SPACER, SPACER};
1496 NSImageView *imgView;
1497 FlippedView *contentView;
1500 dialog_return = Qundefined;
1501 button_values = NULL;
1502 area.origin.x = 3*SPACER;
1503 area.origin.y = 2*SPACER;
1504 area.size.width = ICONSIZE;
1505 area.size.height= ICONSIZE;
1506 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1507 [img setScalesWhenResized: YES];
1508 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1509 imgView = [[NSImageView alloc] initWithFrame: area];
1510 [imgView setImage: img];
1511 [imgView setEditable: NO];
1513 [imgView autorelease];
1515 aStyle = NSTitledWindowMask;
1519 [super initWithContentRect: contentRect styleMask: aStyle
1520 backing: backingType defer: flag];
1521 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1522 [contentView autorelease];
1524 [self setContentView: contentView];
1526 [[self contentView] setAutoresizesSubviews: YES];
1528 [[self contentView] addSubview: imgView];
1529 [self setTitle: @""];
1531 area.origin.x += ICONSIZE+2*SPACER;
1532 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1533 area.size.width = 400;
1534 area.size.height= TEXTHEIGHT;
1535 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1536 [[self contentView] addSubview: command];
1537 [command setStringValue: ns_app_name];
1538 [command setDrawsBackground: NO];
1539 [command setBezeled: NO];
1540 [command setSelectable: NO];
1541 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1543 /* area.origin.x = ICONSIZE+2*SPACER;
1544 area.origin.y = TEXTHEIGHT + 2*SPACER;
1545 area.size.width = 400;
1546 area.size.height= 2;
1547 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1548 [[self contentView] addSubview: tem];
1549 [tem setTitlePosition: NSNoTitle];
1550 [tem setAutoresizingMask: NSViewWidthSizable];*/
1552 /* area.origin.x = ICONSIZE+2*SPACER; */
1553 area.origin.y += TEXTHEIGHT+SPACER;
1554 area.size.width = 400;
1555 area.size.height= TEXTHEIGHT;
1556 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1557 [[self contentView] addSubview: title];
1558 [title setDrawsBackground: NO];
1559 [title setBezeled: NO];
1560 [title setSelectable: NO];
1561 [title setFont: [NSFont systemFontOfSize: 11.0]];
1563 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1564 [cell setBordered: NO];
1565 [cell setEnabled: NO];
1566 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1567 [cell setBezelStyle: NSRoundedBezelStyle];
1569 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1570 mode: NSHighlightModeMatrix
1573 numberOfColumns: 1];
1574 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1575 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1576 [matrix setIntercellSpacing: spacing];
1577 [matrix autorelease];
1579 [[self contentView] addSubview: matrix];
1580 [self setOneShot: YES];
1581 [self setReleasedWhenClosed: YES];
1582 [self setHidesOnDeactivate: YES];
1584 NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask];
1590 - (BOOL)windowShouldClose: (id)sender
1592 window_closed = YES;
1599 xfree (button_values);
1603 - (void)process_dialog: (Lisp_Object) list
1605 Lisp_Object item, lst = list;
1607 int buttons = 0, btnnr = 0;
1609 for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
1612 if (XTYPE (item) == Lisp_Cons)
1617 button_values = (Lisp_Object *) xmalloc (buttons * sizeof (*button_values));
1619 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1622 if (XTYPE (item) == Lisp_String)
1624 [self addString: SSDATA (item) row: row++];
1626 else if (XTYPE (item) == Lisp_Cons)
1628 button_values[btnnr] = XCDR (item);
1629 [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
1632 else if (NILP (item))
1641 - (void)addButton: (char *)str value: (int)tag row: (int)row
1650 cell = [matrix cellAtRow: row column: cols-1];
1651 [cell setTarget: self];
1652 [cell setAction: @selector (clicked: )];
1653 [cell setTitle: [NSString stringWithUTF8String: str]];
1655 [cell setBordered: YES];
1656 [cell setEnabled: YES];
1660 - (void)addString: (char *)str row: (int)row
1669 cell = [matrix cellAtRow: row column: cols-1];
1670 [cell setTitle: [NSString stringWithUTF8String: str]];
1671 [cell setBordered: YES];
1672 [cell setEnabled: NO];
1683 - (void)clicked: sender
1685 NSArray *sellist = nil;
1688 sellist = [sender selectedCells];
1689 if ([sellist count] < 1)
1692 seltag = [[sellist objectAtIndex: 0] tag];
1693 dialog_return = button_values[seltag];
1698 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1703 if (XTYPE (contents) == Lisp_Cons)
1705 head = Fcar (contents);
1706 [self process_dialog: Fcdr (contents)];
1711 if (XTYPE (head) == Lisp_String)
1712 [title setStringValue:
1713 [NSString stringWithUTF8String: SSDATA (head)]];
1714 else if (isQ == YES)
1715 [title setStringValue: @"Question"];
1717 [title setStringValue: @"Information"];
1723 if (cols == 1 && rows > 1) /* Never told where to split */
1726 for (i = 0; i < rows/2; i++)
1728 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1729 atRow: i column: 1];
1730 [matrix removeRow: (rows+1)/2];
1736 NSSize csize = [matrix cellSize];
1737 if (csize.width < MINCELLWIDTH)
1739 csize.width = MINCELLWIDTH;
1740 [matrix setCellSize: csize];
1741 [matrix sizeToCells];
1746 [command sizeToFit];
1750 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1752 t.origin.x = r.origin.x;
1753 t.size.width = r.size.width;
1755 r = [command frame];
1756 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1758 t.origin.x = r.origin.x;
1759 t.size.width = r.size.width;
1763 s = [(NSView *)[self contentView] frame];
1764 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1765 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1766 [self setFrame: r display: NO];
1774 - (void)timeout_handler: (NSTimer *)timedEntry
1776 NSEvent *nxev = [NSEvent otherEventWithType: NSApplicationDefined
1777 location: NSMakePoint (0, 0)
1780 windowNumber: [[NSApp mainWindow] windowNumber]
1781 context: [NSApp context]
1787 /* We use sto because stopModal/abortModal out of the main loop does not
1788 seem to work in 10.6. But as we use stop we must send a real event so
1789 the stop is seen and acted upon. */
1791 [NSApp postEvent: nxev atStart: NO];
1794 - (Lisp_Object)runDialogAt: (NSPoint)p
1796 Lisp_Object ret = Qundefined;
1798 while (popup_activated_flag)
1801 EMACS_TIME next_time = timer_check ();
1803 if (EMACS_TIME_VALID_P (next_time))
1805 double time = EMACS_TIME_TO_DOUBLE (next_time);
1806 tmo = [NSTimer timerWithTimeInterval: time
1808 selector: @selector (timeout_handler:)
1811 [[NSRunLoop currentRunLoop] addTimer: tmo
1812 forMode: NSModalPanelRunLoopMode];
1815 dialog_return = Qundefined;
1816 [NSApp runModalForWindow: self];
1817 ret = dialog_return;
1820 if (tmo != nil) [tmo invalidate]; /* Cancels timer */
1825 if (EQ (ret, Qundefined) && window_closed)
1826 /* Make close button pressed equivalent to C-g. */
1827 Fsignal (Qquit, Qnil);
1835 /* ==========================================================================
1839 ========================================================================== */
1841 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1842 doc: /* Cause the NS menu to be re-calculated. */)
1845 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1850 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1851 doc: /* Pop up a dialog box and return user's selection.
1852 POSITION specifies which frame to use.
1853 This is normally a mouse button event or a window or frame.
1854 If POSITION is t, it means to use the frame the mouse is on.
1855 The dialog box appears in the middle of the specified frame.
1857 CONTENTS specifies the alternatives to display in the dialog box.
1858 It is a list of the form (DIALOG ITEM1 ITEM2...).
1859 Each ITEM is a cons cell (STRING . VALUE).
1860 The return value is VALUE from the chosen item.
1862 An ITEM may also be just a string--that makes a nonselectable item.
1863 An ITEM may also be nil--that means to put all preceding items
1864 on the left of the dialog box and all following items on the right.
1865 \(By default, approximately half appear on each side.)
1867 If HEADER is non-nil, the frame title for the box is "Information",
1868 otherwise it is "Question".
1870 If the user gets rid of the dialog box without making a valid choice,
1871 for instance using the window manager, then this produces a quit and
1872 `x-popup-dialog' does not return. */)
1873 (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1875 return ns_popup_dialog (position, contents, header);
1878 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1879 doc: /* Return t if a menu or popup dialog is active. */)
1882 return popup_activated () ? Qt : Qnil;
1885 /* ==========================================================================
1887 Lisp interface declaration
1889 ========================================================================== */
1892 syms_of_nsmenu (void)
1894 #ifndef NS_IMPL_COCOA
1895 /* Don't know how to keep track of this in Next/Open/Gnustep. Always
1896 update menus there. */
1899 defsubr (&Sx_popup_dialog);
1900 defsubr (&Sns_reset_menu);
1901 defsubr (&Smenu_or_popup_active_p);
1903 Qdebug_on_next_call = intern_c_string ("debug-on-next-call");
1904 staticpro (&Qdebug_on_next_call);