1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007, 2008, 2009 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. */
34 #include "blockinput.h"
36 #include "termhooks.h"
39 #define NSMENUPROFILE 0
42 #include <sys/timeb.h>
43 #include <sys/types.h>
46 #define MenuStagger 10.0
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 extern Lisp_Object Vmenu_updating_frame;
67 Lisp_Object Qdebug_on_next_call;
68 extern Lisp_Object Voverriding_local_map, Voverriding_local_map_menu_flag,
69 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;
76 static NSModalSession popupSession;
78 /* NOTE: toolbar implementation is at end,
79 following complete menu implementation. */
82 /* ==========================================================================
84 Menu: Externally-called functions
86 ========================================================================== */
89 /* FIXME: not currently used, but should normalize with other terms. */
91 x_activate_menubar (struct frame *f)
93 fprintf (stderr, "XXX: Received x_activate_menubar event.\n");
97 /* Supposed to discard menubar and free storage. Since we share the
98 menubar among frames and update its context for the focused window,
99 there is nothing to do here. */
101 free_frame_menubar (struct frame *f)
110 return popup_activated_flag;
114 /* --------------------------------------------------------------------------
115 Update menubar. Three cases:
116 1) deep_p = 0, submenu = nil: Fresh switch onto a frame -- either set up
117 just top-level menu strings (OS X), or goto case (2) (GNUstep).
118 2) deep_p = 1, submenu = nil: Recompute all submenus.
119 3) deep_p = 1, submenu = non-nil: Update contents of a single submenu.
120 -------------------------------------------------------------------------- */
122 ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
124 NSAutoreleasePool *pool;
125 id menu = [NSApp mainMenu];
126 static EmacsMenu *last_submenu = nil;
128 const char *submenuTitle = [[submenu title] UTF8String];
129 extern int waiting_for_input;
132 widget_value *wv, *first_wv, *prev_wv = 0;
140 NSTRACE (set_frame_menubar);
142 if (f != SELECTED_FRAME ())
144 XSETFRAME (Vmenu_updating_frame, f);
145 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
148 pool = [[NSAutoreleasePool alloc] init];
150 /* Menu may have been created automatically; if so, discard it. */
151 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
159 menu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
163 { /* close up anything on there */
164 id attMenu = [menu attachedMenu];
171 t = -(1000*tb.time+tb.millitm);
174 /* widget_value is a straightforward object translation of emacs's
175 Byzantine lisp menu structures */
176 wv = xmalloc_widget_value ();
180 wv->button_type = BUTTON_TYPE_NONE;
184 #ifdef NS_IMPL_GNUSTEP
185 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
190 /* Fully parse one or more of the submenus. */
192 int *submenu_start, *submenu_end;
193 int *submenu_top_level_items, *submenu_n_panes;
194 struct buffer *prev = current_buffer;
196 int specpdl_count = SPECPDL_INDEX ();
197 int previous_menu_items_used = f->menu_bar_items_used;
198 Lisp_Object *previous_items
199 = (Lisp_Object *) alloca (previous_menu_items_used
200 * sizeof (Lisp_Object));
202 /* lisp preliminaries */
203 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
204 specbind (Qinhibit_quit, Qt);
205 specbind (Qdebug_on_next_call, Qnil);
206 record_unwind_save_match_data ();
207 if (NILP (Voverriding_local_map_menu_flag))
209 specbind (Qoverriding_terminal_local_map, Qnil);
210 specbind (Qoverriding_local_map, Qnil);
212 set_buffer_internal_1 (XBUFFER (buffer));
214 /* TODO: for some reason this is not needed in other terms,
215 but some menu updates call Info-extract-pointer which causes
216 abort-on-error if waiting-for-input. Needs further investigation. */
217 owfi = waiting_for_input;
218 waiting_for_input = 0;
220 /* lucid hook and possible reset */
221 safe_run_hooks (Qactivate_menubar_hook);
222 if (! NILP (Vlucid_menu_bar_dirty_flag))
223 call0 (Qrecompute_lucid_menubar);
224 safe_run_hooks (Qmenu_bar_update_hook);
225 FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
227 /* Now ready to go */
228 items = FRAME_MENU_BAR_ITEMS (f);
230 /* Save the frame's previous menu bar contents data */
231 if (previous_menu_items_used)
232 bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
233 previous_menu_items_used * sizeof (Lisp_Object));
235 /* parse stage 1: extract from lisp */
238 menu_items = f->menu_bar_vector;
239 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
240 submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
241 submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
242 submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int));
243 submenu_top_level_items
244 = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
246 for (i = 0; i < XVECTOR (items)->size; i += 4)
248 Lisp_Object key, string, maps;
250 key = XVECTOR (items)->contents[i];
251 string = XVECTOR (items)->contents[i + 1];
252 maps = XVECTOR (items)->contents[i + 2];
256 /* FIXME: we'd like to only parse the needed submenu, but this
257 was causing crashes in the _common parsing code.. need to make
258 sure proper initialization done.. */
259 /* if (submenu && strcmp (submenuTitle, SDATA (string)))
262 submenu_start[i] = menu_items_used;
264 menu_items_n_panes = 0;
265 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
266 submenu_n_panes[i] = menu_items_n_panes;
267 submenu_end[i] = menu_items_used;
271 finish_menu_items ();
272 waiting_for_input = owfi;
275 if (submenu && n == 0)
277 /* should have found a menu for this one but didn't */
278 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
280 discard_menu_items ();
281 unbind_to (specpdl_count, Qnil);
287 /* parse stage 2: insert into lucid 'widget_value' structures
288 [comments in other terms say not to evaluate lisp code here] */
289 wv = xmalloc_widget_value ();
290 wv->name = "menubar";
293 wv->button_type = BUTTON_TYPE_NONE;
297 for (i = 0; i < 4*n; i += 4)
299 menu_items_n_panes = submenu_n_panes[i];
300 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
301 submenu_top_level_items[i]);
305 first_wv->contents = wv;
306 /* Don't set wv->name here; GC during the loop might relocate it. */
308 wv->button_type = BUTTON_TYPE_NONE;
312 set_buffer_internal_1 (prev);
314 /* Compare the new menu items with previous, and leave off if no change */
315 /* FIXME: following other terms here, but seems like this should be
316 done before parse stage 2 above, since its results aren't used */
317 if (previous_menu_items_used
318 && (!submenu || (submenu && submenu == last_submenu))
319 && menu_items_used == previous_menu_items_used)
321 for (i = 0; i < previous_menu_items_used; i++)
322 /* FIXME: this ALWAYS fails on Buffers menu items.. something
323 about their strings causes them to change every time, so we
324 double-check failures */
325 if (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i]))
326 if (!(STRINGP (previous_items[i])
327 && STRINGP (XVECTOR (menu_items)->contents[i])
328 && !strcmp (SDATA (previous_items[i]),
329 SDATA (XVECTOR (menu_items)->contents[i]))))
331 if (i == previous_menu_items_used)
337 t += 1000*tb.time+tb.millitm;
338 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
341 free_menubar_widget_value_tree (first_wv);
342 discard_menu_items ();
343 unbind_to (specpdl_count, Qnil);
349 /* The menu items are different, so store them in the frame */
350 /* FIXME: this is not correct for single-submenu case */
351 f->menu_bar_vector = menu_items;
352 f->menu_bar_items_used = menu_items_used;
354 /* Calls restore_menu_items, etc., as they were outside */
355 unbind_to (specpdl_count, Qnil);
357 /* Parse stage 2a: now GC cannot happen during the lifetime of the
358 widget_value, so it's safe to store data from a Lisp_String */
359 wv = first_wv->contents;
360 for (i = 0; i < XVECTOR (items)->size; i += 4)
363 string = XVECTOR (items)->contents[i + 1];
366 /* if (submenu && strcmp (submenuTitle, SDATA (string)))
369 wv->name = (char *) SDATA (string);
370 update_submenu_strings (wv->contents);
374 /* Now, update the NS menu; if we have a submenu, use that, otherwise
375 create a new menu for each sub and fill it. */
378 for (wv = first_wv->contents; wv; wv = wv->next)
380 if (!strcmp (submenuTitle, wv->name))
382 [submenu fillWithWidgetValue: wv->contents];
383 last_submenu = submenu;
390 [menu fillWithWidgetValue: first_wv->contents];
396 static int n_previous_strings = 0;
397 static char previous_strings[100][10];
398 static struct frame *last_f = NULL;
402 /* Make widget-value tree w/ just the top level menu bar strings */
403 items = FRAME_MENU_BAR_ITEMS (f);
412 /* check if no change.. this mechanism is a bit rough, but ready */
413 n = XVECTOR (items)->size / 4;
414 if (f == last_f && n_previous_strings == n)
416 for (i = 0; i<n; i++)
418 string = AREF (items, 4*i+1);
420 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
423 if (previous_strings[i][0])
427 if (strncmp (previous_strings[i], SDATA (string), 10))
440 for (i = 0; i < XVECTOR (items)->size; i += 4)
442 string = XVECTOR (items)->contents[i + 1];
447 strncpy (previous_strings[i/4], SDATA (string), 10);
449 wv = xmalloc_widget_value ();
450 wv->name = (char *) SDATA (string);
453 wv->button_type = BUTTON_TYPE_NONE;
455 wv->call_data = (void *) (EMACS_INT) (-1);
458 /* we'll update the real copy under app menu when time comes */
459 if (!strcmp ("Services", wv->name))
461 /* but we need to make sure it will update on demand */
462 [svcsMenu setFrame: f];
463 [svcsMenu setDelegate: svcsMenu];
467 [menu addSubmenuWithTitle: wv->name forFrame: f];
472 first_wv->contents = wv;
478 n_previous_strings = n;
480 n_previous_strings = 0;
483 free_menubar_widget_value_tree (first_wv);
488 t += 1000*tb.time+tb.millitm;
489 fprintf (stderr, "Menu update took %ld msec.\n", t);
494 [NSApp setMainMenu: menu];
502 /* Main emacs core entry point for menubar menus: called to indicate that the
503 frame's menus have changed, and the *step representation should be updated
506 set_frame_menubar (struct frame *f, int first_time, int deep_p)
508 ns_update_menubar (f, deep_p, nil);
512 /* Utility (from macmenu.c): is this item a separator? */
514 name_is_separator (name)
517 const char *start = name;
519 /* Check if name string consists of only dashes ('-'). */
520 while (*name == '-') name++;
521 /* Separators can also be of the form "--:TripleSuperMegaEtched"
522 or "--deep-shadow". We don't implement them yet, se we just treat
523 them like normal separators. */
524 return (*name == '\0' || start + 2 == name);
528 /* ==========================================================================
530 Menu: class implementation
532 ========================================================================== */
535 /* Menu that can define itself from Emacs "widget_value"s and will lazily
536 update itself when user clicked. Based on Carbon/AppKit implementation
537 by Yamamoto Mitsuharu. */
538 @implementation EmacsMenu
540 /* override designated initializer */
541 - initWithTitle: (NSString *)title
543 if (self = [super initWithTitle: title])
544 [self setAutoenablesItems: NO];
549 /* used for top-level */
550 - initWithTitle: (NSString *)title frame: (struct frame *)f
552 [self initWithTitle: title];
555 [self setDelegate: self];
561 - (void)setFrame: (struct frame *)f
567 /* delegate method called when a submenu is being opened: run a 'deep' call
568 to set_frame_menubar */
569 - (void)menuNeedsUpdate: (NSMenu *)menu
571 NSEvent *event = [[FRAME_NS_VIEW (frame) window] currentEvent];
572 /* HACK: Cocoa/Carbon will request update on every keystroke
573 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
574 since key equivalents are handled through emacs.
575 On Leopard, even keystroke events generate SystemDefined events, but
576 their subtype is 8. */
577 if ([event type] != NSSystemDefined || [event subtype] == 8
578 /* Also, don't try this if from an event picked up asynchronously,
579 as lots of lisp evaluation happens in ns_update_menubar. */
580 || handling_signal != 0)
582 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
583 ns_update_menubar (frame, 1, self);
587 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
589 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
590 && FRAME_NS_VIEW (SELECTED_FRAME ()))
591 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
596 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
597 into an accelerator string. We are only able to display a single character
598 for an accelerator, together with an optional modifier combination. (Under
599 Carbon more control was possible, but in Cocoa multi-char strings passed to
600 NSMenuItem get ignored. For now we try to display a super-single letter
601 combo, and return the others as strings to be appended to the item title.
602 (This is signaled by setting keyEquivModMask to 0 for now.) */
603 -(NSString *)parseKeyEquiv: (char *)key
606 keyEquivModMask = NSCommandKeyMask;
608 if (!key || !strlen (key))
611 while (*tpos == ' ' || *tpos == '(')
614 keyEquivModMask = 0; /* signal */
615 return [NSString stringWithUTF8String: tpos];
617 return [NSString stringWithFormat: @"%c", tpos[2]];
621 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
624 widget_value *wv = (widget_value *)wvptr;
626 if (name_is_separator (wv->name))
628 item = [NSMenuItem separatorItem];
629 [self addItem: item];
633 NSString *title, *keyEq;
634 title = [NSString stringWithUTF8String: wv->name];
636 title = @"< ? >"; /* (get out in the open so we know about it) */
638 keyEq = [self parseKeyEquiv: wv->key];
640 /* OS X just ignores modifier strings longer than one character */
641 if (keyEquivModMask == 0)
642 title = [title stringByAppendingFormat: @" (%@)", keyEq];
645 item = [self addItemWithTitle: (NSString *)title
646 action: @selector (menuDown:)
647 keyEquivalent: keyEq];
648 [item setKeyEquivalentModifierMask: keyEquivModMask];
650 [item setEnabled: wv->enabled];
652 /* Draw radio buttons and tickboxes */
653 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
654 wv->button_type == BUTTON_TYPE_RADIO))
655 [item setState: NSOnState];
657 [item setState: NSOffState];
659 [item setTag: (int)wv->call_data];
671 for (n = [self numberOfItems]-1; n >= 0; n--)
673 NSMenuItem *item = [self itemAtIndex: n];
674 NSString *title = [item title];
675 if (([title length] == 0 || [@"Apple" isEqualToString: title])
676 && ![item isSeparatorItem])
678 [self removeItemAtIndex: n];
683 - (void)fillWithWidgetValue: (void *)wvptr
685 widget_value *wv = (widget_value *)wvptr;
687 /* clear existing contents */
688 [self setMenuChangedMessagesEnabled: NO];
691 /* add new contents */
692 for (; wv != NULL; wv = wv->next)
694 NSMenuItem *item = [self addItemWithWidgetValue: wv];
698 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
700 [self setSubmenu: submenu forItem: item];
701 [submenu fillWithWidgetValue: wv->contents];
703 [item setAction: nil];
707 [self setMenuChangedMessagesEnabled: YES];
708 #ifdef NS_IMPL_GNUSTEP
709 if ([[self window] isVisible])
712 if ([self supermenu] == nil)
718 /* adds an empty submenu and returns it */
719 - (EmacsMenu *)addSubmenuWithTitle: (char *)title forFrame: (struct frame *)f
721 NSString *titleStr = [NSString stringWithUTF8String: title];
722 NSMenuItem *item = [self addItemWithTitle: titleStr
723 action: nil /*@selector (menuDown:) */
725 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
726 [self setSubmenu: submenu forItem: item];
731 /* run a menu in popup mode */
732 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
733 keymaps: (int)keymaps
735 EmacsView *view = FRAME_NS_VIEW (f);
736 /* p = [view convertPoint:p fromView: nil]; */
737 p.y = NSHeight ([view frame]) - p.y;
738 NSEvent *e = [[view window] currentEvent];
739 NSEvent *event = [NSEvent mouseEventWithType: NSRightMouseDown
742 timestamp: [e timestamp]
743 windowNumber: [[view window] windowNumber]
745 eventNumber: 0/*[e eventNumber] */
750 context_menu_value = -1;
751 [NSMenu popUpContextMenu: self withEvent: event forView: view];
752 retVal = context_menu_value;
753 context_menu_value = 0;
755 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
763 /* ==========================================================================
765 Context Menu: implementing functions
767 ========================================================================== */
770 cleanup_popup_menu (Lisp_Object arg)
772 discard_menu_items ();
778 ns_popup_menu (Lisp_Object position, Lisp_Object menu)
781 struct frame *f = NULL;
783 Lisp_Object window, x, y, tem, keymap, title;
785 int specpdl_count = SPECPDL_INDEX (), specpdl_count2;
786 char *error_name = NULL;
788 widget_value *wv, *first_wv = 0;
790 NSTRACE (ns_popup_menu);
792 if (!NILP (position))
796 if (EQ (position, Qt)
797 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
798 || EQ (XCAR (position), Qtool_bar))))
800 /* Use the mouse's current position. */
801 struct frame *new_f = SELECTED_FRAME ();
803 if (FRAME_TERMINAL (new_f)->mouse_position_hook)
804 (*FRAME_TERMINAL (new_f)->mouse_position_hook)
805 (&new_f, 0, 0, 0, &x, &y, 0);
807 XSETFRAME (window, new_f);
810 window = selected_window;
817 CHECK_CONS (position);
818 tem = Fcar (position);
819 if (XTYPE (tem) == Lisp_Cons)
821 window = Fcar (Fcdr (position));
823 y = Fcar (Fcdr (tem));
827 tem = Fcar (Fcdr (position));
829 tem = Fcar (Fcdr (Fcdr (tem)));
847 struct window *win = XWINDOW (window);
848 CHECK_LIVE_WINDOW (window);
849 f = XFRAME (WINDOW_FRAME (win));
850 p.x = FRAME_COLUMN_WIDTH (f) * WINDOW_LEFT_EDGE_COL (win);
851 p.y = FRAME_LINE_HEIGHT (f) * WINDOW_TOP_EDGE_LINE (win);
854 p.x += XINT (x); p.y += XINT (y);
856 XSETFRAME (Vmenu_updating_frame, f);
859 { /* no position given */
860 /* FIXME: if called during dump, we need to stop precomputation of
861 key equivalents (see below) because the keydefs in ns-win.el have
862 not been loaded yet. */
865 Vmenu_updating_frame = Qnil;
868 /* now parse the lisp menus */
869 record_unwind_protect (unuse_menu_items, Qnil);
873 /* Decode the menu items from what was specified. */
875 keymap = get_keymap (menu, 0, 0);
878 /* We were given a keymap. Extract menu info from the keymap. */
881 /* Extract the detailed info to make one pane. */
882 keymap_panes (&menu, 1, NILP (position));
884 /* Search for a string appearing directly as an element of the keymap.
885 That string is the title of the menu. */
886 prompt = Fkeymap_prompt (keymap);
887 title = NILP (prompt) ? build_string ("Select") : prompt;
889 /* Make that be the pane title of the first pane. */
890 if (!NILP (prompt) && menu_items_n_panes >= 0)
891 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
895 else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
897 /* We were given a list of keymaps. */
898 int nmaps = XFASTINT (Flength (menu));
900 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
905 /* The first keymap that has a prompt string
906 supplies the menu title. */
907 for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
911 maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
913 prompt = Fkeymap_prompt (keymap);
914 if (NILP (title) && !NILP (prompt))
918 /* Extract the detailed info to make one pane. */
919 keymap_panes (maps, nmaps, NILP (position));
921 /* Make the title be the pane title of the first pane. */
922 if (!NILP (title) && menu_items_n_panes >= 0)
923 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
929 /* We were given an old-fashioned menu. */
931 CHECK_STRING (title);
933 list_of_panes (Fcdr (menu));
938 unbind_to (specpdl_count, Qnil);
940 /* If no position given, that was a signal to just precompute and cache
941 key equivalents, which was a side-effect of what we just did. */
944 discard_menu_items ();
949 record_unwind_protect (cleanup_popup_menu, Qnil);
952 /* now parse stage 2 as in ns_update_menubar */
953 wv = xmalloc_widget_value ();
954 wv->name = "contextmenu";
957 wv->button_type = BUTTON_TYPE_NONE;
961 specpdl_count2 = SPECPDL_INDEX ();
964 /* FIXME: a couple of one-line differences prevent reuse */
965 wv = digest_single_submenu (0, menu_items_used, Qnil);
968 widget_value *save_wv = 0, *prev_wv = 0;
969 widget_value **submenu_stack
970 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
971 /* Lisp_Object *subprefix_stack
972 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object)); */
973 int submenu_depth = 0;
977 /* Loop over all panes and items, filling in the tree. */
979 while (i < menu_items_used)
981 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
983 submenu_stack[submenu_depth++] = save_wv;
989 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
992 save_wv = submenu_stack[--submenu_depth];
996 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
997 && submenu_depth != 0)
998 i += MENU_ITEMS_PANE_LENGTH;
999 /* Ignore a nil in the item list.
1000 It's meaningful only for dialog boxes. */
1001 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1003 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1005 /* Create a new pane. */
1006 Lisp_Object pane_name, prefix;
1009 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
1010 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
1012 #ifndef HAVE_MULTILINGUAL_MENU
1013 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
1015 pane_name = ENCODE_MENU_STRING (pane_name);
1016 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
1019 pane_string = (NILP (pane_name)
1020 ? "" : (char *) SDATA (pane_name));
1021 /* If there is just one top-level pane, put all its items directly
1022 under the top-level menu. */
1023 if (menu_items_n_panes == 1)
1026 /* If the pane has a meaningful name,
1027 make the pane a top-level menu item
1028 with its items as a submenu beneath it. */
1029 if (!keymaps && strcmp (pane_string, ""))
1031 wv = xmalloc_widget_value ();
1035 first_wv->contents = wv;
1036 wv->name = pane_string;
1037 if (keymaps && !NILP (prefix))
1041 wv->button_type = BUTTON_TYPE_NONE;
1046 else if (first_pane)
1052 i += MENU_ITEMS_PANE_LENGTH;
1056 /* Create a new item within current pane. */
1057 Lisp_Object item_name, enable, descrip, def, type, selected, help;
1058 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
1059 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
1060 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
1061 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
1062 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
1063 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
1064 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
1066 #ifndef HAVE_MULTILINGUAL_MENU
1067 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
1069 item_name = ENCODE_MENU_STRING (item_name);
1070 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
1073 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
1075 descrip = ENCODE_MENU_STRING (descrip);
1076 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
1078 #endif /* not HAVE_MULTILINGUAL_MENU */
1080 wv = xmalloc_widget_value ();
1084 save_wv->contents = wv;
1085 wv->name = (char *) SDATA (item_name);
1086 if (!NILP (descrip))
1087 wv->key = (char *) SDATA (descrip);
1089 /* If this item has a null value,
1090 make the call_data null so that it won't display a box
1091 when the mouse is on it. */
1093 = !NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0;
1094 wv->enabled = !NILP (enable);
1097 wv->button_type = BUTTON_TYPE_NONE;
1098 else if (EQ (type, QCtoggle))
1099 wv->button_type = BUTTON_TYPE_TOGGLE;
1100 else if (EQ (type, QCradio))
1101 wv->button_type = BUTTON_TYPE_RADIO;
1105 wv->selected = !NILP (selected);
1107 if (! STRINGP (help))
1114 i += MENU_ITEMS_ITEM_LENGTH;
1122 widget_value *wv_title = xmalloc_widget_value ();
1123 widget_value *wv_sep = xmalloc_widget_value ();
1125 /* Maybe replace this separator with a bitmap or owner-draw item
1126 so that it looks better. Having two separators looks odd. */
1127 wv_sep->name = "--";
1128 wv_sep->next = first_wv->contents;
1129 wv_sep->help = Qnil;
1131 #ifndef HAVE_MULTILINGUAL_MENU
1132 if (STRING_MULTIBYTE (title))
1133 title = ENCODE_MENU_STRING (title);
1136 wv_title->name = (char *) SDATA (title);
1137 wv_title->enabled = NO;
1138 wv_title->button_type = BUTTON_TYPE_NONE;
1139 wv_title->help = Qnil;
1140 wv_title->next = wv_sep;
1141 first_wv->contents = wv_title;
1144 pmenu = [[EmacsMenu alloc] initWithTitle:
1145 [NSString stringWithUTF8String: SDATA (title)]];
1146 [pmenu fillWithWidgetValue: first_wv->contents];
1147 free_menubar_widget_value_tree (first_wv);
1148 unbind_to (specpdl_count2, Qnil);
1150 popup_activated_flag = 1;
1151 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
1152 popup_activated_flag = 0;
1153 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1156 discard_menu_items ();
1157 unbind_to (specpdl_count, Qnil);
1160 if (error_name) error (error_name);
1167 /* ==========================================================================
1169 Toolbar: externally-called functions
1171 ========================================================================== */
1174 free_frame_tool_bar (FRAME_PTR f)
1175 /* --------------------------------------------------------------------------
1176 Under NS we just hide the toolbar until it might be needed again.
1177 -------------------------------------------------------------------------- */
1179 [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
1183 update_frame_tool_bar (FRAME_PTR f)
1184 /* --------------------------------------------------------------------------
1185 Update toolbar contents
1186 -------------------------------------------------------------------------- */
1189 EmacsToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
1191 [toolbar clearActive];
1193 /* update EmacsToolbar as in GtkUtils, build items list */
1194 for (i = 0; i < f->n_tool_bar_items; ++i)
1196 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1197 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1199 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1200 BOOL selected_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_SELECTED_P));
1205 Lisp_Object helpObj;
1208 /* If image is a vector, choose the image according to the
1210 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1211 if (VECTORP (image))
1213 /* NS toolbar auto-computes disabled and selected images */
1214 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1215 xassert (ASIZE (image) >= idx);
1216 image = AREF (image, idx);
1222 /* Ignore invalid image specifications. */
1223 if (!valid_image_p (image))
1225 NSLog (@"Invalid image for toolbar item");
1229 img_id = lookup_image (f, image);
1230 img = IMAGE_FROM_ID (f, img_id);
1231 prepare_image_for_display (f, img);
1233 if (img->load_failed_p || img->pixmap == nil)
1235 NSLog (@"Could not prepare toolbar image for display.");
1239 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1241 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1242 helpText = NILP (helpObj) ? "" : (char *)SDATA (helpObj);
1244 [toolbar addDisplayItemWithImage: img->pixmap idx: i helpText: helpText
1245 enabled: enabled_p];
1249 if (![toolbar isVisible])
1250 [toolbar setVisible: YES];
1252 if ([toolbar changed])
1254 /* inform app that toolbar has changed */
1255 NSDictionary *dict = [toolbar configurationDictionary];
1256 NSMutableDictionary *newDict = [dict mutableCopy];
1257 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1259 while ((key = [keys nextObject]) != nil)
1261 NSObject *val = [dict objectForKey: key];
1262 if ([val isKindOfClass: [NSArray class]])
1265 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1270 [toolbar setConfigurationFromDictionary: newDict];
1277 /* ==========================================================================
1279 Toolbar: class implementation
1281 ========================================================================== */
1283 @implementation EmacsToolbar
1285 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1287 self = [super initWithIdentifier: identifier];
1289 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1290 [self setSizeMode: NSToolbarSizeModeSmall];
1291 [self setDelegate: self];
1292 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1293 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1294 prevEnablement = enablement = 0L;
1300 [prevIdentifiers release];
1301 [activeIdentifiers release];
1302 [identifierToItem release];
1306 - (void) clearActive
1308 [prevIdentifiers release];
1309 prevIdentifiers = [activeIdentifiers copy];
1310 [activeIdentifiers removeAllObjects];
1311 prevEnablement = enablement;
1317 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1318 enablement == prevEnablement ? NO : YES;
1321 - (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx
1322 helpText: (char *)help enabled: (BOOL)enabled
1324 /* 1) come up w/identifier */
1325 NSString *identifier
1326 = [NSString stringWithFormat: @"%u", [img hash]];
1328 /* 2) create / reuse item */
1329 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1332 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1334 [item setImage: img];
1335 [item setToolTip: [NSString stringWithCString: help]];
1336 [item setTarget: emacsView];
1337 [item setAction: @selector (toolbarClicked:)];
1341 [item setEnabled: enabled];
1343 /* 3) update state */
1344 [identifierToItem setObject: item forKey: identifier];
1345 [activeIdentifiers addObject: identifier];
1346 enablement = (enablement << 1) | (enabled == YES);
1349 /* This overrides super's implementation, which automatically sets
1350 all items to enabled state (for some reason). */
1351 - (void)validateVisibleItems { }
1354 /* delegate methods */
1356 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1357 itemForItemIdentifier: (NSString *)itemIdentifier
1358 willBeInsertedIntoToolbar: (BOOL)flag
1360 /* look up NSToolbarItem by identifier and return... */
1361 return [identifierToItem objectForKey: itemIdentifier];
1364 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1366 /* return entire set.. */
1367 return activeIdentifiers;
1370 /* for configuration palette (not yet supported) */
1371 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1373 /* return entire set... */
1374 return [identifierToItem allKeys];
1377 /* optional and unneeded */
1378 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1379 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1380 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1382 @end /* EmacsToolbar */
1386 /* ==========================================================================
1388 Tooltip: class implementation
1390 ========================================================================== */
1392 /* Needed because NeXTstep does not provide enough control over tooltip
1394 @implementation EmacsTooltip
1398 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1399 blue: 0.792 alpha: 0.95];
1400 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1401 NSFont *sfont = [font screenFont];
1402 int height = [sfont ascender] - [sfont descender];
1403 /*[font boundingRectForFont].size.height; */
1404 NSRect r = NSMakeRect (0, 0, 100, height+6);
1406 textField = [[NSTextField alloc] initWithFrame: r];
1407 [textField setFont: font];
1408 [textField setBackgroundColor: col];
1410 [textField setEditable: NO];
1411 [textField setSelectable: NO];
1412 [textField setBordered: YES];
1413 [textField setBezeled: YES];
1414 [textField setDrawsBackground: YES];
1416 win = [[NSWindow alloc]
1417 initWithContentRect: [textField frame]
1419 backing: NSBackingStoreBuffered
1421 [win setReleasedWhenClosed: NO];
1422 [win setDelegate: self];
1423 [[win contentView] addSubview: textField];
1424 /* [win setBackgroundColor: col]; */
1425 [win setOpaque: NO];
1434 [textField release];
1438 - (void) setText: (char *)text
1440 NSString *str = [NSString stringWithUTF8String: text];
1441 NSRect r = [textField frame];
1442 r.size.width = [[[textField font] screenFont] widthOfString: str] + 8;
1443 [textField setFrame: r];
1444 [textField setStringValue: str];
1447 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1449 NSRect wr = [win frame];
1451 wr.origin = NSMakePoint (x, y);
1452 wr.size = [textField frame].size;
1454 [win setFrame: wr display: YES];
1455 [win orderFront: self];
1457 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1458 selector: @selector (hide)
1459 userInfo: nil repeats: NO];
1468 if ([timer isValid])
1477 return timer != nil;
1482 return [textField frame];
1485 @end /* EmacsTooltip */
1489 /* ==========================================================================
1491 Popup Dialog: implementing functions
1493 ========================================================================== */
1497 pop_down_menu (Lisp_Object arg)
1499 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
1500 if (popup_activated_flag)
1502 popup_activated_flag = 0;
1504 [NSApp endModalSession: popupSession];
1505 [((EmacsDialogPanel *) (p->pointer)) close];
1506 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1514 ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1517 Lisp_Object window, tem;
1522 NSTRACE (x-popup-dialog);
1526 isQ = NILP (header);
1528 if (EQ (position, Qt)
1529 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1530 || EQ (XCAR (position), Qtool_bar))))
1532 window = selected_window;
1534 else if (CONSP (position))
1537 tem = Fcar (position);
1538 if (XTYPE (tem) == Lisp_Cons)
1539 window = Fcar (Fcdr (position));
1542 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1543 window = Fcar (tem); /* POSN_WINDOW (tem) */
1546 else if (WINDOWP (position) || FRAMEP (position))
1553 if (FRAMEP (window))
1554 f = XFRAME (window);
1555 else if (WINDOWP (window))
1557 CHECK_LIVE_WINDOW (window);
1558 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1561 CHECK_WINDOW (window);
1563 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1564 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1567 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1570 int specpdl_count = SPECPDL_INDEX ();
1571 record_unwind_protect (pop_down_menu, make_save_value (dialog, 0));
1572 popup_activated_flag = 1;
1573 tem = [dialog runDialogAt: p];
1574 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1582 /* ==========================================================================
1584 Popup Dialog: class implementation
1586 ========================================================================== */
1588 @interface FlippedView : NSView
1593 @implementation FlippedView
1600 @implementation EmacsDialogPanel
1603 #define ICONSIZE 64.0
1604 #define TEXTHEIGHT 20.0
1605 #define MINCELLWIDTH 90.0
1607 - initWithContentRect: (NSRect)contentRect styleMask: (unsigned int)aStyle
1608 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1610 NSSize spacing = {SPACER, SPACER};
1612 char this_cmd_name[80];
1614 static NSImageView *imgView;
1615 static FlippedView *contentView;
1620 area.origin.x = 3*SPACER;
1621 area.origin.y = 2*SPACER;
1622 area.size.width = ICONSIZE;
1623 area.size.height= ICONSIZE;
1624 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1625 [img setScalesWhenResized: YES];
1626 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1627 imgView = [[NSImageView alloc] initWithFrame: area];
1628 [imgView setImage: img];
1629 [imgView setEditable: NO];
1633 aStyle = NSTitledWindowMask;
1637 [super initWithContentRect: contentRect styleMask: aStyle
1638 backing: backingType defer: flag];
1639 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1640 [self setContentView: contentView];
1642 [[self contentView] setAutoresizesSubviews: YES];
1644 [[self contentView] addSubview: imgView];
1645 [self setTitle: @""];
1647 area.origin.x += ICONSIZE+2*SPACER;
1648 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1649 area.size.width = 400;
1650 area.size.height= TEXTHEIGHT;
1651 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1652 [[self contentView] addSubview: command];
1653 [command setStringValue: @"Emacs"];
1654 [command setDrawsBackground: NO];
1655 [command setBezeled: NO];
1656 [command setSelectable: NO];
1657 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1659 /* area.origin.x = ICONSIZE+2*SPACER;
1660 area.origin.y = TEXTHEIGHT + 2*SPACER;
1661 area.size.width = 400;
1662 area.size.height= 2;
1663 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1664 [[self contentView] addSubview: tem];
1665 [tem setTitlePosition: NSNoTitle];
1666 [tem setAutoresizingMask: NSViewWidthSizable];*/
1668 /* area.origin.x = ICONSIZE+2*SPACER; */
1669 area.origin.y += TEXTHEIGHT+SPACER;
1670 area.size.width = 400;
1671 area.size.height= TEXTHEIGHT;
1672 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1673 [[self contentView] addSubview: title];
1674 [title setDrawsBackground: NO];
1675 [title setBezeled: NO];
1676 [title setSelectable: NO];
1677 [title setFont: [NSFont systemFontOfSize: 11.0]];
1679 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1680 [cell setBordered: NO];
1681 [cell setEnabled: NO];
1682 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1683 [cell setBezelStyle: NSRoundedBezelStyle];
1685 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1686 mode: NSHighlightModeMatrix
1689 numberOfColumns: 1];
1690 [[self contentView] addSubview: matrix];
1692 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1693 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1694 [matrix setIntercellSpacing: spacing];
1696 [self setOneShot: YES];
1697 [self setReleasedWhenClosed: YES];
1698 [self setHidesOnDeactivate: YES];
1703 - (BOOL)windowShouldClose: (id)sender
1705 [NSApp stopModalWithCode: XHASH (Qnil)]; // FIXME: BIG UGLY HACK!!
1710 void process_dialog (id window, Lisp_Object list)
1715 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1718 if (XTYPE (item) == Lisp_String)
1720 [window addString: XSTRING (item)->data row: row++];
1722 else if (XTYPE (item) == Lisp_Cons)
1724 [window addButton: XSTRING (XCAR (item))->data
1725 value: XCDR (item) row: row++];
1727 else if (NILP (item))
1736 - addButton: (char *)str value: (Lisp_Object)val row: (int)row
1745 cell = [matrix cellAtRow: row column: cols-1];
1746 [cell setTarget: self];
1747 [cell setAction: @selector (clicked: )];
1748 [cell setTitle: [NSString stringWithUTF8String: str]];
1749 [cell setTag: XHASH (val)]; // FIXME: BIG UGLY HACK!!
1750 [cell setBordered: YES];
1751 [cell setEnabled: YES];
1757 - addString: (char *)str row: (int)row
1766 cell = [matrix cellAtRow: row column: cols-1];
1767 [cell setTitle: [NSString stringWithUTF8String: str]];
1768 [cell setBordered: YES];
1769 [cell setEnabled: NO];
1785 NSArray *sellist = nil;
1788 sellist = [sender selectedCells];
1789 if ([sellist count]<1)
1792 seltag = [[sellist objectAtIndex: 0] tag];
1793 if (seltag != XHASH (Qundefined)) // FIXME: BIG UGLY HACK!!
1794 [NSApp stopModalWithCode: seltag];
1799 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1804 if (XTYPE (contents) == Lisp_Cons)
1806 head = Fcar (contents);
1807 process_dialog (self, Fcdr (contents));
1812 if (XTYPE (head) == Lisp_String)
1813 [title setStringValue:
1814 [NSString stringWithUTF8String: XSTRING (head)->data]];
1815 else if (isQ == YES)
1816 [title setStringValue: @"Question"];
1818 [title setStringValue: @"Information"];
1824 if (cols == 1 && rows > 1) /* Never told where to split */
1827 for (i = 0; i<rows/2; i++)
1829 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1830 atRow: i column: 1];
1831 [matrix removeRow: (rows+1)/2];
1837 NSSize csize = [matrix cellSize];
1838 if (csize.width < MINCELLWIDTH)
1840 csize.width = MINCELLWIDTH;
1841 [matrix setCellSize: csize];
1842 [matrix sizeToCells];
1847 [command sizeToFit];
1851 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1853 t.origin.x = r.origin.x;
1854 t.size.width = r.size.width;
1856 r = [command frame];
1857 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1859 t.origin.x = r.origin.x;
1860 t.size.width = r.size.width;
1864 s = [(NSView *)[self contentView] frame];
1865 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1866 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1867 [self setFrame: r display: NO];
1876 { [super dealloc]; return; };
1880 - (Lisp_Object)runDialogAt: (NSPoint)p
1884 /* initiate a session that will be ended by pop_down_menu */
1885 popupSession = [NSApp beginModalSessionForWindow: self];
1886 while (popup_activated_flag
1887 && (ret = [NSApp runModalSession: popupSession])
1888 == NSRunContinuesResponse)
1890 /* Run this for timers.el, indep of atimers; might not return.
1891 TODO: use return value to avoid calling every iteration. */
1893 [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
1896 { /* FIXME: BIG UGLY HACK!!! */
1898 *(EMACS_INT*)(&tmp) = ret;
1906 /* ==========================================================================
1910 ========================================================================== */
1912 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1913 doc: /* Cause the NS menu to be re-calculated. */)
1916 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1921 DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
1922 doc: /* Pop up a deck-of-cards menu and return user's selection.
1923 POSITION is a position specification. This is either a mouse button event
1924 or a list ((XOFFSET YOFFSET) WINDOW)
1925 where XOFFSET and YOFFSET are positions in pixels from the top left
1926 corner of WINDOW. (WINDOW may be a window or a frame object.)
1927 This controls the position of the top left of the menu as a whole.
1928 If POSITION is t, it means to use the current mouse position.
1930 MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
1931 The menu items come from key bindings that have a menu string as well as
1932 a definition; actually, the \"definition\" in such a key binding looks like
1933 \(STRING . REAL-DEFINITION). To give the menu a title, put a string into
1934 the keymap as a top-level element.
1936 If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
1937 Otherwise, REAL-DEFINITION should be a valid key binding definition.
1939 You can also use a list of keymaps as MENU.
1940 Then each keymap makes a separate pane.
1942 When MENU is a keymap or a list of keymaps, the return value is the
1943 list of events corresponding to the user's choice. Note that
1944 `x-popup-menu' does not actually execute the command bound to that
1947 Alternatively, you can specify a menu of multiple panes
1948 with a list of the form (TITLE PANE1 PANE2...),
1949 where each pane is a list of form (TITLE ITEM1 ITEM2...).
1950 Each ITEM is normally a cons cell (STRING . VALUE);
1951 but a string can appear as an item--that makes a nonselectable line
1953 With this form of menu, the return value is VALUE from the chosen item. */)
1955 Lisp_Object position, menu;
1957 return ns_popup_menu (position, menu);
1961 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1962 doc: /* Pop up a dialog box and return user's selection.
1963 POSITION specifies which frame to use.
1964 This is normally a mouse button event or a window or frame.
1965 If POSITION is t, it means to use the frame the mouse is on.
1966 The dialog box appears in the middle of the specified frame.
1968 CONTENTS specifies the alternatives to display in the dialog box.
1969 It is a list of the form (DIALOG ITEM1 ITEM2...).
1970 Each ITEM is a cons cell (STRING . VALUE).
1971 The return value is VALUE from the chosen item.
1973 An ITEM may also be just a string--that makes a nonselectable item.
1974 An ITEM may also be nil--that means to put all preceding items
1975 on the left of the dialog box and all following items on the right.
1976 \(By default, approximately half appear on each side.)
1978 If HEADER is non-nil, the frame title for the box is "Information",
1979 otherwise it is "Question".
1981 If the user gets rid of the dialog box without making a valid choice,
1982 for instance using the window manager, then this produces a quit and
1983 `x-popup-dialog' does not return. */)
1984 (position, contents, header)
1985 Lisp_Object position, contents, header;
1987 return ns_popup_dialog (position, contents, header);
1990 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1991 doc: /* Return t if a menu or popup dialog is active. */)
1994 return popup_activated () ? Qt : Qnil;
1997 /* ==========================================================================
1999 Lisp interface declaration
2001 ========================================================================== */
2006 defsubr (&Sx_popup_menu);
2007 defsubr (&Sx_popup_dialog);
2008 defsubr (&Sns_reset_menu);
2009 defsubr (&Smenu_or_popup_active_p);
2010 staticpro (&menu_items);
2013 Qdebug_on_next_call = intern ("debug-on-next-call");
2014 staticpro (&Qdebug_on_next_call);
2017 // arch-tag: 75773656-52e5-4c44-a398-47bd87b32619