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. */
31 #include "blockinput.h"
33 #include "termhooks.h"
37 #include <sys/timeb.h>
38 #include <sys/types.h>
40 #define MenuStagger 10.0
43 int menu_trace_num = 0;
44 #define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \
45 __FILE__, __LINE__, ++menu_trace_num)
51 /* Include lisp -> C common menu parsing code */
52 #define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
53 #include "nsmenu_common.c"
56 extern Lisp_Object Qundefined, Qmenu_enable, Qmenu_bar_update_hook;
57 extern Lisp_Object QCtoggle, QCradio;
59 extern Lisp_Object Vmenu_updating_frame;
61 Lisp_Object Qdebug_on_next_call;
62 extern Lisp_Object Voverriding_local_map, Voverriding_local_map_menu_flag,
63 Qoverriding_local_map, Qoverriding_terminal_local_map;
65 extern long context_menu_value;
66 EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
68 /* Nonzero means a menu is currently active. */
69 static int popup_activated_flag;
71 /* NOTE: toolbar implementation is at end,
72 following complete menu implementation. */
75 /* ==========================================================================
77 Menu: Externally-called functions
79 ========================================================================== */
82 /*23: FIXME: not currently used, but should normalize with other terms. */
84 x_activate_menubar (struct frame *f)
86 fprintf (stderr, "XXX: Received x_activate_menubar event.\n");
90 /* Supposed to discard menubar and free storage. Since we share the
91 menubar among frames and update its context for the focused window,
92 there is nothing to do here. */
94 free_frame_menubar (struct frame *f)
103 return popup_activated_flag;
107 /* --------------------------------------------------------------------------
108 Update menubar. Three cases:
109 1) deep_p = 0, submenu = nil: Fresh switch onto a frame -- either set up
110 just top-level menu strings (OS X), or goto case (2) (GNUstep).
111 2) deep_p = 1, submenu = nil: Recompute all submenus.
112 3) deep_p = 1, submenu = non-nil: Update contents of a single submenu.
113 -------------------------------------------------------------------------- */
114 /*#define NSMENUPROFILE 1 */
116 ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
118 NSAutoreleasePool *pool;
119 id menu = [NSApp mainMenu];
120 static EmacsMenu *last_submenu = nil;
122 const char *submenuTitle = [[submenu title] UTF8String];
123 extern int waiting_for_input;
126 widget_value *wv, *first_wv, *prev_wv = 0;
134 NSTRACE (set_frame_menubar);
136 if (f != SELECTED_FRAME ())
138 XSETFRAME (Vmenu_updating_frame, f);
139 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
142 pool = [[NSAutoreleasePool alloc] init];
144 /* Menu may have been created automatically; if so, discard it. */
145 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
153 menu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
157 { /* close up anything on there */
158 id attMenu = [menu attachedMenu];
165 t = -(1000*tb.time+tb.millitm);
168 /* widget_value is a straightforward object translation of emacs's
169 Byzantine lisp menu structures */
170 wv = xmalloc_widget_value ();
174 wv->button_type = BUTTON_TYPE_NONE;
178 #ifdef NS_IMPL_GNUSTEP
179 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
184 /* Fully parse one or more of the submenus. */
186 int *submenu_start, *submenu_end;
187 int *submenu_top_level_items, *submenu_n_panes;
188 struct buffer *prev = current_buffer;
190 int specpdl_count = SPECPDL_INDEX ();
191 int previous_menu_items_used = f->menu_bar_items_used;
192 Lisp_Object *previous_items
193 = (Lisp_Object *) alloca (previous_menu_items_used
194 * sizeof (Lisp_Object));
196 /* lisp preliminaries */
197 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
198 specbind (Qinhibit_quit, Qt);
199 specbind (Qdebug_on_next_call, Qnil);
200 record_unwind_save_match_data ();
201 if (NILP (Voverriding_local_map_menu_flag))
203 specbind (Qoverriding_terminal_local_map, Qnil);
204 specbind (Qoverriding_local_map, Qnil);
206 set_buffer_internal_1 (XBUFFER (buffer));
208 /* TODO: for some reason this is not needed in other terms,
209 but some menu updates call Info-extract-pointer which causes
210 abort-on-error if waiting-for-input. Needs further investigation. */
211 owfi = waiting_for_input;
212 waiting_for_input = 0;
214 /* lucid hook and possible reset */
215 safe_run_hooks (Qactivate_menubar_hook);
216 if (! NILP (Vlucid_menu_bar_dirty_flag))
217 call0 (Qrecompute_lucid_menubar);
218 safe_run_hooks (Qmenu_bar_update_hook);
219 FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
221 /* Now ready to go */
222 items = FRAME_MENU_BAR_ITEMS (f);
224 /* Save the frame's previous menu bar contents data */
225 if (previous_menu_items_used)
226 bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
227 previous_menu_items_used * sizeof (Lisp_Object));
229 /* parse stage 1: extract from lisp */
232 menu_items = f->menu_bar_vector;
233 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
234 submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
235 submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
236 submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int));
237 submenu_top_level_items
238 = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
240 for (i = 0; i < XVECTOR (items)->size; i += 4)
242 Lisp_Object key, string, maps;
244 key = XVECTOR (items)->contents[i];
245 string = XVECTOR (items)->contents[i + 1];
246 maps = XVECTOR (items)->contents[i + 2];
250 /* FIXME: we'd like to only parse the needed submenu, but this
251 was causing crashes in the _common parsing code.. need to make
252 sure proper initialization done.. */
253 /* if (submenu && strcmp (submenuTitle, SDATA (string)))
256 submenu_start[i] = menu_items_used;
258 menu_items_n_panes = 0;
259 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
260 submenu_n_panes[i] = menu_items_n_panes;
261 submenu_end[i] = menu_items_used;
265 finish_menu_items ();
266 waiting_for_input = owfi;
269 if (submenu && n == 0)
271 /* should have found a menu for this one but didn't */
272 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
274 discard_menu_items ();
275 unbind_to (specpdl_count, Qnil);
281 /* parse stage 2: insert into lucid 'widget_value' structures
282 [comments in other terms say not to evaluate lisp code here] */
283 wv = xmalloc_widget_value ();
284 wv->name = "menubar";
287 wv->button_type = BUTTON_TYPE_NONE;
291 for (i = 0; i < 4*n; i += 4)
293 menu_items_n_panes = submenu_n_panes[i];
294 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
295 submenu_top_level_items[i]);
299 first_wv->contents = wv;
300 /* Don't set wv->name here; GC during the loop might relocate it. */
302 wv->button_type = BUTTON_TYPE_NONE;
306 set_buffer_internal_1 (prev);
308 /* Compare the new menu items with previous, and leave off if no change */
309 /* FIXME: following other terms here, but seems like this should be
310 done before parse stage 2 above, since its results aren't used */
311 if (previous_menu_items_used
312 && (!submenu || (submenu && submenu == last_submenu))
313 && menu_items_used == previous_menu_items_used)
315 for (i = 0; i < previous_menu_items_used; i++)
316 /* FIXME: this ALWAYS fails on Buffers menu items.. something
317 about their strings causes them to change every time, so we
318 double-check failures */
319 if (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i]))
320 if (!(STRINGP (previous_items[i])
321 && STRINGP (XVECTOR (menu_items)->contents[i])
322 && !strcmp (SDATA (previous_items[i]),
323 SDATA (XVECTOR (menu_items)->contents[i]))))
325 if (i == previous_menu_items_used)
331 t += 1000*tb.time+tb.millitm;
332 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
335 free_menubar_widget_value_tree (first_wv);
336 discard_menu_items ();
337 unbind_to (specpdl_count, Qnil);
343 /* The menu items are different, so store them in the frame */
344 /* FIXME: this is not correct for single-submenu case */
345 f->menu_bar_vector = menu_items;
346 f->menu_bar_items_used = menu_items_used;
348 /* Calls restore_menu_items, etc., as they were outside */
349 unbind_to (specpdl_count, Qnil);
351 /* Parse stage 2a: now GC cannot happen during the lifetime of the
352 widget_value, so it's safe to store data from a Lisp_String */
353 wv = first_wv->contents;
354 for (i = 0; i < XVECTOR (items)->size; i += 4)
357 string = XVECTOR (items)->contents[i + 1];
360 /* if (submenu && strcmp (submenuTitle, SDATA (string)))
363 wv->name = (char *) SDATA (string);
364 update_submenu_strings (wv->contents);
368 /* Now, update the NS menu; if we have a submenu, use that, otherwise
369 create a new menu for each sub and fill it. */
372 for (wv = first_wv->contents; wv; wv = wv->next)
374 if (!strcmp (submenuTitle, wv->name))
376 [submenu fillWithWidgetValue: wv->contents];
377 last_submenu = submenu;
384 [menu fillWithWidgetValue: first_wv->contents];
390 static int n_previous_strings = 0;
391 static char previous_strings[100][10];
392 static struct frame *last_f = NULL;
396 /* Make widget-value tree w/ just the top level menu bar strings */
397 items = FRAME_MENU_BAR_ITEMS (f);
406 /* check if no change.. this mechanism is a bit rough, but ready */
407 n = XVECTOR (items)->size / 4;
408 if (f == last_f && n_previous_strings == n)
410 for (i = 0; i<n; i++)
412 string = AREF (items, 4*i+1);
414 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
417 if (previous_strings[i][0])
421 if (strncmp (previous_strings[i], SDATA (string), 10))
434 for (i = 0; i < XVECTOR (items)->size; i += 4)
436 string = XVECTOR (items)->contents[i + 1];
441 strncpy (previous_strings[i/4], SDATA (string), 10);
443 wv = xmalloc_widget_value ();
444 wv->name = (char *) SDATA (string);
447 wv->button_type = BUTTON_TYPE_NONE;
449 wv->call_data = (void *) (EMACS_INT) (-1);
452 /* we'll update the real copy under app menu when time comes */
453 if (!strcmp ("Services", wv->name))
455 /* but we need to make sure it will update on demand */
456 [svcsMenu setFrame: f];
457 [svcsMenu setDelegate: svcsMenu];
461 [menu addSubmenuWithTitle: wv->name forFrame: f];
466 first_wv->contents = wv;
472 n_previous_strings = n;
474 n_previous_strings = 0;
477 free_menubar_widget_value_tree (first_wv);
482 t += 1000*tb.time+tb.millitm;
483 fprintf (stderr, "Menu update took %ld msec.\n", t);
488 [NSApp setMainMenu: menu];
496 /* Main emacs core entry point for menubar menus: called to indicate that the
497 frame's menus have changed, and the *step representation should be updated
500 set_frame_menubar (struct frame *f, int first_time, int deep_p)
502 ns_update_menubar (f, deep_p, nil);
506 /* Utility (from macmenu.c): is this item a separator? */
508 name_is_separator (name)
511 const char *start = name;
513 /* Check if name string consists of only dashes ('-'). */
514 while (*name == '-') name++;
515 /* Separators can also be of the form "--:TripleSuperMegaEtched"
516 or "--deep-shadow". We don't implement them yet, se we just treat
517 them like normal separators. */
518 return (*name == '\0' || start + 2 == name);
522 /* ==========================================================================
524 Menu: class implementation
526 ========================================================================== */
529 /* Menu that can define itself from Emacs "widget_value"s and will lazily
530 update itself when user clicked. Based on Carbon/AppKit implementation
531 by Yamamoto Mitsuharu. */
532 @implementation EmacsMenu
534 /* override designated initializer */
535 - initWithTitle: (NSString *)title
537 if (self = [super initWithTitle: title])
538 [self setAutoenablesItems: NO];
543 /* used for top-level */
544 - initWithTitle: (NSString *)title frame: (struct frame *)f
546 [self initWithTitle: title];
549 [self setDelegate: self];
555 - (void)setFrame: (struct frame *)f
561 /* delegate method called when a submenu is being opened: run a 'deep' call
562 to set_frame_menubar */
563 - (void)menuNeedsUpdate: (NSMenu *)menu
565 NSEvent *event = [[FRAME_NS_VIEW (frame) window] currentEvent];
566 /* HACK: Cocoa/Carbon will request update on every keystroke
567 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
568 since key equivalents are handled through emacs.
569 On Leopard, even keystroke events generate SystemDefined events, but
570 their subtype is 8. */
571 if ([event type] != NSSystemDefined || [event subtype] == 8)
573 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
574 ns_update_menubar (frame, 1, self);
578 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
580 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
581 && FRAME_NS_VIEW (SELECTED_FRAME ()))
582 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
587 /* parse a wdiget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
588 into an accelerator string */
589 -(NSString *)parseKeyEquiv: (char *)key
593 /* currently we just parse 'super' combinations;
594 later we'll set keyEquivModMask */
595 if (!key || !strlen (key))
598 while (*tpos == ' ' || *tpos == '(')
600 if (*tpos != 's'/* || tpos[3] != ')'*/)
602 return [NSString stringWithFormat: @"%c", tpos[2]];
606 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
609 widget_value *wv = (widget_value *)wvptr;
611 if (name_is_separator (wv->name))
613 item = [NSMenuItem separatorItem];
614 [self addItem: item];
618 NSString *title, *keyEq;
619 title = [NSString stringWithUTF8String: wv->name];
621 title = @"< ? >"; /* (get out in the open so we know about it) */
623 keyEq = [self parseKeyEquiv: wv->key];
625 item = [self addItemWithTitle: (NSString *)title
626 action: @selector (menuDown:)
627 keyEquivalent: keyEq];
629 [item setKeyEquivalentModifierMask: keyEquivModMask];
631 [item setEnabled: wv->enabled];
633 /* Draw radio buttons and tickboxes */
634 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
635 wv->button_type == BUTTON_TYPE_RADIO))
636 [item setState: NSOnState];
638 [item setState: NSOffState];
640 [item setTag: (int)wv->call_data];
652 for (n = [self numberOfItems]-1; n >= 0; n--)
654 NSMenuItem *item = [self itemAtIndex: n];
655 NSString *title = [item title];
656 if (([title length] == 0 || [@"Apple" isEqualToString: title])
657 && ![item isSeparatorItem])
659 [self removeItemAtIndex: n];
664 - (void)fillWithWidgetValue: (void *)wvptr
666 widget_value *wv = (widget_value *)wvptr;
668 /* clear existing contents */
669 [self setMenuChangedMessagesEnabled: NO];
672 /* add new contents */
673 for (; wv != NULL; wv = wv->next)
675 NSMenuItem *item = [self addItemWithWidgetValue: wv];
679 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: @"Submenu"];
681 [self setSubmenu: submenu forItem: item];
682 [submenu fillWithWidgetValue: wv->contents];
684 [item setAction: nil];
688 [self setMenuChangedMessagesEnabled: YES];
689 #ifdef NS_IMPL_GNUSTEP
690 if ([[self window] isVisible])
693 if ([self supermenu] == nil)
699 /* adds an empty submenu and returns it */
700 - (EmacsMenu *)addSubmenuWithTitle: (char *)title forFrame: (struct frame *)f
702 NSString *titleStr = [NSString stringWithUTF8String: title];
703 NSMenuItem *item = [self addItemWithTitle: titleStr
704 action: nil /*@selector (menuDown:) */
706 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
707 [self setSubmenu: submenu forItem: item];
712 /* run a menu in popup mode */
713 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
714 keymaps: (int)keymaps
716 EmacsView *view = FRAME_NS_VIEW (f);
717 /* p = [view convertPoint:p fromView: nil]; */
718 p.y = NSHeight ([view frame]) - p.y;
719 NSEvent *e = [[view window] currentEvent];
720 NSEvent *event = [NSEvent mouseEventWithType: NSRightMouseDown
723 timestamp: [e timestamp]
724 windowNumber: [[view window] windowNumber]
726 eventNumber: 0/*[e eventNumber] */
731 context_menu_value = -1;
732 [NSMenu popUpContextMenu: self withEvent: event forView: view];
733 retVal = context_menu_value;
734 context_menu_value = 0;
736 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
744 /* ==========================================================================
746 Context Menu: implementing functions
748 ========================================================================== */
751 cleanup_popup_menu (Lisp_Object arg)
753 discard_menu_items ();
759 ns_popup_menu (Lisp_Object position, Lisp_Object menu)
762 struct frame *f = NULL;
764 Lisp_Object window, x, y, tem, keymap, title;
766 int specpdl_count = SPECPDL_INDEX (), specpdl_count2;
767 char *error_name = NULL;
769 widget_value *wv, *first_wv = 0;
771 NSTRACE (ns_popup_menu);
773 if (!NILP (position))
777 if (EQ (position, Qt)
778 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
779 || EQ (XCAR (position), Qtool_bar))))
781 /* Use the mouse's current position. */
782 struct frame *new_f = SELECTED_FRAME ();
784 if (FRAME_TERMINAL (new_f)->mouse_position_hook)
785 (*FRAME_TERMINAL (new_f)->mouse_position_hook)
786 (&new_f, 0, 0, 0, &x, &y, 0);
788 XSETFRAME (window, new_f);
791 window = selected_window;
798 CHECK_CONS (position);
799 tem = Fcar (position);
800 if (XTYPE (tem) == Lisp_Cons)
802 window = Fcar (Fcdr (position));
804 y = Fcar (Fcdr (tem));
808 tem = Fcar (Fcdr (position));
810 tem = Fcar (Fcdr (Fcdr (tem)));
828 struct window *win = XWINDOW (window);
829 CHECK_LIVE_WINDOW (window);
830 f = XFRAME (WINDOW_FRAME (win));
831 p.x = FRAME_COLUMN_WIDTH (f) * WINDOW_LEFT_EDGE_COL (win);
832 p.y = FRAME_LINE_HEIGHT (f) * WINDOW_TOP_EDGE_LINE (win);
835 p.x += XINT (x); p.y += XINT (y);
837 XSETFRAME (Vmenu_updating_frame, f);
840 { /* no position given */
841 /* FIXME: if called during dump, we need to stop precomputation of
842 key equivalents (see below) because the keydefs in ns-win.el have
843 not been loaded yet. */
846 Vmenu_updating_frame = Qnil;
849 /* now parse the lisp menus */
850 record_unwind_protect (unuse_menu_items, Qnil);
854 /* Decode the menu items from what was specified. */
856 keymap = get_keymap (menu, 0, 0);
859 /* We were given a keymap. Extract menu info from the keymap. */
862 /* Extract the detailed info to make one pane. */
863 keymap_panes (&menu, 1, NILP (position));
865 /* Search for a string appearing directly as an element of the keymap.
866 That string is the title of the menu. */
867 prompt = Fkeymap_prompt (keymap);
868 title = NILP (prompt) ? build_string ("Select") : prompt;
870 /* Make that be the pane title of the first pane. */
871 if (!NILP (prompt) && menu_items_n_panes >= 0)
872 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
876 else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
878 /* We were given a list of keymaps. */
879 int nmaps = XFASTINT (Flength (menu));
881 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
886 /* The first keymap that has a prompt string
887 supplies the menu title. */
888 for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
892 maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
894 prompt = Fkeymap_prompt (keymap);
895 if (NILP (title) && !NILP (prompt))
899 /* Extract the detailed info to make one pane. */
900 keymap_panes (maps, nmaps, NILP (position));
902 /* Make the title be the pane title of the first pane. */
903 if (!NILP (title) && menu_items_n_panes >= 0)
904 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
910 /* We were given an old-fashioned menu. */
912 CHECK_STRING (title);
914 list_of_panes (Fcdr (menu));
919 unbind_to (specpdl_count, Qnil);
921 /* If no position given, that was a signal to just precompute and cache
922 key equivalents, which was a side-effect of what we just did. */
925 discard_menu_items ();
930 record_unwind_protect (cleanup_popup_menu, Qnil);
933 /* now parse stage 2 as in ns_update_menubar */
934 wv = xmalloc_widget_value ();
935 wv->name = "contextmenu";
938 wv->button_type = BUTTON_TYPE_NONE;
942 specpdl_count2 = SPECPDL_INDEX ();
945 /* FIXME: a couple of one-line differences prevent reuse */
946 wv = digest_single_submenu (0, menu_items_used, Qnil);
949 widget_value *save_wv = 0, *prev_wv = 0;
950 widget_value **submenu_stack
951 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
952 /* Lisp_Object *subprefix_stack
953 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object)); */
954 int submenu_depth = 0;
958 /* Loop over all panes and items, filling in the tree. */
960 while (i < menu_items_used)
962 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
964 submenu_stack[submenu_depth++] = save_wv;
970 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
973 save_wv = submenu_stack[--submenu_depth];
977 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
978 && submenu_depth != 0)
979 i += MENU_ITEMS_PANE_LENGTH;
980 /* Ignore a nil in the item list.
981 It's meaningful only for dialog boxes. */
982 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
984 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
986 /* Create a new pane. */
987 Lisp_Object pane_name, prefix;
990 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
991 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
993 #ifndef HAVE_MULTILINGUAL_MENU
994 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
996 pane_name = ENCODE_MENU_STRING (pane_name);
997 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
1000 pane_string = (NILP (pane_name)
1001 ? "" : (char *) SDATA (pane_name));
1002 /* If there is just one top-level pane, put all its items directly
1003 under the top-level menu. */
1004 if (menu_items_n_panes == 1)
1007 /* If the pane has a meaningful name,
1008 make the pane a top-level menu item
1009 with its items as a submenu beneath it. */
1010 if (!keymaps && strcmp (pane_string, ""))
1012 wv = xmalloc_widget_value ();
1016 first_wv->contents = wv;
1017 wv->name = pane_string;
1018 if (keymaps && !NILP (prefix))
1022 wv->button_type = BUTTON_TYPE_NONE;
1027 else if (first_pane)
1033 i += MENU_ITEMS_PANE_LENGTH;
1037 /* Create a new item within current pane. */
1038 Lisp_Object item_name, enable, descrip, def, type, selected, help;
1039 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
1040 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
1041 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
1042 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
1043 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
1044 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
1045 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
1047 #ifndef HAVE_MULTILINGUAL_MENU
1048 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
1050 item_name = ENCODE_MENU_STRING (item_name);
1051 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
1054 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
1056 descrip = ENCODE_MENU_STRING (descrip);
1057 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
1059 #endif /* not HAVE_MULTILINGUAL_MENU */
1061 wv = xmalloc_widget_value ();
1065 save_wv->contents = wv;
1066 wv->name = (char *) SDATA (item_name);
1067 if (!NILP (descrip))
1068 wv->key = (char *) SDATA (descrip);
1070 /* If this item has a null value,
1071 make the call_data null so that it won't display a box
1072 when the mouse is on it. */
1074 = !NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0;
1075 wv->enabled = !NILP (enable);
1078 wv->button_type = BUTTON_TYPE_NONE;
1079 else if (EQ (type, QCtoggle))
1080 wv->button_type = BUTTON_TYPE_TOGGLE;
1081 else if (EQ (type, QCradio))
1082 wv->button_type = BUTTON_TYPE_RADIO;
1086 wv->selected = !NILP (selected);
1088 if (! STRINGP (help))
1095 i += MENU_ITEMS_ITEM_LENGTH;
1103 widget_value *wv_title = xmalloc_widget_value ();
1104 widget_value *wv_sep = xmalloc_widget_value ();
1106 /* Maybe replace this separator with a bitmap or owner-draw item
1107 so that it looks better. Having two separators looks odd. */
1108 wv_sep->name = "--";
1109 wv_sep->next = first_wv->contents;
1110 wv_sep->help = Qnil;
1112 #ifndef HAVE_MULTILINGUAL_MENU
1113 if (STRING_MULTIBYTE (title))
1114 title = ENCODE_MENU_STRING (title);
1117 wv_title->name = (char *) SDATA (title);
1118 wv_title->enabled = NO;
1119 wv_title->button_type = BUTTON_TYPE_NONE;
1120 wv_title->help = Qnil;
1121 wv_title->next = wv_sep;
1122 first_wv->contents = wv_title;
1125 pmenu = [[EmacsMenu alloc] initWithTitle:
1126 [NSString stringWithUTF8String: SDATA (title)]];
1127 [pmenu fillWithWidgetValue: first_wv->contents];
1128 free_menubar_widget_value_tree (first_wv);
1129 unbind_to (specpdl_count2, Qnil);
1131 popup_activated_flag = 1;
1132 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
1133 popup_activated_flag = 0;
1134 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1137 discard_menu_items ();
1138 unbind_to (specpdl_count, Qnil);
1141 if (error_name) error (error_name);
1148 /* ==========================================================================
1150 Toolbar: externally-called functions
1152 ========================================================================== */
1155 free_frame_tool_bar (FRAME_PTR f)
1156 /* --------------------------------------------------------------------------
1157 Under NS we just hide the toolbar until it might be needed again.
1158 -------------------------------------------------------------------------- */
1160 [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
1164 update_frame_tool_bar (FRAME_PTR f)
1165 /* --------------------------------------------------------------------------
1166 Update toolbar contents
1167 -------------------------------------------------------------------------- */
1170 EmacsToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
1172 [toolbar clearActive];
1174 /* update EmacsToolbar as in GtkUtils, build items list */
1175 for (i = 0; i < f->n_tool_bar_items; ++i)
1177 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1178 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1180 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1181 BOOL selected_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_SELECTED_P));
1186 Lisp_Object helpObj;
1189 /* If image is a vector, choose the image according to the
1191 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1192 if (VECTORP (image))
1194 /* NS toolbar auto-computes disabled and selected images */
1195 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1196 xassert (ASIZE (image) >= idx);
1197 image = AREF (image, idx);
1203 /* Ignore invalid image specifications. */
1204 if (!valid_image_p (image))
1206 NSLog (@"Invalid image for toolbar item");
1210 img_id = lookup_image (f, image);
1211 img = IMAGE_FROM_ID (f, img_id);
1212 prepare_image_for_display (f, img);
1214 if (img->load_failed_p || img->pixmap == nil)
1216 NSLog (@"Could not prepare toolbar image for display.");
1220 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1222 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1223 helpText = NILP (helpObj) ? "" : (char *)SDATA (helpObj);
1225 [toolbar addDisplayItemWithImage: img->pixmap idx: i helpText: helpText
1226 enabled: enabled_p];
1230 if (![toolbar isVisible])
1231 [toolbar setVisible: YES];
1233 if ([toolbar changed])
1235 /* inform app that toolbar has changed */
1236 NSDictionary *dict = [toolbar configurationDictionary];
1237 NSMutableDictionary *newDict = [dict mutableCopy];
1238 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1240 while ((key = [keys nextObject]) != nil)
1242 NSObject *val = [dict objectForKey: key];
1243 if ([val isKindOfClass: [NSArray class]])
1246 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1251 [toolbar setConfigurationFromDictionary: newDict];
1258 /* ==========================================================================
1260 Toolbar: class implementation
1262 ========================================================================== */
1264 @implementation EmacsToolbar
1266 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1268 self = [super initWithIdentifier: identifier];
1270 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1271 [self setSizeMode: NSToolbarSizeModeSmall];
1272 [self setDelegate: self];
1273 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1274 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1275 prevEnablement = enablement = 0L;
1281 [prevIdentifiers release];
1282 [activeIdentifiers release];
1283 [identifierToItem release];
1287 - (void) clearActive
1289 [prevIdentifiers release];
1290 prevIdentifiers = [activeIdentifiers copy];
1291 [activeIdentifiers removeAllObjects];
1292 prevEnablement = enablement;
1298 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1299 enablement == prevEnablement ? NO : YES;
1302 - (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx
1303 helpText: (char *)help enabled: (BOOL)enabled
1305 /* 1) come up w/identifier */
1306 NSString *identifier
1307 = [NSString stringWithFormat: @"%u", [img hash]];
1309 /* 2) create / reuse item */
1310 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1313 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1315 [item setImage: img];
1316 [item setToolTip: [NSString stringWithCString: help]];
1317 [item setTarget: emacsView];
1318 [item setAction: @selector (toolbarClicked:)];
1322 [item setEnabled: enabled];
1324 /* 3) update state */
1325 [identifierToItem setObject: item forKey: identifier];
1326 [activeIdentifiers addObject: identifier];
1327 enablement = (enablement << 1) | (enabled == YES);
1330 /* This overrides super's implementation, which automatically sets
1331 all items to enabled state (for some reason). */
1332 - (void)validateVisibleItems { }
1335 /* delegate methods */
1337 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1338 itemForItemIdentifier: (NSString *)itemIdentifier
1339 willBeInsertedIntoToolbar: (BOOL)flag
1341 /* look up NSToolbarItem by identifier and return... */
1342 return [identifierToItem objectForKey: itemIdentifier];
1345 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1347 /* return entire set.. */
1348 return activeIdentifiers;
1351 /* for configuration palette (not yet supported) */
1352 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1354 /* return entire set... */
1355 return [identifierToItem allKeys];
1358 /* optional and unneeded */
1359 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1360 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1361 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1363 @end /* EmacsToolbar */
1367 /* ==========================================================================
1369 Tooltip: class implementation
1371 ========================================================================== */
1373 /* Needed because NeXTstep does not provide enough control over tooltip
1375 @implementation EmacsTooltip
1379 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1380 blue: 0.792 alpha: 0.95];
1381 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1382 NSFont *sfont = [font screenFont];
1383 int height = [sfont ascender] - [sfont descender];
1384 /*[font boundingRectForFont].size.height; */
1385 NSRect r = NSMakeRect (0, 0, 100, height+6);
1387 textField = [[NSTextField alloc] initWithFrame: r];
1388 [textField setFont: font];
1389 [textField setBackgroundColor: col];
1391 [textField setEditable: NO];
1392 [textField setSelectable: NO];
1393 [textField setBordered: YES];
1394 [textField setBezeled: YES];
1395 [textField setDrawsBackground: YES];
1397 win = [[NSWindow alloc]
1398 initWithContentRect: [textField frame]
1400 backing: NSBackingStoreBuffered
1402 [win setReleasedWhenClosed: NO];
1403 [win setDelegate: self];
1404 [[win contentView] addSubview: textField];
1405 /* [win setBackgroundColor: col]; */
1406 [win setOpaque: NO];
1415 [textField release];
1419 - (void) setText: (char *)text
1421 NSString *str = [NSString stringWithUTF8String: text];
1422 NSRect r = [textField frame];
1423 r.size.width = [[[textField font] screenFont] widthOfString: str] + 8;
1424 [textField setFrame: r];
1425 [textField setStringValue: str];
1428 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1430 NSRect wr = [win frame];
1432 wr.origin = NSMakePoint (x, y);
1433 wr.size = [textField frame].size;
1435 [win setFrame: wr display: YES];
1436 [win orderFront: self];
1438 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1439 selector: @selector (hide)
1440 userInfo: nil repeats: NO];
1449 if ([timer isValid])
1458 return timer != nil;
1463 return [textField frame];
1466 @end /* EmacsTooltip */
1470 /* ==========================================================================
1472 Popup Dialog: implementing functions
1474 ========================================================================== */
1477 ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1480 Lisp_Object window, tem;
1485 NSTRACE (x-popup-dialog);
1489 isQ = NILP (header);
1491 if (EQ (position, Qt))
1493 window = selected_window;
1495 else if (CONSP (position))
1498 tem = Fcar (position);
1499 if (XTYPE (tem) == Lisp_Cons)
1500 window = Fcar (Fcdr (position));
1503 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1504 window = Fcar (tem); /* POSN_WINDOW (tem) */
1507 else if (FRAMEP (position))
1513 CHECK_LIVE_WINDOW (position);
1517 if (FRAMEP (window))
1518 f = XFRAME (window);
1521 CHECK_LIVE_WINDOW (window);
1522 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1524 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1525 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1526 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1528 popup_activated_flag = 1;
1529 tem = [dialog runDialogAt: p];
1530 popup_activated_flag = 0;
1534 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1539 /* ==========================================================================
1541 Popup Dialog: class implementation
1543 ========================================================================== */
1545 @interface FlippedView : NSView
1550 @implementation FlippedView
1557 @implementation EmacsDialogPanel
1560 #define ICONSIZE 64.0
1561 #define TEXTHEIGHT 20.0
1562 #define MINCELLWIDTH 90.0
1564 - initWithContentRect: (NSRect)contentRect styleMask: (unsigned int)aStyle
1565 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1567 NSSize spacing = {SPACER, SPACER};
1569 char this_cmd_name[80];
1571 static NSImageView *imgView;
1572 static FlippedView *contentView;
1577 area.origin.x = 3*SPACER;
1578 area.origin.y = 2*SPACER;
1579 area.size.width = ICONSIZE;
1580 area.size.height= ICONSIZE;
1581 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1582 [img setScalesWhenResized: YES];
1583 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1584 imgView = [[NSImageView alloc] initWithFrame: area];
1585 [imgView setImage: img];
1586 [imgView setEditable: NO];
1590 aStyle = NSTitledWindowMask;
1594 [super initWithContentRect: contentRect styleMask: aStyle
1595 backing: backingType defer: flag];
1596 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1597 [self setContentView: contentView];
1599 [[self contentView] setAutoresizesSubviews: YES];
1601 [[self contentView] addSubview: imgView];
1602 [self setTitle: @""];
1604 area.origin.x += ICONSIZE+2*SPACER;
1605 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1606 area.size.width = 400;
1607 area.size.height= TEXTHEIGHT;
1608 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1609 [[self contentView] addSubview: command];
1610 [command setStringValue: @"Emacs"];
1611 [command setDrawsBackground: NO];
1612 [command setBezeled: NO];
1613 [command setSelectable: NO];
1614 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1616 /* area.origin.x = ICONSIZE+2*SPACER;
1617 area.origin.y = TEXTHEIGHT + 2*SPACER;
1618 area.size.width = 400;
1619 area.size.height= 2;
1620 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1621 [[self contentView] addSubview: tem];
1622 [tem setTitlePosition: NSNoTitle];
1623 [tem setAutoresizingMask: NSViewWidthSizable];*/
1625 /* area.origin.x = ICONSIZE+2*SPACER; */
1626 area.origin.y += TEXTHEIGHT+SPACER;
1627 area.size.width = 400;
1628 area.size.height= TEXTHEIGHT;
1629 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1630 [[self contentView] addSubview: title];
1631 [title setDrawsBackground: NO];
1632 [title setBezeled: NO];
1633 [title setSelectable: NO];
1634 [title setFont: [NSFont systemFontOfSize: 11.0]];
1636 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1637 [cell setBordered: NO];
1638 [cell setEnabled: NO];
1639 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1640 [cell setBezelStyle: NSRoundedBezelStyle];
1642 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1643 mode: NSHighlightModeMatrix
1646 numberOfColumns: 1];
1647 [[self contentView] addSubview: matrix];
1649 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1650 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1651 [matrix setIntercellSpacing: spacing];
1653 [self setOneShot: YES];
1654 [self setReleasedWhenClosed: YES];
1655 [self setHidesOnDeactivate: YES];
1660 - (BOOL)windowShouldClose: (id)sender
1662 [NSApp stopModalWithCode: XHASH (Qnil)]; // FIXME: BIG UGLY HACK!!
1667 void process_dialog (id window, Lisp_Object list)
1672 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1675 if (XTYPE (item) == Lisp_String)
1677 [window addString: XSTRING (item)->data row: row++];
1679 else if (XTYPE (item) == Lisp_Cons)
1681 [window addButton: XSTRING (XCAR (item))->data
1682 value: XCDR (item) row: row++];
1684 else if (NILP (item))
1693 - addButton: (char *)str value: (Lisp_Object)val row: (int)row
1702 cell = [matrix cellAtRow: row column: cols-1];
1703 [cell setTarget: self];
1704 [cell setAction: @selector (clicked: )];
1705 [cell setTitle: [NSString stringWithUTF8String: str]];
1706 [cell setTag: XHASH (val)]; // FIXME: BIG UGLY HACK!!
1707 [cell setBordered: YES];
1708 [cell setEnabled: YES];
1714 - addString: (char *)str row: (int)row
1723 cell = [matrix cellAtRow: row column: cols-1];
1724 [cell setTitle: [NSString stringWithUTF8String: str]];
1725 [cell setBordered: YES];
1726 [cell setEnabled: NO];
1742 NSArray *sellist = nil;
1745 sellist = [sender selectedCells];
1746 if ([sellist count]<1)
1749 seltag = [[sellist objectAtIndex: 0] tag];
1750 if (seltag != XHASH (Qundefined)) // FIXME: BIG UGLY HACK!!
1751 [NSApp stopModalWithCode: seltag];
1756 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1761 if (XTYPE (contents) == Lisp_Cons)
1763 head = Fcar (contents);
1764 process_dialog (self, Fcdr (contents));
1769 if (XTYPE (head) == Lisp_String)
1770 [title setStringValue:
1771 [NSString stringWithUTF8String: XSTRING (head)->data]];
1772 else if (isQ == YES)
1773 [title setStringValue: @"Question"];
1775 [title setStringValue: @"Information"];
1781 if (cols == 1 && rows > 1) /* Never told where to split */
1784 for (i = 0; i<rows/2; i++)
1786 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1787 atRow: i column: 1];
1788 [matrix removeRow: (rows+1)/2];
1794 NSSize csize = [matrix cellSize];
1795 if (csize.width < MINCELLWIDTH)
1797 csize.width = MINCELLWIDTH;
1798 [matrix setCellSize: csize];
1799 [matrix sizeToCells];
1804 [command sizeToFit];
1808 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1810 t.origin.x = r.origin.x;
1811 t.size.width = r.size.width;
1813 r = [command frame];
1814 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1816 t.origin.x = r.origin.x;
1817 t.size.width = r.size.width;
1821 s = [(NSView *)[self contentView] frame];
1822 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1823 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1824 [self setFrame: r display: NO];
1833 { [super dealloc]; return; };
1837 - (Lisp_Object)runDialogAt: (NSPoint)p
1840 NSModalSession session;
1843 [self center]; /*XXX p ignored? */
1844 [self orderFront: NSApp];
1846 session = [NSApp beginModalSessionForWindow: self];
1847 while ((ret = [NSApp runModalSession: session]) == NSRunContinuesResponse)
1849 (e = [NSApp nextEventMatchingMask: NSAnyEventMask
1850 untilDate: [NSDate distantFuture]
1851 inMode: NSEventTrackingRunLoopMode
1853 /*fprintf (stderr, "ret = %d\te = %p\n", ret, e); */
1855 [NSApp endModalSession: session];
1857 { // FIXME: BIG UGLY HACK!!!
1859 *(EMACS_INT*)(&tmp) = ret;
1868 /* ==========================================================================
1872 ========================================================================== */
1874 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1875 doc: /* Cause the NS menu to be re-calculated. */)
1878 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1883 DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
1884 doc: /* Pop up a deck-of-cards menu and return user's selection.
1885 POSITION is a position specification. This is either a mouse button event
1886 or a list ((XOFFSET YOFFSET) WINDOW)
1887 where XOFFSET and YOFFSET are positions in pixels from the top left
1888 corner of WINDOW. (WINDOW may be a window or a frame object.)
1889 This controls the position of the top left of the menu as a whole.
1890 If POSITION is t, it means to use the current mouse position.
1892 MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
1893 The menu items come from key bindings that have a menu string as well as
1894 a definition; actually, the \"definition\" in such a key binding looks like
1895 \(STRING . REAL-DEFINITION). To give the menu a title, put a string into
1896 the keymap as a top-level element.
1898 If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
1899 Otherwise, REAL-DEFINITION should be a valid key binding definition.
1901 You can also use a list of keymaps as MENU.
1902 Then each keymap makes a separate pane.
1904 When MENU is a keymap or a list of keymaps, the return value is the
1905 list of events corresponding to the user's choice. Note that
1906 `x-popup-menu' does not actually execute the command bound to that
1909 Alternatively, you can specify a menu of multiple panes
1910 with a list of the form (TITLE PANE1 PANE2...),
1911 where each pane is a list of form (TITLE ITEM1 ITEM2...).
1912 Each ITEM is normally a cons cell (STRING . VALUE);
1913 but a string can appear as an item--that makes a nonselectable line
1915 With this form of menu, the return value is VALUE from the chosen item. */)
1917 Lisp_Object position, menu;
1919 return ns_popup_menu (position, menu);
1923 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1924 doc: /* Pop up a dialog box and return user's selection.
1925 POSITION specifies which frame to use.
1926 This is normally a mouse button event or a window or frame.
1927 If POSITION is t, it means to use the frame the mouse is on.
1928 The dialog box appears in the middle of the specified frame.
1930 CONTENTS specifies the alternatives to display in the dialog box.
1931 It is a list of the form (DIALOG ITEM1 ITEM2...).
1932 Each ITEM is a cons cell (STRING . VALUE).
1933 The return value is VALUE from the chosen item.
1935 An ITEM may also be just a string--that makes a nonselectable item.
1936 An ITEM may also be nil--that means to put all preceding items
1937 on the left of the dialog box and all following items on the right.
1938 \(By default, approximately half appear on each side.)
1940 If HEADER is non-nil, the frame title for the box is "Information",
1941 otherwise it is "Question".
1943 If the user gets rid of the dialog box without making a valid choice,
1944 for instance using the window manager, then this produces a quit and
1945 `x-popup-dialog' does not return. */)
1946 (position, contents, header)
1947 Lisp_Object position, contents, header;
1949 return ns_popup_dialog (position, contents, header);
1952 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1953 doc: /* Return t if a menu or popup dialog is active. */)
1956 return popup_activated () ? Qt : Qnil;
1959 /* ==========================================================================
1961 Lisp interface declaration
1963 ========================================================================== */
1968 defsubr (&Sx_popup_menu);
1969 defsubr (&Sx_popup_dialog);
1970 defsubr (&Sns_reset_menu);
1971 defsubr (&Smenu_or_popup_active_p);
1972 staticpro (&menu_items);
1975 Qdebug_on_next_call = intern ("debug-on-next-call");
1976 staticpro (&Qdebug_on_next_call);
1979 // arch-tag: 75773656-52e5-4c44-a398-47bd87b32619