1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007, 2008 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"
40 #include <sys/timeb.h>
41 #include <sys/types.h>
43 #define MenuStagger 10.0
46 int menu_trace_num = 0;
47 #define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \
48 __FILE__, __LINE__, ++menu_trace_num)
54 /* Include lisp -> C common menu parsing code */
55 #define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
56 #include "nsmenu_common.c"
59 extern Lisp_Object Qundefined, Qmenu_enable, Qmenu_bar_update_hook;
60 extern Lisp_Object QCtoggle, QCradio;
62 extern Lisp_Object Vmenu_updating_frame;
64 Lisp_Object Qdebug_on_next_call;
65 extern Lisp_Object Voverriding_local_map, Voverriding_local_map_menu_flag,
66 Qoverriding_local_map, Qoverriding_terminal_local_map;
68 extern long context_menu_value;
69 EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
71 /* Nonzero means a menu is currently active. */
72 static int popup_activated_flag;
74 /* NOTE: toolbar implementation is at end,
75 following complete menu implementation. */
78 /* ==========================================================================
80 Menu: Externally-called functions
82 ========================================================================== */
85 /*23: FIXME: not currently used, but should normalize with other terms. */
87 x_activate_menubar (struct frame *f)
89 fprintf (stderr, "XXX: Received x_activate_menubar event.\n");
93 /* Supposed to discard menubar and free storage. Since we share the
94 menubar among frames and update its context for the focused window,
95 there is nothing to do here. */
97 free_frame_menubar (struct frame *f)
106 return popup_activated_flag;
110 /* --------------------------------------------------------------------------
111 Update menubar. Three cases:
112 1) deep_p = 0, submenu = nil: Fresh switch onto a frame -- either set up
113 just top-level menu strings (OS X), or goto case (2) (GNUstep).
114 2) deep_p = 1, submenu = nil: Recompute all submenus.
115 3) deep_p = 1, submenu = non-nil: Update contents of a single submenu.
116 -------------------------------------------------------------------------- */
117 /*#define NSMENUPROFILE 1 */
119 ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
121 NSAutoreleasePool *pool;
122 id menu = [NSApp mainMenu];
123 static EmacsMenu *last_submenu = nil;
125 const char *submenuTitle = [[submenu title] UTF8String];
126 extern int waiting_for_input;
129 widget_value *wv, *first_wv, *prev_wv = 0;
137 NSTRACE (set_frame_menubar);
139 if (f != SELECTED_FRAME ())
141 XSETFRAME (Vmenu_updating_frame, f);
142 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
145 pool = [[NSAutoreleasePool alloc] init];
147 /* Menu may have been created automatically; if so, discard it. */
148 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
156 menu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
160 { /* close up anything on there */
161 id attMenu = [menu attachedMenu];
168 t = -(1000*tb.time+tb.millitm);
171 /* widget_value is a straightforward object translation of emacs's
172 Byzantine lisp menu structures */
173 wv = xmalloc_widget_value ();
177 wv->button_type = BUTTON_TYPE_NONE;
181 #ifdef NS_IMPL_GNUSTEP
182 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
187 /* Fully parse one or more of the submenus. */
189 int *submenu_start, *submenu_end;
190 int *submenu_top_level_items, *submenu_n_panes;
191 struct buffer *prev = current_buffer;
193 int specpdl_count = SPECPDL_INDEX ();
194 int previous_menu_items_used = f->menu_bar_items_used;
195 Lisp_Object *previous_items
196 = (Lisp_Object *) alloca (previous_menu_items_used
197 * sizeof (Lisp_Object));
199 /* lisp preliminaries */
200 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
201 specbind (Qinhibit_quit, Qt);
202 specbind (Qdebug_on_next_call, Qnil);
203 record_unwind_save_match_data ();
204 if (NILP (Voverriding_local_map_menu_flag))
206 specbind (Qoverriding_terminal_local_map, Qnil);
207 specbind (Qoverriding_local_map, Qnil);
209 set_buffer_internal_1 (XBUFFER (buffer));
211 /* TODO: for some reason this is not needed in other terms,
212 but some menu updates call Info-extract-pointer which causes
213 abort-on-error if waiting-for-input. Needs further investigation. */
214 owfi = waiting_for_input;
215 waiting_for_input = 0;
217 /* lucid hook and possible reset */
218 safe_run_hooks (Qactivate_menubar_hook);
219 if (! NILP (Vlucid_menu_bar_dirty_flag))
220 call0 (Qrecompute_lucid_menubar);
221 safe_run_hooks (Qmenu_bar_update_hook);
222 FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
224 /* Now ready to go */
225 items = FRAME_MENU_BAR_ITEMS (f);
227 /* Save the frame's previous menu bar contents data */
228 if (previous_menu_items_used)
229 bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
230 previous_menu_items_used * sizeof (Lisp_Object));
232 /* parse stage 1: extract from lisp */
235 menu_items = f->menu_bar_vector;
236 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
237 submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
238 submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
239 submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int));
240 submenu_top_level_items
241 = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
243 for (i = 0; i < XVECTOR (items)->size; i += 4)
245 Lisp_Object key, string, maps;
247 key = XVECTOR (items)->contents[i];
248 string = XVECTOR (items)->contents[i + 1];
249 maps = XVECTOR (items)->contents[i + 2];
253 /* FIXME: we'd like to only parse the needed submenu, but this
254 was causing crashes in the _common parsing code.. need to make
255 sure proper initialization done.. */
256 /* if (submenu && strcmp (submenuTitle, SDATA (string)))
259 submenu_start[i] = menu_items_used;
261 menu_items_n_panes = 0;
262 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
263 submenu_n_panes[i] = menu_items_n_panes;
264 submenu_end[i] = menu_items_used;
268 finish_menu_items ();
269 waiting_for_input = owfi;
272 if (submenu && n == 0)
274 /* should have found a menu for this one but didn't */
275 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
277 discard_menu_items ();
278 unbind_to (specpdl_count, Qnil);
284 /* parse stage 2: insert into lucid 'widget_value' structures
285 [comments in other terms say not to evaluate lisp code here] */
286 wv = xmalloc_widget_value ();
287 wv->name = "menubar";
290 wv->button_type = BUTTON_TYPE_NONE;
294 for (i = 0; i < 4*n; i += 4)
296 menu_items_n_panes = submenu_n_panes[i];
297 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
298 submenu_top_level_items[i]);
302 first_wv->contents = wv;
303 /* Don't set wv->name here; GC during the loop might relocate it. */
305 wv->button_type = BUTTON_TYPE_NONE;
309 set_buffer_internal_1 (prev);
311 /* Compare the new menu items with previous, and leave off if no change */
312 /* FIXME: following other terms here, but seems like this should be
313 done before parse stage 2 above, since its results aren't used */
314 if (previous_menu_items_used
315 && (!submenu || (submenu && submenu == last_submenu))
316 && menu_items_used == previous_menu_items_used)
318 for (i = 0; i < previous_menu_items_used; i++)
319 /* FIXME: this ALWAYS fails on Buffers menu items.. something
320 about their strings causes them to change every time, so we
321 double-check failures */
322 if (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i]))
323 if (!(STRINGP (previous_items[i])
324 && STRINGP (XVECTOR (menu_items)->contents[i])
325 && !strcmp (SDATA (previous_items[i]),
326 SDATA (XVECTOR (menu_items)->contents[i]))))
328 if (i == previous_menu_items_used)
334 t += 1000*tb.time+tb.millitm;
335 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
338 free_menubar_widget_value_tree (first_wv);
339 discard_menu_items ();
340 unbind_to (specpdl_count, Qnil);
346 /* The menu items are different, so store them in the frame */
347 /* FIXME: this is not correct for single-submenu case */
348 f->menu_bar_vector = menu_items;
349 f->menu_bar_items_used = menu_items_used;
351 /* Calls restore_menu_items, etc., as they were outside */
352 unbind_to (specpdl_count, Qnil);
354 /* Parse stage 2a: now GC cannot happen during the lifetime of the
355 widget_value, so it's safe to store data from a Lisp_String */
356 wv = first_wv->contents;
357 for (i = 0; i < XVECTOR (items)->size; i += 4)
360 string = XVECTOR (items)->contents[i + 1];
363 /* if (submenu && strcmp (submenuTitle, SDATA (string)))
366 wv->name = (char *) SDATA (string);
367 update_submenu_strings (wv->contents);
371 /* Now, update the NS menu; if we have a submenu, use that, otherwise
372 create a new menu for each sub and fill it. */
375 for (wv = first_wv->contents; wv; wv = wv->next)
377 if (!strcmp (submenuTitle, wv->name))
379 [submenu fillWithWidgetValue: wv->contents];
380 last_submenu = submenu;
387 [menu fillWithWidgetValue: first_wv->contents];
393 static int n_previous_strings = 0;
394 static char previous_strings[100][10];
395 static struct frame *last_f = NULL;
399 /* Make widget-value tree w/ just the top level menu bar strings */
400 items = FRAME_MENU_BAR_ITEMS (f);
409 /* check if no change.. this mechanism is a bit rough, but ready */
410 n = XVECTOR (items)->size / 4;
411 if (f == last_f && n_previous_strings == n)
413 for (i = 0; i<n; i++)
415 string = AREF (items, 4*i+1);
417 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
420 if (previous_strings[i][0])
424 if (strncmp (previous_strings[i], SDATA (string), 10))
437 for (i = 0; i < XVECTOR (items)->size; i += 4)
439 string = XVECTOR (items)->contents[i + 1];
444 strncpy (previous_strings[i/4], SDATA (string), 10);
446 wv = xmalloc_widget_value ();
447 wv->name = (char *) SDATA (string);
450 wv->button_type = BUTTON_TYPE_NONE;
452 wv->call_data = (void *) (EMACS_INT) (-1);
455 /* we'll update the real copy under app menu when time comes */
456 if (!strcmp ("Services", wv->name))
458 /* but we need to make sure it will update on demand */
459 [svcsMenu setFrame: f];
460 [svcsMenu setDelegate: svcsMenu];
464 [menu addSubmenuWithTitle: wv->name forFrame: f];
469 first_wv->contents = wv;
475 n_previous_strings = n;
477 n_previous_strings = 0;
480 free_menubar_widget_value_tree (first_wv);
485 t += 1000*tb.time+tb.millitm;
486 fprintf (stderr, "Menu update took %ld msec.\n", t);
491 [NSApp setMainMenu: menu];
499 /* Main emacs core entry point for menubar menus: called to indicate that the
500 frame's menus have changed, and the *step representation should be updated
503 set_frame_menubar (struct frame *f, int first_time, int deep_p)
505 ns_update_menubar (f, deep_p, nil);
509 /* Utility (from macmenu.c): is this item a separator? */
511 name_is_separator (name)
514 const char *start = name;
516 /* Check if name string consists of only dashes ('-'). */
517 while (*name == '-') name++;
518 /* Separators can also be of the form "--:TripleSuperMegaEtched"
519 or "--deep-shadow". We don't implement them yet, se we just treat
520 them like normal separators. */
521 return (*name == '\0' || start + 2 == name);
525 /* ==========================================================================
527 Menu: class implementation
529 ========================================================================== */
532 /* Menu that can define itself from Emacs "widget_value"s and will lazily
533 update itself when user clicked. Based on Carbon/AppKit implementation
534 by Yamamoto Mitsuharu. */
535 @implementation EmacsMenu
537 /* override designated initializer */
538 - initWithTitle: (NSString *)title
540 if (self = [super initWithTitle: title])
541 [self setAutoenablesItems: NO];
546 /* used for top-level */
547 - initWithTitle: (NSString *)title frame: (struct frame *)f
549 [self initWithTitle: title];
552 [self setDelegate: self];
558 - (void)setFrame: (struct frame *)f
564 /* delegate method called when a submenu is being opened: run a 'deep' call
565 to set_frame_menubar */
566 - (void)menuNeedsUpdate: (NSMenu *)menu
568 NSEvent *event = [[FRAME_NS_VIEW (frame) window] currentEvent];
569 /* HACK: Cocoa/Carbon will request update on every keystroke
570 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
571 since key equivalents are handled through emacs.
572 On Leopard, even keystroke events generate SystemDefined events, but
573 their subtype is 8. */
574 if ([event type] != NSSystemDefined || [event subtype] == 8)
576 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
577 ns_update_menubar (frame, 1, self);
581 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
583 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
584 && FRAME_NS_VIEW (SELECTED_FRAME ()))
585 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
590 /* parse a wdiget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
591 into an accelerator string */
592 -(NSString *)parseKeyEquiv: (char *)key
596 /* currently we just parse 'super' combinations;
597 later we'll set keyEquivModMask */
598 if (!key || !strlen (key))
601 while (*tpos == ' ' || *tpos == '(')
603 if (*tpos != 's'/* || tpos[3] != ')'*/)
605 return [NSString stringWithFormat: @"%c", tpos[2]];
609 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
612 widget_value *wv = (widget_value *)wvptr;
614 if (name_is_separator (wv->name))
616 item = [NSMenuItem separatorItem];
617 [self addItem: item];
621 NSString *title, *keyEq;
622 title = [NSString stringWithUTF8String: wv->name];
624 title = @"< ? >"; /* (get out in the open so we know about it) */
626 keyEq = [self parseKeyEquiv: wv->key];
628 item = [self addItemWithTitle: (NSString *)title
629 action: @selector (menuDown:)
630 keyEquivalent: keyEq];
632 [item setKeyEquivalentModifierMask: keyEquivModMask];
634 [item setEnabled: wv->enabled];
636 /* Draw radio buttons and tickboxes */
637 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
638 wv->button_type == BUTTON_TYPE_RADIO))
639 [item setState: NSOnState];
641 [item setState: NSOffState];
643 [item setTag: (int)wv->call_data];
655 for (n = [self numberOfItems]-1; n >= 0; n--)
657 NSMenuItem *item = [self itemAtIndex: n];
658 NSString *title = [item title];
659 if (([title length] == 0 || [@"Apple" isEqualToString: title])
660 && ![item isSeparatorItem])
662 [self removeItemAtIndex: n];
667 - (void)fillWithWidgetValue: (void *)wvptr
669 widget_value *wv = (widget_value *)wvptr;
671 /* clear existing contents */
672 [self setMenuChangedMessagesEnabled: NO];
675 /* add new contents */
676 for (; wv != NULL; wv = wv->next)
678 NSMenuItem *item = [self addItemWithWidgetValue: wv];
682 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: @"Submenu"];
684 [self setSubmenu: submenu forItem: item];
685 [submenu fillWithWidgetValue: wv->contents];
687 [item setAction: nil];
691 [self setMenuChangedMessagesEnabled: YES];
692 #ifdef NS_IMPL_GNUSTEP
693 if ([[self window] isVisible])
696 if ([self supermenu] == nil)
702 /* adds an empty submenu and returns it */
703 - (EmacsMenu *)addSubmenuWithTitle: (char *)title forFrame: (struct frame *)f
705 NSString *titleStr = [NSString stringWithUTF8String: title];
706 NSMenuItem *item = [self addItemWithTitle: titleStr
707 action: nil /*@selector (menuDown:) */
709 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
710 [self setSubmenu: submenu forItem: item];
715 /* run a menu in popup mode */
716 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
717 keymaps: (int)keymaps
719 EmacsView *view = FRAME_NS_VIEW (f);
720 /* p = [view convertPoint:p fromView: nil]; */
721 p.y = NSHeight ([view frame]) - p.y;
722 NSEvent *e = [[view window] currentEvent];
723 NSEvent *event = [NSEvent mouseEventWithType: NSRightMouseDown
726 timestamp: [e timestamp]
727 windowNumber: [[view window] windowNumber]
729 eventNumber: 0/*[e eventNumber] */
734 context_menu_value = -1;
735 [NSMenu popUpContextMenu: self withEvent: event forView: view];
736 retVal = context_menu_value;
737 context_menu_value = 0;
739 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
747 /* ==========================================================================
749 Context Menu: implementing functions
751 ========================================================================== */
754 cleanup_popup_menu (Lisp_Object arg)
756 discard_menu_items ();
762 ns_popup_menu (Lisp_Object position, Lisp_Object menu)
765 struct frame *f = NULL;
767 Lisp_Object window, x, y, tem, keymap, title;
769 int specpdl_count = SPECPDL_INDEX (), specpdl_count2;
770 char *error_name = NULL;
772 widget_value *wv, *first_wv = 0;
774 NSTRACE (ns_popup_menu);
776 if (!NILP (position))
780 if (EQ (position, Qt)
781 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
782 || EQ (XCAR (position), Qtool_bar))))
784 /* Use the mouse's current position. */
785 struct frame *new_f = SELECTED_FRAME ();
787 if (FRAME_TERMINAL (new_f)->mouse_position_hook)
788 (*FRAME_TERMINAL (new_f)->mouse_position_hook)
789 (&new_f, 0, 0, 0, &x, &y, 0);
791 XSETFRAME (window, new_f);
794 window = selected_window;
801 CHECK_CONS (position);
802 tem = Fcar (position);
803 if (XTYPE (tem) == Lisp_Cons)
805 window = Fcar (Fcdr (position));
807 y = Fcar (Fcdr (tem));
811 tem = Fcar (Fcdr (position));
813 tem = Fcar (Fcdr (Fcdr (tem)));
831 struct window *win = XWINDOW (window);
832 CHECK_LIVE_WINDOW (window);
833 f = XFRAME (WINDOW_FRAME (win));
834 p.x = FRAME_COLUMN_WIDTH (f) * WINDOW_LEFT_EDGE_COL (win);
835 p.y = FRAME_LINE_HEIGHT (f) * WINDOW_TOP_EDGE_LINE (win);
838 p.x += XINT (x); p.y += XINT (y);
840 XSETFRAME (Vmenu_updating_frame, f);
843 { /* no position given */
844 /* FIXME: if called during dump, we need to stop precomputation of
845 key equivalents (see below) because the keydefs in ns-win.el have
846 not been loaded yet. */
849 Vmenu_updating_frame = Qnil;
852 /* now parse the lisp menus */
853 record_unwind_protect (unuse_menu_items, Qnil);
857 /* Decode the menu items from what was specified. */
859 keymap = get_keymap (menu, 0, 0);
862 /* We were given a keymap. Extract menu info from the keymap. */
865 /* Extract the detailed info to make one pane. */
866 keymap_panes (&menu, 1, NILP (position));
868 /* Search for a string appearing directly as an element of the keymap.
869 That string is the title of the menu. */
870 prompt = Fkeymap_prompt (keymap);
871 title = NILP (prompt) ? build_string ("Select") : prompt;
873 /* Make that be the pane title of the first pane. */
874 if (!NILP (prompt) && menu_items_n_panes >= 0)
875 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
879 else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
881 /* We were given a list of keymaps. */
882 int nmaps = XFASTINT (Flength (menu));
884 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
889 /* The first keymap that has a prompt string
890 supplies the menu title. */
891 for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
895 maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
897 prompt = Fkeymap_prompt (keymap);
898 if (NILP (title) && !NILP (prompt))
902 /* Extract the detailed info to make one pane. */
903 keymap_panes (maps, nmaps, NILP (position));
905 /* Make the title be the pane title of the first pane. */
906 if (!NILP (title) && menu_items_n_panes >= 0)
907 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
913 /* We were given an old-fashioned menu. */
915 CHECK_STRING (title);
917 list_of_panes (Fcdr (menu));
922 unbind_to (specpdl_count, Qnil);
924 /* If no position given, that was a signal to just precompute and cache
925 key equivalents, which was a side-effect of what we just did. */
928 discard_menu_items ();
933 record_unwind_protect (cleanup_popup_menu, Qnil);
936 /* now parse stage 2 as in ns_update_menubar */
937 wv = xmalloc_widget_value ();
938 wv->name = "contextmenu";
941 wv->button_type = BUTTON_TYPE_NONE;
945 specpdl_count2 = SPECPDL_INDEX ();
948 /* FIXME: a couple of one-line differences prevent reuse */
949 wv = digest_single_submenu (0, menu_items_used, Qnil);
952 widget_value *save_wv = 0, *prev_wv = 0;
953 widget_value **submenu_stack
954 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
955 /* Lisp_Object *subprefix_stack
956 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object)); */
957 int submenu_depth = 0;
961 /* Loop over all panes and items, filling in the tree. */
963 while (i < menu_items_used)
965 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
967 submenu_stack[submenu_depth++] = save_wv;
973 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
976 save_wv = submenu_stack[--submenu_depth];
980 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
981 && submenu_depth != 0)
982 i += MENU_ITEMS_PANE_LENGTH;
983 /* Ignore a nil in the item list.
984 It's meaningful only for dialog boxes. */
985 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
987 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
989 /* Create a new pane. */
990 Lisp_Object pane_name, prefix;
993 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
994 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
996 #ifndef HAVE_MULTILINGUAL_MENU
997 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
999 pane_name = ENCODE_MENU_STRING (pane_name);
1000 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
1003 pane_string = (NILP (pane_name)
1004 ? "" : (char *) SDATA (pane_name));
1005 /* If there is just one top-level pane, put all its items directly
1006 under the top-level menu. */
1007 if (menu_items_n_panes == 1)
1010 /* If the pane has a meaningful name,
1011 make the pane a top-level menu item
1012 with its items as a submenu beneath it. */
1013 if (!keymaps && strcmp (pane_string, ""))
1015 wv = xmalloc_widget_value ();
1019 first_wv->contents = wv;
1020 wv->name = pane_string;
1021 if (keymaps && !NILP (prefix))
1025 wv->button_type = BUTTON_TYPE_NONE;
1030 else if (first_pane)
1036 i += MENU_ITEMS_PANE_LENGTH;
1040 /* Create a new item within current pane. */
1041 Lisp_Object item_name, enable, descrip, def, type, selected, help;
1042 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
1043 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
1044 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
1045 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
1046 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
1047 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
1048 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
1050 #ifndef HAVE_MULTILINGUAL_MENU
1051 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
1053 item_name = ENCODE_MENU_STRING (item_name);
1054 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
1057 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
1059 descrip = ENCODE_MENU_STRING (descrip);
1060 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
1062 #endif /* not HAVE_MULTILINGUAL_MENU */
1064 wv = xmalloc_widget_value ();
1068 save_wv->contents = wv;
1069 wv->name = (char *) SDATA (item_name);
1070 if (!NILP (descrip))
1071 wv->key = (char *) SDATA (descrip);
1073 /* If this item has a null value,
1074 make the call_data null so that it won't display a box
1075 when the mouse is on it. */
1077 = !NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0;
1078 wv->enabled = !NILP (enable);
1081 wv->button_type = BUTTON_TYPE_NONE;
1082 else if (EQ (type, QCtoggle))
1083 wv->button_type = BUTTON_TYPE_TOGGLE;
1084 else if (EQ (type, QCradio))
1085 wv->button_type = BUTTON_TYPE_RADIO;
1089 wv->selected = !NILP (selected);
1091 if (! STRINGP (help))
1098 i += MENU_ITEMS_ITEM_LENGTH;
1106 widget_value *wv_title = xmalloc_widget_value ();
1107 widget_value *wv_sep = xmalloc_widget_value ();
1109 /* Maybe replace this separator with a bitmap or owner-draw item
1110 so that it looks better. Having two separators looks odd. */
1111 wv_sep->name = "--";
1112 wv_sep->next = first_wv->contents;
1113 wv_sep->help = Qnil;
1115 #ifndef HAVE_MULTILINGUAL_MENU
1116 if (STRING_MULTIBYTE (title))
1117 title = ENCODE_MENU_STRING (title);
1120 wv_title->name = (char *) SDATA (title);
1121 wv_title->enabled = NO;
1122 wv_title->button_type = BUTTON_TYPE_NONE;
1123 wv_title->help = Qnil;
1124 wv_title->next = wv_sep;
1125 first_wv->contents = wv_title;
1128 pmenu = [[EmacsMenu alloc] initWithTitle:
1129 [NSString stringWithUTF8String: SDATA (title)]];
1130 [pmenu fillWithWidgetValue: first_wv->contents];
1131 free_menubar_widget_value_tree (first_wv);
1132 unbind_to (specpdl_count2, Qnil);
1134 popup_activated_flag = 1;
1135 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
1136 popup_activated_flag = 0;
1137 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1140 discard_menu_items ();
1141 unbind_to (specpdl_count, Qnil);
1144 if (error_name) error (error_name);
1151 /* ==========================================================================
1153 Toolbar: externally-called functions
1155 ========================================================================== */
1158 free_frame_tool_bar (FRAME_PTR f)
1159 /* --------------------------------------------------------------------------
1160 Under NS we just hide the toolbar until it might be needed again.
1161 -------------------------------------------------------------------------- */
1163 [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
1167 update_frame_tool_bar (FRAME_PTR f)
1168 /* --------------------------------------------------------------------------
1169 Update toolbar contents
1170 -------------------------------------------------------------------------- */
1173 EmacsToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
1175 [toolbar clearActive];
1177 /* update EmacsToolbar as in GtkUtils, build items list */
1178 for (i = 0; i < f->n_tool_bar_items; ++i)
1180 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1181 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1183 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1184 BOOL selected_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_SELECTED_P));
1189 Lisp_Object helpObj;
1192 /* If image is a vector, choose the image according to the
1194 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1195 if (VECTORP (image))
1197 /* NS toolbar auto-computes disabled and selected images */
1198 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1199 xassert (ASIZE (image) >= idx);
1200 image = AREF (image, idx);
1206 /* Ignore invalid image specifications. */
1207 if (!valid_image_p (image))
1209 NSLog (@"Invalid image for toolbar item");
1213 img_id = lookup_image (f, image);
1214 img = IMAGE_FROM_ID (f, img_id);
1215 prepare_image_for_display (f, img);
1217 if (img->load_failed_p || img->pixmap == nil)
1219 NSLog (@"Could not prepare toolbar image for display.");
1223 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1225 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1226 helpText = NILP (helpObj) ? "" : (char *)SDATA (helpObj);
1228 [toolbar addDisplayItemWithImage: img->pixmap idx: i helpText: helpText
1229 enabled: enabled_p];
1233 if (![toolbar isVisible])
1234 [toolbar setVisible: YES];
1236 if ([toolbar changed])
1238 /* inform app that toolbar has changed */
1239 NSDictionary *dict = [toolbar configurationDictionary];
1240 NSMutableDictionary *newDict = [dict mutableCopy];
1241 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1243 while ((key = [keys nextObject]) != nil)
1245 NSObject *val = [dict objectForKey: key];
1246 if ([val isKindOfClass: [NSArray class]])
1249 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1254 [toolbar setConfigurationFromDictionary: newDict];
1261 /* ==========================================================================
1263 Toolbar: class implementation
1265 ========================================================================== */
1267 @implementation EmacsToolbar
1269 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1271 self = [super initWithIdentifier: identifier];
1273 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1274 [self setSizeMode: NSToolbarSizeModeSmall];
1275 [self setDelegate: self];
1276 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1277 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1278 prevEnablement = enablement = 0L;
1284 [prevIdentifiers release];
1285 [activeIdentifiers release];
1286 [identifierToItem release];
1290 - (void) clearActive
1292 [prevIdentifiers release];
1293 prevIdentifiers = [activeIdentifiers copy];
1294 [activeIdentifiers removeAllObjects];
1295 prevEnablement = enablement;
1301 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1302 enablement == prevEnablement ? NO : YES;
1305 - (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx
1306 helpText: (char *)help enabled: (BOOL)enabled
1308 /* 1) come up w/identifier */
1309 NSString *identifier
1310 = [NSString stringWithFormat: @"%u", [img hash]];
1312 /* 2) create / reuse item */
1313 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1316 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1318 [item setImage: img];
1319 [item setToolTip: [NSString stringWithCString: help]];
1320 [item setTarget: emacsView];
1321 [item setAction: @selector (toolbarClicked:)];
1325 [item setEnabled: enabled];
1327 /* 3) update state */
1328 [identifierToItem setObject: item forKey: identifier];
1329 [activeIdentifiers addObject: identifier];
1330 enablement = (enablement << 1) | (enabled == YES);
1333 /* This overrides super's implementation, which automatically sets
1334 all items to enabled state (for some reason). */
1335 - (void)validateVisibleItems { }
1338 /* delegate methods */
1340 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1341 itemForItemIdentifier: (NSString *)itemIdentifier
1342 willBeInsertedIntoToolbar: (BOOL)flag
1344 /* look up NSToolbarItem by identifier and return... */
1345 return [identifierToItem objectForKey: itemIdentifier];
1348 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1350 /* return entire set.. */
1351 return activeIdentifiers;
1354 /* for configuration palette (not yet supported) */
1355 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1357 /* return entire set... */
1358 return [identifierToItem allKeys];
1361 /* optional and unneeded */
1362 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1363 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1364 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1366 @end /* EmacsToolbar */
1370 /* ==========================================================================
1372 Tooltip: class implementation
1374 ========================================================================== */
1376 /* Needed because NeXTstep does not provide enough control over tooltip
1378 @implementation EmacsTooltip
1382 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1383 blue: 0.792 alpha: 0.95];
1384 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1385 NSFont *sfont = [font screenFont];
1386 int height = [sfont ascender] - [sfont descender];
1387 /*[font boundingRectForFont].size.height; */
1388 NSRect r = NSMakeRect (0, 0, 100, height+6);
1390 textField = [[NSTextField alloc] initWithFrame: r];
1391 [textField setFont: font];
1392 [textField setBackgroundColor: col];
1394 [textField setEditable: NO];
1395 [textField setSelectable: NO];
1396 [textField setBordered: YES];
1397 [textField setBezeled: YES];
1398 [textField setDrawsBackground: YES];
1400 win = [[NSWindow alloc]
1401 initWithContentRect: [textField frame]
1403 backing: NSBackingStoreBuffered
1405 [win setReleasedWhenClosed: NO];
1406 [win setDelegate: self];
1407 [[win contentView] addSubview: textField];
1408 /* [win setBackgroundColor: col]; */
1409 [win setOpaque: NO];
1418 [textField release];
1422 - (void) setText: (char *)text
1424 NSString *str = [NSString stringWithUTF8String: text];
1425 NSRect r = [textField frame];
1426 r.size.width = [[[textField font] screenFont] widthOfString: str] + 8;
1427 [textField setFrame: r];
1428 [textField setStringValue: str];
1431 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1433 NSRect wr = [win frame];
1435 wr.origin = NSMakePoint (x, y);
1436 wr.size = [textField frame].size;
1438 [win setFrame: wr display: YES];
1439 [win orderFront: self];
1441 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1442 selector: @selector (hide)
1443 userInfo: nil repeats: NO];
1452 if ([timer isValid])
1461 return timer != nil;
1466 return [textField frame];
1469 @end /* EmacsTooltip */
1473 /* ==========================================================================
1475 Popup Dialog: implementing functions
1477 ========================================================================== */
1480 ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1483 Lisp_Object window, tem;
1488 NSTRACE (x-popup-dialog);
1492 isQ = NILP (header);
1494 if (EQ (position, Qt))
1496 window = selected_window;
1498 else if (CONSP (position))
1501 tem = Fcar (position);
1502 if (XTYPE (tem) == Lisp_Cons)
1503 window = Fcar (Fcdr (position));
1506 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1507 window = Fcar (tem); /* POSN_WINDOW (tem) */
1510 else if (FRAMEP (position))
1516 CHECK_LIVE_WINDOW (position);
1520 if (FRAMEP (window))
1521 f = XFRAME (window);
1524 CHECK_LIVE_WINDOW (window);
1525 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1527 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1528 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1529 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1531 popup_activated_flag = 1;
1532 tem = [dialog runDialogAt: p];
1533 popup_activated_flag = 0;
1537 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1542 /* ==========================================================================
1544 Popup Dialog: class implementation
1546 ========================================================================== */
1548 @interface FlippedView : NSView
1553 @implementation FlippedView
1560 @implementation EmacsDialogPanel
1563 #define ICONSIZE 64.0
1564 #define TEXTHEIGHT 20.0
1565 #define MINCELLWIDTH 90.0
1567 - initWithContentRect: (NSRect)contentRect styleMask: (unsigned int)aStyle
1568 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1570 NSSize spacing = {SPACER, SPACER};
1572 char this_cmd_name[80];
1574 static NSImageView *imgView;
1575 static FlippedView *contentView;
1580 area.origin.x = 3*SPACER;
1581 area.origin.y = 2*SPACER;
1582 area.size.width = ICONSIZE;
1583 area.size.height= ICONSIZE;
1584 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1585 [img setScalesWhenResized: YES];
1586 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1587 imgView = [[NSImageView alloc] initWithFrame: area];
1588 [imgView setImage: img];
1589 [imgView setEditable: NO];
1593 aStyle = NSTitledWindowMask;
1597 [super initWithContentRect: contentRect styleMask: aStyle
1598 backing: backingType defer: flag];
1599 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1600 [self setContentView: contentView];
1602 [[self contentView] setAutoresizesSubviews: YES];
1604 [[self contentView] addSubview: imgView];
1605 [self setTitle: @""];
1607 area.origin.x += ICONSIZE+2*SPACER;
1608 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1609 area.size.width = 400;
1610 area.size.height= TEXTHEIGHT;
1611 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1612 [[self contentView] addSubview: command];
1613 [command setStringValue: @"Emacs"];
1614 [command setDrawsBackground: NO];
1615 [command setBezeled: NO];
1616 [command setSelectable: NO];
1617 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1619 /* area.origin.x = ICONSIZE+2*SPACER;
1620 area.origin.y = TEXTHEIGHT + 2*SPACER;
1621 area.size.width = 400;
1622 area.size.height= 2;
1623 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1624 [[self contentView] addSubview: tem];
1625 [tem setTitlePosition: NSNoTitle];
1626 [tem setAutoresizingMask: NSViewWidthSizable];*/
1628 /* area.origin.x = ICONSIZE+2*SPACER; */
1629 area.origin.y += TEXTHEIGHT+SPACER;
1630 area.size.width = 400;
1631 area.size.height= TEXTHEIGHT;
1632 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1633 [[self contentView] addSubview: title];
1634 [title setDrawsBackground: NO];
1635 [title setBezeled: NO];
1636 [title setSelectable: NO];
1637 [title setFont: [NSFont systemFontOfSize: 11.0]];
1639 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1640 [cell setBordered: NO];
1641 [cell setEnabled: NO];
1642 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1643 [cell setBezelStyle: NSRoundedBezelStyle];
1645 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1646 mode: NSHighlightModeMatrix
1649 numberOfColumns: 1];
1650 [[self contentView] addSubview: matrix];
1652 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1653 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1654 [matrix setIntercellSpacing: spacing];
1656 [self setOneShot: YES];
1657 [self setReleasedWhenClosed: YES];
1658 [self setHidesOnDeactivate: YES];
1663 - (BOOL)windowShouldClose: (id)sender
1665 [NSApp stopModalWithCode: XHASH (Qnil)]; // FIXME: BIG UGLY HACK!!
1670 void process_dialog (id window, Lisp_Object list)
1675 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1678 if (XTYPE (item) == Lisp_String)
1680 [window addString: XSTRING (item)->data row: row++];
1682 else if (XTYPE (item) == Lisp_Cons)
1684 [window addButton: XSTRING (XCAR (item))->data
1685 value: XCDR (item) row: row++];
1687 else if (NILP (item))
1696 - addButton: (char *)str value: (Lisp_Object)val row: (int)row
1705 cell = [matrix cellAtRow: row column: cols-1];
1706 [cell setTarget: self];
1707 [cell setAction: @selector (clicked: )];
1708 [cell setTitle: [NSString stringWithUTF8String: str]];
1709 [cell setTag: XHASH (val)]; // FIXME: BIG UGLY HACK!!
1710 [cell setBordered: YES];
1711 [cell setEnabled: YES];
1717 - addString: (char *)str row: (int)row
1726 cell = [matrix cellAtRow: row column: cols-1];
1727 [cell setTitle: [NSString stringWithUTF8String: str]];
1728 [cell setBordered: YES];
1729 [cell setEnabled: NO];
1745 NSArray *sellist = nil;
1748 sellist = [sender selectedCells];
1749 if ([sellist count]<1)
1752 seltag = [[sellist objectAtIndex: 0] tag];
1753 if (seltag != XHASH (Qundefined)) // FIXME: BIG UGLY HACK!!
1754 [NSApp stopModalWithCode: seltag];
1759 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1764 if (XTYPE (contents) == Lisp_Cons)
1766 head = Fcar (contents);
1767 process_dialog (self, Fcdr (contents));
1772 if (XTYPE (head) == Lisp_String)
1773 [title setStringValue:
1774 [NSString stringWithUTF8String: XSTRING (head)->data]];
1775 else if (isQ == YES)
1776 [title setStringValue: @"Question"];
1778 [title setStringValue: @"Information"];
1784 if (cols == 1 && rows > 1) /* Never told where to split */
1787 for (i = 0; i<rows/2; i++)
1789 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1790 atRow: i column: 1];
1791 [matrix removeRow: (rows+1)/2];
1797 NSSize csize = [matrix cellSize];
1798 if (csize.width < MINCELLWIDTH)
1800 csize.width = MINCELLWIDTH;
1801 [matrix setCellSize: csize];
1802 [matrix sizeToCells];
1807 [command sizeToFit];
1811 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1813 t.origin.x = r.origin.x;
1814 t.size.width = r.size.width;
1816 r = [command frame];
1817 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1819 t.origin.x = r.origin.x;
1820 t.size.width = r.size.width;
1824 s = [(NSView *)[self contentView] frame];
1825 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1826 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1827 [self setFrame: r display: NO];
1836 { [super dealloc]; return; };
1840 - (Lisp_Object)runDialogAt: (NSPoint)p
1843 NSModalSession session;
1846 [self center]; /*XXX p ignored? */
1847 [self orderFront: NSApp];
1849 session = [NSApp beginModalSessionForWindow: self];
1850 while ((ret = [NSApp runModalSession: session]) == NSRunContinuesResponse)
1852 (e = [NSApp nextEventMatchingMask: NSAnyEventMask
1853 untilDate: [NSDate distantFuture]
1854 inMode: NSEventTrackingRunLoopMode
1856 /*fprintf (stderr, "ret = %d\te = %p\n", ret, e); */
1858 [NSApp endModalSession: session];
1860 { // FIXME: BIG UGLY HACK!!!
1862 *(EMACS_INT*)(&tmp) = ret;
1871 /* ==========================================================================
1875 ========================================================================== */
1877 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1878 doc: /* Cause the NS menu to be re-calculated. */)
1881 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1886 DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
1887 doc: /* Pop up a deck-of-cards menu and return user's selection.
1888 POSITION is a position specification. This is either a mouse button event
1889 or a list ((XOFFSET YOFFSET) WINDOW)
1890 where XOFFSET and YOFFSET are positions in pixels from the top left
1891 corner of WINDOW. (WINDOW may be a window or a frame object.)
1892 This controls the position of the top left of the menu as a whole.
1893 If POSITION is t, it means to use the current mouse position.
1895 MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
1896 The menu items come from key bindings that have a menu string as well as
1897 a definition; actually, the \"definition\" in such a key binding looks like
1898 \(STRING . REAL-DEFINITION). To give the menu a title, put a string into
1899 the keymap as a top-level element.
1901 If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
1902 Otherwise, REAL-DEFINITION should be a valid key binding definition.
1904 You can also use a list of keymaps as MENU.
1905 Then each keymap makes a separate pane.
1907 When MENU is a keymap or a list of keymaps, the return value is the
1908 list of events corresponding to the user's choice. Note that
1909 `x-popup-menu' does not actually execute the command bound to that
1912 Alternatively, you can specify a menu of multiple panes
1913 with a list of the form (TITLE PANE1 PANE2...),
1914 where each pane is a list of form (TITLE ITEM1 ITEM2...).
1915 Each ITEM is normally a cons cell (STRING . VALUE);
1916 but a string can appear as an item--that makes a nonselectable line
1918 With this form of menu, the return value is VALUE from the chosen item. */)
1920 Lisp_Object position, menu;
1922 return ns_popup_menu (position, menu);
1926 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1927 doc: /* Pop up a dialog box and return user's selection.
1928 POSITION specifies which frame to use.
1929 This is normally a mouse button event or a window or frame.
1930 If POSITION is t, it means to use the frame the mouse is on.
1931 The dialog box appears in the middle of the specified frame.
1933 CONTENTS specifies the alternatives to display in the dialog box.
1934 It is a list of the form (DIALOG ITEM1 ITEM2...).
1935 Each ITEM is a cons cell (STRING . VALUE).
1936 The return value is VALUE from the chosen item.
1938 An ITEM may also be just a string--that makes a nonselectable item.
1939 An ITEM may also be nil--that means to put all preceding items
1940 on the left of the dialog box and all following items on the right.
1941 \(By default, approximately half appear on each side.)
1943 If HEADER is non-nil, the frame title for the box is "Information",
1944 otherwise it is "Question".
1946 If the user gets rid of the dialog box without making a valid choice,
1947 for instance using the window manager, then this produces a quit and
1948 `x-popup-dialog' does not return. */)
1949 (position, contents, header)
1950 Lisp_Object position, contents, header;
1952 return ns_popup_dialog (position, contents, header);
1955 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1956 doc: /* Return t if a menu or popup dialog is active. */)
1959 return popup_activated () ? Qt : Qnil;
1962 /* ==========================================================================
1964 Lisp interface declaration
1966 ========================================================================== */
1971 defsubr (&Sx_popup_menu);
1972 defsubr (&Sx_popup_dialog);
1973 defsubr (&Sns_reset_menu);
1974 defsubr (&Smenu_or_popup_active_p);
1975 staticpro (&menu_items);
1978 Qdebug_on_next_call = intern ("debug-on-next-call");
1979 staticpro (&Qdebug_on_next_call);
1982 // arch-tag: 75773656-52e5-4c44-a398-47bd87b32619