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