Merge from emacs-24; up to 2013-01-02T10:15:31Z!michael.albinus@gmx.de
[bpt/emacs.git] / src / nsmenu.m
CommitLineData
edfda783 1/* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
ab422c4d 2 Copyright (C) 2007-2013 Free Software Foundation, Inc.
edfda783
AR
3
4This file is part of GNU Emacs.
5
32d235f8 6GNU Emacs is free software: you can redistribute it and/or modify
edfda783 7it under the terms of the GNU General Public License as published by
32d235f8
GM
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
edfda783
AR
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
32d235f8 17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
edfda783 18
32d235f8 19/*
edfda783
AR
20By Adrian Robert, based on code from original nsmenu.m (Carl Edman,
21Christian Limpach, Scott Bender, Christophe de Dinechin) and code in the
22Carbon version by Yamamoto Mitsuharu. */
23
5a06864f
AR
24/* This should be the first include, as it may set up #defines affecting
25 interpretation of even the system includes. */
b29116ef 26#include <config.h>
5a06864f 27
edfda783
AR
28#include "lisp.h"
29#include "window.h"
e5560ff7 30#include "character.h"
edfda783
AR
31#include "buffer.h"
32#include "keymap.h"
33#include "coding.h"
34#include "commands.h"
35#include "blockinput.h"
36#include "nsterm.h"
37#include "termhooks.h"
38#include "keyboard.h"
ef7417fd 39#include "menu.h"
edfda783 40
cbe0b5bf
AR
41#define NSMENUPROFILE 0
42
43#if NSMENUPROFILE
edfda783
AR
44#include <sys/timeb.h>
45#include <sys/types.h>
cbe0b5bf 46#endif
edfda783 47
edfda783
AR
48#if 0
49int menu_trace_num = 0;
50#define NSTRACE(x) fprintf (stderr, "%s:%d: [%d] " #x "\n", \
51 __FILE__, __LINE__, ++menu_trace_num)
52#else
53#define NSTRACE(x)
54#endif
55
56#if 0
57/* Include lisp -> C common menu parsing code */
58#define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
59#include "nsmenu_common.c"
60#endif
61
edfda783
AR
62extern Lisp_Object Qundefined, Qmenu_enable, Qmenu_bar_update_hook;
63extern Lisp_Object QCtoggle, QCradio;
64
edfda783 65Lisp_Object Qdebug_on_next_call;
fb9d0f5a 66extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
edfda783
AR
67
68extern long context_menu_value;
4e622592 69EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
edfda783 70
07b87a10
AR
71/* Nonzero means a menu is currently active. */
72static int popup_activated_flag;
73
5fecd5fc
JD
74/* Nonzero means we are tracking and updating menus. */
75static int trackingMenu;
76
77
edfda783
AR
78/* NOTE: toolbar implementation is at end,
79 following complete menu implementation. */
80
81
82/* ==========================================================================
83
84 Menu: Externally-called functions
85
86 ========================================================================== */
87
88
edfda783
AR
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. */
92void
93free_frame_menubar (struct frame *f)
94{
95 return;
96}
97
98
07b87a10 99int
3d608a86 100popup_activated (void)
07b87a10
AR
101{
102 return popup_activated_flag;
103}
104
105
edfda783
AR
106/* --------------------------------------------------------------------------
107 Update menubar. Three cases:
18e27ea8 108 1) ! deep_p, submenu = nil: Fresh switch onto a frame -- either set up
edfda783 109 just top-level menu strings (OS X), or goto case (2) (GNUstep).
18e27ea8
PE
110 2) deep_p, submenu = nil: Recompute all submenus.
111 3) deep_p, submenu = non-nil: Update contents of a single submenu.
edfda783 112 -------------------------------------------------------------------------- */
c0342369 113static void
18e27ea8 114ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
edfda783
AR
115{
116 NSAutoreleasePool *pool;
117 id menu = [NSApp mainMenu];
118 static EmacsMenu *last_submenu = nil;
119 BOOL needsSet = NO;
9fa1de30 120 bool owfi;
edfda783
AR
121 Lisp_Object items;
122 widget_value *wv, *first_wv, *prev_wv = 0;
123 int i;
124
cbe0b5bf 125#if NSMENUPROFILE
edfda783
AR
126 struct timeb tb;
127 long t;
128#endif
129
6d01f1fe 130 NSTRACE (ns_update_menubar);
edfda783
AR
131
132 if (f != SELECTED_FRAME ())
133 return;
134 XSETFRAME (Vmenu_updating_frame, f);
135/*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
136
4d7e6e51 137 block_input ();
edfda783
AR
138 pool = [[NSAutoreleasePool alloc] init];
139
140 /* Menu may have been created automatically; if so, discard it. */
141 if ([menu isKindOfClass: [EmacsMenu class]] == NO)
142 {
143 [menu release];
144 menu = nil;
145 }
146
147 if (menu == nil)
148 {
3988c7d6 149 menu = [[EmacsMenu alloc] initWithTitle: ns_app_name];
edfda783
AR
150 needsSet = YES;
151 }
152 else
153 { /* close up anything on there */
154 id attMenu = [menu attachedMenu];
155 if (attMenu != nil)
156 [attMenu close];
157 }
158
cbe0b5bf 159#if NSMENUPROFILE
edfda783
AR
160 ftime (&tb);
161 t = -(1000*tb.time+tb.millitm);
162#endif
163
edfda783
AR
164#ifdef NS_IMPL_GNUSTEP
165 deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
166#endif
167
168 if (deep_p)
169 {
170 /* Fully parse one or more of the submenus. */
171 int n = 0;
172 int *submenu_start, *submenu_end;
7cded46f
PE
173 bool *submenu_top_level_items;
174 int *submenu_n_panes;
edfda783
AR
175 struct buffer *prev = current_buffer;
176 Lisp_Object buffer;
d311d28c 177 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
edfda783
AR
178 int previous_menu_items_used = f->menu_bar_items_used;
179 Lisp_Object *previous_items
38182d90 180 = alloca (previous_menu_items_used * sizeof *previous_items);
edfda783
AR
181
182 /* lisp preliminaries */
e74aeda8 183 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
edfda783
AR
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))
188 {
189 specbind (Qoverriding_terminal_local_map, Qnil);
190 specbind (Qoverriding_local_map, Qnil);
191 }
192 set_buffer_internal_1 (XBUFFER (buffer));
193
df2142db
AR
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. */
edfda783
AR
197 owfi = waiting_for_input;
198 waiting_for_input = 0;
199
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);
f00af5b1 205 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
edfda783
AR
206
207 /* Now ready to go */
208 items = FRAME_MENU_BAR_ITEMS (f);
209
210 /* Save the frame's previous menu bar contents data */
211 if (previous_menu_items_used)
e69b0960 212 memcpy (previous_items, aref_addr (f->menu_bar_vector, 0),
72af86bd 213 previous_menu_items_used * sizeof (Lisp_Object));
edfda783
AR
214
215 /* parse stage 1: extract from lisp */
216 save_menu_items ();
217
e69b0960 218 menu_items = f->menu_bar_vector;
edfda783 219 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
38182d90
PE
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);
edfda783 225 init_menu_items ();
96fb4434 226 for (i = 0; i < ASIZE (items); i += 4)
edfda783
AR
227 {
228 Lisp_Object key, string, maps;
229
96fb4434
PE
230 key = AREF (items, i);
231 string = AREF (items, i + 1);
232 maps = AREF (items, i + 2);
edfda783
AR
233 if (NILP (string))
234 break;
235
df2142db
AR
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.. */
e6076b1b 239/* if (submenu && strcmp ([[submenu title] UTF8String], SSDATA (string)))
edfda783
AR
240 continue; */
241
242 submenu_start[i] = menu_items_used;
243
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;
248 n++;
249 }
250
251 finish_menu_items ();
252 waiting_for_input = owfi;
253
254
255 if (submenu && n == 0)
256 {
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",
e6076b1b 259 [[submenu title] UTF8String]);
edfda783
AR
260 discard_menu_items ();
261 unbind_to (specpdl_count, Qnil);
262 [pool release];
4d7e6e51 263 unblock_input ();
edfda783
AR
264 return;
265 }
266
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";
271 wv->value = 0;
272 wv->enabled = 1;
273 wv->button_type = BUTTON_TYPE_NONE;
274 wv->help = Qnil;
275 first_wv = wv;
276
277 for (i = 0; i < 4*n; i += 4)
278 {
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]);
282 if (prev_wv)
283 prev_wv->next = wv;
284 else
285 first_wv->contents = wv;
286 /* Don't set wv->name here; GC during the loop might relocate it. */
287 wv->enabled = 1;
288 wv->button_type = BUTTON_TYPE_NONE;
289 prev_wv = wv;
290 }
291
292 set_buffer_internal_1 (prev);
293
294 /* Compare the new menu items with previous, and leave off if no change */
df2142db
AR
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 */
edfda783
AR
297 if (previous_menu_items_used
298 && (!submenu || (submenu && submenu == last_submenu))
299 && menu_items_used == previous_menu_items_used)
300 {
301 for (i = 0; i < previous_menu_items_used; i++)
df2142db
AR
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 */
96fb4434 305 if (!EQ (previous_items[i], AREF (menu_items, i)))
edfda783 306 if (!(STRINGP (previous_items[i])
96fb4434 307 && STRINGP (AREF (menu_items, i))
0dc8cf50
JD
308 && !strcmp (SSDATA (previous_items[i]),
309 SSDATA (AREF (menu_items, i)))))
edfda783
AR
310 break;
311 if (i == previous_menu_items_used)
312 {
313 /* No change.. */
314
cbe0b5bf 315#if NSMENUPROFILE
edfda783
AR
316 ftime (&tb);
317 t += 1000*tb.time+tb.millitm;
318 fprintf (stderr, "NO CHANGE! CUTTING OUT after %ld msec.\n", t);
319#endif
320
321 free_menubar_widget_value_tree (first_wv);
322 discard_menu_items ();
323 unbind_to (specpdl_count, Qnil);
324 [pool release];
4d7e6e51 325 unblock_input ();
edfda783
AR
326 return;
327 }
328 }
329 /* The menu items are different, so store them in the frame */
df2142db 330 /* FIXME: this is not correct for single-submenu case */
f00af5b1 331 fset_menu_bar_vector (f, menu_items);
edfda783
AR
332 f->menu_bar_items_used = menu_items_used;
333
334 /* Calls restore_menu_items, etc., as they were outside */
335 unbind_to (specpdl_count, Qnil);
336
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;
96fb4434 340 for (i = 0; i < ASIZE (items); i += 4)
edfda783
AR
341 {
342 Lisp_Object string;
96fb4434 343 string = AREF (items, i + 1);
edfda783
AR
344 if (NILP (string))
345 break;
edfda783 346
51b59d79 347 wv->name = SSDATA (string);
edfda783
AR
348 update_submenu_strings (wv->contents);
349 wv = wv->next;
350 }
351
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. */
354 if (submenu)
355 {
e6076b1b 356 const char *submenuTitle = [[submenu title] UTF8String];
edfda783
AR
357 for (wv = first_wv->contents; wv; wv = wv->next)
358 {
359 if (!strcmp (submenuTitle, wv->name))
360 {
361 [submenu fillWithWidgetValue: wv->contents];
362 last_submenu = submenu;
363 break;
364 }
365 }
366 }
367 else
368 {
f90e3ebd 369 [menu fillWithWidgetValue: first_wv->contents frame: f];
edfda783
AR
370 }
371
372 }
373 else
374 {
375 static int n_previous_strings = 0;
376 static char previous_strings[100][10];
377 static struct frame *last_f = NULL;
378 int n;
379 Lisp_Object string;
380
3988c7d6
AR
381 wv = xmalloc_widget_value ();
382 wv->name = "menubar";
383 wv->value = 0;
384 wv->enabled = 1;
385 wv->button_type = BUTTON_TYPE_NONE;
386 wv->help = Qnil;
387 first_wv = wv;
388
edfda783
AR
389 /* Make widget-value tree w/ just the top level menu bar strings */
390 items = FRAME_MENU_BAR_ITEMS (f);
391 if (NILP (items))
392 {
204ee57f 393 free_menubar_widget_value_tree (first_wv);
edfda783 394 [pool release];
4d7e6e51 395 unblock_input ();
edfda783
AR
396 return;
397 }
398
399
400 /* check if no change.. this mechanism is a bit rough, but ready */
96fb4434 401 n = ASIZE (items) / 4;
edfda783
AR
402 if (f == last_f && n_previous_strings == n)
403 {
404 for (i = 0; i<n; i++)
405 {
facfbbbd 406 string = AREF (items, 4*i+1);
edfda783 407
facfbbbd 408 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
edfda783
AR
409 continue;
410 if (NILP (string))
0dc8cf50
JD
411 {
412 if (previous_strings[i][0])
413 break;
414 else
415 continue;
416 }
417 else if (memcmp (previous_strings[i], SDATA (string),
e99a530f 418 min (10, SBYTES (string) + 1)))
edfda783
AR
419 break;
420 }
421
422 if (i == n)
423 {
204ee57f 424 free_menubar_widget_value_tree (first_wv);
edfda783 425 [pool release];
4d7e6e51 426 unblock_input ();
edfda783
AR
427 return;
428 }
429 }
430
431 [menu clear];
96fb4434 432 for (i = 0; i < ASIZE (items); i += 4)
edfda783 433 {
96fb4434 434 string = AREF (items, i + 1);
edfda783
AR
435 if (NILP (string))
436 break;
437
438 if (n < 100)
b55b9f85
JD
439 memcpy (previous_strings[i/4], SDATA (string),
440 min (10, SBYTES (string) + 1));
edfda783
AR
441
442 wv = xmalloc_widget_value ();
51b59d79 443 wv->name = SSDATA (string);
edfda783
AR
444 wv->value = 0;
445 wv->enabled = 1;
446 wv->button_type = BUTTON_TYPE_NONE;
447 wv->help = Qnil;
d311d28c 448 wv->call_data = (void *) (intptr_t) (-1);
edfda783
AR
449
450#ifdef NS_IMPL_COCOA
451 /* we'll update the real copy under app menu when time comes */
452 if (!strcmp ("Services", wv->name))
453 {
454 /* but we need to make sure it will update on demand */
455 [svcsMenu setFrame: f];
edfda783
AR
456 }
457 else
458#endif
459 [menu addSubmenuWithTitle: wv->name forFrame: f];
460
461 if (prev_wv)
462 prev_wv->next = wv;
463 else
464 first_wv->contents = wv;
465 prev_wv = wv;
466 }
467
468 last_f = f;
469 if (n < 100)
470 n_previous_strings = n;
471 else
472 n_previous_strings = 0;
473
474 }
475 free_menubar_widget_value_tree (first_wv);
476
477
cbe0b5bf 478#if NSMENUPROFILE
edfda783
AR
479 ftime (&tb);
480 t += 1000*tb.time+tb.millitm;
481 fprintf (stderr, "Menu update took %ld msec.\n", t);
482#endif
483
484 /* set main menu */
485 if (needsSet)
486 [NSApp setMainMenu: menu];
487
488 [pool release];
4d7e6e51 489 unblock_input ();
edfda783
AR
490
491}
492
493
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
496 from Lisp. */
497void
18e27ea8 498set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
edfda783
AR
499{
500 ns_update_menubar (f, deep_p, nil);
501}
502
6d01f1fe
JD
503void
504x_activate_menubar (struct frame *f)
505{
c0342369 506#ifdef NS_IMPL_COCOA
d2d699ac 507 ns_update_menubar (f, true, nil);
6d01f1fe 508 ns_check_pending_open_menu ();
c0342369 509#endif
6d01f1fe
JD
510}
511
512
513
edfda783 514
edfda783
AR
515/* ==========================================================================
516
517 Menu: class implementation
518
519 ========================================================================== */
520
521
522/* Menu that can define itself from Emacs "widget_value"s and will lazily
523 update itself when user clicked. Based on Carbon/AppKit implementation
524 by Yamamoto Mitsuharu. */
525@implementation EmacsMenu
526
527/* override designated initializer */
528- initWithTitle: (NSString *)title
529{
f90e3ebd 530 frame = 0;
0dc8cf50 531 if ((self = [super initWithTitle: title]))
edfda783
AR
532 [self setAutoenablesItems: NO];
533 return self;
534}
535
536
537/* used for top-level */
538- initWithTitle: (NSString *)title frame: (struct frame *)f
539{
540 [self initWithTitle: title];
541 frame = f;
542#ifdef NS_IMPL_COCOA
543 [self setDelegate: self];
544#endif
545 return self;
546}
547
548
549- (void)setFrame: (struct frame *)f
550{
551 frame = f;
552}
553
5fecd5fc
JD
554#ifdef NS_IMPL_COCOA
555#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
556extern NSString *NSMenuDidBeginTrackingNotification;
557#endif
558#endif
559
560#ifdef NS_IMPL_COCOA
561-(void)trackingNotification:(NSNotification *)notification
562{
563 /* Update menu in menuNeedsUpdate only while tracking menus. */
564 trackingMenu = ([notification name] == NSMenuDidBeginTrackingNotification
565 ? 1 : 0);
d2d699ac 566 if (! trackingMenu) ns_check_menu_open (nil);
5fecd5fc 567}
6d01f1fe
JD
568
569#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
570- (void)menuWillOpen:(NSMenu *)menu
571{
d2d699ac 572 ++trackingMenu;
6d01f1fe 573
d2d699ac
JD
574#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_6
575 // On 10.6 we get repeated calls, only the one for NSSystemDefined is "real".
576 if ([[NSApp currentEvent] type] != NSSystemDefined) return;
5fecd5fc 577#endif
edfda783 578
d2d699ac
JD
579 /* When dragging from one menu to another, we get willOpen followed by didClose,
580 i.e. trackingMenu == 3 in willOpen and then 2 after didClose.
581 We have updated all menus, so avoid doing it when trackingMenu == 3. */
582 if (trackingMenu == 2)
583 ns_check_menu_open (menu);
584}
585
586- (void)menuDidClose:(NSMenu *)menu
587{
588 --trackingMenu;
589}
590#endif /* OSX >= 10.5 */
591
592#endif /* NS_IMPL_COCOA */
593
edfda783
AR
594/* delegate method called when a submenu is being opened: run a 'deep' call
595 to set_frame_menubar */
596- (void)menuNeedsUpdate: (NSMenu *)menu
597{
201949c3
AR
598 if (!FRAME_LIVE_P (frame))
599 return;
5fecd5fc
JD
600
601 /* Cocoa/Carbon will request update on every keystroke
edfda783
AR
602 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
603 since key equivalents are handled through emacs.
5fecd5fc
JD
604 On Leopard, even keystroke events generate SystemDefined event.
605 Third-party applications that enhance mouse / trackpad
606 interaction, or also VNC/Remote Desktop will send events
607 of type AppDefined rather than SysDefined.
608 Menus will fail to show up if they haven't been initialized.
609 AppDefined events may lack timing data.
610
611 Thus, we rely on the didBeginTrackingNotification notification
612 as above to indicate the need for updates.
613 From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
614 key press case, NSMenuPropertyItemImage (e.g.) won't be set.
615 */
0caaedb1 616 if (trackingMenu == 0)
edfda783
AR
617 return;
618/*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
cf7a0de6
PE
619#if (! defined (NS_IMPL_COCOA) \
620 || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
6d01f1fe
JD
621 /* Don't know how to do this for anything other than OSX >= 10.5
622 This is wrong, as it might run Lisp code in the event loop. */
623 ns_update_menubar (frame, true, self);
624#endif
edfda783
AR
625}
626
627
628- (BOOL)performKeyEquivalent: (NSEvent *)theEvent
629{
630 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
631 && FRAME_NS_VIEW (SELECTED_FRAME ()))
632 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
633 return YES;
634}
635
636
c7cef62d
AR
637/* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
638 into an accelerator string. We are only able to display a single character
639 for an accelerator, together with an optional modifier combination. (Under
640 Carbon more control was possible, but in Cocoa multi-char strings passed to
641 NSMenuItem get ignored. For now we try to display a super-single letter
642 combo, and return the others as strings to be appended to the item title.
643 (This is signaled by setting keyEquivModMask to 0 for now.) */
9c5bd55a 644-(NSString *)parseKeyEquiv: (const char *)key
edfda783 645{
9c5bd55a 646 const char *tpos = key;
c7cef62d
AR
647 keyEquivModMask = NSCommandKeyMask;
648
edfda783
AR
649 if (!key || !strlen (key))
650 return @"";
96fb4434 651
edfda783
AR
652 while (*tpos == ' ' || *tpos == '(')
653 tpos++;
0bae4e09
AR
654 if ((*tpos == 's') && (*(tpos+1) == '-'))
655 {
656 return [NSString stringWithFormat: @"%c", tpos[2]];
657 }
658 keyEquivModMask = 0; /* signal */
659 return [NSString stringWithUTF8String: tpos];
edfda783
AR
660}
661
07b87a10 662
15034960 663- (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
edfda783 664{
15034960 665 NSMenuItem *item;
edfda783
AR
666 widget_value *wv = (widget_value *)wvptr;
667
4039c786 668 if (menu_separator_name_p (wv->name))
edfda783
AR
669 {
670 item = [NSMenuItem separatorItem];
671 [self addItem: item];
672 }
673 else
674 {
675 NSString *title, *keyEq;
676 title = [NSString stringWithUTF8String: wv->name];
677 if (title == nil)
678 title = @"< ? >"; /* (get out in the open so we know about it) */
679
680 keyEq = [self parseKeyEquiv: wv->key];
84ee8aba
AR
681#ifdef NS_IMPL_COCOA
682 /* OS X just ignores modifier strings longer than one character */
c7cef62d
AR
683 if (keyEquivModMask == 0)
684 title = [title stringByAppendingFormat: @" (%@)", keyEq];
84ee8aba 685#endif
edfda783
AR
686
687 item = [self addItemWithTitle: (NSString *)title
688 action: @selector (menuDown:)
689 keyEquivalent: keyEq];
c7cef62d 690 [item setKeyEquivalentModifierMask: keyEquivModMask];
edfda783
AR
691
692 [item setEnabled: wv->enabled];
693
694 /* Draw radio buttons and tickboxes */
695 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
696 wv->button_type == BUTTON_TYPE_RADIO))
697 [item setState: NSOnState];
698 else
699 [item setState: NSOffState];
700
f3f08c28 701 [item setTag: (NSInteger)wv->call_data];
edfda783
AR
702 }
703
704 return item;
705}
706
707
708/* convenience */
4ff670a8 709-(void)clear
edfda783
AR
710{
711 int n;
96fb4434 712
edfda783
AR
713 for (n = [self numberOfItems]-1; n >= 0; n--)
714 {
715 NSMenuItem *item = [self itemAtIndex: n];
716 NSString *title = [item title];
4ff670a8 717 if (([title length] == 0 /* OSX 10.5 */
3988c7d6 718 || [ns_app_name isEqualToString: title] /* from 10.6 on */
4ff670a8 719 || [@"Apple" isEqualToString: title]) /* older */
edfda783
AR
720 && ![item isSeparatorItem])
721 continue;
722 [self removeItemAtIndex: n];
723 }
724}
725
726
727- (void)fillWithWidgetValue: (void *)wvptr
d2d699ac 728{
f90e3ebd 729 [self fillWithWidgetValue: wvptr frame:nil];
d2d699ac
JD
730}
731
f90e3ebd 732- (void)fillWithWidgetValue: (void *)wvptr frame: (struct frame *)f
edfda783
AR
733{
734 widget_value *wv = (widget_value *)wvptr;
735
736 /* clear existing contents */
737 [self setMenuChangedMessagesEnabled: NO];
738 [self clear];
739
740 /* add new contents */
741 for (; wv != NULL; wv = wv->next)
742 {
15034960 743 NSMenuItem *item = [self addItemWithWidgetValue: wv];
edfda783
AR
744
745 if (wv->contents)
746 {
f90e3ebd
JD
747 EmacsMenu *submenu;
748
749 if (f)
750 submenu = [[EmacsMenu alloc] initWithTitle: [item title] frame:f];
751 else
752 submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
edfda783
AR
753
754 [self setSubmenu: submenu forItem: item];
755 [submenu fillWithWidgetValue: wv->contents];
756 [submenu release];
c0342369 757 [item setAction: (SEL)nil];
edfda783
AR
758 }
759 }
760
761 [self setMenuChangedMessagesEnabled: YES];
762#ifdef NS_IMPL_GNUSTEP
763 if ([[self window] isVisible])
764 [self sizeToFit];
4393663b 765#endif
edfda783
AR
766}
767
768
769/* adds an empty submenu and returns it */
9c5bd55a 770- (EmacsMenu *)addSubmenuWithTitle: (const char *)title forFrame: (struct frame *)f
edfda783
AR
771{
772 NSString *titleStr = [NSString stringWithUTF8String: title];
15034960 773 NSMenuItem *item = [self addItemWithTitle: titleStr
c0342369 774 action: (SEL)nil /*@selector (menuDown:) */
15034960 775 keyEquivalent: @""];
edfda783
AR
776 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
777 [self setSubmenu: submenu forItem: item];
778 [submenu release];
779 return submenu;
780}
781
782/* run a menu in popup mode */
783- (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
7cded46f 784 keymaps: (bool)keymaps
edfda783
AR
785{
786 EmacsView *view = FRAME_NS_VIEW (f);
3d608a86
J
787 NSEvent *e, *event;
788 long retVal;
789
edfda783
AR
790/* p = [view convertPoint:p fromView: nil]; */
791 p.y = NSHeight ([view frame]) - p.y;
3d608a86
J
792 e = [[view window] currentEvent];
793 event = [NSEvent mouseEventWithType: NSRightMouseDown
794 location: p
795 modifierFlags: 0
796 timestamp: [e timestamp]
797 windowNumber: [[view window] windowNumber]
798 context: [e context]
799 eventNumber: 0/*[e eventNumber] */
800 clickCount: 1
801 pressure: 0];
edfda783
AR
802
803 context_menu_value = -1;
804 [NSMenu popUpContextMenu: self withEvent: event forView: view];
805 retVal = context_menu_value;
806 context_menu_value = 0;
facfbbbd
SM
807 return retVal > 0
808 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
809 : Qnil;
edfda783
AR
810}
811
812@end /* EmacsMenu */
813
814
815
816/* ==========================================================================
817
818 Context Menu: implementing functions
819
820 ========================================================================== */
821
ef7417fd 822Lisp_Object
a10c8269 823ns_menu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
42ca4633 824 Lisp_Object title, const char **error)
edfda783
AR
825{
826 EmacsMenu *pmenu;
edfda783 827 NSPoint p;
0dc8cf50 828 Lisp_Object tem;
d311d28c 829 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
edfda783
AR
830 widget_value *wv, *first_wv = 0;
831
ef7417fd 832 p.x = x; p.y = y;
edfda783
AR
833
834 /* now parse stage 2 as in ns_update_menubar */
835 wv = xmalloc_widget_value ();
836 wv->name = "contextmenu";
837 wv->value = 0;
838 wv->enabled = 1;
839 wv->button_type = BUTTON_TYPE_NONE;
840 wv->help = Qnil;
841 first_wv = wv;
842
edfda783 843#if 0
df2142db 844 /* FIXME: a couple of one-line differences prevent reuse */
7cded46f 845 wv = digest_single_submenu (0, menu_items_used, 0);
edfda783
AR
846#else
847 {
848 widget_value *save_wv = 0, *prev_wv = 0;
849 widget_value **submenu_stack
38182d90 850 = alloca (menu_items_used * sizeof *submenu_stack);
edfda783 851/* Lisp_Object *subprefix_stack
38182d90 852 = alloca (menu_items_used * sizeof *subprefix_stack); */
edfda783
AR
853 int submenu_depth = 0;
854 int first_pane = 1;
855 int i;
856
857 /* Loop over all panes and items, filling in the tree. */
858 i = 0;
859 while (i < menu_items_used)
860 {
96fb4434 861 if (EQ (AREF (menu_items, i), Qnil))
edfda783
AR
862 {
863 submenu_stack[submenu_depth++] = save_wv;
864 save_wv = prev_wv;
865 prev_wv = 0;
866 first_pane = 1;
867 i++;
868 }
96fb4434 869 else if (EQ (AREF (menu_items, i), Qlambda))
edfda783
AR
870 {
871 prev_wv = save_wv;
872 save_wv = submenu_stack[--submenu_depth];
873 first_pane = 0;
874 i++;
875 }
96fb4434 876 else if (EQ (AREF (menu_items, i), Qt)
edfda783
AR
877 && submenu_depth != 0)
878 i += MENU_ITEMS_PANE_LENGTH;
879 /* Ignore a nil in the item list.
880 It's meaningful only for dialog boxes. */
96fb4434 881 else if (EQ (AREF (menu_items, i), Qquote))
edfda783 882 i += 1;
96fb4434 883 else if (EQ (AREF (menu_items, i), Qt))
edfda783
AR
884 {
885 /* Create a new pane. */
886 Lisp_Object pane_name, prefix;
9c5bd55a 887 const char *pane_string;
edfda783
AR
888
889 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
890 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
891
892#ifndef HAVE_MULTILINGUAL_MENU
893 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
894 {
895 pane_name = ENCODE_MENU_STRING (pane_name);
896 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
897 }
898#endif
899 pane_string = (NILP (pane_name)
51b59d79 900 ? "" : SSDATA (pane_name));
edfda783
AR
901 /* If there is just one top-level pane, put all its items directly
902 under the top-level menu. */
903 if (menu_items_n_panes == 1)
904 pane_string = "";
905
906 /* If the pane has a meaningful name,
907 make the pane a top-level menu item
908 with its items as a submenu beneath it. */
909 if (!keymaps && strcmp (pane_string, ""))
910 {
911 wv = xmalloc_widget_value ();
912 if (save_wv)
913 save_wv->next = wv;
914 else
915 first_wv->contents = wv;
916 wv->name = pane_string;
917 if (keymaps && !NILP (prefix))
918 wv->name++;
919 wv->value = 0;
920 wv->enabled = 1;
921 wv->button_type = BUTTON_TYPE_NONE;
922 wv->help = Qnil;
923 save_wv = wv;
924 prev_wv = 0;
925 }
926 else if (first_pane)
927 {
928 save_wv = wv;
929 prev_wv = 0;
930 }
931 first_pane = 0;
932 i += MENU_ITEMS_PANE_LENGTH;
933 }
934 else
935 {
936 /* Create a new item within current pane. */
937 Lisp_Object item_name, enable, descrip, def, type, selected, help;
938 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
939 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
940 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
941 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
942 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
943 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
944 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
945
946#ifndef HAVE_MULTILINGUAL_MENU
947 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
948 {
949 item_name = ENCODE_MENU_STRING (item_name);
950 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
951 }
952
953 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
954 {
955 descrip = ENCODE_MENU_STRING (descrip);
956 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
957 }
958#endif /* not HAVE_MULTILINGUAL_MENU */
959
960 wv = xmalloc_widget_value ();
961 if (prev_wv)
962 prev_wv->next = wv;
963 else
964 save_wv->contents = wv;
51b59d79 965 wv->name = SSDATA (item_name);
edfda783 966 if (!NILP (descrip))
51b59d79 967 wv->key = SSDATA (descrip);
edfda783
AR
968 wv->value = 0;
969 /* If this item has a null value,
970 make the call_data null so that it won't display a box
971 when the mouse is on it. */
4939150c 972 wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
edfda783
AR
973 wv->enabled = !NILP (enable);
974
975 if (NILP (type))
976 wv->button_type = BUTTON_TYPE_NONE;
977 else if (EQ (type, QCtoggle))
978 wv->button_type = BUTTON_TYPE_TOGGLE;
979 else if (EQ (type, QCradio))
980 wv->button_type = BUTTON_TYPE_RADIO;
981 else
1088b922 982 emacs_abort ();
edfda783
AR
983
984 wv->selected = !NILP (selected);
985
986 if (! STRINGP (help))
987 help = Qnil;
988
989 wv->help = help;
990
991 prev_wv = wv;
992
993 i += MENU_ITEMS_ITEM_LENGTH;
994 }
995 }
996 }
997#endif
998
999 if (!NILP (title))
1000 {
1001 widget_value *wv_title = xmalloc_widget_value ();
1002 widget_value *wv_sep = xmalloc_widget_value ();
1003
1004 /* Maybe replace this separator with a bitmap or owner-draw item
1005 so that it looks better. Having two separators looks odd. */
1006 wv_sep->name = "--";
1007 wv_sep->next = first_wv->contents;
1008 wv_sep->help = Qnil;
1009
1010#ifndef HAVE_MULTILINGUAL_MENU
1011 if (STRING_MULTIBYTE (title))
1012 title = ENCODE_MENU_STRING (title);
1013#endif
1014
51b59d79 1015 wv_title->name = SSDATA (title);
15034960 1016 wv_title->enabled = NO;
edfda783
AR
1017 wv_title->button_type = BUTTON_TYPE_NONE;
1018 wv_title->help = Qnil;
1019 wv_title->next = wv_sep;
1020 first_wv->contents = wv_title;
1021 }
1022
1023 pmenu = [[EmacsMenu alloc] initWithTitle:
0dc8cf50 1024 [NSString stringWithUTF8String: SSDATA (title)]];
edfda783
AR
1025 [pmenu fillWithWidgetValue: first_wv->contents];
1026 free_menubar_widget_value_tree (first_wv);
ef7417fd 1027 unbind_to (specpdl_count, Qnil);
edfda783 1028
07b87a10 1029 popup_activated_flag = 1;
edfda783 1030 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
07b87a10 1031 popup_activated_flag = 0;
edfda783
AR
1032 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1033
edfda783
AR
1034 return tem;
1035}
1036
1037
edfda783
AR
1038/* ==========================================================================
1039
1040 Toolbar: externally-called functions
1041
1042 ========================================================================== */
1043
1044void
a10c8269 1045free_frame_tool_bar (struct frame *f)
edfda783
AR
1046/* --------------------------------------------------------------------------
1047 Under NS we just hide the toolbar until it might be needed again.
1048 -------------------------------------------------------------------------- */
1049{
4d7e6e51 1050 block_input ();
edfda783 1051 [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
581a8100 1052 FRAME_TOOLBAR_HEIGHT (f) = 0;
4d7e6e51 1053 unblock_input ();
edfda783
AR
1054}
1055
1056void
a10c8269 1057update_frame_tool_bar (struct frame *f)
edfda783
AR
1058/* --------------------------------------------------------------------------
1059 Update toolbar contents
1060 -------------------------------------------------------------------------- */
1061{
c0342369 1062 int i, k = 0;
581a8100
J
1063 EmacsView *view = FRAME_NS_VIEW (f);
1064 NSWindow *window = [view window];
1065 EmacsToolbar *toolbar = [view toolbar];
edfda783 1066
4d7e6e51 1067 block_input ();
c0342369
JD
1068
1069#ifdef NS_IMPL_COCOA
edfda783 1070 [toolbar clearActive];
c0342369
JD
1071#else
1072 [toolbar clearAll];
1073#endif
edfda783
AR
1074
1075 /* update EmacsToolbar as in GtkUtils, build items list */
1076 for (i = 0; i < f->n_tool_bar_items; ++i)
1077 {
e69b0960 1078#define TOOLPROP(IDX) AREF (f->tool_bar_items, \
edfda783
AR
1079 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1080
1081 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
edfda783 1082 int idx;
13464394 1083 ptrdiff_t img_id;
edfda783
AR
1084 struct image *img;
1085 Lisp_Object image;
1086 Lisp_Object helpObj;
9c5bd55a 1087 const char *helpText;
edfda783 1088
c0342369
JD
1089 /* Check if this is a separator. */
1090 if (EQ (TOOLPROP (TOOL_BAR_ITEM_TYPE), Qt))
1091 {
1092 /* Skip separators. Newer OSX don't show them, and on GNUStep they
1093 are wide as a button, thus overflowing the toolbar most of
1094 the time. */
1095 continue;
1096 }
1097
edfda783
AR
1098 /* If image is a vector, choose the image according to the
1099 button state. */
1100 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1101 if (VECTORP (image))
1102 {
1103 /* NS toolbar auto-computes disabled and selected images */
1104 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
ef884f23 1105 eassert (ASIZE (image) >= idx);
edfda783
AR
1106 image = AREF (image, idx);
1107 }
1108 else
1109 {
1110 idx = -1;
1111 }
dd723bbd
JD
1112 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1113 if (NILP (helpObj))
1114 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
51b59d79 1115 helpText = NILP (helpObj) ? "" : SSDATA (helpObj);
dd723bbd 1116
edfda783
AR
1117 /* Ignore invalid image specifications. */
1118 if (!valid_image_p (image))
1119 {
dd723bbd 1120 /* Don't log anything, GNUS makes invalid images all the time. */
edfda783
AR
1121 continue;
1122 }
1123
1124 img_id = lookup_image (f, image);
1125 img = IMAGE_FROM_ID (f, img_id);
1126 prepare_image_for_display (f, img);
1127
1128 if (img->load_failed_p || img->pixmap == nil)
1129 {
1130 NSLog (@"Could not prepare toolbar image for display.");
1131 continue;
1132 }
1133
c0342369
JD
1134 [toolbar addDisplayItemWithImage: img->pixmap
1135 idx: k++
1136 tag: i
1137 helpText: helpText
edfda783
AR
1138 enabled: enabled_p];
1139#undef TOOLPROP
1140 }
1141
1142 if (![toolbar isVisible])
1143 [toolbar setVisible: YES];
1144
c0342369 1145#ifdef NS_IMPL_COCOA
edfda783
AR
1146 if ([toolbar changed])
1147 {
1148 /* inform app that toolbar has changed */
1149 NSDictionary *dict = [toolbar configurationDictionary];
1150 NSMutableDictionary *newDict = [dict mutableCopy];
1151 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
79e721e0 1152 id key;
edfda783
AR
1153 while ((key = [keys nextObject]) != nil)
1154 {
1155 NSObject *val = [dict objectForKey: key];
1156 if ([val isKindOfClass: [NSArray class]])
1157 {
1158 [newDict setObject:
1159 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1160 forKey: key];
1161 break;
1162 }
1163 }
1164 [toolbar setConfigurationFromDictionary: newDict];
1165 [newDict release];
1166 }
c0342369 1167#endif
edfda783 1168
581a8100
J
1169 FRAME_TOOLBAR_HEIGHT (f) =
1170 NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1171 - FRAME_NS_TITLEBAR_HEIGHT (f);
6871e574
JD
1172 if (FRAME_TOOLBAR_HEIGHT (f) < 0) // happens if frame is fullscreen.
1173 FRAME_TOOLBAR_HEIGHT (f) = 0;
4d7e6e51 1174 unblock_input ();
edfda783
AR
1175}
1176
1177
1178/* ==========================================================================
1179
1180 Toolbar: class implementation
1181
1182 ========================================================================== */
1183
1184@implementation EmacsToolbar
1185
1186- initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1187{
1188 self = [super initWithIdentifier: identifier];
1189 emacsView = view;
1190 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1191 [self setSizeMode: NSToolbarSizeModeSmall];
1192 [self setDelegate: self];
1193 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1194 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
c0342369 1195 prevIdentifiers = nil;
edfda783
AR
1196 prevEnablement = enablement = 0L;
1197 return self;
1198}
1199
1200- (void)dealloc
1201{
1202 [prevIdentifiers release];
1203 [activeIdentifiers release];
1204 [identifierToItem release];
1205 [super dealloc];
1206}
1207
1208- (void) clearActive
1209{
1210 [prevIdentifiers release];
1211 prevIdentifiers = [activeIdentifiers copy];
1212 [activeIdentifiers removeAllObjects];
1213 prevEnablement = enablement;
1214 enablement = 0L;
1215}
1216
c0342369
JD
1217- (void) clearAll
1218{
1219 [self clearActive];
1220 while ([[self items] count] > 0)
1221 [self removeItemAtIndex: 0];
1222}
1223
edfda783
AR
1224- (BOOL) changed
1225{
1226 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1227 enablement == prevEnablement ? NO : YES;
1228}
1229
c0342369
JD
1230- (void) addDisplayItemWithImage: (EmacsImage *)img
1231 idx: (int)idx
1232 tag: (int)tag
1233 helpText: (const char *)help
1234 enabled: (BOOL)enabled
edfda783
AR
1235{
1236 /* 1) come up w/identifier */
facfbbbd
SM
1237 NSString *identifier
1238 = [NSString stringWithFormat: @"%u", [img hash]];
c0342369 1239 [activeIdentifiers addObject: identifier];
edfda783
AR
1240
1241 /* 2) create / reuse item */
1242 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1243 if (item == nil)
1244 {
1245 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1246 autorelease];
1247 [item setImage: img];
f3f08c28 1248 [item setToolTip: [NSString stringWithUTF8String: help]];
edfda783
AR
1249 [item setTarget: emacsView];
1250 [item setAction: @selector (toolbarClicked:)];
c0342369 1251 [identifierToItem setObject: item forKey: identifier];
edfda783
AR
1252 }
1253
c0342369
JD
1254#ifdef NS_IMPL_GNUSTEP
1255 [self insertItemWithItemIdentifier: identifier atIndex: idx];
1256#endif
cf7a0de6 1257
c0342369 1258 [item setTag: tag];
edfda783
AR
1259 [item setEnabled: enabled];
1260
1261 /* 3) update state */
edfda783
AR
1262 enablement = (enablement << 1) | (enabled == YES);
1263}
1264
1265/* This overrides super's implementation, which automatically sets
1266 all items to enabled state (for some reason). */
c0342369
JD
1267- (void)validateVisibleItems
1268{
1269}
edfda783
AR
1270
1271
1272/* delegate methods */
1273
1274- (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1275 itemForItemIdentifier: (NSString *)itemIdentifier
1276 willBeInsertedIntoToolbar: (BOOL)flag
1277{
1278 /* look up NSToolbarItem by identifier and return... */
1279 return [identifierToItem objectForKey: itemIdentifier];
1280}
1281
1282- (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1283{
1284 /* return entire set.. */
1285 return activeIdentifiers;
1286}
1287
1288/* for configuration palette (not yet supported) */
1289- (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1290{
1291 /* return entire set... */
c0342369
JD
1292 return activeIdentifiers;
1293 //return [identifierToItem allKeys];
edfda783
AR
1294}
1295
1296/* optional and unneeded */
1297/* - toolbarWillAddItem: (NSNotification *)notification { } */
1298/* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1299/* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1300
1301@end /* EmacsToolbar */
1302
1303
1304
1305/* ==========================================================================
1306
1307 Tooltip: class implementation
1308
1309 ========================================================================== */
1310
1311/* Needed because NeXTstep does not provide enough control over tooltip
1312 display. */
1313@implementation EmacsTooltip
1314
1315- init
1316{
1317 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1318 blue: 0.792 alpha: 0.95];
1319 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1320 NSFont *sfont = [font screenFont];
1321 int height = [sfont ascender] - [sfont descender];
1322/*[font boundingRectForFont].size.height; */
1323 NSRect r = NSMakeRect (0, 0, 100, height+6);
1324
1325 textField = [[NSTextField alloc] initWithFrame: r];
1326 [textField setFont: font];
1327 [textField setBackgroundColor: col];
1328
1329 [textField setEditable: NO];
1330 [textField setSelectable: NO];
ffe57a7a
AA
1331 [textField setBordered: NO];
1332 [textField setBezeled: NO];
edfda783
AR
1333 [textField setDrawsBackground: YES];
1334
1335 win = [[NSWindow alloc]
1336 initWithContentRect: [textField frame]
1337 styleMask: 0
1338 backing: NSBackingStoreBuffered
1339 defer: YES];
ffe57a7a 1340 [win setHasShadow: YES];
edfda783
AR
1341 [win setReleasedWhenClosed: NO];
1342 [win setDelegate: self];
1343 [[win contentView] addSubview: textField];
1344/* [win setBackgroundColor: col]; */
1345 [win setOpaque: NO];
1346
1347 return self;
1348}
1349
1350- (void) dealloc
1351{
1352 [win close];
1353 [win release];
1354 [textField release];
1355 [super dealloc];
1356}
1357
1358- (void) setText: (char *)text
1359{
1360 NSString *str = [NSString stringWithUTF8String: text];
ffe57a7a
AA
1361 NSRect r = [textField frame];
1362 NSSize tooltipDims;
1363
edfda783 1364 [textField setStringValue: str];
ffe57a7a
AA
1365 tooltipDims = [[textField cell] cellSize];
1366
1367 r.size.width = tooltipDims.width;
1368 r.size.height = tooltipDims.height;
1369 [textField setFrame: r];
edfda783
AR
1370}
1371
1372- (void) showAtX: (int)x Y: (int)y for: (int)seconds
1373{
1374 NSRect wr = [win frame];
1375
1376 wr.origin = NSMakePoint (x, y);
1377 wr.size = [textField frame].size;
1378
1379 [win setFrame: wr display: YES];
f38ab167 1380 [win setLevel: NSPopUpMenuWindowLevel];
edfda783
AR
1381 [win orderFront: self];
1382 [win display];
1383 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1384 selector: @selector (hide)
1385 userInfo: nil repeats: NO];
1386 [timer retain];
1387}
1388
1389- (void) hide
1390{
1391 [win close];
1392 if (timer != nil)
1393 {
1394 if ([timer isValid])
1395 [timer invalidate];
1396 [timer release];
1397 timer = nil;
1398 }
1399}
1400
1401- (BOOL) isActive
1402{
1403 return timer != nil;
1404}
1405
1406- (NSRect) frame
1407{
1408 return [textField frame];
1409}
1410
1411@end /* EmacsTooltip */
1412
1413
1414
1415/* ==========================================================================
1416
1417 Popup Dialog: implementing functions
1418
1419 ========================================================================== */
1420
9d7fa573
JD
1421struct Popdown_data
1422{
1423 NSAutoreleasePool *pool;
1424 EmacsDialogPanel *dialog;
1425};
c96169a0 1426
27e498e6
PE
1427static void
1428pop_down_menu (void *arg)
c96169a0 1429{
27e498e6 1430 struct Popdown_data *unwind_data = arg;
9d7fa573 1431
4d7e6e51 1432 block_input ();
ba301db3
AR
1433 if (popup_activated_flag)
1434 {
9d7fa573 1435 EmacsDialogPanel *panel = unwind_data->dialog;
ba301db3 1436 popup_activated_flag = 0;
9d7fa573
JD
1437 [panel close];
1438 [unwind_data->pool release];
ba301db3 1439 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
ba301db3 1440 }
9d7fa573
JD
1441
1442 xfree (unwind_data);
4d7e6e51 1443 unblock_input ();
c96169a0
AR
1444}
1445
1446
edfda783
AR
1447Lisp_Object
1448ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1449{
1450 id dialog;
d6f0886c 1451 Lisp_Object window, tem, title;
edfda783
AR
1452 struct frame *f;
1453 NSPoint p;
1454 BOOL isQ;
9d7fa573 1455 NSAutoreleasePool *pool;
edfda783
AR
1456
1457 NSTRACE (x-popup-dialog);
96fb4434 1458
edfda783
AR
1459 isQ = NILP (header);
1460
8612b71a
AR
1461 if (EQ (position, Qt)
1462 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1463 || EQ (XCAR (position), Qtool_bar))))
edfda783
AR
1464 {
1465 window = selected_window;
1466 }
1467 else if (CONSP (position))
1468 {
1469 Lisp_Object tem;
1470 tem = Fcar (position);
1471 if (XTYPE (tem) == Lisp_Cons)
1472 window = Fcar (Fcdr (position));
1473 else
1474 {
1475 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1476 window = Fcar (tem); /* POSN_WINDOW (tem) */
1477 }
1478 }
8612b71a 1479 else if (WINDOWP (position) || FRAMEP (position))
edfda783
AR
1480 {
1481 window = position;
1482 }
1483 else
8612b71a
AR
1484 window = Qnil;
1485
edfda783
AR
1486 if (FRAMEP (window))
1487 f = XFRAME (window);
8612b71a 1488 else if (WINDOWP (window))
edfda783
AR
1489 {
1490 CHECK_LIVE_WINDOW (window);
1491 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1492 }
8612b71a
AR
1493 else
1494 CHECK_WINDOW (window);
1495
7452b7bd
DA
1496 check_window_system (f);
1497
edfda783
AR
1498 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1499 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
3175b12a 1500
d6f0886c
JD
1501 title = Fcar (contents);
1502 CHECK_STRING (title);
1503
1504 if (NILP (Fcar (Fcdr (contents))))
1505 /* No buttons specified, add an "Ok" button so users can pop down
1506 the dialog. */
6c6f1994 1507 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
d6f0886c 1508
4d7e6e51 1509 block_input ();
9d7fa573 1510 pool = [[NSAutoreleasePool alloc] init];
edfda783
AR
1511 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1512 isQuestion: isQ];
9d7fa573 1513
c96169a0 1514 {
d311d28c 1515 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
9d7fa573
JD
1516 struct Popdown_data *unwind_data = xmalloc (sizeof (*unwind_data));
1517
1518 unwind_data->pool = pool;
1519 unwind_data->dialog = dialog;
4939150c 1520
27e498e6 1521 record_unwind_protect_ptr (pop_down_menu, unwind_data);
c96169a0
AR
1522 popup_activated_flag = 1;
1523 tem = [dialog runDialogAt: p];
ba301db3 1524 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
c96169a0 1525 }
9d7fa573 1526
4d7e6e51 1527 unblock_input ();
c96169a0 1528
edfda783
AR
1529 return tem;
1530}
1531
1532
1533/* ==========================================================================
1534
1535 Popup Dialog: class implementation
1536
1537 ========================================================================== */
1538
1539@interface FlippedView : NSView
1540{
1541}
1542@end
1543
1544@implementation FlippedView
1545- (BOOL)isFlipped
1546{
1547 return YES;
1548}
1549@end
1550
1551@implementation EmacsDialogPanel
1552
1553#define SPACER 8.0
1554#define ICONSIZE 64.0
1555#define TEXTHEIGHT 20.0
1556#define MINCELLWIDTH 90.0
1557
f3f08c28 1558- initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
edfda783
AR
1559 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1560{
1561 NSSize spacing = {SPACER, SPACER};
1562 NSRect area;
facfbbbd 1563 id cell;
9d7fa573
JD
1564 NSImageView *imgView;
1565 FlippedView *contentView;
1566 NSImage *img;
1567
0f19feff 1568 dialog_return = Qundefined;
7f8941d8 1569 button_values = NULL;
9d7fa573
JD
1570 area.origin.x = 3*SPACER;
1571 area.origin.y = 2*SPACER;
1572 area.size.width = ICONSIZE;
1573 area.size.height= ICONSIZE;
1574 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1575 [img setScalesWhenResized: YES];
1576 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1577 imgView = [[NSImageView alloc] initWithFrame: area];
1578 [imgView setImage: img];
1579 [imgView setEditable: NO];
1580 [img autorelease];
1581 [imgView autorelease];
edfda783 1582
c0342369 1583 aStyle = NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask;
edfda783
AR
1584 flag = YES;
1585 rows = 0;
1586 cols = 1;
1587 [super initWithContentRect: contentRect styleMask: aStyle
1588 backing: backingType defer: flag];
1589 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
9d7fa573
JD
1590 [contentView autorelease];
1591
edfda783
AR
1592 [self setContentView: contentView];
1593
1594 [[self contentView] setAutoresizesSubviews: YES];
1595
1596 [[self contentView] addSubview: imgView];
1597 [self setTitle: @""];
1598
1599 area.origin.x += ICONSIZE+2*SPACER;
1600/* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1601 area.size.width = 400;
1602 area.size.height= TEXTHEIGHT;
1603 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1604 [[self contentView] addSubview: command];
3988c7d6 1605 [command setStringValue: ns_app_name];
edfda783
AR
1606 [command setDrawsBackground: NO];
1607 [command setBezeled: NO];
1608 [command setSelectable: NO];
1609 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1610
1611/* area.origin.x = ICONSIZE+2*SPACER;
1612 area.origin.y = TEXTHEIGHT + 2*SPACER;
1613 area.size.width = 400;
1614 area.size.height= 2;
1615 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1616 [[self contentView] addSubview: tem];
1617 [tem setTitlePosition: NSNoTitle];
1618 [tem setAutoresizingMask: NSViewWidthSizable];*/
1619
1620/* area.origin.x = ICONSIZE+2*SPACER; */
1621 area.origin.y += TEXTHEIGHT+SPACER;
1622 area.size.width = 400;
1623 area.size.height= TEXTHEIGHT;
1624 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1625 [[self contentView] addSubview: title];
1626 [title setDrawsBackground: NO];
1627 [title setBezeled: NO];
1628 [title setSelectable: NO];
1629 [title setFont: [NSFont systemFontOfSize: 11.0]];
1630
1631 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1632 [cell setBordered: NO];
1633 [cell setEnabled: NO];
1634 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1635 [cell setBezelStyle: NSRoundedBezelStyle];
1636
96fb4434
PE
1637 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1638 mode: NSHighlightModeMatrix
1639 prototype: cell
1640 numberOfRows: 0
edfda783 1641 numberOfColumns: 1];
edfda783
AR
1642 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1643 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1644 [matrix setIntercellSpacing: spacing];
9d7fa573 1645 [matrix autorelease];
edfda783 1646
9d7fa573 1647 [[self contentView] addSubview: matrix];
edfda783
AR
1648 [self setOneShot: YES];
1649 [self setReleasedWhenClosed: YES];
1650 [self setHidesOnDeactivate: YES];
1651 return self;
1652}
1653
1654
1655- (BOOL)windowShouldClose: (id)sender
1656{
7f8941d8 1657 window_closed = YES;
0f19feff 1658 [NSApp stop:self];
edfda783
AR
1659 return NO;
1660}
1661
7f8941d8
JD
1662- (void)dealloc
1663{
1664 xfree (button_values);
1665 [super dealloc];
1666}
edfda783 1667
7f8941d8 1668- (void)process_dialog: (Lisp_Object) list
edfda783 1669{
7f8941d8 1670 Lisp_Object item, lst = list;
edfda783 1671 int row = 0;
7f8941d8
JD
1672 int buttons = 0, btnnr = 0;
1673
1674 for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
1675 {
1676 item = XCAR (list);
1677 if (XTYPE (item) == Lisp_Cons)
1678 ++buttons;
1679 }
1680
1681 if (buttons > 0)
7d652d97 1682 button_values = xmalloc (buttons * sizeof *button_values);
edfda783
AR
1683
1684 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1685 {
1686 item = XCAR (list);
1687 if (XTYPE (item) == Lisp_String)
1688 {
7f8941d8 1689 [self addString: SSDATA (item) row: row++];
edfda783
AR
1690 }
1691 else if (XTYPE (item) == Lisp_Cons)
1692 {
7f8941d8
JD
1693 button_values[btnnr] = XCDR (item);
1694 [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
1695 ++btnnr;
edfda783
AR
1696 }
1697 else if (NILP (item))
1698 {
7f8941d8 1699 [self addSplit];
edfda783
AR
1700 row = 0;
1701 }
1702 }
1703}
1704
1705
7f8941d8 1706- (void)addButton: (char *)str value: (int)tag row: (int)row
edfda783
AR
1707{
1708 id cell;
96fb4434 1709
edfda783
AR
1710 if (row >= rows)
1711 {
1712 [matrix addRow];
1713 rows++;
1714 }
1715 cell = [matrix cellAtRow: row column: cols-1];
1716 [cell setTarget: self];
1717 [cell setAction: @selector (clicked: )];
1718 [cell setTitle: [NSString stringWithUTF8String: str]];
7f8941d8 1719 [cell setTag: tag];
edfda783
AR
1720 [cell setBordered: YES];
1721 [cell setEnabled: YES];
edfda783
AR
1722}
1723
1724
7f8941d8 1725- (void)addString: (char *)str row: (int)row
edfda783
AR
1726{
1727 id cell;
96fb4434 1728
edfda783
AR
1729 if (row >= rows)
1730 {
1731 [matrix addRow];
1732 rows++;
1733 }
1734 cell = [matrix cellAtRow: row column: cols-1];
1735 [cell setTitle: [NSString stringWithUTF8String: str]];
1736 [cell setBordered: YES];
1737 [cell setEnabled: NO];
edfda783
AR
1738}
1739
1740
7f8941d8 1741- (void)addSplit
edfda783
AR
1742{
1743 [matrix addColumn];
1744 cols++;
edfda783
AR
1745}
1746
1747
7f8941d8 1748- (void)clicked: sender
edfda783
AR
1749{
1750 NSArray *sellist = nil;
facfbbbd 1751 EMACS_INT seltag;
edfda783
AR
1752
1753 sellist = [sender selectedCells];
7f8941d8
JD
1754 if ([sellist count] < 1)
1755 return;
edfda783 1756
facfbbbd 1757 seltag = [[sellist objectAtIndex: 0] tag];
7f8941d8
JD
1758 dialog_return = button_values[seltag];
1759 [NSApp stop:self];
edfda783
AR
1760}
1761
1762
1763- initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1764{
1765 Lisp_Object head;
1766 [super init];
1767
1768 if (XTYPE (contents) == Lisp_Cons)
1769 {
1770 head = Fcar (contents);
7f8941d8 1771 [self process_dialog: Fcdr (contents)];
edfda783
AR
1772 }
1773 else
1774 head = contents;
1775
1776 if (XTYPE (head) == Lisp_String)
1777 [title setStringValue:
0dc8cf50 1778 [NSString stringWithUTF8String: SSDATA (head)]];
edfda783
AR
1779 else if (isQ == YES)
1780 [title setStringValue: @"Question"];
1781 else
1782 [title setStringValue: @"Information"];
1783
1784 {
1785 int i;
1786 NSRect r, s, t;
1787
1788 if (cols == 1 && rows > 1) /* Never told where to split */
1789 {
1790 [matrix addColumn];
7f8941d8 1791 for (i = 0; i < rows/2; i++)
edfda783
AR
1792 {
1793 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1794 atRow: i column: 1];
1795 [matrix removeRow: (rows+1)/2];
1796 }
1797 }
1798
1799 [matrix sizeToFit];
1800 {
1801 NSSize csize = [matrix cellSize];
1802 if (csize.width < MINCELLWIDTH)
1803 {
1804 csize.width = MINCELLWIDTH;
1805 [matrix setCellSize: csize];
1806 [matrix sizeToCells];
1807 }
1808 }
1809
1810 [title sizeToFit];
1811 [command sizeToFit];
1812
1813 t = [matrix frame];
1814 r = [title frame];
1815 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1816 {
1817 t.origin.x = r.origin.x;
1818 t.size.width = r.size.width;
1819 }
1820 r = [command frame];
1821 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1822 {
1823 t.origin.x = r.origin.x;
1824 t.size.width = r.size.width;
1825 }
1826
1827 r = [self frame];
1828 s = [(NSView *)[self contentView] frame];
1829 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1830 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1831 [self setFrame: r display: NO];
1832 }
1833
1834 return self;
1835}
1836
1837
18e27ea8 1838
ddee6515
JD
1839- (void)timeout_handler: (NSTimer *)timedEntry
1840{
0f19feff
JD
1841 NSEvent *nxev = [NSEvent otherEventWithType: NSApplicationDefined
1842 location: NSMakePoint (0, 0)
1843 modifierFlags: 0
1844 timestamp: 0
1845 windowNumber: [[NSApp mainWindow] windowNumber]
1846 context: [NSApp context]
1847 subtype: 0
1848 data1: 0
1849 data2: 0];
1850
7f8941d8 1851 timer_fired = YES;
0f19feff
JD
1852 /* We use sto because stopModal/abortModal out of the main loop does not
1853 seem to work in 10.6. But as we use stop we must send a real event so
1854 the stop is seen and acted upon. */
1855 [NSApp stop:self];
1856 [NSApp postEvent: nxev atStart: NO];
ddee6515
JD
1857}
1858
edfda783
AR
1859- (Lisp_Object)runDialogAt: (NSPoint)p
1860{
0f19feff 1861 Lisp_Object ret = Qundefined;
edfda783 1862
ddee6515 1863 while (popup_activated_flag)
edfda783 1864 {
ddee6515
JD
1865 NSTimer *tmo = nil;
1866 EMACS_TIME next_time = timer_check ();
1867
1868 if (EMACS_TIME_VALID_P (next_time))
1869 {
1870 double time = EMACS_TIME_TO_DOUBLE (next_time);
1871 tmo = [NSTimer timerWithTimeInterval: time
1872 target: self
1873 selector: @selector (timeout_handler:)
1874 userInfo: 0
1875 repeats: NO];
1876 [[NSRunLoop currentRunLoop] addTimer: tmo
1877 forMode: NSModalPanelRunLoopMode];
1878 }
7f8941d8 1879 timer_fired = NO;
0f19feff 1880 dialog_return = Qundefined;
7f8941d8 1881 [NSApp runModalForWindow: self];
0f19feff 1882 ret = dialog_return;
ddee6515
JD
1883 if (! timer_fired)
1884 {
1885 if (tmo != nil) [tmo invalidate]; /* Cancels timer */
1886 break;
1887 }
edfda783 1888 }
edfda783 1889
7f8941d8
JD
1890 if (EQ (ret, Qundefined) && window_closed)
1891 /* Make close button pressed equivalent to C-g. */
1892 Fsignal (Qquit, Qnil);
1893
1894 return ret;
edfda783
AR
1895}
1896
1897@end
1898
1899
edfda783
AR
1900/* ==========================================================================
1901
1902 Lisp definitions
1903
1904 ========================================================================== */
1905
1906DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
eb6f7ed0 1907 doc: /* Cause the NS menu to be re-calculated. */)
5842a27b 1908 (void)
edfda783
AR
1909{
1910 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1911 return Qnil;
1912}
1913
1914
edfda783
AR
1915DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1916 doc: /* Pop up a dialog box and return user's selection.
1917POSITION specifies which frame to use.
1918This is normally a mouse button event or a window or frame.
1919If POSITION is t, it means to use the frame the mouse is on.
1920The dialog box appears in the middle of the specified frame.
1921
1922CONTENTS specifies the alternatives to display in the dialog box.
1923It is a list of the form (DIALOG ITEM1 ITEM2...).
1924Each ITEM is a cons cell (STRING . VALUE).
1925The return value is VALUE from the chosen item.
1926
1927An ITEM may also be just a string--that makes a nonselectable item.
1928An ITEM may also be nil--that means to put all preceding items
1929on the left of the dialog box and all following items on the right.
1930\(By default, approximately half appear on each side.)
1931
1932If HEADER is non-nil, the frame title for the box is "Information",
1933otherwise it is "Question".
1934
1935If the user gets rid of the dialog box without making a valid choice,
1936for instance using the window manager, then this produces a quit and
1937`x-popup-dialog' does not return. */)
5842a27b 1938 (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
edfda783
AR
1939{
1940 return ns_popup_dialog (position, contents, header);
1941}
1942
07b87a10
AR
1943DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1944 doc: /* Return t if a menu or popup dialog is active. */)
5842a27b 1945 (void)
07b87a10
AR
1946{
1947 return popup_activated () ? Qt : Qnil;
1948}
edfda783
AR
1949
1950/* ==========================================================================
1951
1952 Lisp interface declaration
1953
1954 ========================================================================== */
1955
1956void
3d608a86 1957syms_of_nsmenu (void)
edfda783 1958{
5fecd5fc
JD
1959#ifndef NS_IMPL_COCOA
1960 /* Don't know how to keep track of this in Next/Open/Gnustep. Always
1961 update menus there. */
1962 trackingMenu = 1;
1963#endif
edfda783
AR
1964 defsubr (&Sx_popup_dialog);
1965 defsubr (&Sns_reset_menu);
07b87a10 1966 defsubr (&Smenu_or_popup_active_p);
edfda783 1967
088dcc3e 1968 Qdebug_on_next_call = intern_c_string ("debug-on-next-call");
edfda783
AR
1969 staticpro (&Qdebug_on_next_call);
1970}