1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
20 By Adrian Robert, based on code from original nsmenu.m (Carl Edman,
21 Christian Limpach, Scott Bender, Christophe de Dinechin) and code in the
22 Carbon version by Yamamoto Mitsuharu. */
24 /* This should be the first include, as it may set up #defines affecting
25 interpretation of even the system includes. */
34 #include "blockinput.h"
36 #include "termhooks.h"
39 #define NSMENUPROFILE 0
42 #include <sys/timeb.h>
43 #include <sys/types.h>
46 #define MenuStagger 10.0
49 int menu_trace_num = 0;
50 #define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \
51 __FILE__, __LINE__, ++menu_trace_num)
57 /* Include lisp -> C common menu parsing code */
58 #define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
59 #include "nsmenu_common.c"
62 extern Lisp_Object Qundefined, Qmenu_enable, Qmenu_bar_update_hook;
63 extern Lisp_Object QCtoggle, QCradio;
65 extern Lisp_Object Vmenu_updating_frame;
67 Lisp_Object Qdebug_on_next_call;
68 extern Lisp_Object Voverriding_local_map, Voverriding_local_map_menu_flag,
69 Qoverriding_local_map, Qoverriding_terminal_local_map;
71 extern long context_menu_value;
72 EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
74 /* Nonzero means a menu is currently active. */
75 static int popup_activated_flag;
76 static NSModalSession popupSession;
78 /* NOTE: toolbar implementation is at end,
79 following complete menu implementation. */
82 /* ==========================================================================
84 Menu: Externally-called functions
86 ========================================================================== */
89 /* FIXME: not currently used, but should normalize with other terms. */
91 x_activate_menubar (struct frame *f)
93 fprintf (stderr, "XXX: Received x_activate_menubar event.\n");
97 /* Supposed to discard menubar and free storage. Since we share the
98 menubar among frames and update its context for the focused window,
99 there is nothing to do here. */
101 free_frame_menubar (struct frame *f)
110 return popup_activated_flag;
114 /* --------------------------------------------------------------------------
115 Update menubar. Three cases:
116 1) deep_p = 0, submenu = nil: Fresh switch onto a frame -- either set up
117 just top-level menu strings (OS X), or goto case (2) (GNUstep).
118 2) deep_p = 1, submenu = nil: Recompute all submenus.
119 3) deep_p = 1, submenu = non-nil: Update contents of a single submenu.
120 -------------------------------------------------------------------------- */
122 ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
124 NSAutoreleasePool *pool;
125 id menu = [NSApp mainMenu];
126 static EmacsMenu *last_submenu = nil;
128 const char *submenuTitle = [[submenu title] UTF8String];
129 extern int waiting_for_input;
132 widget_value *wv, *first_wv, *prev_wv = 0;
140 NSTRACE (set_frame_menubar);
142 if (f != SELECTED_FRAME ())
144 XSETFRAME (Vmenu_updating_frame, f);
145 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
148 pool = [[NSAutoreleasePool alloc] init];
150 /* Menu may have been created automatically; if so, discard it. */
151 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
159 menu = [[EmacsMenu alloc] initWithTitle: ns_app_name];
163 { /* close up anything on there */
164 id attMenu = [menu attachedMenu];
171 t = -(1000*tb.time+tb.millitm);
174 #ifdef NS_IMPL_GNUSTEP
175 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
180 /* Fully parse one or more of the submenus. */
182 int *submenu_start, *submenu_end;
183 int *submenu_top_level_items, *submenu_n_panes;
184 struct buffer *prev = current_buffer;
186 int specpdl_count = SPECPDL_INDEX ();
187 int previous_menu_items_used = f->menu_bar_items_used;
188 Lisp_Object *previous_items
189 = (Lisp_Object *) alloca (previous_menu_items_used
190 * sizeof (Lisp_Object));
192 /* lisp preliminaries */
193 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
194 specbind (Qinhibit_quit, Qt);
195 specbind (Qdebug_on_next_call, Qnil);
196 record_unwind_save_match_data ();
197 if (NILP (Voverriding_local_map_menu_flag))
199 specbind (Qoverriding_terminal_local_map, Qnil);
200 specbind (Qoverriding_local_map, Qnil);
202 set_buffer_internal_1 (XBUFFER (buffer));
204 /* TODO: for some reason this is not needed in other terms,
205 but some menu updates call Info-extract-pointer which causes
206 abort-on-error if waiting-for-input. Needs further investigation. */
207 owfi = waiting_for_input;
208 waiting_for_input = 0;
210 /* lucid hook and possible reset */
211 safe_run_hooks (Qactivate_menubar_hook);
212 if (! NILP (Vlucid_menu_bar_dirty_flag))
213 call0 (Qrecompute_lucid_menubar);
214 safe_run_hooks (Qmenu_bar_update_hook);
215 FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
217 /* Now ready to go */
218 items = FRAME_MENU_BAR_ITEMS (f);
220 /* Save the frame's previous menu bar contents data */
221 if (previous_menu_items_used)
222 bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
223 previous_menu_items_used * sizeof (Lisp_Object));
225 /* parse stage 1: extract from lisp */
228 menu_items = f->menu_bar_vector;
229 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
230 submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
231 submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
232 submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int));
233 submenu_top_level_items
234 = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
236 for (i = 0; i < XVECTOR (items)->size; i += 4)
238 Lisp_Object key, string, maps;
240 key = XVECTOR (items)->contents[i];
241 string = XVECTOR (items)->contents[i + 1];
242 maps = XVECTOR (items)->contents[i + 2];
246 /* FIXME: we'd like to only parse the needed submenu, but this
247 was causing crashes in the _common parsing code.. need to make
248 sure proper initialization done.. */
249 /* if (submenu && strcmp (submenuTitle, SDATA (string)))
252 submenu_start[i] = menu_items_used;
254 menu_items_n_panes = 0;
255 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
256 submenu_n_panes[i] = menu_items_n_panes;
257 submenu_end[i] = menu_items_used;
261 finish_menu_items ();
262 waiting_for_input = owfi;
265 if (submenu && n == 0)
267 /* should have found a menu for this one but didn't */
268 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
270 discard_menu_items ();
271 unbind_to (specpdl_count, Qnil);
277 /* parse stage 2: insert into lucid 'widget_value' structures
278 [comments in other terms say not to evaluate lisp code here] */
279 wv = xmalloc_widget_value ();
280 wv->name = "menubar";
283 wv->button_type = BUTTON_TYPE_NONE;
287 for (i = 0; i < 4*n; i += 4)
289 menu_items_n_panes = submenu_n_panes[i];
290 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
291 submenu_top_level_items[i]);
295 first_wv->contents = wv;
296 /* Don't set wv->name here; GC during the loop might relocate it. */
298 wv->button_type = BUTTON_TYPE_NONE;
302 set_buffer_internal_1 (prev);
304 /* Compare the new menu items with previous, and leave off if no change */
305 /* FIXME: following other terms here, but seems like this should be
306 done before parse stage 2 above, since its results aren't used */
307 if (previous_menu_items_used
308 && (!submenu || (submenu && submenu == last_submenu))
309 && menu_items_used == previous_menu_items_used)
311 for (i = 0; i < previous_menu_items_used; i++)
312 /* FIXME: this ALWAYS fails on Buffers menu items.. something
313 about their strings causes them to change every time, so we
314 double-check failures */
315 if (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i]))
316 if (!(STRINGP (previous_items[i])
317 && STRINGP (XVECTOR (menu_items)->contents[i])
318 && !strcmp (SDATA (previous_items[i]),
319 SDATA (XVECTOR (menu_items)->contents[i]))))
321 if (i == previous_menu_items_used)
327 t += 1000*tb.time+tb.millitm;
328 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
331 free_menubar_widget_value_tree (first_wv);
332 discard_menu_items ();
333 unbind_to (specpdl_count, Qnil);
339 /* The menu items are different, so store them in the frame */
340 /* FIXME: this is not correct for single-submenu case */
341 f->menu_bar_vector = menu_items;
342 f->menu_bar_items_used = menu_items_used;
344 /* Calls restore_menu_items, etc., as they were outside */
345 unbind_to (specpdl_count, Qnil);
347 /* Parse stage 2a: now GC cannot happen during the lifetime of the
348 widget_value, so it's safe to store data from a Lisp_String */
349 wv = first_wv->contents;
350 for (i = 0; i < XVECTOR (items)->size; i += 4)
353 string = XVECTOR (items)->contents[i + 1];
356 /* if (submenu && strcmp (submenuTitle, SDATA (string)))
359 wv->name = (char *) SDATA (string);
360 update_submenu_strings (wv->contents);
364 /* Now, update the NS menu; if we have a submenu, use that, otherwise
365 create a new menu for each sub and fill it. */
368 for (wv = first_wv->contents; wv; wv = wv->next)
370 if (!strcmp (submenuTitle, wv->name))
372 [submenu fillWithWidgetValue: wv->contents];
373 last_submenu = submenu;
380 [menu fillWithWidgetValue: first_wv->contents];
386 static int n_previous_strings = 0;
387 static char previous_strings[100][10];
388 static struct frame *last_f = NULL;
392 wv = xmalloc_widget_value ();
393 wv->name = "menubar";
396 wv->button_type = BUTTON_TYPE_NONE;
400 /* Make widget-value tree w/ just the top level menu bar strings */
401 items = FRAME_MENU_BAR_ITEMS (f);
410 /* check if no change.. this mechanism is a bit rough, but ready */
411 n = XVECTOR (items)->size / 4;
412 if (f == last_f && n_previous_strings == n)
414 for (i = 0; i<n; i++)
416 string = AREF (items, 4*i+1);
418 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
421 if (previous_strings[i][0])
425 if (strncmp (previous_strings[i], SDATA (string), 10))
438 for (i = 0; i < XVECTOR (items)->size; i += 4)
440 string = XVECTOR (items)->contents[i + 1];
445 strncpy (previous_strings[i/4], SDATA (string), 10);
447 wv = xmalloc_widget_value ();
448 wv->name = (char *) SDATA (string);
451 wv->button_type = BUTTON_TYPE_NONE;
453 wv->call_data = (void *) (EMACS_INT) (-1);
456 /* we'll update the real copy under app menu when time comes */
457 if (!strcmp ("Services", wv->name))
459 /* but we need to make sure it will update on demand */
460 [svcsMenu setFrame: f];
461 [svcsMenu setDelegate: svcsMenu];
465 [menu addSubmenuWithTitle: wv->name forFrame: f];
470 first_wv->contents = wv;
476 n_previous_strings = n;
478 n_previous_strings = 0;
481 free_menubar_widget_value_tree (first_wv);
486 t += 1000*tb.time+tb.millitm;
487 fprintf (stderr, "Menu update took %ld msec.\n", t);
492 [NSApp setMainMenu: menu];
500 /* Main emacs core entry point for menubar menus: called to indicate that the
501 frame's menus have changed, and the *step representation should be updated
504 set_frame_menubar (struct frame *f, int first_time, int deep_p)
506 ns_update_menubar (f, deep_p, nil);
510 /* Utility (from macmenu.c): is this item a separator? */
512 name_is_separator (name)
515 const char *start = name;
517 /* Check if name string consists of only dashes ('-'). */
518 while (*name == '-') name++;
519 /* Separators can also be of the form "--:TripleSuperMegaEtched"
520 or "--deep-shadow". We don't implement them yet, se we just treat
521 them like normal separators. */
522 return (*name == '\0' || start + 2 == name);
526 /* ==========================================================================
528 Menu: class implementation
530 ========================================================================== */
533 /* Menu that can define itself from Emacs "widget_value"s and will lazily
534 update itself when user clicked. Based on Carbon/AppKit implementation
535 by Yamamoto Mitsuharu. */
536 @implementation EmacsMenu
538 /* override designated initializer */
539 - initWithTitle: (NSString *)title
541 if (self = [super initWithTitle: title])
542 [self setAutoenablesItems: NO];
547 /* used for top-level */
548 - initWithTitle: (NSString *)title frame: (struct frame *)f
550 [self initWithTitle: title];
553 [self setDelegate: self];
559 - (void)setFrame: (struct frame *)f
565 /* delegate method called when a submenu is being opened: run a 'deep' call
566 to set_frame_menubar */
567 - (void)menuNeedsUpdate: (NSMenu *)menu
570 if (!FRAME_LIVE_P (frame))
572 event = [[FRAME_NS_VIEW (frame) window] currentEvent];
573 /* HACK: Cocoa/Carbon will request update on every keystroke
574 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
575 since key equivalents are handled through emacs.
576 On Leopard, even keystroke events generate SystemDefined events, but
577 their subtype is 8. */
578 if ([event type] != NSSystemDefined || [event subtype] == 8
579 /* Also, don't try this if from an event picked up asynchronously,
580 as lots of lisp evaluation happens in ns_update_menubar. */
581 || handling_signal != 0)
583 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
584 ns_update_menubar (frame, 1, self);
588 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
590 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
591 && FRAME_NS_VIEW (SELECTED_FRAME ()))
592 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
597 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
598 into an accelerator string. We are only able to display a single character
599 for an accelerator, together with an optional modifier combination. (Under
600 Carbon more control was possible, but in Cocoa multi-char strings passed to
601 NSMenuItem get ignored. For now we try to display a super-single letter
602 combo, and return the others as strings to be appended to the item title.
603 (This is signaled by setting keyEquivModMask to 0 for now.) */
604 -(NSString *)parseKeyEquiv: (char *)key
607 keyEquivModMask = NSCommandKeyMask;
609 if (!key || !strlen (key))
612 while (*tpos == ' ' || *tpos == '(')
614 if ((*tpos == 's') && (*(tpos+1) == '-'))
616 return [NSString stringWithFormat: @"%c", tpos[2]];
618 keyEquivModMask = 0; /* signal */
619 return [NSString stringWithUTF8String: tpos];
623 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
626 widget_value *wv = (widget_value *)wvptr;
628 if (name_is_separator (wv->name))
630 item = [NSMenuItem separatorItem];
631 [self addItem: item];
635 NSString *title, *keyEq;
636 title = [NSString stringWithUTF8String: wv->name];
638 title = @"< ? >"; /* (get out in the open so we know about it) */
640 keyEq = [self parseKeyEquiv: wv->key];
642 /* OS X just ignores modifier strings longer than one character */
643 if (keyEquivModMask == 0)
644 title = [title stringByAppendingFormat: @" (%@)", keyEq];
647 item = [self addItemWithTitle: (NSString *)title
648 action: @selector (menuDown:)
649 keyEquivalent: keyEq];
650 [item setKeyEquivalentModifierMask: keyEquivModMask];
652 [item setEnabled: wv->enabled];
654 /* Draw radio buttons and tickboxes */
655 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
656 wv->button_type == BUTTON_TYPE_RADIO))
657 [item setState: NSOnState];
659 [item setState: NSOffState];
661 [item setTag: (int)wv->call_data];
673 for (n = [self numberOfItems]-1; n >= 0; n--)
675 NSMenuItem *item = [self itemAtIndex: n];
676 NSString *title = [item title];
677 if (([title length] == 0 /* OSX 10.5 */
678 || [ns_app_name isEqualToString: title] /* from 10.6 on */
679 || [@"Apple" isEqualToString: title]) /* older */
680 && ![item isSeparatorItem])
682 [self removeItemAtIndex: n];
687 - (void)fillWithWidgetValue: (void *)wvptr
689 widget_value *wv = (widget_value *)wvptr;
691 /* clear existing contents */
692 [self setMenuChangedMessagesEnabled: NO];
695 /* add new contents */
696 for (; wv != NULL; wv = wv->next)
698 NSMenuItem *item = [self addItemWithWidgetValue: wv];
702 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
704 [self setSubmenu: submenu forItem: item];
705 [submenu fillWithWidgetValue: wv->contents];
707 [item setAction: nil];
711 [self setMenuChangedMessagesEnabled: YES];
712 #ifdef NS_IMPL_GNUSTEP
713 if ([[self window] isVisible])
716 if ([self supermenu] == nil)
722 /* adds an empty submenu and returns it */
723 - (EmacsMenu *)addSubmenuWithTitle: (char *)title forFrame: (struct frame *)f
725 NSString *titleStr = [NSString stringWithUTF8String: title];
726 NSMenuItem *item = [self addItemWithTitle: titleStr
727 action: nil /*@selector (menuDown:) */
729 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
730 [self setSubmenu: submenu forItem: item];
735 /* run a menu in popup mode */
736 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
737 keymaps: (int)keymaps
739 EmacsView *view = FRAME_NS_VIEW (f);
740 /* p = [view convertPoint:p fromView: nil]; */
741 p.y = NSHeight ([view frame]) - p.y;
742 NSEvent *e = [[view window] currentEvent];
743 NSEvent *event = [NSEvent mouseEventWithType: NSRightMouseDown
746 timestamp: [e timestamp]
747 windowNumber: [[view window] windowNumber]
749 eventNumber: 0/*[e eventNumber] */
754 context_menu_value = -1;
755 [NSMenu popUpContextMenu: self withEvent: event forView: view];
756 retVal = context_menu_value;
757 context_menu_value = 0;
759 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
767 /* ==========================================================================
769 Context Menu: implementing functions
771 ========================================================================== */
774 cleanup_popup_menu (Lisp_Object arg)
776 discard_menu_items ();
782 ns_popup_menu (Lisp_Object position, Lisp_Object menu)
785 struct frame *f = NULL;
787 Lisp_Object window, x, y, tem, keymap, title;
789 int specpdl_count = SPECPDL_INDEX (), specpdl_count2;
790 char *error_name = NULL;
792 widget_value *wv, *first_wv = 0;
794 NSTRACE (ns_popup_menu);
796 if (!NILP (position))
800 if (EQ (position, Qt)
801 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
802 || EQ (XCAR (position), Qtool_bar))))
804 /* Use the mouse's current position. */
805 struct frame *new_f = SELECTED_FRAME ();
807 if (FRAME_TERMINAL (new_f)->mouse_position_hook)
808 (*FRAME_TERMINAL (new_f)->mouse_position_hook)
809 (&new_f, 0, 0, 0, &x, &y, 0);
811 XSETFRAME (window, new_f);
814 window = selected_window;
821 CHECK_CONS (position);
822 tem = Fcar (position);
823 if (XTYPE (tem) == Lisp_Cons)
825 window = Fcar (Fcdr (position));
827 y = Fcar (Fcdr (tem));
831 tem = Fcar (Fcdr (position));
833 tem = Fcar (Fcdr (Fcdr (tem)));
851 struct window *win = XWINDOW (window);
852 CHECK_LIVE_WINDOW (window);
853 f = XFRAME (WINDOW_FRAME (win));
854 p.x = FRAME_COLUMN_WIDTH (f) * WINDOW_LEFT_EDGE_COL (win);
855 p.y = FRAME_LINE_HEIGHT (f) * WINDOW_TOP_EDGE_LINE (win);
858 p.x += XINT (x); p.y += XINT (y);
860 XSETFRAME (Vmenu_updating_frame, f);
863 { /* no position given */
864 /* FIXME: if called during dump, we need to stop precomputation of
865 key equivalents (see below) because the keydefs in ns-win.el have
866 not been loaded yet. */
869 Vmenu_updating_frame = Qnil;
872 /* now parse the lisp menus */
873 record_unwind_protect (unuse_menu_items, Qnil);
877 /* Decode the menu items from what was specified. */
879 keymap = get_keymap (menu, 0, 0);
882 /* We were given a keymap. Extract menu info from the keymap. */
885 /* Extract the detailed info to make one pane. */
886 keymap_panes (&menu, 1, NILP (position));
888 /* Search for a string appearing directly as an element of the keymap.
889 That string is the title of the menu. */
890 prompt = Fkeymap_prompt (keymap);
891 title = NILP (prompt) ? build_string ("Select") : prompt;
893 /* Make that be the pane title of the first pane. */
894 if (!NILP (prompt) && menu_items_n_panes >= 0)
895 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
899 else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
901 /* We were given a list of keymaps. */
902 int nmaps = XFASTINT (Flength (menu));
904 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
909 /* The first keymap that has a prompt string
910 supplies the menu title. */
911 for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
915 maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
917 prompt = Fkeymap_prompt (keymap);
918 if (NILP (title) && !NILP (prompt))
922 /* Extract the detailed info to make one pane. */
923 keymap_panes (maps, nmaps, NILP (position));
925 /* Make the title be the pane title of the first pane. */
926 if (!NILP (title) && menu_items_n_panes >= 0)
927 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
933 /* We were given an old-fashioned menu. */
935 CHECK_STRING (title);
937 list_of_panes (Fcdr (menu));
942 unbind_to (specpdl_count, Qnil);
944 /* If no position given, that was a signal to just precompute and cache
945 key equivalents, which was a side-effect of what we just did. */
948 discard_menu_items ();
953 record_unwind_protect (cleanup_popup_menu, Qnil);
956 /* now parse stage 2 as in ns_update_menubar */
957 wv = xmalloc_widget_value ();
958 wv->name = "contextmenu";
961 wv->button_type = BUTTON_TYPE_NONE;
965 specpdl_count2 = SPECPDL_INDEX ();
968 /* FIXME: a couple of one-line differences prevent reuse */
969 wv = digest_single_submenu (0, menu_items_used, Qnil);
972 widget_value *save_wv = 0, *prev_wv = 0;
973 widget_value **submenu_stack
974 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
975 /* Lisp_Object *subprefix_stack
976 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object)); */
977 int submenu_depth = 0;
981 /* Loop over all panes and items, filling in the tree. */
983 while (i < menu_items_used)
985 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
987 submenu_stack[submenu_depth++] = save_wv;
993 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
996 save_wv = submenu_stack[--submenu_depth];
1000 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
1001 && submenu_depth != 0)
1002 i += MENU_ITEMS_PANE_LENGTH;
1003 /* Ignore a nil in the item list.
1004 It's meaningful only for dialog boxes. */
1005 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1007 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1009 /* Create a new pane. */
1010 Lisp_Object pane_name, prefix;
1013 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
1014 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
1016 #ifndef HAVE_MULTILINGUAL_MENU
1017 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
1019 pane_name = ENCODE_MENU_STRING (pane_name);
1020 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
1023 pane_string = (NILP (pane_name)
1024 ? "" : (char *) SDATA (pane_name));
1025 /* If there is just one top-level pane, put all its items directly
1026 under the top-level menu. */
1027 if (menu_items_n_panes == 1)
1030 /* If the pane has a meaningful name,
1031 make the pane a top-level menu item
1032 with its items as a submenu beneath it. */
1033 if (!keymaps && strcmp (pane_string, ""))
1035 wv = xmalloc_widget_value ();
1039 first_wv->contents = wv;
1040 wv->name = pane_string;
1041 if (keymaps && !NILP (prefix))
1045 wv->button_type = BUTTON_TYPE_NONE;
1050 else if (first_pane)
1056 i += MENU_ITEMS_PANE_LENGTH;
1060 /* Create a new item within current pane. */
1061 Lisp_Object item_name, enable, descrip, def, type, selected, help;
1062 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
1063 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
1064 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
1065 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
1066 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
1067 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
1068 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
1070 #ifndef HAVE_MULTILINGUAL_MENU
1071 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
1073 item_name = ENCODE_MENU_STRING (item_name);
1074 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
1077 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
1079 descrip = ENCODE_MENU_STRING (descrip);
1080 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
1082 #endif /* not HAVE_MULTILINGUAL_MENU */
1084 wv = xmalloc_widget_value ();
1088 save_wv->contents = wv;
1089 wv->name = (char *) SDATA (item_name);
1090 if (!NILP (descrip))
1091 wv->key = (char *) SDATA (descrip);
1093 /* If this item has a null value,
1094 make the call_data null so that it won't display a box
1095 when the mouse is on it. */
1097 = !NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0;
1098 wv->enabled = !NILP (enable);
1101 wv->button_type = BUTTON_TYPE_NONE;
1102 else if (EQ (type, QCtoggle))
1103 wv->button_type = BUTTON_TYPE_TOGGLE;
1104 else if (EQ (type, QCradio))
1105 wv->button_type = BUTTON_TYPE_RADIO;
1109 wv->selected = !NILP (selected);
1111 if (! STRINGP (help))
1118 i += MENU_ITEMS_ITEM_LENGTH;
1126 widget_value *wv_title = xmalloc_widget_value ();
1127 widget_value *wv_sep = xmalloc_widget_value ();
1129 /* Maybe replace this separator with a bitmap or owner-draw item
1130 so that it looks better. Having two separators looks odd. */
1131 wv_sep->name = "--";
1132 wv_sep->next = first_wv->contents;
1133 wv_sep->help = Qnil;
1135 #ifndef HAVE_MULTILINGUAL_MENU
1136 if (STRING_MULTIBYTE (title))
1137 title = ENCODE_MENU_STRING (title);
1140 wv_title->name = (char *) SDATA (title);
1141 wv_title->enabled = NO;
1142 wv_title->button_type = BUTTON_TYPE_NONE;
1143 wv_title->help = Qnil;
1144 wv_title->next = wv_sep;
1145 first_wv->contents = wv_title;
1148 pmenu = [[EmacsMenu alloc] initWithTitle:
1149 [NSString stringWithUTF8String: SDATA (title)]];
1150 [pmenu fillWithWidgetValue: first_wv->contents];
1151 free_menubar_widget_value_tree (first_wv);
1152 unbind_to (specpdl_count2, Qnil);
1154 popup_activated_flag = 1;
1155 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
1156 popup_activated_flag = 0;
1157 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1160 discard_menu_items ();
1161 unbind_to (specpdl_count, Qnil);
1164 if (error_name) error (error_name);
1171 /* ==========================================================================
1173 Toolbar: externally-called functions
1175 ========================================================================== */
1178 free_frame_tool_bar (FRAME_PTR f)
1179 /* --------------------------------------------------------------------------
1180 Under NS we just hide the toolbar until it might be needed again.
1181 -------------------------------------------------------------------------- */
1183 [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
1187 update_frame_tool_bar (FRAME_PTR f)
1188 /* --------------------------------------------------------------------------
1189 Update toolbar contents
1190 -------------------------------------------------------------------------- */
1193 EmacsToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
1195 [toolbar clearActive];
1197 /* update EmacsToolbar as in GtkUtils, build items list */
1198 for (i = 0; i < f->n_tool_bar_items; ++i)
1200 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1201 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1203 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1204 BOOL selected_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_SELECTED_P));
1209 Lisp_Object helpObj;
1212 /* If image is a vector, choose the image according to the
1214 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1215 if (VECTORP (image))
1217 /* NS toolbar auto-computes disabled and selected images */
1218 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1219 xassert (ASIZE (image) >= idx);
1220 image = AREF (image, idx);
1226 /* Ignore invalid image specifications. */
1227 if (!valid_image_p (image))
1229 NSLog (@"Invalid image for toolbar item");
1233 img_id = lookup_image (f, image);
1234 img = IMAGE_FROM_ID (f, img_id);
1235 prepare_image_for_display (f, img);
1237 if (img->load_failed_p || img->pixmap == nil)
1239 NSLog (@"Could not prepare toolbar image for display.");
1243 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1245 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1246 helpText = NILP (helpObj) ? "" : (char *)SDATA (helpObj);
1248 [toolbar addDisplayItemWithImage: img->pixmap idx: i helpText: helpText
1249 enabled: enabled_p];
1253 if (![toolbar isVisible])
1254 [toolbar setVisible: YES];
1256 if ([toolbar changed])
1258 /* inform app that toolbar has changed */
1259 NSDictionary *dict = [toolbar configurationDictionary];
1260 NSMutableDictionary *newDict = [dict mutableCopy];
1261 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1263 while ((key = [keys nextObject]) != nil)
1265 NSObject *val = [dict objectForKey: key];
1266 if ([val isKindOfClass: [NSArray class]])
1269 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1274 [toolbar setConfigurationFromDictionary: newDict];
1281 /* ==========================================================================
1283 Toolbar: class implementation
1285 ========================================================================== */
1287 @implementation EmacsToolbar
1289 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1291 self = [super initWithIdentifier: identifier];
1293 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1294 [self setSizeMode: NSToolbarSizeModeSmall];
1295 [self setDelegate: self];
1296 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1297 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1298 prevEnablement = enablement = 0L;
1304 [prevIdentifiers release];
1305 [activeIdentifiers release];
1306 [identifierToItem release];
1310 - (void) clearActive
1312 [prevIdentifiers release];
1313 prevIdentifiers = [activeIdentifiers copy];
1314 [activeIdentifiers removeAllObjects];
1315 prevEnablement = enablement;
1321 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1322 enablement == prevEnablement ? NO : YES;
1325 - (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx
1326 helpText: (char *)help enabled: (BOOL)enabled
1328 /* 1) come up w/identifier */
1329 NSString *identifier
1330 = [NSString stringWithFormat: @"%u", [img hash]];
1332 /* 2) create / reuse item */
1333 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1336 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1338 [item setImage: img];
1339 [item setToolTip: [NSString stringWithCString: help]];
1340 [item setTarget: emacsView];
1341 [item setAction: @selector (toolbarClicked:)];
1345 [item setEnabled: enabled];
1347 /* 3) update state */
1348 [identifierToItem setObject: item forKey: identifier];
1349 [activeIdentifiers addObject: identifier];
1350 enablement = (enablement << 1) | (enabled == YES);
1353 /* This overrides super's implementation, which automatically sets
1354 all items to enabled state (for some reason). */
1355 - (void)validateVisibleItems { }
1358 /* delegate methods */
1360 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1361 itemForItemIdentifier: (NSString *)itemIdentifier
1362 willBeInsertedIntoToolbar: (BOOL)flag
1364 /* look up NSToolbarItem by identifier and return... */
1365 return [identifierToItem objectForKey: itemIdentifier];
1368 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1370 /* return entire set.. */
1371 return activeIdentifiers;
1374 /* for configuration palette (not yet supported) */
1375 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1377 /* return entire set... */
1378 return [identifierToItem allKeys];
1381 /* optional and unneeded */
1382 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1383 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1384 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1386 @end /* EmacsToolbar */
1390 /* ==========================================================================
1392 Tooltip: class implementation
1394 ========================================================================== */
1396 /* Needed because NeXTstep does not provide enough control over tooltip
1398 @implementation EmacsTooltip
1402 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1403 blue: 0.792 alpha: 0.95];
1404 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1405 NSFont *sfont = [font screenFont];
1406 int height = [sfont ascender] - [sfont descender];
1407 /*[font boundingRectForFont].size.height; */
1408 NSRect r = NSMakeRect (0, 0, 100, height+6);
1410 textField = [[NSTextField alloc] initWithFrame: r];
1411 [textField setFont: font];
1412 [textField setBackgroundColor: col];
1414 [textField setEditable: NO];
1415 [textField setSelectable: NO];
1416 [textField setBordered: YES];
1417 [textField setBezeled: YES];
1418 [textField setDrawsBackground: YES];
1420 win = [[NSWindow alloc]
1421 initWithContentRect: [textField frame]
1423 backing: NSBackingStoreBuffered
1425 [win setReleasedWhenClosed: NO];
1426 [win setDelegate: self];
1427 [[win contentView] addSubview: textField];
1428 /* [win setBackgroundColor: col]; */
1429 [win setOpaque: NO];
1438 [textField release];
1442 - (void) setText: (char *)text
1444 NSString *str = [NSString stringWithUTF8String: text];
1445 NSRect r = [textField frame];
1446 NSSize textSize = [str sizeWithAttributes:
1447 [NSDictionary dictionaryWithObject: [[textField font] screenFont]
1448 forKey: NSFontAttributeName]];
1449 NSSize padSize = [[[textField font] screenFont]
1450 boundingRectForFont].size;
1452 r.size.width = textSize.width + padSize.width/2;
1453 r.size.height = textSize.height + padSize.height/2;
1454 [textField setFrame: r];
1455 [textField setStringValue: str];
1458 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1460 NSRect wr = [win frame];
1462 wr.origin = NSMakePoint (x, y);
1463 wr.size = [textField frame].size;
1465 [win setFrame: wr display: YES];
1466 [win orderFront: self];
1468 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1469 selector: @selector (hide)
1470 userInfo: nil repeats: NO];
1479 if ([timer isValid])
1488 return timer != nil;
1493 return [textField frame];
1496 @end /* EmacsTooltip */
1500 /* ==========================================================================
1502 Popup Dialog: implementing functions
1504 ========================================================================== */
1508 pop_down_menu (Lisp_Object arg)
1510 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
1511 if (popup_activated_flag)
1513 popup_activated_flag = 0;
1515 [NSApp endModalSession: popupSession];
1516 [((EmacsDialogPanel *) (p->pointer)) close];
1517 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1525 ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1528 Lisp_Object window, tem;
1533 NSTRACE (x-popup-dialog);
1537 isQ = NILP (header);
1539 if (EQ (position, Qt)
1540 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1541 || EQ (XCAR (position), Qtool_bar))))
1543 window = selected_window;
1545 else if (CONSP (position))
1548 tem = Fcar (position);
1549 if (XTYPE (tem) == Lisp_Cons)
1550 window = Fcar (Fcdr (position));
1553 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1554 window = Fcar (tem); /* POSN_WINDOW (tem) */
1557 else if (WINDOWP (position) || FRAMEP (position))
1564 if (FRAMEP (window))
1565 f = XFRAME (window);
1566 else if (WINDOWP (window))
1568 CHECK_LIVE_WINDOW (window);
1569 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1572 CHECK_WINDOW (window);
1574 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1575 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1578 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1581 int specpdl_count = SPECPDL_INDEX ();
1582 record_unwind_protect (pop_down_menu, make_save_value (dialog, 0));
1583 popup_activated_flag = 1;
1584 tem = [dialog runDialogAt: p];
1585 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1593 /* ==========================================================================
1595 Popup Dialog: class implementation
1597 ========================================================================== */
1599 @interface FlippedView : NSView
1604 @implementation FlippedView
1611 @implementation EmacsDialogPanel
1614 #define ICONSIZE 64.0
1615 #define TEXTHEIGHT 20.0
1616 #define MINCELLWIDTH 90.0
1618 - initWithContentRect: (NSRect)contentRect styleMask: (unsigned int)aStyle
1619 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1621 NSSize spacing = {SPACER, SPACER};
1623 char this_cmd_name[80];
1625 static NSImageView *imgView;
1626 static FlippedView *contentView;
1631 area.origin.x = 3*SPACER;
1632 area.origin.y = 2*SPACER;
1633 area.size.width = ICONSIZE;
1634 area.size.height= ICONSIZE;
1635 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1636 [img setScalesWhenResized: YES];
1637 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1638 imgView = [[NSImageView alloc] initWithFrame: area];
1639 [imgView setImage: img];
1640 [imgView setEditable: NO];
1644 aStyle = NSTitledWindowMask;
1648 [super initWithContentRect: contentRect styleMask: aStyle
1649 backing: backingType defer: flag];
1650 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1651 [self setContentView: contentView];
1653 [[self contentView] setAutoresizesSubviews: YES];
1655 [[self contentView] addSubview: imgView];
1656 [self setTitle: @""];
1658 area.origin.x += ICONSIZE+2*SPACER;
1659 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1660 area.size.width = 400;
1661 area.size.height= TEXTHEIGHT;
1662 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1663 [[self contentView] addSubview: command];
1664 [command setStringValue: ns_app_name];
1665 [command setDrawsBackground: NO];
1666 [command setBezeled: NO];
1667 [command setSelectable: NO];
1668 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1670 /* area.origin.x = ICONSIZE+2*SPACER;
1671 area.origin.y = TEXTHEIGHT + 2*SPACER;
1672 area.size.width = 400;
1673 area.size.height= 2;
1674 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1675 [[self contentView] addSubview: tem];
1676 [tem setTitlePosition: NSNoTitle];
1677 [tem setAutoresizingMask: NSViewWidthSizable];*/
1679 /* area.origin.x = ICONSIZE+2*SPACER; */
1680 area.origin.y += TEXTHEIGHT+SPACER;
1681 area.size.width = 400;
1682 area.size.height= TEXTHEIGHT;
1683 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1684 [[self contentView] addSubview: title];
1685 [title setDrawsBackground: NO];
1686 [title setBezeled: NO];
1687 [title setSelectable: NO];
1688 [title setFont: [NSFont systemFontOfSize: 11.0]];
1690 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1691 [cell setBordered: NO];
1692 [cell setEnabled: NO];
1693 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1694 [cell setBezelStyle: NSRoundedBezelStyle];
1696 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1697 mode: NSHighlightModeMatrix
1700 numberOfColumns: 1];
1701 [[self contentView] addSubview: matrix];
1703 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1704 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1705 [matrix setIntercellSpacing: spacing];
1707 [self setOneShot: YES];
1708 [self setReleasedWhenClosed: YES];
1709 [self setHidesOnDeactivate: YES];
1714 - (BOOL)windowShouldClose: (id)sender
1716 [NSApp stopModalWithCode: XHASH (Qnil)]; // FIXME: BIG UGLY HACK!!
1721 void process_dialog (id window, Lisp_Object list)
1726 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1729 if (XTYPE (item) == Lisp_String)
1731 [window addString: SDATA (item) row: row++];
1733 else if (XTYPE (item) == Lisp_Cons)
1735 [window addButton: SDATA (XCAR (item))
1736 value: XCDR (item) row: row++];
1738 else if (NILP (item))
1747 - addButton: (char *)str value: (Lisp_Object)val row: (int)row
1756 cell = [matrix cellAtRow: row column: cols-1];
1757 [cell setTarget: self];
1758 [cell setAction: @selector (clicked: )];
1759 [cell setTitle: [NSString stringWithUTF8String: str]];
1760 [cell setTag: XHASH (val)]; // FIXME: BIG UGLY HACK!!
1761 [cell setBordered: YES];
1762 [cell setEnabled: YES];
1768 - addString: (char *)str row: (int)row
1777 cell = [matrix cellAtRow: row column: cols-1];
1778 [cell setTitle: [NSString stringWithUTF8String: str]];
1779 [cell setBordered: YES];
1780 [cell setEnabled: NO];
1796 NSArray *sellist = nil;
1799 sellist = [sender selectedCells];
1800 if ([sellist count]<1)
1803 seltag = [[sellist objectAtIndex: 0] tag];
1804 if (seltag != XHASH (Qundefined)) // FIXME: BIG UGLY HACK!!
1805 [NSApp stopModalWithCode: seltag];
1810 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1815 if (XTYPE (contents) == Lisp_Cons)
1817 head = Fcar (contents);
1818 process_dialog (self, Fcdr (contents));
1823 if (XTYPE (head) == Lisp_String)
1824 [title setStringValue:
1825 [NSString stringWithUTF8String: SDATA (head)]];
1826 else if (isQ == YES)
1827 [title setStringValue: @"Question"];
1829 [title setStringValue: @"Information"];
1835 if (cols == 1 && rows > 1) /* Never told where to split */
1838 for (i = 0; i<rows/2; i++)
1840 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1841 atRow: i column: 1];
1842 [matrix removeRow: (rows+1)/2];
1848 NSSize csize = [matrix cellSize];
1849 if (csize.width < MINCELLWIDTH)
1851 csize.width = MINCELLWIDTH;
1852 [matrix setCellSize: csize];
1853 [matrix sizeToCells];
1858 [command sizeToFit];
1862 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1864 t.origin.x = r.origin.x;
1865 t.size.width = r.size.width;
1867 r = [command frame];
1868 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1870 t.origin.x = r.origin.x;
1871 t.size.width = r.size.width;
1875 s = [(NSView *)[self contentView] frame];
1876 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1877 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1878 [self setFrame: r display: NO];
1887 { [super dealloc]; return; };
1891 - (Lisp_Object)runDialogAt: (NSPoint)p
1894 extern EMACS_TIME timer_check (int do_it_now); /* TODO: add to a header */
1896 /* initiate a session that will be ended by pop_down_menu */
1897 popupSession = [NSApp beginModalSessionForWindow: self];
1898 while (popup_activated_flag
1899 && (ret = [NSApp runModalSession: popupSession])
1900 == NSRunContinuesResponse)
1902 /* Run this for timers.el, indep of atimers; might not return.
1903 TODO: use return value to avoid calling every iteration. */
1905 [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
1908 { /* FIXME: BIG UGLY HACK!!! */
1910 *(EMACS_INT*)(&tmp) = ret;
1918 /* ==========================================================================
1922 ========================================================================== */
1924 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1925 doc: /* Cause the NS menu to be re-calculated. */)
1928 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1933 DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
1934 doc: /* Pop up a deck-of-cards menu and return user's selection.
1935 POSITION is a position specification. This is either a mouse button event
1936 or a list ((XOFFSET YOFFSET) WINDOW)
1937 where XOFFSET and YOFFSET are positions in pixels from the top left
1938 corner of WINDOW. (WINDOW may be a window or a frame object.)
1939 This controls the position of the top left of the menu as a whole.
1940 If POSITION is t, it means to use the current mouse position.
1942 MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
1943 The menu items come from key bindings that have a menu string as well as
1944 a definition; actually, the \"definition\" in such a key binding looks like
1945 \(STRING . REAL-DEFINITION). To give the menu a title, put a string into
1946 the keymap as a top-level element.
1948 If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
1949 Otherwise, REAL-DEFINITION should be a valid key binding definition.
1951 You can also use a list of keymaps as MENU.
1952 Then each keymap makes a separate pane.
1954 When MENU is a keymap or a list of keymaps, the return value is the
1955 list of events corresponding to the user's choice. Note that
1956 `x-popup-menu' does not actually execute the command bound to that
1959 Alternatively, you can specify a menu of multiple panes
1960 with a list of the form (TITLE PANE1 PANE2...),
1961 where each pane is a list of form (TITLE ITEM1 ITEM2...).
1962 Each ITEM is normally a cons cell (STRING . VALUE);
1963 but a string can appear as an item--that makes a nonselectable line
1965 With this form of menu, the return value is VALUE from the chosen item. */)
1967 Lisp_Object position, menu;
1969 return ns_popup_menu (position, menu);
1973 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1974 doc: /* Pop up a dialog box and return user's selection.
1975 POSITION specifies which frame to use.
1976 This is normally a mouse button event or a window or frame.
1977 If POSITION is t, it means to use the frame the mouse is on.
1978 The dialog box appears in the middle of the specified frame.
1980 CONTENTS specifies the alternatives to display in the dialog box.
1981 It is a list of the form (DIALOG ITEM1 ITEM2...).
1982 Each ITEM is a cons cell (STRING . VALUE).
1983 The return value is VALUE from the chosen item.
1985 An ITEM may also be just a string--that makes a nonselectable item.
1986 An ITEM may also be nil--that means to put all preceding items
1987 on the left of the dialog box and all following items on the right.
1988 \(By default, approximately half appear on each side.)
1990 If HEADER is non-nil, the frame title for the box is "Information",
1991 otherwise it is "Question".
1993 If the user gets rid of the dialog box without making a valid choice,
1994 for instance using the window manager, then this produces a quit and
1995 `x-popup-dialog' does not return. */)
1996 (position, contents, header)
1997 Lisp_Object position, contents, header;
1999 return ns_popup_dialog (position, contents, header);
2002 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
2003 doc: /* Return t if a menu or popup dialog is active. */)
2006 return popup_activated () ? Qt : Qnil;
2009 /* ==========================================================================
2011 Lisp interface declaration
2013 ========================================================================== */
2018 defsubr (&Sx_popup_menu);
2019 defsubr (&Sx_popup_dialog);
2020 defsubr (&Sns_reset_menu);
2021 defsubr (&Smenu_or_popup_active_p);
2022 staticpro (&menu_items);
2025 Qdebug_on_next_call = intern ("debug-on-next-call");
2026 staticpro (&Qdebug_on_next_call);
2029 // arch-tag: 75773656-52e5-4c44-a398-47bd87b32619