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. */
35 #include "blockinput.h"
37 #include "termhooks.h"
40 #define NSMENUPROFILE 0
43 #include <sys/timeb.h>
44 #include <sys/types.h>
47 #define MenuStagger 10.0
50 int menu_trace_num = 0;
51 #define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \
52 __FILE__, __LINE__, ++menu_trace_num)
58 /* Include lisp -> C common menu parsing code */
59 #define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
60 #include "nsmenu_common.c"
63 extern Lisp_Object Qundefined, Qmenu_enable, Qmenu_bar_update_hook;
64 extern Lisp_Object QCtoggle, QCradio;
66 extern Lisp_Object Vmenu_updating_frame;
68 Lisp_Object Qdebug_on_next_call;
69 extern Lisp_Object Voverriding_local_map, Voverriding_local_map_menu_flag,
70 Qoverriding_local_map, Qoverriding_terminal_local_map;
72 extern long context_menu_value;
73 EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
75 /* Nonzero means a menu is currently active. */
76 static int popup_activated_flag;
77 static NSModalSession popupSession;
79 /* NOTE: toolbar implementation is at end,
80 following complete menu implementation. */
83 /* ==========================================================================
85 Menu: Externally-called functions
87 ========================================================================== */
90 /* FIXME: not currently used, but should normalize with other terms. */
92 x_activate_menubar (struct frame *f)
94 fprintf (stderr, "XXX: Received x_activate_menubar event.\n");
98 /* Supposed to discard menubar and free storage. Since we share the
99 menubar among frames and update its context for the focused window,
100 there is nothing to do here. */
102 free_frame_menubar (struct frame *f)
111 return popup_activated_flag;
115 /* --------------------------------------------------------------------------
116 Update menubar. Three cases:
117 1) deep_p = 0, submenu = nil: Fresh switch onto a frame -- either set up
118 just top-level menu strings (OS X), or goto case (2) (GNUstep).
119 2) deep_p = 1, submenu = nil: Recompute all submenus.
120 3) deep_p = 1, submenu = non-nil: Update contents of a single submenu.
121 -------------------------------------------------------------------------- */
123 ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
125 NSAutoreleasePool *pool;
126 id menu = [NSApp mainMenu];
127 static EmacsMenu *last_submenu = nil;
129 const char *submenuTitle = [[submenu title] UTF8String];
130 extern int waiting_for_input;
133 widget_value *wv, *first_wv, *prev_wv = 0;
141 NSTRACE (set_frame_menubar);
143 if (f != SELECTED_FRAME ())
145 XSETFRAME (Vmenu_updating_frame, f);
146 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
149 pool = [[NSAutoreleasePool alloc] init];
151 /* Menu may have been created automatically; if so, discard it. */
152 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
160 menu = [[EmacsMenu alloc] initWithTitle: ns_app_name];
164 { /* close up anything on there */
165 id attMenu = [menu attachedMenu];
172 t = -(1000*tb.time+tb.millitm);
175 #ifdef NS_IMPL_GNUSTEP
176 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
181 /* Fully parse one or more of the submenus. */
183 int *submenu_start, *submenu_end;
184 int *submenu_top_level_items, *submenu_n_panes;
185 struct buffer *prev = current_buffer;
187 int specpdl_count = SPECPDL_INDEX ();
188 int previous_menu_items_used = f->menu_bar_items_used;
189 Lisp_Object *previous_items
190 = (Lisp_Object *) alloca (previous_menu_items_used
191 * sizeof (Lisp_Object));
193 /* lisp preliminaries */
194 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
195 specbind (Qinhibit_quit, Qt);
196 specbind (Qdebug_on_next_call, Qnil);
197 record_unwind_save_match_data ();
198 if (NILP (Voverriding_local_map_menu_flag))
200 specbind (Qoverriding_terminal_local_map, Qnil);
201 specbind (Qoverriding_local_map, Qnil);
203 set_buffer_internal_1 (XBUFFER (buffer));
205 /* TODO: for some reason this is not needed in other terms,
206 but some menu updates call Info-extract-pointer which causes
207 abort-on-error if waiting-for-input. Needs further investigation. */
208 owfi = waiting_for_input;
209 waiting_for_input = 0;
211 /* lucid hook and possible reset */
212 safe_run_hooks (Qactivate_menubar_hook);
213 if (! NILP (Vlucid_menu_bar_dirty_flag))
214 call0 (Qrecompute_lucid_menubar);
215 safe_run_hooks (Qmenu_bar_update_hook);
216 FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
218 /* Now ready to go */
219 items = FRAME_MENU_BAR_ITEMS (f);
221 /* Save the frame's previous menu bar contents data */
222 if (previous_menu_items_used)
223 bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
224 previous_menu_items_used * sizeof (Lisp_Object));
226 /* parse stage 1: extract from lisp */
229 menu_items = f->menu_bar_vector;
230 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
231 submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
232 submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
233 submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int));
234 submenu_top_level_items
235 = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
237 for (i = 0; i < XVECTOR (items)->size; i += 4)
239 Lisp_Object key, string, maps;
241 key = XVECTOR (items)->contents[i];
242 string = XVECTOR (items)->contents[i + 1];
243 maps = XVECTOR (items)->contents[i + 2];
247 /* FIXME: we'd like to only parse the needed submenu, but this
248 was causing crashes in the _common parsing code.. need to make
249 sure proper initialization done.. */
250 /* if (submenu && strcmp (submenuTitle, SDATA (string)))
253 submenu_start[i] = menu_items_used;
255 menu_items_n_panes = 0;
256 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
257 submenu_n_panes[i] = menu_items_n_panes;
258 submenu_end[i] = menu_items_used;
262 finish_menu_items ();
263 waiting_for_input = owfi;
266 if (submenu && n == 0)
268 /* should have found a menu for this one but didn't */
269 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
271 discard_menu_items ();
272 unbind_to (specpdl_count, Qnil);
278 /* parse stage 2: insert into lucid 'widget_value' structures
279 [comments in other terms say not to evaluate lisp code here] */
280 wv = xmalloc_widget_value ();
281 wv->name = "menubar";
284 wv->button_type = BUTTON_TYPE_NONE;
288 for (i = 0; i < 4*n; i += 4)
290 menu_items_n_panes = submenu_n_panes[i];
291 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
292 submenu_top_level_items[i]);
296 first_wv->contents = wv;
297 /* Don't set wv->name here; GC during the loop might relocate it. */
299 wv->button_type = BUTTON_TYPE_NONE;
303 set_buffer_internal_1 (prev);
305 /* Compare the new menu items with previous, and leave off if no change */
306 /* FIXME: following other terms here, but seems like this should be
307 done before parse stage 2 above, since its results aren't used */
308 if (previous_menu_items_used
309 && (!submenu || (submenu && submenu == last_submenu))
310 && menu_items_used == previous_menu_items_used)
312 for (i = 0; i < previous_menu_items_used; i++)
313 /* FIXME: this ALWAYS fails on Buffers menu items.. something
314 about their strings causes them to change every time, so we
315 double-check failures */
316 if (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i]))
317 if (!(STRINGP (previous_items[i])
318 && STRINGP (XVECTOR (menu_items)->contents[i])
319 && !strcmp (SDATA (previous_items[i]),
320 SDATA (XVECTOR (menu_items)->contents[i]))))
322 if (i == previous_menu_items_used)
328 t += 1000*tb.time+tb.millitm;
329 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
332 free_menubar_widget_value_tree (first_wv);
333 discard_menu_items ();
334 unbind_to (specpdl_count, Qnil);
340 /* The menu items are different, so store them in the frame */
341 /* FIXME: this is not correct for single-submenu case */
342 f->menu_bar_vector = menu_items;
343 f->menu_bar_items_used = menu_items_used;
345 /* Calls restore_menu_items, etc., as they were outside */
346 unbind_to (specpdl_count, Qnil);
348 /* Parse stage 2a: now GC cannot happen during the lifetime of the
349 widget_value, so it's safe to store data from a Lisp_String */
350 wv = first_wv->contents;
351 for (i = 0; i < XVECTOR (items)->size; i += 4)
354 string = XVECTOR (items)->contents[i + 1];
357 /* if (submenu && strcmp (submenuTitle, SDATA (string)))
360 wv->name = (char *) SDATA (string);
361 update_submenu_strings (wv->contents);
365 /* Now, update the NS menu; if we have a submenu, use that, otherwise
366 create a new menu for each sub and fill it. */
369 for (wv = first_wv->contents; wv; wv = wv->next)
371 if (!strcmp (submenuTitle, wv->name))
373 [submenu fillWithWidgetValue: wv->contents];
374 last_submenu = submenu;
381 [menu fillWithWidgetValue: first_wv->contents];
387 static int n_previous_strings = 0;
388 static char previous_strings[100][10];
389 static struct frame *last_f = NULL;
393 wv = xmalloc_widget_value ();
394 wv->name = "menubar";
397 wv->button_type = BUTTON_TYPE_NONE;
401 /* Make widget-value tree w/ just the top level menu bar strings */
402 items = FRAME_MENU_BAR_ITEMS (f);
411 /* check if no change.. this mechanism is a bit rough, but ready */
412 n = XVECTOR (items)->size / 4;
413 if (f == last_f && n_previous_strings == n)
415 for (i = 0; i<n; i++)
417 string = AREF (items, 4*i+1);
419 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
422 if (previous_strings[i][0])
426 if (strncmp (previous_strings[i], SDATA (string), 10))
439 for (i = 0; i < XVECTOR (items)->size; i += 4)
441 string = XVECTOR (items)->contents[i + 1];
446 strncpy (previous_strings[i/4], SDATA (string), 10);
448 wv = xmalloc_widget_value ();
449 wv->name = (char *) SDATA (string);
452 wv->button_type = BUTTON_TYPE_NONE;
454 wv->call_data = (void *) (EMACS_INT) (-1);
457 /* we'll update the real copy under app menu when time comes */
458 if (!strcmp ("Services", wv->name))
460 /* but we need to make sure it will update on demand */
461 [svcsMenu setFrame: f];
462 [svcsMenu setDelegate: svcsMenu];
466 [menu addSubmenuWithTitle: wv->name forFrame: f];
471 first_wv->contents = wv;
477 n_previous_strings = n;
479 n_previous_strings = 0;
482 free_menubar_widget_value_tree (first_wv);
487 t += 1000*tb.time+tb.millitm;
488 fprintf (stderr, "Menu update took %ld msec.\n", t);
493 [NSApp setMainMenu: menu];
501 /* Main emacs core entry point for menubar menus: called to indicate that the
502 frame's menus have changed, and the *step representation should be updated
505 set_frame_menubar (struct frame *f, int first_time, int deep_p)
507 ns_update_menubar (f, deep_p, nil);
511 /* Utility (from macmenu.c): is this item a separator? */
513 name_is_separator (name)
516 const char *start = name;
518 /* Check if name string consists of only dashes ('-'). */
519 while (*name == '-') name++;
520 /* Separators can also be of the form "--:TripleSuperMegaEtched"
521 or "--deep-shadow". We don't implement them yet, se we just treat
522 them like normal separators. */
523 return (*name == '\0' || start + 2 == name);
527 /* ==========================================================================
529 Menu: class implementation
531 ========================================================================== */
534 /* Menu that can define itself from Emacs "widget_value"s and will lazily
535 update itself when user clicked. Based on Carbon/AppKit implementation
536 by Yamamoto Mitsuharu. */
537 @implementation EmacsMenu
539 /* override designated initializer */
540 - initWithTitle: (NSString *)title
542 if (self = [super initWithTitle: title])
543 [self setAutoenablesItems: NO];
548 /* used for top-level */
549 - initWithTitle: (NSString *)title frame: (struct frame *)f
551 [self initWithTitle: title];
554 [self setDelegate: self];
560 - (void)setFrame: (struct frame *)f
566 /* delegate method called when a submenu is being opened: run a 'deep' call
567 to set_frame_menubar */
568 - (void)menuNeedsUpdate: (NSMenu *)menu
571 if (!FRAME_LIVE_P (frame))
573 event = [[FRAME_NS_VIEW (frame) window] currentEvent];
574 /* HACK: Cocoa/Carbon will request update on every keystroke
575 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
576 since key equivalents are handled through emacs.
577 On Leopard, even keystroke events generate SystemDefined events, but
578 their subtype is 8. */
579 if ([event type] != NSSystemDefined || [event subtype] == 8
580 /* Also, don't try this if from an event picked up asynchronously,
581 as lots of lisp evaluation happens in ns_update_menubar. */
582 || handling_signal != 0)
584 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
585 ns_update_menubar (frame, 1, self);
589 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
591 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
592 && FRAME_NS_VIEW (SELECTED_FRAME ()))
593 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
598 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
599 into an accelerator string. We are only able to display a single character
600 for an accelerator, together with an optional modifier combination. (Under
601 Carbon more control was possible, but in Cocoa multi-char strings passed to
602 NSMenuItem get ignored. For now we try to display a super-single letter
603 combo, and return the others as strings to be appended to the item title.
604 (This is signaled by setting keyEquivModMask to 0 for now.) */
605 -(NSString *)parseKeyEquiv: (char *)key
608 keyEquivModMask = NSCommandKeyMask;
610 if (!key || !strlen (key))
613 while (*tpos == ' ' || *tpos == '(')
615 if ((*tpos == 's') && (*(tpos+1) == '-'))
617 return [NSString stringWithFormat: @"%c", tpos[2]];
619 keyEquivModMask = 0; /* signal */
620 return [NSString stringWithUTF8String: tpos];
624 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
627 widget_value *wv = (widget_value *)wvptr;
629 if (name_is_separator (wv->name))
631 item = [NSMenuItem separatorItem];
632 [self addItem: item];
636 NSString *title, *keyEq;
637 title = [NSString stringWithUTF8String: wv->name];
639 title = @"< ? >"; /* (get out in the open so we know about it) */
641 keyEq = [self parseKeyEquiv: wv->key];
643 /* OS X just ignores modifier strings longer than one character */
644 if (keyEquivModMask == 0)
645 title = [title stringByAppendingFormat: @" (%@)", keyEq];
648 item = [self addItemWithTitle: (NSString *)title
649 action: @selector (menuDown:)
650 keyEquivalent: keyEq];
651 [item setKeyEquivalentModifierMask: keyEquivModMask];
653 [item setEnabled: wv->enabled];
655 /* Draw radio buttons and tickboxes */
656 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
657 wv->button_type == BUTTON_TYPE_RADIO))
658 [item setState: NSOnState];
660 [item setState: NSOffState];
662 [item setTag: (NSInteger)wv->call_data];
674 for (n = [self numberOfItems]-1; n >= 0; n--)
676 NSMenuItem *item = [self itemAtIndex: n];
677 NSString *title = [item title];
678 if (([title length] == 0 /* OSX 10.5 */
679 || [ns_app_name isEqualToString: title] /* from 10.6 on */
680 || [@"Apple" isEqualToString: title]) /* older */
681 && ![item isSeparatorItem])
683 [self removeItemAtIndex: n];
688 - (void)fillWithWidgetValue: (void *)wvptr
690 widget_value *wv = (widget_value *)wvptr;
692 /* clear existing contents */
693 [self setMenuChangedMessagesEnabled: NO];
696 /* add new contents */
697 for (; wv != NULL; wv = wv->next)
699 NSMenuItem *item = [self addItemWithWidgetValue: wv];
703 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
705 [self setSubmenu: submenu forItem: item];
706 [submenu fillWithWidgetValue: wv->contents];
708 [item setAction: nil];
712 [self setMenuChangedMessagesEnabled: YES];
713 #ifdef NS_IMPL_GNUSTEP
714 if ([[self window] isVisible])
717 if ([self supermenu] == nil)
723 /* adds an empty submenu and returns it */
724 - (EmacsMenu *)addSubmenuWithTitle: (char *)title forFrame: (struct frame *)f
726 NSString *titleStr = [NSString stringWithUTF8String: title];
727 NSMenuItem *item = [self addItemWithTitle: titleStr
728 action: nil /*@selector (menuDown:) */
730 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
731 [self setSubmenu: submenu forItem: item];
736 /* run a menu in popup mode */
737 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
738 keymaps: (int)keymaps
740 EmacsView *view = FRAME_NS_VIEW (f);
741 /* p = [view convertPoint:p fromView: nil]; */
742 p.y = NSHeight ([view frame]) - p.y;
743 NSEvent *e = [[view window] currentEvent];
744 NSEvent *event = [NSEvent mouseEventWithType: NSRightMouseDown
747 timestamp: [e timestamp]
748 windowNumber: [[view window] windowNumber]
750 eventNumber: 0/*[e eventNumber] */
755 context_menu_value = -1;
756 [NSMenu popUpContextMenu: self withEvent: event forView: view];
757 retVal = context_menu_value;
758 context_menu_value = 0;
760 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
768 /* ==========================================================================
770 Context Menu: implementing functions
772 ========================================================================== */
775 cleanup_popup_menu (Lisp_Object arg)
777 discard_menu_items ();
783 ns_popup_menu (Lisp_Object position, Lisp_Object menu)
786 struct frame *f = NULL;
788 Lisp_Object window, x, y, tem, keymap, title;
790 int specpdl_count = SPECPDL_INDEX (), specpdl_count2;
791 char *error_name = NULL;
793 widget_value *wv, *first_wv = 0;
795 NSTRACE (ns_popup_menu);
797 if (!NILP (position))
801 if (EQ (position, Qt)
802 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
803 || EQ (XCAR (position), Qtool_bar))))
805 /* Use the mouse's current position. */
806 struct frame *new_f = SELECTED_FRAME ();
808 if (FRAME_TERMINAL (new_f)->mouse_position_hook)
809 (*FRAME_TERMINAL (new_f)->mouse_position_hook)
810 (&new_f, 0, 0, 0, &x, &y, 0);
812 XSETFRAME (window, new_f);
815 window = selected_window;
822 CHECK_CONS (position);
823 tem = Fcar (position);
824 if (XTYPE (tem) == Lisp_Cons)
826 window = Fcar (Fcdr (position));
828 y = Fcar (Fcdr (tem));
832 tem = Fcar (Fcdr (position));
834 tem = Fcar (Fcdr (Fcdr (tem)));
852 struct window *win = XWINDOW (window);
853 CHECK_LIVE_WINDOW (window);
854 f = XFRAME (WINDOW_FRAME (win));
855 p.x = FRAME_COLUMN_WIDTH (f) * WINDOW_LEFT_EDGE_COL (win);
856 p.y = FRAME_LINE_HEIGHT (f) * WINDOW_TOP_EDGE_LINE (win);
859 p.x += XINT (x); p.y += XINT (y);
861 XSETFRAME (Vmenu_updating_frame, f);
864 { /* no position given */
865 /* FIXME: if called during dump, we need to stop precomputation of
866 key equivalents (see below) because the keydefs in ns-win.el have
867 not been loaded yet. */
870 Vmenu_updating_frame = Qnil;
873 /* now parse the lisp menus */
874 record_unwind_protect (unuse_menu_items, Qnil);
878 /* Decode the menu items from what was specified. */
880 keymap = get_keymap (menu, 0, 0);
883 /* We were given a keymap. Extract menu info from the keymap. */
886 /* Extract the detailed info to make one pane. */
887 keymap_panes (&menu, 1, NILP (position));
889 /* Search for a string appearing directly as an element of the keymap.
890 That string is the title of the menu. */
891 prompt = Fkeymap_prompt (keymap);
892 title = NILP (prompt) ? build_string ("Select") : prompt;
894 /* Make that be the pane title of the first pane. */
895 if (!NILP (prompt) && menu_items_n_panes >= 0)
896 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
900 else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
902 /* We were given a list of keymaps. */
903 int nmaps = XFASTINT (Flength (menu));
905 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
910 /* The first keymap that has a prompt string
911 supplies the menu title. */
912 for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
916 maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
918 prompt = Fkeymap_prompt (keymap);
919 if (NILP (title) && !NILP (prompt))
923 /* Extract the detailed info to make one pane. */
924 keymap_panes (maps, nmaps, NILP (position));
926 /* Make the title be the pane title of the first pane. */
927 if (!NILP (title) && menu_items_n_panes >= 0)
928 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
934 /* We were given an old-fashioned menu. */
936 CHECK_STRING (title);
938 list_of_panes (Fcdr (menu));
943 unbind_to (specpdl_count, Qnil);
945 /* If no position given, that was a signal to just precompute and cache
946 key equivalents, which was a side-effect of what we just did. */
949 discard_menu_items ();
954 record_unwind_protect (cleanup_popup_menu, Qnil);
957 /* now parse stage 2 as in ns_update_menubar */
958 wv = xmalloc_widget_value ();
959 wv->name = "contextmenu";
962 wv->button_type = BUTTON_TYPE_NONE;
966 specpdl_count2 = SPECPDL_INDEX ();
969 /* FIXME: a couple of one-line differences prevent reuse */
970 wv = digest_single_submenu (0, menu_items_used, Qnil);
973 widget_value *save_wv = 0, *prev_wv = 0;
974 widget_value **submenu_stack
975 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
976 /* Lisp_Object *subprefix_stack
977 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object)); */
978 int submenu_depth = 0;
982 /* Loop over all panes and items, filling in the tree. */
984 while (i < menu_items_used)
986 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
988 submenu_stack[submenu_depth++] = save_wv;
994 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
997 save_wv = submenu_stack[--submenu_depth];
1001 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
1002 && submenu_depth != 0)
1003 i += MENU_ITEMS_PANE_LENGTH;
1004 /* Ignore a nil in the item list.
1005 It's meaningful only for dialog boxes. */
1006 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1008 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1010 /* Create a new pane. */
1011 Lisp_Object pane_name, prefix;
1014 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
1015 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
1017 #ifndef HAVE_MULTILINGUAL_MENU
1018 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
1020 pane_name = ENCODE_MENU_STRING (pane_name);
1021 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
1024 pane_string = (NILP (pane_name)
1025 ? "" : (char *) SDATA (pane_name));
1026 /* If there is just one top-level pane, put all its items directly
1027 under the top-level menu. */
1028 if (menu_items_n_panes == 1)
1031 /* If the pane has a meaningful name,
1032 make the pane a top-level menu item
1033 with its items as a submenu beneath it. */
1034 if (!keymaps && strcmp (pane_string, ""))
1036 wv = xmalloc_widget_value ();
1040 first_wv->contents = wv;
1041 wv->name = pane_string;
1042 if (keymaps && !NILP (prefix))
1046 wv->button_type = BUTTON_TYPE_NONE;
1051 else if (first_pane)
1057 i += MENU_ITEMS_PANE_LENGTH;
1061 /* Create a new item within current pane. */
1062 Lisp_Object item_name, enable, descrip, def, type, selected, help;
1063 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
1064 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
1065 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
1066 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
1067 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
1068 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
1069 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
1071 #ifndef HAVE_MULTILINGUAL_MENU
1072 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
1074 item_name = ENCODE_MENU_STRING (item_name);
1075 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
1078 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
1080 descrip = ENCODE_MENU_STRING (descrip);
1081 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
1083 #endif /* not HAVE_MULTILINGUAL_MENU */
1085 wv = xmalloc_widget_value ();
1089 save_wv->contents = wv;
1090 wv->name = (char *) SDATA (item_name);
1091 if (!NILP (descrip))
1092 wv->key = (char *) SDATA (descrip);
1094 /* If this item has a null value,
1095 make the call_data null so that it won't display a box
1096 when the mouse is on it. */
1098 = !NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0;
1099 wv->enabled = !NILP (enable);
1102 wv->button_type = BUTTON_TYPE_NONE;
1103 else if (EQ (type, QCtoggle))
1104 wv->button_type = BUTTON_TYPE_TOGGLE;
1105 else if (EQ (type, QCradio))
1106 wv->button_type = BUTTON_TYPE_RADIO;
1110 wv->selected = !NILP (selected);
1112 if (! STRINGP (help))
1119 i += MENU_ITEMS_ITEM_LENGTH;
1127 widget_value *wv_title = xmalloc_widget_value ();
1128 widget_value *wv_sep = xmalloc_widget_value ();
1130 /* Maybe replace this separator with a bitmap or owner-draw item
1131 so that it looks better. Having two separators looks odd. */
1132 wv_sep->name = "--";
1133 wv_sep->next = first_wv->contents;
1134 wv_sep->help = Qnil;
1136 #ifndef HAVE_MULTILINGUAL_MENU
1137 if (STRING_MULTIBYTE (title))
1138 title = ENCODE_MENU_STRING (title);
1141 wv_title->name = (char *) SDATA (title);
1142 wv_title->enabled = NO;
1143 wv_title->button_type = BUTTON_TYPE_NONE;
1144 wv_title->help = Qnil;
1145 wv_title->next = wv_sep;
1146 first_wv->contents = wv_title;
1149 pmenu = [[EmacsMenu alloc] initWithTitle:
1150 [NSString stringWithUTF8String: SDATA (title)]];
1151 [pmenu fillWithWidgetValue: first_wv->contents];
1152 free_menubar_widget_value_tree (first_wv);
1153 unbind_to (specpdl_count2, Qnil);
1155 popup_activated_flag = 1;
1156 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
1157 popup_activated_flag = 0;
1158 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1161 discard_menu_items ();
1162 unbind_to (specpdl_count, Qnil);
1165 if (error_name) error (error_name);
1172 /* ==========================================================================
1174 Toolbar: externally-called functions
1176 ========================================================================== */
1179 free_frame_tool_bar (FRAME_PTR f)
1180 /* --------------------------------------------------------------------------
1181 Under NS we just hide the toolbar until it might be needed again.
1182 -------------------------------------------------------------------------- */
1184 [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
1188 update_frame_tool_bar (FRAME_PTR f)
1189 /* --------------------------------------------------------------------------
1190 Update toolbar contents
1191 -------------------------------------------------------------------------- */
1194 EmacsToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
1196 [toolbar clearActive];
1198 /* update EmacsToolbar as in GtkUtils, build items list */
1199 for (i = 0; i < f->n_tool_bar_items; ++i)
1201 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1202 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1204 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1205 BOOL selected_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_SELECTED_P));
1210 Lisp_Object helpObj;
1213 /* If image is a vector, choose the image according to the
1215 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1216 if (VECTORP (image))
1218 /* NS toolbar auto-computes disabled and selected images */
1219 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1220 xassert (ASIZE (image) >= idx);
1221 image = AREF (image, idx);
1227 /* Ignore invalid image specifications. */
1228 if (!valid_image_p (image))
1230 NSLog (@"Invalid image for toolbar item");
1234 img_id = lookup_image (f, image);
1235 img = IMAGE_FROM_ID (f, img_id);
1236 prepare_image_for_display (f, img);
1238 if (img->load_failed_p || img->pixmap == nil)
1240 NSLog (@"Could not prepare toolbar image for display.");
1244 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1246 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1247 helpText = NILP (helpObj) ? "" : (char *)SDATA (helpObj);
1249 [toolbar addDisplayItemWithImage: img->pixmap idx: i helpText: helpText
1250 enabled: enabled_p];
1254 if (![toolbar isVisible])
1255 [toolbar setVisible: YES];
1257 if ([toolbar changed])
1259 /* inform app that toolbar has changed */
1260 NSDictionary *dict = [toolbar configurationDictionary];
1261 NSMutableDictionary *newDict = [dict mutableCopy];
1262 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1264 while ((key = [keys nextObject]) != nil)
1266 NSObject *val = [dict objectForKey: key];
1267 if ([val isKindOfClass: [NSArray class]])
1270 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1275 [toolbar setConfigurationFromDictionary: newDict];
1282 /* ==========================================================================
1284 Toolbar: class implementation
1286 ========================================================================== */
1288 @implementation EmacsToolbar
1290 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1292 self = [super initWithIdentifier: identifier];
1294 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1295 [self setSizeMode: NSToolbarSizeModeSmall];
1296 [self setDelegate: self];
1297 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1298 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1299 prevEnablement = enablement = 0L;
1305 [prevIdentifiers release];
1306 [activeIdentifiers release];
1307 [identifierToItem release];
1311 - (void) clearActive
1313 [prevIdentifiers release];
1314 prevIdentifiers = [activeIdentifiers copy];
1315 [activeIdentifiers removeAllObjects];
1316 prevEnablement = enablement;
1322 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1323 enablement == prevEnablement ? NO : YES;
1326 - (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx
1327 helpText: (char *)help enabled: (BOOL)enabled
1329 /* 1) come up w/identifier */
1330 NSString *identifier
1331 = [NSString stringWithFormat: @"%u", [img hash]];
1333 /* 2) create / reuse item */
1334 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1337 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1339 [item setImage: img];
1340 [item setToolTip: [NSString stringWithUTF8String: help]];
1341 [item setTarget: emacsView];
1342 [item setAction: @selector (toolbarClicked:)];
1346 [item setEnabled: enabled];
1348 /* 3) update state */
1349 [identifierToItem setObject: item forKey: identifier];
1350 [activeIdentifiers addObject: identifier];
1351 enablement = (enablement << 1) | (enabled == YES);
1354 /* This overrides super's implementation, which automatically sets
1355 all items to enabled state (for some reason). */
1356 - (void)validateVisibleItems { }
1359 /* delegate methods */
1361 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1362 itemForItemIdentifier: (NSString *)itemIdentifier
1363 willBeInsertedIntoToolbar: (BOOL)flag
1365 /* look up NSToolbarItem by identifier and return... */
1366 return [identifierToItem objectForKey: itemIdentifier];
1369 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1371 /* return entire set.. */
1372 return activeIdentifiers;
1375 /* for configuration palette (not yet supported) */
1376 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1378 /* return entire set... */
1379 return [identifierToItem allKeys];
1382 /* optional and unneeded */
1383 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1384 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1385 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1387 @end /* EmacsToolbar */
1391 /* ==========================================================================
1393 Tooltip: class implementation
1395 ========================================================================== */
1397 /* Needed because NeXTstep does not provide enough control over tooltip
1399 @implementation EmacsTooltip
1403 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1404 blue: 0.792 alpha: 0.95];
1405 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1406 NSFont *sfont = [font screenFont];
1407 int height = [sfont ascender] - [sfont descender];
1408 /*[font boundingRectForFont].size.height; */
1409 NSRect r = NSMakeRect (0, 0, 100, height+6);
1411 textField = [[NSTextField alloc] initWithFrame: r];
1412 [textField setFont: font];
1413 [textField setBackgroundColor: col];
1415 [textField setEditable: NO];
1416 [textField setSelectable: NO];
1417 [textField setBordered: YES];
1418 [textField setBezeled: YES];
1419 [textField setDrawsBackground: YES];
1421 win = [[NSWindow alloc]
1422 initWithContentRect: [textField frame]
1424 backing: NSBackingStoreBuffered
1426 [win setReleasedWhenClosed: NO];
1427 [win setDelegate: self];
1428 [[win contentView] addSubview: textField];
1429 /* [win setBackgroundColor: col]; */
1430 [win setOpaque: NO];
1439 [textField release];
1443 - (void) setText: (char *)text
1445 NSString *str = [NSString stringWithUTF8String: text];
1446 NSRect r = [textField frame];
1447 NSSize textSize = [str sizeWithAttributes:
1448 [NSDictionary dictionaryWithObject: [[textField font] screenFont]
1449 forKey: NSFontAttributeName]];
1450 NSSize padSize = [[[textField font] screenFont]
1451 boundingRectForFont].size;
1453 r.size.width = textSize.width + padSize.width/2;
1454 r.size.height = textSize.height + padSize.height/2;
1455 [textField setFrame: r];
1456 [textField setStringValue: str];
1459 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1461 NSRect wr = [win frame];
1463 wr.origin = NSMakePoint (x, y);
1464 wr.size = [textField frame].size;
1466 [win setFrame: wr display: YES];
1467 [win orderFront: self];
1469 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1470 selector: @selector (hide)
1471 userInfo: nil repeats: NO];
1480 if ([timer isValid])
1489 return timer != nil;
1494 return [textField frame];
1497 @end /* EmacsTooltip */
1501 /* ==========================================================================
1503 Popup Dialog: implementing functions
1505 ========================================================================== */
1509 pop_down_menu (Lisp_Object arg)
1511 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
1512 if (popup_activated_flag)
1514 popup_activated_flag = 0;
1516 [NSApp endModalSession: popupSession];
1517 [((EmacsDialogPanel *) (p->pointer)) close];
1518 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1526 ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1529 Lisp_Object window, tem;
1534 NSTRACE (x-popup-dialog);
1538 isQ = NILP (header);
1540 if (EQ (position, Qt)
1541 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1542 || EQ (XCAR (position), Qtool_bar))))
1544 window = selected_window;
1546 else if (CONSP (position))
1549 tem = Fcar (position);
1550 if (XTYPE (tem) == Lisp_Cons)
1551 window = Fcar (Fcdr (position));
1554 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1555 window = Fcar (tem); /* POSN_WINDOW (tem) */
1558 else if (WINDOWP (position) || FRAMEP (position))
1565 if (FRAMEP (window))
1566 f = XFRAME (window);
1567 else if (WINDOWP (window))
1569 CHECK_LIVE_WINDOW (window);
1570 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1573 CHECK_WINDOW (window);
1575 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1576 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1579 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1582 int specpdl_count = SPECPDL_INDEX ();
1583 record_unwind_protect (pop_down_menu, make_save_value (dialog, 0));
1584 popup_activated_flag = 1;
1585 tem = [dialog runDialogAt: p];
1586 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1594 /* ==========================================================================
1596 Popup Dialog: class implementation
1598 ========================================================================== */
1600 @interface FlippedView : NSView
1605 @implementation FlippedView
1612 @implementation EmacsDialogPanel
1615 #define ICONSIZE 64.0
1616 #define TEXTHEIGHT 20.0
1617 #define MINCELLWIDTH 90.0
1619 - initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
1620 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1622 NSSize spacing = {SPACER, SPACER};
1624 char this_cmd_name[80];
1626 static NSImageView *imgView;
1627 static FlippedView *contentView;
1632 area.origin.x = 3*SPACER;
1633 area.origin.y = 2*SPACER;
1634 area.size.width = ICONSIZE;
1635 area.size.height= ICONSIZE;
1636 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1637 [img setScalesWhenResized: YES];
1638 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1639 imgView = [[NSImageView alloc] initWithFrame: area];
1640 [imgView setImage: img];
1641 [imgView setEditable: NO];
1645 aStyle = NSTitledWindowMask;
1649 [super initWithContentRect: contentRect styleMask: aStyle
1650 backing: backingType defer: flag];
1651 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1652 [self setContentView: contentView];
1654 [[self contentView] setAutoresizesSubviews: YES];
1656 [[self contentView] addSubview: imgView];
1657 [self setTitle: @""];
1659 area.origin.x += ICONSIZE+2*SPACER;
1660 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1661 area.size.width = 400;
1662 area.size.height= TEXTHEIGHT;
1663 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1664 [[self contentView] addSubview: command];
1665 [command setStringValue: ns_app_name];
1666 [command setDrawsBackground: NO];
1667 [command setBezeled: NO];
1668 [command setSelectable: NO];
1669 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1671 /* area.origin.x = ICONSIZE+2*SPACER;
1672 area.origin.y = TEXTHEIGHT + 2*SPACER;
1673 area.size.width = 400;
1674 area.size.height= 2;
1675 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1676 [[self contentView] addSubview: tem];
1677 [tem setTitlePosition: NSNoTitle];
1678 [tem setAutoresizingMask: NSViewWidthSizable];*/
1680 /* area.origin.x = ICONSIZE+2*SPACER; */
1681 area.origin.y += TEXTHEIGHT+SPACER;
1682 area.size.width = 400;
1683 area.size.height= TEXTHEIGHT;
1684 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1685 [[self contentView] addSubview: title];
1686 [title setDrawsBackground: NO];
1687 [title setBezeled: NO];
1688 [title setSelectable: NO];
1689 [title setFont: [NSFont systemFontOfSize: 11.0]];
1691 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1692 [cell setBordered: NO];
1693 [cell setEnabled: NO];
1694 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1695 [cell setBezelStyle: NSRoundedBezelStyle];
1697 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1698 mode: NSHighlightModeMatrix
1701 numberOfColumns: 1];
1702 [[self contentView] addSubview: matrix];
1704 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1705 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1706 [matrix setIntercellSpacing: spacing];
1708 [self setOneShot: YES];
1709 [self setReleasedWhenClosed: YES];
1710 [self setHidesOnDeactivate: YES];
1715 - (BOOL)windowShouldClose: (id)sender
1717 [NSApp stopModalWithCode: XHASH (Qnil)]; // FIXME: BIG UGLY HACK!!
1722 void process_dialog (id window, Lisp_Object list)
1727 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1730 if (XTYPE (item) == Lisp_String)
1732 [window addString: SDATA (item) row: row++];
1734 else if (XTYPE (item) == Lisp_Cons)
1736 [window addButton: SDATA (XCAR (item))
1737 value: XCDR (item) row: row++];
1739 else if (NILP (item))
1748 - addButton: (char *)str value: (Lisp_Object)val row: (int)row
1757 cell = [matrix cellAtRow: row column: cols-1];
1758 [cell setTarget: self];
1759 [cell setAction: @selector (clicked: )];
1760 [cell setTitle: [NSString stringWithUTF8String: str]];
1761 [cell setTag: XHASH (val)]; // FIXME: BIG UGLY HACK!!
1762 [cell setBordered: YES];
1763 [cell setEnabled: YES];
1769 - addString: (char *)str row: (int)row
1778 cell = [matrix cellAtRow: row column: cols-1];
1779 [cell setTitle: [NSString stringWithUTF8String: str]];
1780 [cell setBordered: YES];
1781 [cell setEnabled: NO];
1797 NSArray *sellist = nil;
1800 sellist = [sender selectedCells];
1801 if ([sellist count]<1)
1804 seltag = [[sellist objectAtIndex: 0] tag];
1805 if (seltag != XHASH (Qundefined)) // FIXME: BIG UGLY HACK!!
1806 [NSApp stopModalWithCode: seltag];
1811 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1816 if (XTYPE (contents) == Lisp_Cons)
1818 head = Fcar (contents);
1819 process_dialog (self, Fcdr (contents));
1824 if (XTYPE (head) == Lisp_String)
1825 [title setStringValue:
1826 [NSString stringWithUTF8String: SDATA (head)]];
1827 else if (isQ == YES)
1828 [title setStringValue: @"Question"];
1830 [title setStringValue: @"Information"];
1836 if (cols == 1 && rows > 1) /* Never told where to split */
1839 for (i = 0; i<rows/2; i++)
1841 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1842 atRow: i column: 1];
1843 [matrix removeRow: (rows+1)/2];
1849 NSSize csize = [matrix cellSize];
1850 if (csize.width < MINCELLWIDTH)
1852 csize.width = MINCELLWIDTH;
1853 [matrix setCellSize: csize];
1854 [matrix sizeToCells];
1859 [command sizeToFit];
1863 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1865 t.origin.x = r.origin.x;
1866 t.size.width = r.size.width;
1868 r = [command frame];
1869 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1871 t.origin.x = r.origin.x;
1872 t.size.width = r.size.width;
1876 s = [(NSView *)[self contentView] frame];
1877 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1878 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1879 [self setFrame: r display: NO];
1888 { [super dealloc]; return; };
1892 - (Lisp_Object)runDialogAt: (NSPoint)p
1895 extern EMACS_TIME timer_check (int do_it_now); /* TODO: add to a header */
1897 /* initiate a session that will be ended by pop_down_menu */
1898 popupSession = [NSApp beginModalSessionForWindow: self];
1899 while (popup_activated_flag
1900 && (ret = [NSApp runModalSession: popupSession])
1901 == NSRunContinuesResponse)
1903 /* Run this for timers.el, indep of atimers; might not return.
1904 TODO: use return value to avoid calling every iteration. */
1906 [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
1909 { /* FIXME: BIG UGLY HACK!!! */
1911 *(EMACS_INT*)(&tmp) = ret;
1919 /* ==========================================================================
1923 ========================================================================== */
1925 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1926 doc: /* Cause the NS menu to be re-calculated. */)
1929 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1934 DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
1935 doc: /* Pop up a deck-of-cards menu and return user's selection.
1936 POSITION is a position specification. This is either a mouse button event
1937 or a list ((XOFFSET YOFFSET) WINDOW)
1938 where XOFFSET and YOFFSET are positions in pixels from the top left
1939 corner of WINDOW. (WINDOW may be a window or a frame object.)
1940 This controls the position of the top left of the menu as a whole.
1941 If POSITION is t, it means to use the current mouse position.
1943 MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
1944 The menu items come from key bindings that have a menu string as well as
1945 a definition; actually, the \"definition\" in such a key binding looks like
1946 \(STRING . REAL-DEFINITION). To give the menu a title, put a string into
1947 the keymap as a top-level element.
1949 If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
1950 Otherwise, REAL-DEFINITION should be a valid key binding definition.
1952 You can also use a list of keymaps as MENU.
1953 Then each keymap makes a separate pane.
1955 When MENU is a keymap or a list of keymaps, the return value is the
1956 list of events corresponding to the user's choice. Note that
1957 `x-popup-menu' does not actually execute the command bound to that
1960 Alternatively, you can specify a menu of multiple panes
1961 with a list of the form (TITLE PANE1 PANE2...),
1962 where each pane is a list of form (TITLE ITEM1 ITEM2...).
1963 Each ITEM is normally a cons cell (STRING . VALUE);
1964 but a string can appear as an item--that makes a nonselectable line
1966 With this form of menu, the return value is VALUE from the chosen item. */)
1968 Lisp_Object position, menu;
1970 return ns_popup_menu (position, menu);
1974 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1975 doc: /* Pop up a dialog box and return user's selection.
1976 POSITION specifies which frame to use.
1977 This is normally a mouse button event or a window or frame.
1978 If POSITION is t, it means to use the frame the mouse is on.
1979 The dialog box appears in the middle of the specified frame.
1981 CONTENTS specifies the alternatives to display in the dialog box.
1982 It is a list of the form (DIALOG ITEM1 ITEM2...).
1983 Each ITEM is a cons cell (STRING . VALUE).
1984 The return value is VALUE from the chosen item.
1986 An ITEM may also be just a string--that makes a nonselectable item.
1987 An ITEM may also be nil--that means to put all preceding items
1988 on the left of the dialog box and all following items on the right.
1989 \(By default, approximately half appear on each side.)
1991 If HEADER is non-nil, the frame title for the box is "Information",
1992 otherwise it is "Question".
1994 If the user gets rid of the dialog box without making a valid choice,
1995 for instance using the window manager, then this produces a quit and
1996 `x-popup-dialog' does not return. */)
1997 (position, contents, header)
1998 Lisp_Object position, contents, header;
2000 return ns_popup_dialog (position, contents, header);
2003 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
2004 doc: /* Return t if a menu or popup dialog is active. */)
2007 return popup_activated () ? Qt : Qnil;
2010 /* ==========================================================================
2012 Lisp interface declaration
2014 ========================================================================== */
2019 defsubr (&Sx_popup_menu);
2020 defsubr (&Sx_popup_dialog);
2021 defsubr (&Sns_reset_menu);
2022 defsubr (&Smenu_or_popup_active_p);
2023 staticpro (&menu_items);
2026 Qdebug_on_next_call = intern ("debug-on-next-call");
2027 staticpro (&Qdebug_on_next_call);
2030 // arch-tag: 75773656-52e5-4c44-a398-47bd87b32619