1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007-2013 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
20 By Adrian Robert, based on code from original nsmenu.m (Carl Edman,
21 Christian Limpach, Scott Bender, Christophe de Dinechin) and code in the
22 Carbon version by Yamamoto Mitsuharu. */
24 /* This should be the first include, as it may set up #defines affecting
25 interpretation of even the system includes. */
30 #include "character.h"
35 #include "blockinput.h"
37 #include "termhooks.h"
41 #define NSMENUPROFILE 0
44 #include <sys/timeb.h>
45 #include <sys/types.h>
49 int menu_trace_num = 0;
50 #define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \
51 __FILE__, __LINE__, ++menu_trace_num)
57 /* Include lisp -> C common menu parsing code */
58 #define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
59 #include "nsmenu_common.c"
62 extern Lisp_Object Qundefined, Qmenu_enable, Qmenu_bar_update_hook;
63 extern Lisp_Object QCtoggle, QCradio;
65 Lisp_Object Qdebug_on_next_call;
66 extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
68 extern long context_menu_value;
69 EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
71 /* Nonzero means a menu is currently active. */
72 static int popup_activated_flag;
74 /* Nonzero means we are tracking and updating menus. */
75 static int trackingMenu;
78 /* NOTE: toolbar implementation is at end,
79 following complete menu implementation. */
82 /* ==========================================================================
84 Menu: Externally-called functions
86 ========================================================================== */
89 /* Supposed to discard menubar and free storage. Since we share the
90 menubar among frames and update its context for the focused window,
91 there is nothing to do here. */
93 free_frame_menubar (struct frame *f)
100 popup_activated (void)
102 return popup_activated_flag;
106 /* --------------------------------------------------------------------------
107 Update menubar. Three cases:
108 1) ! deep_p, submenu = nil: Fresh switch onto a frame -- either set up
109 just top-level menu strings (OS X), or goto case (2) (GNUstep).
110 2) deep_p, submenu = nil: Recompute all submenus.
111 3) deep_p, submenu = non-nil: Update contents of a single submenu.
112 -------------------------------------------------------------------------- */
114 ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
116 NSAutoreleasePool *pool;
117 id menu = [NSApp mainMenu];
118 static EmacsMenu *last_submenu = nil;
122 widget_value *wv, *first_wv, *prev_wv = 0;
130 NSTRACE (ns_update_menubar);
132 if (f != SELECTED_FRAME ())
134 XSETFRAME (Vmenu_updating_frame, f);
135 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
138 pool = [[NSAutoreleasePool alloc] init];
140 /* Menu may have been created automatically; if so, discard it. */
141 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
149 menu = [[EmacsMenu alloc] initWithTitle: ns_app_name];
153 { /* close up anything on there */
154 id attMenu = [menu attachedMenu];
161 t = -(1000*tb.time+tb.millitm);
164 #ifdef NS_IMPL_GNUSTEP
165 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
170 /* Fully parse one or more of the submenus. */
172 int *submenu_start, *submenu_end;
173 bool *submenu_top_level_items;
174 int *submenu_n_panes;
175 struct buffer *prev = current_buffer;
177 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
178 int previous_menu_items_used = f->menu_bar_items_used;
179 Lisp_Object *previous_items
180 = alloca (previous_menu_items_used * sizeof *previous_items);
182 /* lisp preliminaries */
183 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
184 specbind (Qinhibit_quit, Qt);
185 specbind (Qdebug_on_next_call, Qnil);
186 record_unwind_save_match_data ();
187 if (NILP (Voverriding_local_map_menu_flag))
189 specbind (Qoverriding_terminal_local_map, Qnil);
190 specbind (Qoverriding_local_map, Qnil);
192 set_buffer_internal_1 (XBUFFER (buffer));
194 /* TODO: for some reason this is not needed in other terms,
195 but some menu updates call Info-extract-pointer which causes
196 abort-on-error if waiting-for-input. Needs further investigation. */
197 owfi = waiting_for_input;
198 waiting_for_input = 0;
200 /* lucid hook and possible reset */
201 safe_run_hooks (Qactivate_menubar_hook);
202 if (! NILP (Vlucid_menu_bar_dirty_flag))
203 call0 (Qrecompute_lucid_menubar);
204 safe_run_hooks (Qmenu_bar_update_hook);
205 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
207 /* Now ready to go */
208 items = FRAME_MENU_BAR_ITEMS (f);
210 /* Save the frame's previous menu bar contents data */
211 if (previous_menu_items_used)
212 memcpy (previous_items, aref_addr (f->menu_bar_vector, 0),
213 previous_menu_items_used * sizeof (Lisp_Object));
215 /* parse stage 1: extract from lisp */
218 menu_items = f->menu_bar_vector;
219 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
220 submenu_start = alloca (ASIZE (items) * sizeof *submenu_start);
221 submenu_end = alloca (ASIZE (items) * sizeof *submenu_end);
222 submenu_n_panes = alloca (ASIZE (items) * sizeof *submenu_n_panes);
223 submenu_top_level_items = alloca (ASIZE (items)
224 * sizeof *submenu_top_level_items);
226 for (i = 0; i < ASIZE (items); i += 4)
228 Lisp_Object key, string, maps;
230 key = AREF (items, i);
231 string = AREF (items, i + 1);
232 maps = AREF (items, i + 2);
236 /* FIXME: we'd like to only parse the needed submenu, but this
237 was causing crashes in the _common parsing code.. need to make
238 sure proper initialization done.. */
239 /* if (submenu && strcmp ([[submenu title] UTF8String], SSDATA (string)))
242 submenu_start[i] = menu_items_used;
244 menu_items_n_panes = 0;
245 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
246 submenu_n_panes[i] = menu_items_n_panes;
247 submenu_end[i] = menu_items_used;
251 finish_menu_items ();
252 waiting_for_input = owfi;
255 if (submenu && n == 0)
257 /* should have found a menu for this one but didn't */
258 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
259 [[submenu title] UTF8String]);
260 discard_menu_items ();
261 unbind_to (specpdl_count, Qnil);
267 /* parse stage 2: insert into lucid 'widget_value' structures
268 [comments in other terms say not to evaluate lisp code here] */
269 wv = xmalloc_widget_value ();
270 wv->name = "menubar";
273 wv->button_type = BUTTON_TYPE_NONE;
277 for (i = 0; i < 4*n; i += 4)
279 menu_items_n_panes = submenu_n_panes[i];
280 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
281 submenu_top_level_items[i]);
285 first_wv->contents = wv;
286 /* Don't set wv->name here; GC during the loop might relocate it. */
288 wv->button_type = BUTTON_TYPE_NONE;
292 set_buffer_internal_1 (prev);
294 /* Compare the new menu items with previous, and leave off if no change */
295 /* FIXME: following other terms here, but seems like this should be
296 done before parse stage 2 above, since its results aren't used */
297 if (previous_menu_items_used
298 && (!submenu || (submenu && submenu == last_submenu))
299 && menu_items_used == previous_menu_items_used)
301 for (i = 0; i < previous_menu_items_used; i++)
302 /* FIXME: this ALWAYS fails on Buffers menu items.. something
303 about their strings causes them to change every time, so we
304 double-check failures */
305 if (!EQ (previous_items[i], AREF (menu_items, i)))
306 if (!(STRINGP (previous_items[i])
307 && STRINGP (AREF (menu_items, i))
308 && !strcmp (SSDATA (previous_items[i]),
309 SSDATA (AREF (menu_items, i)))))
311 if (i == previous_menu_items_used)
317 t += 1000*tb.time+tb.millitm;
318 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
321 free_menubar_widget_value_tree (first_wv);
322 discard_menu_items ();
323 unbind_to (specpdl_count, Qnil);
329 /* The menu items are different, so store them in the frame */
330 /* FIXME: this is not correct for single-submenu case */
331 fset_menu_bar_vector (f, menu_items);
332 f->menu_bar_items_used = menu_items_used;
334 /* Calls restore_menu_items, etc., as they were outside */
335 unbind_to (specpdl_count, Qnil);
337 /* Parse stage 2a: now GC cannot happen during the lifetime of the
338 widget_value, so it's safe to store data from a Lisp_String */
339 wv = first_wv->contents;
340 for (i = 0; i < ASIZE (items); i += 4)
343 string = AREF (items, i + 1);
347 wv->name = SSDATA (string);
348 update_submenu_strings (wv->contents);
352 /* Now, update the NS menu; if we have a submenu, use that, otherwise
353 create a new menu for each sub and fill it. */
356 const char *submenuTitle = [[submenu title] UTF8String];
357 for (wv = first_wv->contents; wv; wv = wv->next)
359 if (!strcmp (submenuTitle, wv->name))
361 [submenu fillWithWidgetValue: wv->contents];
362 last_submenu = submenu;
369 [menu fillWithWidgetValue: first_wv->contents];
375 static int n_previous_strings = 0;
376 static char previous_strings[100][10];
377 static struct frame *last_f = NULL;
381 wv = xmalloc_widget_value ();
382 wv->name = "menubar";
385 wv->button_type = BUTTON_TYPE_NONE;
389 /* Make widget-value tree w/ just the top level menu bar strings */
390 items = FRAME_MENU_BAR_ITEMS (f);
393 free_menubar_widget_value_tree (first_wv);
400 /* check if no change.. this mechanism is a bit rough, but ready */
401 n = ASIZE (items) / 4;
402 if (f == last_f && n_previous_strings == n)
404 for (i = 0; i<n; i++)
406 string = AREF (items, 4*i+1);
408 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
412 if (previous_strings[i][0])
417 else if (memcmp (previous_strings[i], SDATA (string),
418 min (10, SBYTES (string) + 1)))
424 free_menubar_widget_value_tree (first_wv);
432 for (i = 0; i < ASIZE (items); i += 4)
434 string = AREF (items, i + 1);
439 memcpy (previous_strings[i/4], SDATA (string),
440 min (10, SBYTES (string) + 1));
442 wv = xmalloc_widget_value ();
443 wv->name = SSDATA (string);
446 wv->button_type = BUTTON_TYPE_NONE;
448 wv->call_data = (void *) (intptr_t) (-1);
451 /* we'll update the real copy under app menu when time comes */
452 if (!strcmp ("Services", wv->name))
454 /* but we need to make sure it will update on demand */
455 [svcsMenu setFrame: f];
459 [menu addSubmenuWithTitle: wv->name forFrame: f];
464 first_wv->contents = wv;
470 n_previous_strings = n;
472 n_previous_strings = 0;
475 free_menubar_widget_value_tree (first_wv);
480 t += 1000*tb.time+tb.millitm;
481 fprintf (stderr, "Menu update took %ld msec.\n", t);
486 [NSApp setMainMenu: menu];
494 /* Main emacs core entry point for menubar menus: called to indicate that the
495 frame's menus have changed, and the *step representation should be updated
498 set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
500 ns_update_menubar (f, deep_p, nil);
504 x_activate_menubar (struct frame *f)
507 NSArray *a = [[NSApp mainMenu] itemArray];
508 /* Update each submenu separately so ns_update_menubar doesn't reset
511 while (i < [a count])
513 EmacsMenu *menu = (EmacsMenu *)[[a objectAtIndex:i] submenu];
514 const char *title = [[menu title] UTF8String];
515 if (strcmp (title, ns_get_pending_menu_title ()) == 0)
517 ns_update_menubar (f, true, menu);
522 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: (SEL)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: (SEL)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];
1055 #ifdef NS_IMPL_COCOA
1056 [toolbar clearActive];
1061 /* update EmacsToolbar as in GtkUtils, build items list */
1062 for (i = 0; i < f->n_tool_bar_items; ++i)
1064 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1065 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1067 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1072 Lisp_Object helpObj;
1073 const char *helpText;
1075 /* Check if this is a separator. */
1076 if (EQ (TOOLPROP (TOOL_BAR_ITEM_TYPE), Qt))
1078 /* Skip separators. Newer OSX don't show them, and on GNUStep they
1079 are wide as a button, thus overflowing the toolbar most of
1084 /* If image is a vector, choose the image according to the
1086 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1087 if (VECTORP (image))
1089 /* NS toolbar auto-computes disabled and selected images */
1090 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1091 eassert (ASIZE (image) >= idx);
1092 image = AREF (image, idx);
1098 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1100 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1101 helpText = NILP (helpObj) ? "" : SSDATA (helpObj);
1103 /* Ignore invalid image specifications. */
1104 if (!valid_image_p (image))
1106 /* Don't log anything, GNUS makes invalid images all the time. */
1110 img_id = lookup_image (f, image);
1111 img = IMAGE_FROM_ID (f, img_id);
1112 prepare_image_for_display (f, img);
1114 if (img->load_failed_p || img->pixmap == nil)
1116 NSLog (@"Could not prepare toolbar image for display.");
1120 [toolbar addDisplayItemWithImage: img->pixmap
1124 enabled: enabled_p];
1128 if (![toolbar isVisible])
1129 [toolbar setVisible: YES];
1131 #ifdef NS_IMPL_COCOA
1132 if ([toolbar changed])
1134 /* inform app that toolbar has changed */
1135 NSDictionary *dict = [toolbar configurationDictionary];
1136 NSMutableDictionary *newDict = [dict mutableCopy];
1137 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1139 while ((key = [keys nextObject]) != nil)
1141 NSObject *val = [dict objectForKey: key];
1142 if ([val isKindOfClass: [NSArray class]])
1145 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1150 [toolbar setConfigurationFromDictionary: newDict];
1155 FRAME_TOOLBAR_HEIGHT (f) =
1156 NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1157 - FRAME_NS_TITLEBAR_HEIGHT (f);
1158 if (FRAME_TOOLBAR_HEIGHT (f) < 0) // happens if frame is fullscreen.
1159 FRAME_TOOLBAR_HEIGHT (f) = 0;
1164 /* ==========================================================================
1166 Toolbar: class implementation
1168 ========================================================================== */
1170 @implementation EmacsToolbar
1172 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1174 self = [super initWithIdentifier: identifier];
1176 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1177 [self setSizeMode: NSToolbarSizeModeSmall];
1178 [self setDelegate: self];
1179 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1180 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1181 prevIdentifiers = nil;
1182 prevEnablement = enablement = 0L;
1188 [prevIdentifiers release];
1189 [activeIdentifiers release];
1190 [identifierToItem release];
1194 - (void) clearActive
1196 [prevIdentifiers release];
1197 prevIdentifiers = [activeIdentifiers copy];
1198 [activeIdentifiers removeAllObjects];
1199 prevEnablement = enablement;
1206 while ([[self items] count] > 0)
1207 [self removeItemAtIndex: 0];
1212 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1213 enablement == prevEnablement ? NO : YES;
1216 - (void) addDisplayItemWithImage: (EmacsImage *)img
1219 helpText: (const char *)help
1220 enabled: (BOOL)enabled
1222 /* 1) come up w/identifier */
1223 NSString *identifier
1224 = [NSString stringWithFormat: @"%u", [img hash]];
1225 [activeIdentifiers addObject: identifier];
1227 /* 2) create / reuse item */
1228 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1231 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1233 [item setImage: img];
1234 [item setToolTip: [NSString stringWithUTF8String: help]];
1235 [item setTarget: emacsView];
1236 [item setAction: @selector (toolbarClicked:)];
1237 [identifierToItem setObject: item forKey: identifier];
1240 #ifdef NS_IMPL_GNUSTEP
1241 [self insertItemWithItemIdentifier: identifier atIndex: idx];
1245 [item setEnabled: enabled];
1247 /* 3) update state */
1248 enablement = (enablement << 1) | (enabled == YES);
1251 /* This overrides super's implementation, which automatically sets
1252 all items to enabled state (for some reason). */
1253 - (void)validateVisibleItems
1258 /* delegate methods */
1260 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1261 itemForItemIdentifier: (NSString *)itemIdentifier
1262 willBeInsertedIntoToolbar: (BOOL)flag
1264 /* look up NSToolbarItem by identifier and return... */
1265 return [identifierToItem objectForKey: itemIdentifier];
1268 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1270 /* return entire set.. */
1271 return activeIdentifiers;
1274 /* for configuration palette (not yet supported) */
1275 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1277 /* return entire set... */
1278 return activeIdentifiers;
1279 //return [identifierToItem allKeys];
1282 /* optional and unneeded */
1283 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1284 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1285 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1287 @end /* EmacsToolbar */
1291 /* ==========================================================================
1293 Tooltip: class implementation
1295 ========================================================================== */
1297 /* Needed because NeXTstep does not provide enough control over tooltip
1299 @implementation EmacsTooltip
1303 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1304 blue: 0.792 alpha: 0.95];
1305 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1306 NSFont *sfont = [font screenFont];
1307 int height = [sfont ascender] - [sfont descender];
1308 /*[font boundingRectForFont].size.height; */
1309 NSRect r = NSMakeRect (0, 0, 100, height+6);
1311 textField = [[NSTextField alloc] initWithFrame: r];
1312 [textField setFont: font];
1313 [textField setBackgroundColor: col];
1315 [textField setEditable: NO];
1316 [textField setSelectable: NO];
1317 [textField setBordered: NO];
1318 [textField setBezeled: NO];
1319 [textField setDrawsBackground: YES];
1321 win = [[NSWindow alloc]
1322 initWithContentRect: [textField frame]
1324 backing: NSBackingStoreBuffered
1326 [win setHasShadow: YES];
1327 [win setReleasedWhenClosed: NO];
1328 [win setDelegate: self];
1329 [[win contentView] addSubview: textField];
1330 /* [win setBackgroundColor: col]; */
1331 [win setOpaque: NO];
1340 [textField release];
1344 - (void) setText: (char *)text
1346 NSString *str = [NSString stringWithUTF8String: text];
1347 NSRect r = [textField frame];
1350 [textField setStringValue: str];
1351 tooltipDims = [[textField cell] cellSize];
1353 r.size.width = tooltipDims.width;
1354 r.size.height = tooltipDims.height;
1355 [textField setFrame: r];
1358 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1360 NSRect wr = [win frame];
1362 wr.origin = NSMakePoint (x, y);
1363 wr.size = [textField frame].size;
1365 [win setFrame: wr display: YES];
1366 [win setLevel: NSPopUpMenuWindowLevel];
1367 [win orderFront: self];
1369 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1370 selector: @selector (hide)
1371 userInfo: nil repeats: NO];
1380 if ([timer isValid])
1389 return timer != nil;
1394 return [textField frame];
1397 @end /* EmacsTooltip */
1401 /* ==========================================================================
1403 Popup Dialog: implementing functions
1405 ========================================================================== */
1409 NSAutoreleasePool *pool;
1410 EmacsDialogPanel *dialog;
1414 pop_down_menu (void *arg)
1416 struct Popdown_data *unwind_data = arg;
1419 if (popup_activated_flag)
1421 EmacsDialogPanel *panel = unwind_data->dialog;
1422 popup_activated_flag = 0;
1424 [unwind_data->pool release];
1425 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1428 xfree (unwind_data);
1434 ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1437 Lisp_Object window, tem, title;
1441 NSAutoreleasePool *pool;
1443 NSTRACE (x-popup-dialog);
1445 isQ = NILP (header);
1447 if (EQ (position, Qt)
1448 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1449 || EQ (XCAR (position), Qtool_bar))))
1451 window = selected_window;
1453 else if (CONSP (position))
1456 tem = Fcar (position);
1457 if (XTYPE (tem) == Lisp_Cons)
1458 window = Fcar (Fcdr (position));
1461 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1462 window = Fcar (tem); /* POSN_WINDOW (tem) */
1465 else if (WINDOWP (position) || FRAMEP (position))
1472 if (FRAMEP (window))
1473 f = XFRAME (window);
1474 else if (WINDOWP (window))
1476 CHECK_LIVE_WINDOW (window);
1477 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1480 CHECK_WINDOW (window);
1482 check_window_system (f);
1484 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1485 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1487 title = Fcar (contents);
1488 CHECK_STRING (title);
1490 if (NILP (Fcar (Fcdr (contents))))
1491 /* No buttons specified, add an "Ok" button so users can pop down
1493 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
1496 pool = [[NSAutoreleasePool alloc] init];
1497 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1501 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1502 struct Popdown_data *unwind_data = xmalloc (sizeof (*unwind_data));
1504 unwind_data->pool = pool;
1505 unwind_data->dialog = dialog;
1507 record_unwind_protect_ptr (pop_down_menu, unwind_data);
1508 popup_activated_flag = 1;
1509 tem = [dialog runDialogAt: p];
1510 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1519 /* ==========================================================================
1521 Popup Dialog: class implementation
1523 ========================================================================== */
1525 @interface FlippedView : NSView
1530 @implementation FlippedView
1537 @implementation EmacsDialogPanel
1540 #define ICONSIZE 64.0
1541 #define TEXTHEIGHT 20.0
1542 #define MINCELLWIDTH 90.0
1544 - initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
1545 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1547 NSSize spacing = {SPACER, SPACER};
1550 NSImageView *imgView;
1551 FlippedView *contentView;
1554 dialog_return = Qundefined;
1555 button_values = NULL;
1556 area.origin.x = 3*SPACER;
1557 area.origin.y = 2*SPACER;
1558 area.size.width = ICONSIZE;
1559 area.size.height= ICONSIZE;
1560 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1561 [img setScalesWhenResized: YES];
1562 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1563 imgView = [[NSImageView alloc] initWithFrame: area];
1564 [imgView setImage: img];
1565 [imgView setEditable: NO];
1567 [imgView autorelease];
1569 aStyle = NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask;
1573 [super initWithContentRect: contentRect styleMask: aStyle
1574 backing: backingType defer: flag];
1575 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1576 [contentView autorelease];
1578 [self setContentView: contentView];
1580 [[self contentView] setAutoresizesSubviews: YES];
1582 [[self contentView] addSubview: imgView];
1583 [self setTitle: @""];
1585 area.origin.x += ICONSIZE+2*SPACER;
1586 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1587 area.size.width = 400;
1588 area.size.height= TEXTHEIGHT;
1589 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1590 [[self contentView] addSubview: command];
1591 [command setStringValue: ns_app_name];
1592 [command setDrawsBackground: NO];
1593 [command setBezeled: NO];
1594 [command setSelectable: NO];
1595 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1597 /* area.origin.x = ICONSIZE+2*SPACER;
1598 area.origin.y = TEXTHEIGHT + 2*SPACER;
1599 area.size.width = 400;
1600 area.size.height= 2;
1601 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1602 [[self contentView] addSubview: tem];
1603 [tem setTitlePosition: NSNoTitle];
1604 [tem setAutoresizingMask: NSViewWidthSizable];*/
1606 /* area.origin.x = ICONSIZE+2*SPACER; */
1607 area.origin.y += TEXTHEIGHT+SPACER;
1608 area.size.width = 400;
1609 area.size.height= TEXTHEIGHT;
1610 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1611 [[self contentView] addSubview: title];
1612 [title setDrawsBackground: NO];
1613 [title setBezeled: NO];
1614 [title setSelectable: NO];
1615 [title setFont: [NSFont systemFontOfSize: 11.0]];
1617 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1618 [cell setBordered: NO];
1619 [cell setEnabled: NO];
1620 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1621 [cell setBezelStyle: NSRoundedBezelStyle];
1623 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1624 mode: NSHighlightModeMatrix
1627 numberOfColumns: 1];
1628 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1629 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1630 [matrix setIntercellSpacing: spacing];
1631 [matrix autorelease];
1633 [[self contentView] addSubview: matrix];
1634 [self setOneShot: YES];
1635 [self setReleasedWhenClosed: YES];
1636 [self setHidesOnDeactivate: YES];
1641 - (BOOL)windowShouldClose: (id)sender
1643 window_closed = YES;
1650 xfree (button_values);
1654 - (void)process_dialog: (Lisp_Object) list
1656 Lisp_Object item, lst = list;
1658 int buttons = 0, btnnr = 0;
1660 for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
1663 if (XTYPE (item) == Lisp_Cons)
1668 button_values = (Lisp_Object *) xmalloc (buttons * sizeof (*button_values));
1670 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1673 if (XTYPE (item) == Lisp_String)
1675 [self addString: SSDATA (item) row: row++];
1677 else if (XTYPE (item) == Lisp_Cons)
1679 button_values[btnnr] = XCDR (item);
1680 [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
1683 else if (NILP (item))
1692 - (void)addButton: (char *)str value: (int)tag row: (int)row
1701 cell = [matrix cellAtRow: row column: cols-1];
1702 [cell setTarget: self];
1703 [cell setAction: @selector (clicked: )];
1704 [cell setTitle: [NSString stringWithUTF8String: str]];
1706 [cell setBordered: YES];
1707 [cell setEnabled: YES];
1711 - (void)addString: (char *)str row: (int)row
1720 cell = [matrix cellAtRow: row column: cols-1];
1721 [cell setTitle: [NSString stringWithUTF8String: str]];
1722 [cell setBordered: YES];
1723 [cell setEnabled: NO];
1734 - (void)clicked: sender
1736 NSArray *sellist = nil;
1739 sellist = [sender selectedCells];
1740 if ([sellist count] < 1)
1743 seltag = [[sellist objectAtIndex: 0] tag];
1744 dialog_return = button_values[seltag];
1749 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1754 if (XTYPE (contents) == Lisp_Cons)
1756 head = Fcar (contents);
1757 [self process_dialog: Fcdr (contents)];
1762 if (XTYPE (head) == Lisp_String)
1763 [title setStringValue:
1764 [NSString stringWithUTF8String: SSDATA (head)]];
1765 else if (isQ == YES)
1766 [title setStringValue: @"Question"];
1768 [title setStringValue: @"Information"];
1774 if (cols == 1 && rows > 1) /* Never told where to split */
1777 for (i = 0; i < rows/2; i++)
1779 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1780 atRow: i column: 1];
1781 [matrix removeRow: (rows+1)/2];
1787 NSSize csize = [matrix cellSize];
1788 if (csize.width < MINCELLWIDTH)
1790 csize.width = MINCELLWIDTH;
1791 [matrix setCellSize: csize];
1792 [matrix sizeToCells];
1797 [command sizeToFit];
1801 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1803 t.origin.x = r.origin.x;
1804 t.size.width = r.size.width;
1806 r = [command frame];
1807 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1809 t.origin.x = r.origin.x;
1810 t.size.width = r.size.width;
1814 s = [(NSView *)[self contentView] frame];
1815 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1816 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1817 [self setFrame: r display: NO];
1825 - (void)timeout_handler: (NSTimer *)timedEntry
1827 NSEvent *nxev = [NSEvent otherEventWithType: NSApplicationDefined
1828 location: NSMakePoint (0, 0)
1831 windowNumber: [[NSApp mainWindow] windowNumber]
1832 context: [NSApp context]
1838 /* We use sto because stopModal/abortModal out of the main loop does not
1839 seem to work in 10.6. But as we use stop we must send a real event so
1840 the stop is seen and acted upon. */
1842 [NSApp postEvent: nxev atStart: NO];
1845 - (Lisp_Object)runDialogAt: (NSPoint)p
1847 Lisp_Object ret = Qundefined;
1849 while (popup_activated_flag)
1852 EMACS_TIME next_time = timer_check ();
1854 if (EMACS_TIME_VALID_P (next_time))
1856 double time = EMACS_TIME_TO_DOUBLE (next_time);
1857 tmo = [NSTimer timerWithTimeInterval: time
1859 selector: @selector (timeout_handler:)
1862 [[NSRunLoop currentRunLoop] addTimer: tmo
1863 forMode: NSModalPanelRunLoopMode];
1866 dialog_return = Qundefined;
1867 [NSApp runModalForWindow: self];
1868 ret = dialog_return;
1871 if (tmo != nil) [tmo invalidate]; /* Cancels timer */
1876 if (EQ (ret, Qundefined) && window_closed)
1877 /* Make close button pressed equivalent to C-g. */
1878 Fsignal (Qquit, Qnil);
1886 /* ==========================================================================
1890 ========================================================================== */
1892 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1893 doc: /* Cause the NS menu to be re-calculated. */)
1896 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1901 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1902 doc: /* Pop up a dialog box and return user's selection.
1903 POSITION specifies which frame to use.
1904 This is normally a mouse button event or a window or frame.
1905 If POSITION is t, it means to use the frame the mouse is on.
1906 The dialog box appears in the middle of the specified frame.
1908 CONTENTS specifies the alternatives to display in the dialog box.
1909 It is a list of the form (DIALOG ITEM1 ITEM2...).
1910 Each ITEM is a cons cell (STRING . VALUE).
1911 The return value is VALUE from the chosen item.
1913 An ITEM may also be just a string--that makes a nonselectable item.
1914 An ITEM may also be nil--that means to put all preceding items
1915 on the left of the dialog box and all following items on the right.
1916 \(By default, approximately half appear on each side.)
1918 If HEADER is non-nil, the frame title for the box is "Information",
1919 otherwise it is "Question".
1921 If the user gets rid of the dialog box without making a valid choice,
1922 for instance using the window manager, then this produces a quit and
1923 `x-popup-dialog' does not return. */)
1924 (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1926 return ns_popup_dialog (position, contents, header);
1929 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1930 doc: /* Return t if a menu or popup dialog is active. */)
1933 return popup_activated () ? Qt : Qnil;
1936 /* ==========================================================================
1938 Lisp interface declaration
1940 ========================================================================== */
1943 syms_of_nsmenu (void)
1945 #ifndef NS_IMPL_COCOA
1946 /* Don't know how to keep track of this in Next/Open/Gnustep. Always
1947 update menus there. */
1950 defsubr (&Sx_popup_dialog);
1951 defsubr (&Sns_reset_menu);
1952 defsubr (&Smenu_or_popup_active_p);
1954 Qdebug_on_next_call = intern_c_string ("debug-on-next-call");
1955 staticpro (&Qdebug_on_next_call);