1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007-2014 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. */
30 #include "character.h"
35 #include "blockinput.h"
37 #include "termhooks.h"
41 #define NSMENUPROFILE 0
44 #include <sys/timeb.h>
45 #include <sys/types.h>
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 Lisp_Object Qdebug_on_next_call;
66 extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
68 extern long context_menu_value;
69 EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
71 /* Nonzero means a menu is currently active. */
72 static int popup_activated_flag;
74 /* Nonzero means we are tracking and updating menus. */
75 static int trackingMenu;
78 /* NOTE: toolbar implementation is at end,
79 following complete menu implementation. */
82 /* ==========================================================================
84 Menu: Externally-called functions
86 ========================================================================== */
89 /* Supposed to discard menubar and free storage. Since we share the
90 menubar among frames and update its context for the focused window,
91 there is nothing to do here. */
93 free_frame_menubar (struct frame *f)
100 popup_activated (void)
102 return popup_activated_flag;
106 /* --------------------------------------------------------------------------
107 Update menubar. Three cases:
108 1) ! deep_p, submenu = nil: Fresh switch onto a frame -- either set up
109 just top-level menu strings (OS X), or goto case (2) (GNUstep).
110 2) deep_p, submenu = nil: Recompute all submenus.
111 3) deep_p, submenu = non-nil: Update contents of a single submenu.
112 -------------------------------------------------------------------------- */
114 ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
116 NSAutoreleasePool *pool;
117 id menu = [NSApp mainMenu];
118 static EmacsMenu *last_submenu = nil;
122 widget_value *wv, *first_wv, *prev_wv = 0;
130 NSTRACE (ns_update_menubar);
132 if (f != SELECTED_FRAME ())
134 XSETFRAME (Vmenu_updating_frame, f);
135 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
138 pool = [[NSAutoreleasePool alloc] init];
140 /* Menu may have been created automatically; if so, discard it. */
141 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
149 menu = [[EmacsMenu alloc] initWithTitle: ns_app_name];
153 { /* close up anything on there */
154 id attMenu = [menu attachedMenu];
161 t = -(1000*tb.time+tb.millitm);
164 #ifdef NS_IMPL_GNUSTEP
165 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
170 /* Fully parse one or more of the submenus. */
172 int *submenu_start, *submenu_end;
173 bool *submenu_top_level_items;
174 int *submenu_n_panes;
175 struct buffer *prev = current_buffer;
177 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
178 int previous_menu_items_used = f->menu_bar_items_used;
179 Lisp_Object *previous_items
180 = alloca (previous_menu_items_used * sizeof *previous_items);
182 /* lisp preliminaries */
183 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
184 specbind (Qinhibit_quit, Qt);
185 specbind (Qdebug_on_next_call, Qnil);
186 record_unwind_save_match_data ();
187 if (NILP (Voverriding_local_map_menu_flag))
189 specbind (Qoverriding_terminal_local_map, Qnil);
190 specbind (Qoverriding_local_map, Qnil);
192 set_buffer_internal_1 (XBUFFER (buffer));
194 /* TODO: for some reason this is not needed in other terms,
195 but some menu updates call Info-extract-pointer which causes
196 abort-on-error if waiting-for-input. Needs further investigation. */
197 owfi = waiting_for_input;
198 waiting_for_input = 0;
200 /* lucid hook and possible reset */
201 safe_run_hooks (Qactivate_menubar_hook);
202 if (! NILP (Vlucid_menu_bar_dirty_flag))
203 call0 (Qrecompute_lucid_menubar);
204 safe_run_hooks (Qmenu_bar_update_hook);
205 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
207 /* Now ready to go */
208 items = FRAME_MENU_BAR_ITEMS (f);
210 /* Save the frame's previous menu bar contents data */
211 if (previous_menu_items_used)
212 memcpy (previous_items, aref_addr (f->menu_bar_vector, 0),
213 previous_menu_items_used * sizeof (Lisp_Object));
215 /* parse stage 1: extract from lisp */
218 menu_items = f->menu_bar_vector;
219 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
220 submenu_start = alloca (ASIZE (items) * sizeof *submenu_start);
221 submenu_end = alloca (ASIZE (items) * sizeof *submenu_end);
222 submenu_n_panes = alloca (ASIZE (items) * sizeof *submenu_n_panes);
223 submenu_top_level_items = alloca (ASIZE (items)
224 * sizeof *submenu_top_level_items);
226 for (i = 0; i < ASIZE (items); i += 4)
228 Lisp_Object key, string, maps;
230 key = AREF (items, i);
231 string = AREF (items, i + 1);
232 maps = AREF (items, i + 2);
236 /* FIXME: we'd like to only parse the needed submenu, but this
237 was causing crashes in the _common parsing code.. need to make
238 sure proper initialization done.. */
239 /* if (submenu && strcmp ([[submenu title] UTF8String], SSDATA (string)))
242 submenu_start[i] = menu_items_used;
244 menu_items_n_panes = 0;
245 submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
246 submenu_n_panes[i] = menu_items_n_panes;
247 submenu_end[i] = menu_items_used;
251 finish_menu_items ();
252 waiting_for_input = owfi;
255 if (submenu && n == 0)
257 /* should have found a menu for this one but didn't */
258 fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
259 [[submenu title] UTF8String]);
260 discard_menu_items ();
261 unbind_to (specpdl_count, Qnil);
267 /* parse stage 2: insert into lucid 'widget_value' structures
268 [comments in other terms say not to evaluate lisp code here] */
269 wv = xmalloc_widget_value ();
270 wv->name = "menubar";
273 wv->button_type = BUTTON_TYPE_NONE;
277 for (i = 0; i < 4*n; i += 4)
279 menu_items_n_panes = submenu_n_panes[i];
280 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
281 submenu_top_level_items[i]);
285 first_wv->contents = wv;
286 /* Don't set wv->name here; GC during the loop might relocate it. */
288 wv->button_type = BUTTON_TYPE_NONE;
292 set_buffer_internal_1 (prev);
294 /* Compare the new menu items with previous, and leave off if no change */
295 /* FIXME: following other terms here, but seems like this should be
296 done before parse stage 2 above, since its results aren't used */
297 if (previous_menu_items_used
298 && (!submenu || (submenu && submenu == last_submenu))
299 && menu_items_used == previous_menu_items_used)
301 for (i = 0; i < previous_menu_items_used; i++)
302 /* FIXME: this ALWAYS fails on Buffers menu items.. something
303 about their strings causes them to change every time, so we
304 double-check failures */
305 if (!EQ (previous_items[i], AREF (menu_items, i)))
306 if (!(STRINGP (previous_items[i])
307 && STRINGP (AREF (menu_items, i))
308 && !strcmp (SSDATA (previous_items[i]),
309 SSDATA (AREF (menu_items, i)))))
311 if (i == previous_menu_items_used)
317 t += 1000*tb.time+tb.millitm;
318 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
321 free_menubar_widget_value_tree (first_wv);
322 discard_menu_items ();
323 unbind_to (specpdl_count, Qnil);
329 /* The menu items are different, so store them in the frame */
330 /* FIXME: this is not correct for single-submenu case */
331 fset_menu_bar_vector (f, menu_items);
332 f->menu_bar_items_used = menu_items_used;
334 /* Calls restore_menu_items, etc., as they were outside */
335 unbind_to (specpdl_count, Qnil);
337 /* Parse stage 2a: now GC cannot happen during the lifetime of the
338 widget_value, so it's safe to store data from a Lisp_String */
339 wv = first_wv->contents;
340 for (i = 0; i < ASIZE (items); i += 4)
343 string = AREF (items, i + 1);
347 wv->name = SSDATA (string);
348 update_submenu_strings (wv->contents);
352 /* Now, update the NS menu; if we have a submenu, use that, otherwise
353 create a new menu for each sub and fill it. */
356 const char *submenuTitle = [[submenu title] UTF8String];
357 for (wv = first_wv->contents; wv; wv = wv->next)
359 if (!strcmp (submenuTitle, wv->name))
361 [submenu fillWithWidgetValue: wv->contents];
362 last_submenu = submenu;
369 [menu fillWithWidgetValue: first_wv->contents frame: f];
375 static int n_previous_strings = 0;
376 static char previous_strings[100][10];
377 static struct frame *last_f = NULL;
381 wv = xmalloc_widget_value ();
382 wv->name = "menubar";
385 wv->button_type = BUTTON_TYPE_NONE;
389 /* Make widget-value tree w/ just the top level menu bar strings */
390 items = FRAME_MENU_BAR_ITEMS (f);
393 free_menubar_widget_value_tree (first_wv);
400 /* check if no change.. this mechanism is a bit rough, but ready */
401 n = ASIZE (items) / 4;
402 if (f == last_f && n_previous_strings == n)
404 for (i = 0; i<n; i++)
406 string = AREF (items, 4*i+1);
408 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
412 if (previous_strings[i][0])
417 else if (memcmp (previous_strings[i], SDATA (string),
418 min (10, SBYTES (string) + 1)))
424 free_menubar_widget_value_tree (first_wv);
432 for (i = 0; i < ASIZE (items); i += 4)
434 string = AREF (items, i + 1);
439 memcpy (previous_strings[i/4], SDATA (string),
440 min (10, SBYTES (string) + 1));
442 wv = xmalloc_widget_value ();
443 wv->name = SSDATA (string);
446 wv->button_type = BUTTON_TYPE_NONE;
448 wv->call_data = (void *) (intptr_t) (-1);
451 /* we'll update the real copy under app menu when time comes */
452 if (!strcmp ("Services", wv->name))
454 /* but we need to make sure it will update on demand */
455 [svcsMenu setFrame: f];
459 [menu addSubmenuWithTitle: wv->name forFrame: f];
464 first_wv->contents = wv;
470 n_previous_strings = n;
472 n_previous_strings = 0;
475 free_menubar_widget_value_tree (first_wv);
480 t += 1000*tb.time+tb.millitm;
481 fprintf (stderr, "Menu update took %ld msec.\n", t);
486 [NSApp setMainMenu: menu];
494 /* Main emacs core entry point for menubar menus: called to indicate that the
495 frame's menus have changed, and the *step representation should be updated
498 set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
500 ns_update_menubar (f, deep_p, nil);
504 x_activate_menubar (struct frame *f)
507 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
508 ns_update_menubar (f, true, nil);
509 ns_check_pending_open_menu ();
517 /* ==========================================================================
519 Menu: class implementation
521 ========================================================================== */
524 /* Menu that can define itself from Emacs "widget_value"s and will lazily
525 update itself when user clicked. Based on Carbon/AppKit implementation
526 by Yamamoto Mitsuharu. */
527 @implementation EmacsMenu
529 /* override designated initializer */
530 - initWithTitle: (NSString *)title
533 if ((self = [super initWithTitle: title]))
534 [self setAutoenablesItems: NO];
539 /* used for top-level */
540 - initWithTitle: (NSString *)title frame: (struct frame *)f
542 [self initWithTitle: title];
545 [self setDelegate: self];
551 - (void)setFrame: (struct frame *)f
557 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
558 extern NSString *NSMenuDidBeginTrackingNotification;
563 -(void)trackingNotification:(NSNotification *)notification
565 /* Update menu in menuNeedsUpdate only while tracking menus. */
566 trackingMenu = ([notification name] == NSMenuDidBeginTrackingNotification
568 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
569 if (! trackingMenu) ns_check_menu_open (nil);
573 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
574 - (void)menuWillOpen:(NSMenu *)menu
578 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
579 // On 10.6 we get repeated calls, only the one for NSSystemDefined is "real".
580 if ([[NSApp currentEvent] type] != NSSystemDefined) return;
583 /* When dragging from one menu to another, we get willOpen followed by didClose,
584 i.e. trackingMenu == 3 in willOpen and then 2 after didClose.
585 We have updated all menus, so avoid doing it when trackingMenu == 3. */
586 if (trackingMenu == 2)
587 ns_check_menu_open (menu);
590 - (void)menuDidClose:(NSMenu *)menu
594 #endif /* OSX >= 10.5 */
596 #endif /* NS_IMPL_COCOA */
598 /* delegate method called when a submenu is being opened: run a 'deep' call
599 to set_frame_menubar */
600 - (void)menuNeedsUpdate: (NSMenu *)menu
602 if (!FRAME_LIVE_P (frame))
605 /* Cocoa/Carbon will request update on every keystroke
606 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
607 since key equivalents are handled through emacs.
608 On Leopard, even keystroke events generate SystemDefined event.
609 Third-party applications that enhance mouse / trackpad
610 interaction, or also VNC/Remote Desktop will send events
611 of type AppDefined rather than SysDefined.
612 Menus will fail to show up if they haven't been initialized.
613 AppDefined events may lack timing data.
615 Thus, we rely on the didBeginTrackingNotification notification
616 as above to indicate the need for updates.
617 From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
618 key press case, NSMenuPropertyItemImage (e.g.) won't be set.
620 if (trackingMenu == 0)
622 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
623 #if (! defined (NS_IMPL_COCOA) \
624 || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
625 /* Don't know how to do this for anything other than OSX >= 10.5
626 This is wrong, as it might run Lisp code in the event loop. */
627 ns_update_menubar (frame, true, self);
632 - (BOOL)performKeyEquivalent: (NSEvent *)theEvent
634 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
635 && FRAME_NS_VIEW (SELECTED_FRAME ()))
636 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
641 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
642 into an accelerator string. We are only able to display a single character
643 for an accelerator, together with an optional modifier combination. (Under
644 Carbon more control was possible, but in Cocoa multi-char strings passed to
645 NSMenuItem get ignored. For now we try to display a super-single letter
646 combo, and return the others as strings to be appended to the item title.
647 (This is signaled by setting keyEquivModMask to 0 for now.) */
648 -(NSString *)parseKeyEquiv: (const char *)key
650 const char *tpos = key;
651 keyEquivModMask = NSCommandKeyMask;
653 if (!key || !strlen (key))
656 while (*tpos == ' ' || *tpos == '(')
658 if ((*tpos == 's') && (*(tpos+1) == '-'))
660 return [NSString stringWithFormat: @"%c", tpos[2]];
662 keyEquivModMask = 0; /* signal */
663 return [NSString stringWithUTF8String: tpos];
667 - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
670 widget_value *wv = (widget_value *)wvptr;
672 if (menu_separator_name_p (wv->name))
674 item = [NSMenuItem separatorItem];
675 [self addItem: item];
679 NSString *title, *keyEq;
680 title = [NSString stringWithUTF8String: wv->name];
682 title = @"< ? >"; /* (get out in the open so we know about it) */
684 keyEq = [self parseKeyEquiv: wv->key];
686 /* OS X just ignores modifier strings longer than one character */
687 if (keyEquivModMask == 0)
688 title = [title stringByAppendingFormat: @" (%@)", keyEq];
691 item = [self addItemWithTitle: (NSString *)title
692 action: @selector (menuDown:)
693 keyEquivalent: keyEq];
694 [item setKeyEquivalentModifierMask: keyEquivModMask];
696 [item setEnabled: wv->enabled];
698 /* Draw radio buttons and tickboxes */
699 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
700 wv->button_type == BUTTON_TYPE_RADIO))
701 [item setState: NSOnState];
703 [item setState: NSOffState];
705 [item setTag: (NSInteger)wv->call_data];
717 for (n = [self numberOfItems]-1; n >= 0; n--)
719 NSMenuItem *item = [self itemAtIndex: n];
720 NSString *title = [item title];
721 if (([title length] == 0 /* OSX 10.5 */
722 || [ns_app_name isEqualToString: title] /* from 10.6 on */
723 || [@"Apple" isEqualToString: title]) /* older */
724 && ![item isSeparatorItem])
726 [self removeItemAtIndex: n];
731 - (void)fillWithWidgetValue: (void *)wvptr
733 [self fillWithWidgetValue: wvptr frame: (struct frame *)nil];
736 - (void)fillWithWidgetValue: (void *)wvptr frame: (struct frame *)f
738 widget_value *wv = (widget_value *)wvptr;
740 /* clear existing contents */
741 [self setMenuChangedMessagesEnabled: NO];
744 /* add new contents */
745 for (; wv != NULL; wv = wv->next)
747 NSMenuItem *item = [self addItemWithWidgetValue: wv];
754 submenu = [[EmacsMenu alloc] initWithTitle: [item title] frame:f];
756 submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
758 [self setSubmenu: submenu forItem: item];
759 [submenu fillWithWidgetValue: wv->contents];
761 [item setAction: (SEL)nil];
765 [self setMenuChangedMessagesEnabled: YES];
766 #ifdef NS_IMPL_GNUSTEP
767 if ([[self window] isVisible])
773 /* adds an empty submenu and returns it */
774 - (EmacsMenu *)addSubmenuWithTitle: (const char *)title forFrame: (struct frame *)f
776 NSString *titleStr = [NSString stringWithUTF8String: title];
777 NSMenuItem *item = [self addItemWithTitle: titleStr
778 action: (SEL)nil /*@selector (menuDown:) */
780 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
781 [self setSubmenu: submenu forItem: item];
786 /* run a menu in popup mode */
787 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
788 keymaps: (bool)keymaps
790 EmacsView *view = FRAME_NS_VIEW (f);
794 /* p = [view convertPoint:p fromView: nil]; */
795 p.y = NSHeight ([view frame]) - p.y;
796 e = [[view window] currentEvent];
797 event = [NSEvent mouseEventWithType: NSRightMouseDown
800 timestamp: [e timestamp]
801 windowNumber: [[view window] windowNumber]
803 eventNumber: 0/*[e eventNumber] */
807 context_menu_value = -1;
808 [NSMenu popUpContextMenu: self withEvent: event forView: view];
809 retVal = context_menu_value;
810 context_menu_value = 0;
812 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
820 /* ==========================================================================
822 Context Menu: implementing functions
824 ========================================================================== */
827 ns_menu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
828 Lisp_Object title, const char **error)
833 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
834 widget_value *wv, *first_wv = 0;
840 /* now parse stage 2 as in ns_update_menubar */
841 wv = xmalloc_widget_value ();
842 wv->name = "contextmenu";
845 wv->button_type = BUTTON_TYPE_NONE;
850 /* FIXME: a couple of one-line differences prevent reuse */
851 wv = digest_single_submenu (0, menu_items_used, 0);
854 widget_value *save_wv = 0, *prev_wv = 0;
855 widget_value **submenu_stack
856 = alloca (menu_items_used * sizeof *submenu_stack);
857 /* Lisp_Object *subprefix_stack
858 = alloca (menu_items_used * sizeof *subprefix_stack); */
859 int submenu_depth = 0;
863 /* Loop over all panes and items, filling in the tree. */
865 while (i < menu_items_used)
867 if (EQ (AREF (menu_items, i), Qnil))
869 submenu_stack[submenu_depth++] = save_wv;
875 else if (EQ (AREF (menu_items, i), Qlambda))
878 save_wv = submenu_stack[--submenu_depth];
882 else if (EQ (AREF (menu_items, i), Qt)
883 && submenu_depth != 0)
884 i += MENU_ITEMS_PANE_LENGTH;
885 /* Ignore a nil in the item list.
886 It's meaningful only for dialog boxes. */
887 else if (EQ (AREF (menu_items, i), Qquote))
889 else if (EQ (AREF (menu_items, i), Qt))
891 /* Create a new pane. */
892 Lisp_Object pane_name, prefix;
893 const char *pane_string;
895 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
896 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
898 #ifndef HAVE_MULTILINGUAL_MENU
899 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
901 pane_name = ENCODE_MENU_STRING (pane_name);
902 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
905 pane_string = (NILP (pane_name)
906 ? "" : SSDATA (pane_name));
907 /* If there is just one top-level pane, put all its items directly
908 under the top-level menu. */
909 if (menu_items_n_panes == 1)
912 /* If the pane has a meaningful name,
913 make the pane a top-level menu item
914 with its items as a submenu beneath it. */
915 if (!keymaps && strcmp (pane_string, ""))
917 wv = xmalloc_widget_value ();
921 first_wv->contents = wv;
922 wv->name = pane_string;
923 if (keymaps && !NILP (prefix))
927 wv->button_type = BUTTON_TYPE_NONE;
938 i += MENU_ITEMS_PANE_LENGTH;
942 /* Create a new item within current pane. */
943 Lisp_Object item_name, enable, descrip, def, type, selected, help;
944 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
945 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
946 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
947 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
948 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
949 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
950 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
952 #ifndef HAVE_MULTILINGUAL_MENU
953 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
955 item_name = ENCODE_MENU_STRING (item_name);
956 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
959 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
961 descrip = ENCODE_MENU_STRING (descrip);
962 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
964 #endif /* not HAVE_MULTILINGUAL_MENU */
966 wv = xmalloc_widget_value ();
970 save_wv->contents = wv;
971 wv->name = SSDATA (item_name);
973 wv->key = SSDATA (descrip);
975 /* If this item has a null value,
976 make the call_data null so that it won't display a box
977 when the mouse is on it. */
978 wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
979 wv->enabled = !NILP (enable);
982 wv->button_type = BUTTON_TYPE_NONE;
983 else if (EQ (type, QCtoggle))
984 wv->button_type = BUTTON_TYPE_TOGGLE;
985 else if (EQ (type, QCradio))
986 wv->button_type = BUTTON_TYPE_RADIO;
990 wv->selected = !NILP (selected);
992 if (! STRINGP (help))
999 i += MENU_ITEMS_ITEM_LENGTH;
1007 widget_value *wv_title = xmalloc_widget_value ();
1008 widget_value *wv_sep = xmalloc_widget_value ();
1010 /* Maybe replace this separator with a bitmap or owner-draw item
1011 so that it looks better. Having two separators looks odd. */
1012 wv_sep->name = "--";
1013 wv_sep->next = first_wv->contents;
1014 wv_sep->help = Qnil;
1016 #ifndef HAVE_MULTILINGUAL_MENU
1017 if (STRING_MULTIBYTE (title))
1018 title = ENCODE_MENU_STRING (title);
1021 wv_title->name = SSDATA (title);
1022 wv_title->enabled = NO;
1023 wv_title->button_type = BUTTON_TYPE_NONE;
1024 wv_title->help = Qnil;
1025 wv_title->next = wv_sep;
1026 first_wv->contents = wv_title;
1029 pmenu = [[EmacsMenu alloc] initWithTitle:
1030 [NSString stringWithUTF8String: SSDATA (title)]];
1031 [pmenu fillWithWidgetValue: first_wv->contents];
1032 free_menubar_widget_value_tree (first_wv);
1033 unbind_to (specpdl_count, Qnil);
1035 popup_activated_flag = 1;
1036 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
1037 popup_activated_flag = 0;
1038 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1045 /* ==========================================================================
1047 Toolbar: externally-called functions
1049 ========================================================================== */
1052 free_frame_tool_bar (struct frame *f)
1053 /* --------------------------------------------------------------------------
1054 Under NS we just hide the toolbar until it might be needed again.
1055 -------------------------------------------------------------------------- */
1057 EmacsView *view = FRAME_NS_VIEW (f);
1059 view->wait_for_tool_bar = NO;
1060 [[view toolbar] setVisible: NO];
1061 FRAME_TOOLBAR_HEIGHT (f) = 0;
1066 update_frame_tool_bar (struct frame *f)
1067 /* --------------------------------------------------------------------------
1068 Update toolbar contents
1069 -------------------------------------------------------------------------- */
1072 EmacsView *view = FRAME_NS_VIEW (f);
1073 NSWindow *window = [view window];
1074 EmacsToolbar *toolbar = [view toolbar];
1076 if (view == nil || toolbar == nil) return;
1079 #ifdef NS_IMPL_COCOA
1080 [toolbar clearActive];
1085 /* update EmacsToolbar as in GtkUtils, build items list */
1086 for (i = 0; i < f->n_tool_bar_items; ++i)
1088 #define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1089 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1091 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1096 Lisp_Object helpObj;
1097 const char *helpText;
1099 /* Check if this is a separator. */
1100 if (EQ (TOOLPROP (TOOL_BAR_ITEM_TYPE), Qt))
1102 /* Skip separators. Newer OSX don't show them, and on GNUstep they
1103 are wide as a button, thus overflowing the toolbar most of
1108 /* If image is a vector, choose the image according to the
1110 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1111 if (VECTORP (image))
1113 /* NS toolbar auto-computes disabled and selected images */
1114 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1115 eassert (ASIZE (image) >= idx);
1116 image = AREF (image, idx);
1122 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1124 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1125 helpText = NILP (helpObj) ? "" : SSDATA (helpObj);
1127 /* Ignore invalid image specifications. */
1128 if (!valid_image_p (image))
1130 /* Don't log anything, GNUS makes invalid images all the time. */
1134 img_id = lookup_image (f, image);
1135 img = IMAGE_FROM_ID (f, img_id);
1136 prepare_image_for_display (f, img);
1138 if (img->load_failed_p || img->pixmap == nil)
1140 NSLog (@"Could not prepare toolbar image for display.");
1144 [toolbar addDisplayItemWithImage: img->pixmap
1148 enabled: enabled_p];
1152 if (![toolbar isVisible])
1153 [toolbar setVisible: YES];
1155 #ifdef NS_IMPL_COCOA
1156 if ([toolbar changed])
1158 /* inform app that toolbar has changed */
1159 NSDictionary *dict = [toolbar configurationDictionary];
1160 NSMutableDictionary *newDict = [dict mutableCopy];
1161 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1163 while ((key = [keys nextObject]) != nil)
1165 NSObject *val = [dict objectForKey: key];
1166 if ([val isKindOfClass: [NSArray class]])
1169 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1174 [toolbar setConfigurationFromDictionary: newDict];
1179 FRAME_TOOLBAR_HEIGHT (f) =
1180 NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1181 - FRAME_NS_TITLEBAR_HEIGHT (f);
1182 if (FRAME_TOOLBAR_HEIGHT (f) < 0) // happens if frame is fullscreen.
1183 FRAME_TOOLBAR_HEIGHT (f) = 0;
1185 if (view->wait_for_tool_bar && FRAME_TOOLBAR_HEIGHT (f) > 0)
1186 [view setNeedsDisplay: YES];
1192 /* ==========================================================================
1194 Toolbar: class implementation
1196 ========================================================================== */
1198 @implementation EmacsToolbar
1200 - initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1202 self = [super initWithIdentifier: identifier];
1204 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1205 [self setSizeMode: NSToolbarSizeModeSmall];
1206 [self setDelegate: self];
1207 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1208 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1209 prevIdentifiers = nil;
1210 prevEnablement = enablement = 0L;
1216 [prevIdentifiers release];
1217 [activeIdentifiers release];
1218 [identifierToItem release];
1222 - (void) clearActive
1224 [prevIdentifiers release];
1225 prevIdentifiers = [activeIdentifiers copy];
1226 [activeIdentifiers removeAllObjects];
1227 prevEnablement = enablement;
1234 while ([[self items] count] > 0)
1235 [self removeItemAtIndex: 0];
1240 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1241 enablement == prevEnablement ? NO : YES;
1244 - (void) addDisplayItemWithImage: (EmacsImage *)img
1247 helpText: (const char *)help
1248 enabled: (BOOL)enabled
1250 /* 1) come up w/identifier */
1251 NSString *identifier
1252 = [NSString stringWithFormat: @"%lu", (unsigned long)[img hash]];
1253 [activeIdentifiers addObject: identifier];
1255 /* 2) create / reuse item */
1256 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1259 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1261 [item setImage: img];
1262 [item setToolTip: [NSString stringWithUTF8String: help]];
1263 [item setTarget: emacsView];
1264 [item setAction: @selector (toolbarClicked:)];
1265 [identifierToItem setObject: item forKey: identifier];
1268 #ifdef NS_IMPL_GNUSTEP
1269 [self insertItemWithItemIdentifier: identifier atIndex: idx];
1273 [item setEnabled: enabled];
1275 /* 3) update state */
1276 enablement = (enablement << 1) | (enabled == YES);
1279 /* This overrides super's implementation, which automatically sets
1280 all items to enabled state (for some reason). */
1281 - (void)validateVisibleItems
1286 /* delegate methods */
1288 - (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1289 itemForItemIdentifier: (NSString *)itemIdentifier
1290 willBeInsertedIntoToolbar: (BOOL)flag
1292 /* look up NSToolbarItem by identifier and return... */
1293 return [identifierToItem objectForKey: itemIdentifier];
1296 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1298 /* return entire set.. */
1299 return activeIdentifiers;
1302 /* for configuration palette (not yet supported) */
1303 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1305 /* return entire set... */
1306 return activeIdentifiers;
1307 //return [identifierToItem allKeys];
1310 /* optional and unneeded */
1311 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1312 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1313 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1315 @end /* EmacsToolbar */
1319 /* ==========================================================================
1321 Tooltip: class implementation
1323 ========================================================================== */
1325 /* Needed because NeXTstep does not provide enough control over tooltip
1327 @implementation EmacsTooltip
1331 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1332 blue: 0.792 alpha: 0.95];
1333 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1334 NSFont *sfont = [font screenFont];
1335 int height = [sfont ascender] - [sfont descender];
1336 /*[font boundingRectForFont].size.height; */
1337 NSRect r = NSMakeRect (0, 0, 100, height+6);
1339 textField = [[NSTextField alloc] initWithFrame: r];
1340 [textField setFont: font];
1341 [textField setBackgroundColor: col];
1343 [textField setEditable: NO];
1344 [textField setSelectable: NO];
1345 [textField setBordered: NO];
1346 [textField setBezeled: NO];
1347 [textField setDrawsBackground: YES];
1349 win = [[NSWindow alloc]
1350 initWithContentRect: [textField frame]
1352 backing: NSBackingStoreBuffered
1354 [win setHasShadow: YES];
1355 [win setReleasedWhenClosed: NO];
1356 [win setDelegate: self];
1357 [[win contentView] addSubview: textField];
1358 /* [win setBackgroundColor: col]; */
1359 [win setOpaque: NO];
1368 [textField release];
1372 - (void) setText: (char *)text
1374 NSString *str = [NSString stringWithUTF8String: text];
1375 NSRect r = [textField frame];
1378 [textField setStringValue: str];
1379 tooltipDims = [[textField cell] cellSize];
1381 r.size.width = tooltipDims.width;
1382 r.size.height = tooltipDims.height;
1383 [textField setFrame: r];
1386 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
1388 NSRect wr = [win frame];
1390 wr.origin = NSMakePoint (x, y);
1391 wr.size = [textField frame].size;
1393 [win setFrame: wr display: YES];
1394 [win setLevel: NSPopUpMenuWindowLevel];
1395 [win orderFront: self];
1397 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1398 selector: @selector (hide)
1399 userInfo: nil repeats: NO];
1408 if ([timer isValid])
1417 return timer != nil;
1422 return [textField frame];
1425 @end /* EmacsTooltip */
1429 /* ==========================================================================
1431 Popup Dialog: implementing functions
1433 ========================================================================== */
1437 NSAutoreleasePool *pool;
1438 EmacsDialogPanel *dialog;
1442 pop_down_menu (void *arg)
1444 struct Popdown_data *unwind_data = arg;
1447 if (popup_activated_flag)
1449 EmacsDialogPanel *panel = unwind_data->dialog;
1450 popup_activated_flag = 0;
1452 [unwind_data->pool release];
1453 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1456 xfree (unwind_data);
1462 ns_popup_dialog (Lisp_Object position, Lisp_Object header, Lisp_Object contents)
1465 Lisp_Object window, tem, title;
1469 NSAutoreleasePool *pool;
1471 NSTRACE (x-popup-dialog);
1473 isQ = NILP (header);
1475 if (EQ (position, Qt)
1476 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1477 || EQ (XCAR (position), Qtool_bar))))
1479 window = selected_window;
1481 else if (CONSP (position))
1484 tem = Fcar (position);
1485 if (XTYPE (tem) == Lisp_Cons)
1486 window = Fcar (Fcdr (position));
1489 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1490 window = Fcar (tem); /* POSN_WINDOW (tem) */
1493 else if (WINDOWP (position) || FRAMEP (position))
1500 if (FRAMEP (window))
1501 f = XFRAME (window);
1502 else if (WINDOWP (window))
1504 CHECK_LIVE_WINDOW (window);
1505 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1508 CHECK_WINDOW (window);
1510 check_window_system (f);
1512 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1513 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
1515 title = Fcar (contents);
1516 CHECK_STRING (title);
1518 if (NILP (Fcar (Fcdr (contents))))
1519 /* No buttons specified, add an "Ok" button so users can pop down
1521 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
1524 pool = [[NSAutoreleasePool alloc] init];
1525 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1529 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1530 struct Popdown_data *unwind_data = xmalloc (sizeof (*unwind_data));
1532 unwind_data->pool = pool;
1533 unwind_data->dialog = dialog;
1535 record_unwind_protect_ptr (pop_down_menu, unwind_data);
1536 popup_activated_flag = 1;
1537 tem = [dialog runDialogAt: p];
1538 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1547 /* ==========================================================================
1549 Popup Dialog: class implementation
1551 ========================================================================== */
1553 @interface FlippedView : NSView
1558 @implementation FlippedView
1565 @implementation EmacsDialogPanel
1568 #define ICONSIZE 64.0
1569 #define TEXTHEIGHT 20.0
1570 #define MINCELLWIDTH 90.0
1572 - initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
1573 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1575 NSSize spacing = {SPACER, SPACER};
1578 NSImageView *imgView;
1579 FlippedView *contentView;
1582 dialog_return = Qundefined;
1583 button_values = NULL;
1584 area.origin.x = 3*SPACER;
1585 area.origin.y = 2*SPACER;
1586 area.size.width = ICONSIZE;
1587 area.size.height= ICONSIZE;
1588 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1589 [img setScalesWhenResized: YES];
1590 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1591 imgView = [[NSImageView alloc] initWithFrame: area];
1592 [imgView setImage: img];
1593 [imgView setEditable: NO];
1595 [imgView autorelease];
1597 aStyle = NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask;
1601 [super initWithContentRect: contentRect styleMask: aStyle
1602 backing: backingType defer: flag];
1603 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1604 [contentView autorelease];
1606 [self setContentView: contentView];
1608 [[self contentView] setAutoresizesSubviews: YES];
1610 [[self contentView] addSubview: imgView];
1611 [self setTitle: @""];
1613 area.origin.x += ICONSIZE+2*SPACER;
1614 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1615 area.size.width = 400;
1616 area.size.height= TEXTHEIGHT;
1617 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1618 [[self contentView] addSubview: command];
1619 [command setStringValue: ns_app_name];
1620 [command setDrawsBackground: NO];
1621 [command setBezeled: NO];
1622 [command setSelectable: NO];
1623 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1625 /* area.origin.x = ICONSIZE+2*SPACER;
1626 area.origin.y = TEXTHEIGHT + 2*SPACER;
1627 area.size.width = 400;
1628 area.size.height= 2;
1629 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1630 [[self contentView] addSubview: tem];
1631 [tem setTitlePosition: NSNoTitle];
1632 [tem setAutoresizingMask: NSViewWidthSizable];*/
1634 /* area.origin.x = ICONSIZE+2*SPACER; */
1635 area.origin.y += TEXTHEIGHT+SPACER;
1636 area.size.width = 400;
1637 area.size.height= TEXTHEIGHT;
1638 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1639 [[self contentView] addSubview: title];
1640 [title setDrawsBackground: NO];
1641 [title setBezeled: NO];
1642 [title setSelectable: NO];
1643 [title setFont: [NSFont systemFontOfSize: 11.0]];
1645 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1646 [cell setBordered: NO];
1647 [cell setEnabled: NO];
1648 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1649 [cell setBezelStyle: NSRoundedBezelStyle];
1651 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1652 mode: NSHighlightModeMatrix
1655 numberOfColumns: 1];
1656 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1657 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1658 [matrix setIntercellSpacing: spacing];
1659 [matrix autorelease];
1661 [[self contentView] addSubview: matrix];
1662 [self setOneShot: YES];
1663 [self setReleasedWhenClosed: YES];
1664 [self setHidesOnDeactivate: YES];
1669 - (BOOL)windowShouldClose: (id)sender
1671 window_closed = YES;
1678 xfree (button_values);
1682 - (void)process_dialog: (Lisp_Object) list
1684 Lisp_Object item, lst = list;
1686 int buttons = 0, btnnr = 0;
1688 for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
1691 if (XTYPE (item) == Lisp_Cons)
1696 button_values = xmalloc (buttons * sizeof *button_values);
1698 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1701 if (XTYPE (item) == Lisp_String)
1703 [self addString: SSDATA (item) row: row++];
1705 else if (XTYPE (item) == Lisp_Cons)
1707 button_values[btnnr] = XCDR (item);
1708 [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
1711 else if (NILP (item))
1720 - (void)addButton: (char *)str value: (int)tag row: (int)row
1729 cell = [matrix cellAtRow: row column: cols-1];
1730 [cell setTarget: self];
1731 [cell setAction: @selector (clicked: )];
1732 [cell setTitle: [NSString stringWithUTF8String: str]];
1734 [cell setBordered: YES];
1735 [cell setEnabled: YES];
1739 - (void)addString: (char *)str row: (int)row
1748 cell = [matrix cellAtRow: row column: cols-1];
1749 [cell setTitle: [NSString stringWithUTF8String: str]];
1750 [cell setBordered: YES];
1751 [cell setEnabled: NO];
1762 - (void)clicked: sender
1764 NSArray *sellist = nil;
1767 sellist = [sender selectedCells];
1768 if ([sellist count] < 1)
1771 seltag = [[sellist objectAtIndex: 0] tag];
1772 dialog_return = button_values[seltag];
1777 - initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1782 if (XTYPE (contents) == Lisp_Cons)
1784 head = Fcar (contents);
1785 [self process_dialog: Fcdr (contents)];
1790 if (XTYPE (head) == Lisp_String)
1791 [title setStringValue:
1792 [NSString stringWithUTF8String: SSDATA (head)]];
1793 else if (isQ == YES)
1794 [title setStringValue: @"Question"];
1796 [title setStringValue: @"Information"];
1802 if (cols == 1 && rows > 1) /* Never told where to split */
1805 for (i = 0; i < rows/2; i++)
1807 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1808 atRow: i column: 1];
1809 [matrix removeRow: (rows+1)/2];
1815 NSSize csize = [matrix cellSize];
1816 if (csize.width < MINCELLWIDTH)
1818 csize.width = MINCELLWIDTH;
1819 [matrix setCellSize: csize];
1820 [matrix sizeToCells];
1825 [command sizeToFit];
1829 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1831 t.origin.x = r.origin.x;
1832 t.size.width = r.size.width;
1834 r = [command frame];
1835 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1837 t.origin.x = r.origin.x;
1838 t.size.width = r.size.width;
1842 s = [(NSView *)[self contentView] frame];
1843 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1844 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1845 [self setFrame: r display: NO];
1853 - (void)timeout_handler: (NSTimer *)timedEntry
1855 NSEvent *nxev = [NSEvent otherEventWithType: NSApplicationDefined
1856 location: NSMakePoint (0, 0)
1859 windowNumber: [[NSApp mainWindow] windowNumber]
1860 context: [NSApp context]
1866 /* We use sto because stopModal/abortModal out of the main loop does not
1867 seem to work in 10.6. But as we use stop we must send a real event so
1868 the stop is seen and acted upon. */
1870 [NSApp postEvent: nxev atStart: NO];
1873 - (Lisp_Object)runDialogAt: (NSPoint)p
1875 Lisp_Object ret = Qundefined;
1877 while (popup_activated_flag)
1880 struct timespec next_time = timer_check ();
1882 if (timespec_valid_p (next_time))
1884 double time = timespectod (next_time);
1885 tmo = [NSTimer timerWithTimeInterval: time
1887 selector: @selector (timeout_handler:)
1890 [[NSRunLoop currentRunLoop] addTimer: tmo
1891 forMode: NSModalPanelRunLoopMode];
1894 dialog_return = Qundefined;
1895 [NSApp runModalForWindow: self];
1896 ret = dialog_return;
1899 if (tmo != nil) [tmo invalidate]; /* Cancels timer */
1904 if (EQ (ret, Qundefined) && window_closed)
1905 /* Make close button pressed equivalent to C-g. */
1906 Fsignal (Qquit, Qnil);
1914 /* ==========================================================================
1918 ========================================================================== */
1920 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1921 doc: /* Cause the NS menu to be re-calculated. */)
1924 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1929 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1930 doc: /* Return t if a menu or popup dialog is active. */)
1933 return popup_activated () ? Qt : Qnil;
1936 /* ==========================================================================
1938 Lisp interface declaration
1940 ========================================================================== */
1943 syms_of_nsmenu (void)
1945 #ifndef NS_IMPL_COCOA
1946 /* Don't know how to keep track of this in Next/Open/GNUstep. Always
1947 update menus there. */
1950 defsubr (&Sns_reset_menu);
1951 defsubr (&Smenu_or_popup_active_p);
1953 Qdebug_on_next_call = intern_c_string ("debug-on-next-call");
1954 staticpro (&Qdebug_on_next_call);