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 NSSize textSize = [str sizeWithAttributes:
1443 [NSDictionary dictionaryWithObject: [[textField font] screenFont]
1444 forKey: NSFontAttributeName]];
1445 NSSize padSize = [[[textField font] screenFont]
1446 boundingRectForFont].size;
1448 r.size.width = textSize.width + padSize.width/2;
1449 r.size.height = textSize.height + padSize.height/2;
1450 [textField setFrame: r];
1451 [textField setStringValue: str];
1454 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1456 NSRect wr = [win frame];
1458 wr.origin = NSMakePoint (x, y);
1459 wr.size = [textField frame].size;
1461 [win setFrame: wr display: YES];
1462 [win orderFront: self];
1464 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1465 selector: @selector (hide)
1466 userInfo: nil repeats: NO];
1475 if ([timer isValid])
1484 return timer != nil;
1489 return [textField frame];
1492 @end /* EmacsTooltip */
1496 /* ==========================================================================
1498 Popup Dialog: implementing functions
1500 ========================================================================== */
1504 pop_down_menu (Lisp_Object arg)
1506 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
1507 if (popup_activated_flag)
1509 popup_activated_flag = 0;
1511 [NSApp endModalSession: popupSession];
1512 [((EmacsDialogPanel *) (p->pointer)) close];
1513 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1521 ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1524 Lisp_Object window, tem;
1529 NSTRACE (x-popup-dialog);
1533 isQ = NILP (header);
1535 if (EQ (position, Qt)
1536 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1537 || EQ (XCAR (position), Qtool_bar))))
1539 window = selected_window;
1541 else if (CONSP (position))
1544 tem = Fcar (position);
1545 if (XTYPE (tem) == Lisp_Cons)
1546 window = Fcar (Fcdr (position));
1549 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1550 window = Fcar (tem); /* POSN_WINDOW (tem) */
1553 else if (WINDOWP (position) || FRAMEP (position))
1560 if (FRAMEP (window))
1561 f = XFRAME (window);
1562 else if (WINDOWP (window))
1564 CHECK_LIVE_WINDOW (window);
1565 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1568 CHECK_WINDOW (window);
1570 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1571 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1574 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1577 int specpdl_count = SPECPDL_INDEX ();
1578 record_unwind_protect (pop_down_menu, make_save_value (dialog, 0));
1579 popup_activated_flag = 1;
1580 tem = [dialog runDialogAt: p];
1581 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1589 /* ==========================================================================
1591 Popup Dialog: class implementation
1593 ========================================================================== */
1595 @interface FlippedView : NSView
1600 @implementation FlippedView
1607 @implementation EmacsDialogPanel
1610 #define ICONSIZE 64.0
1611 #define TEXTHEIGHT 20.0
1612 #define MINCELLWIDTH 90.0
1614 - initWithContentRect: (NSRect)contentRect styleMask: (unsigned int)aStyle
1615 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1617 NSSize spacing = {SPACER, SPACER};
1619 char this_cmd_name[80];
1621 static NSImageView *imgView;
1622 static FlippedView *contentView;
1627 area.origin.x = 3*SPACER;
1628 area.origin.y = 2*SPACER;
1629 area.size.width = ICONSIZE;
1630 area.size.height= ICONSIZE;
1631 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1632 [img setScalesWhenResized: YES];
1633 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1634 imgView = [[NSImageView alloc] initWithFrame: area];
1635 [imgView setImage: img];
1636 [imgView setEditable: NO];
1640 aStyle = NSTitledWindowMask;
1644 [super initWithContentRect: contentRect styleMask: aStyle
1645 backing: backingType defer: flag];
1646 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1647 [self setContentView: contentView];
1649 [[self contentView] setAutoresizesSubviews: YES];
1651 [[self contentView] addSubview: imgView];
1652 [self setTitle: @""];
1654 area.origin.x += ICONSIZE+2*SPACER;
1655 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1656 area.size.width = 400;
1657 area.size.height= TEXTHEIGHT;
1658 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1659 [[self contentView] addSubview: command];
1660 [command setStringValue: @"Emacs"];
1661 [command setDrawsBackground: NO];
1662 [command setBezeled: NO];
1663 [command setSelectable: NO];
1664 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1666 /* area.origin.x = ICONSIZE+2*SPACER;
1667 area.origin.y = TEXTHEIGHT + 2*SPACER;
1668 area.size.width = 400;
1669 area.size.height= 2;
1670 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1671 [[self contentView] addSubview: tem];
1672 [tem setTitlePosition: NSNoTitle];
1673 [tem setAutoresizingMask: NSViewWidthSizable];*/
1675 /* area.origin.x = ICONSIZE+2*SPACER; */
1676 area.origin.y += TEXTHEIGHT+SPACER;
1677 area.size.width = 400;
1678 area.size.height= TEXTHEIGHT;
1679 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1680 [[self contentView] addSubview: title];
1681 [title setDrawsBackground: NO];
1682 [title setBezeled: NO];
1683 [title setSelectable: NO];
1684 [title setFont: [NSFont systemFontOfSize: 11.0]];
1686 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1687 [cell setBordered: NO];
1688 [cell setEnabled: NO];
1689 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1690 [cell setBezelStyle: NSRoundedBezelStyle];
1692 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1693 mode: NSHighlightModeMatrix
1696 numberOfColumns: 1];
1697 [[self contentView] addSubview: matrix];
1699 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1700 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1701 [matrix setIntercellSpacing: spacing];
1703 [self setOneShot: YES];
1704 [self setReleasedWhenClosed: YES];
1705 [self setHidesOnDeactivate: YES];
1710 - (BOOL)windowShouldClose: (id)sender
1712 [NSApp stopModalWithCode: XHASH (Qnil)]; // FIXME: BIG UGLY HACK!!
1717 void process_dialog (id window, Lisp_Object list)
1722 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1725 if (XTYPE (item) == Lisp_String)
1727 [window addString: SDATA (item) row: row++];
1729 else if (XTYPE (item) == Lisp_Cons)
1731 [window addButton: SDATA (XCAR (item))
1732 value: XCDR (item) row: row++];
1734 else if (NILP (item))
1743 - addButton: (char *)str value: (Lisp_Object)val row: (int)row
1752 cell = [matrix cellAtRow: row column: cols-1];
1753 [cell setTarget: self];
1754 [cell setAction: @selector (clicked: )];
1755 [cell setTitle: [NSString stringWithUTF8String: str]];
1756 [cell setTag: XHASH (val)]; // FIXME: BIG UGLY HACK!!
1757 [cell setBordered: YES];
1758 [cell setEnabled: YES];
1764 - addString: (char *)str row: (int)row
1773 cell = [matrix cellAtRow: row column: cols-1];
1774 [cell setTitle: [NSString stringWithUTF8String: str]];
1775 [cell setBordered: YES];
1776 [cell setEnabled: NO];
1792 NSArray *sellist = nil;
1795 sellist = [sender selectedCells];
1796 if ([sellist count]<1)
1799 seltag = [[sellist objectAtIndex: 0] tag];
1800 if (seltag != XHASH (Qundefined)) // FIXME: BIG UGLY HACK!!
1801 [NSApp stopModalWithCode: seltag];
1806 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1811 if (XTYPE (contents) == Lisp_Cons)
1813 head = Fcar (contents);
1814 process_dialog (self, Fcdr (contents));
1819 if (XTYPE (head) == Lisp_String)
1820 [title setStringValue:
1821 [NSString stringWithUTF8String: SDATA (head)]];
1822 else if (isQ == YES)
1823 [title setStringValue: @"Question"];
1825 [title setStringValue: @"Information"];
1831 if (cols == 1 && rows > 1) /* Never told where to split */
1834 for (i = 0; i<rows/2; i++)
1836 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1837 atRow: i column: 1];
1838 [matrix removeRow: (rows+1)/2];
1844 NSSize csize = [matrix cellSize];
1845 if (csize.width < MINCELLWIDTH)
1847 csize.width = MINCELLWIDTH;
1848 [matrix setCellSize: csize];
1849 [matrix sizeToCells];
1854 [command sizeToFit];
1858 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1860 t.origin.x = r.origin.x;
1861 t.size.width = r.size.width;
1863 r = [command frame];
1864 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1866 t.origin.x = r.origin.x;
1867 t.size.width = r.size.width;
1871 s = [(NSView *)[self contentView] frame];
1872 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1873 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1874 [self setFrame: r display: NO];
1883 { [super dealloc]; return; };
1887 - (Lisp_Object)runDialogAt: (NSPoint)p
1891 /* initiate a session that will be ended by pop_down_menu */
1892 popupSession = [NSApp beginModalSessionForWindow: self];
1893 while (popup_activated_flag
1894 && (ret = [NSApp runModalSession: popupSession])
1895 == NSRunContinuesResponse)
1897 /* Run this for timers.el, indep of atimers; might not return.
1898 TODO: use return value to avoid calling every iteration. */
1900 [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
1903 { /* FIXME: BIG UGLY HACK!!! */
1905 *(EMACS_INT*)(&tmp) = ret;
1913 /* ==========================================================================
1917 ========================================================================== */
1919 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1920 doc: /* Cause the NS menu to be re-calculated. */)
1923 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1928 DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
1929 doc: /* Pop up a deck-of-cards menu and return user's selection.
1930 POSITION is a position specification. This is either a mouse button event
1931 or a list ((XOFFSET YOFFSET) WINDOW)
1932 where XOFFSET and YOFFSET are positions in pixels from the top left
1933 corner of WINDOW. (WINDOW may be a window or a frame object.)
1934 This controls the position of the top left of the menu as a whole.
1935 If POSITION is t, it means to use the current mouse position.
1937 MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
1938 The menu items come from key bindings that have a menu string as well as
1939 a definition; actually, the \"definition\" in such a key binding looks like
1940 \(STRING . REAL-DEFINITION). To give the menu a title, put a string into
1941 the keymap as a top-level element.
1943 If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
1944 Otherwise, REAL-DEFINITION should be a valid key binding definition.
1946 You can also use a list of keymaps as MENU.
1947 Then each keymap makes a separate pane.
1949 When MENU is a keymap or a list of keymaps, the return value is the
1950 list of events corresponding to the user's choice. Note that
1951 `x-popup-menu' does not actually execute the command bound to that
1954 Alternatively, you can specify a menu of multiple panes
1955 with a list of the form (TITLE PANE1 PANE2...),
1956 where each pane is a list of form (TITLE ITEM1 ITEM2...).
1957 Each ITEM is normally a cons cell (STRING . VALUE);
1958 but a string can appear as an item--that makes a nonselectable line
1960 With this form of menu, the return value is VALUE from the chosen item. */)
1962 Lisp_Object position, menu;
1964 return ns_popup_menu (position, menu);
1968 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1969 doc: /* Pop up a dialog box and return user's selection.
1970 POSITION specifies which frame to use.
1971 This is normally a mouse button event or a window or frame.
1972 If POSITION is t, it means to use the frame the mouse is on.
1973 The dialog box appears in the middle of the specified frame.
1975 CONTENTS specifies the alternatives to display in the dialog box.
1976 It is a list of the form (DIALOG ITEM1 ITEM2...).
1977 Each ITEM is a cons cell (STRING . VALUE).
1978 The return value is VALUE from the chosen item.
1980 An ITEM may also be just a string--that makes a nonselectable item.
1981 An ITEM may also be nil--that means to put all preceding items
1982 on the left of the dialog box and all following items on the right.
1983 \(By default, approximately half appear on each side.)
1985 If HEADER is non-nil, the frame title for the box is "Information",
1986 otherwise it is "Question".
1988 If the user gets rid of the dialog box without making a valid choice,
1989 for instance using the window manager, then this produces a quit and
1990 `x-popup-dialog' does not return. */)
1991 (position, contents, header)
1992 Lisp_Object position, contents, header;
1994 return ns_popup_dialog (position, contents, header);
1997 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1998 doc: /* Return t if a menu or popup dialog is active. */)
2001 return popup_activated () ? Qt : Qnil;
2004 /* ==========================================================================
2006 Lisp interface declaration
2008 ========================================================================== */
2013 defsubr (&Sx_popup_menu);
2014 defsubr (&Sx_popup_dialog);
2015 defsubr (&Sns_reset_menu);
2016 defsubr (&Smenu_or_popup_active_p);
2017 staticpro (&menu_items);
2020 Qdebug_on_next_call = intern ("debug-on-next-call");
2021 staticpro (&Qdebug_on_next_call);
2024 // arch-tag: 75773656-52e5-4c44-a398-47bd87b32619