Fix -Wwrite-strings warnings in fakemail.c.
[bpt/emacs.git] / src / nsmenu.m
CommitLineData
edfda783 1/* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
114f9c96 2 Copyright (C) 2007, 2008, 2009, 2010 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. */
edfda783 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
510/* Utility (from macmenu.c): is this item a separator? */
511static int
3d608a86 512name_is_separator ( const char *name)
edfda783
AR
513{
514 const char *start = name;
515
516 /* Check if name string consists of only dashes ('-'). */
517 while (*name == '-') name++;
518 /* Separators can also be of the form "--:TripleSuperMegaEtched"
519 or "--deep-shadow". We don't implement them yet, se we just treat
520 them like normal separators. */
521 return (*name == '\0' || start + 2 == name);
522}
523
524
525/* ==========================================================================
526
527 Menu: class implementation
528
529 ========================================================================== */
530
531
532/* Menu that can define itself from Emacs "widget_value"s and will lazily
533 update itself when user clicked. Based on Carbon/AppKit implementation
534 by Yamamoto Mitsuharu. */
535@implementation EmacsMenu
536
537/* override designated initializer */
538- initWithTitle: (NSString *)title
539{
540 if (self = [super initWithTitle: title])
541 [self setAutoenablesItems: NO];
542 return self;
543}
544
545
546/* used for top-level */
547- initWithTitle: (NSString *)title frame: (struct frame *)f
548{
549 [self initWithTitle: title];
550 frame = f;
551#ifdef NS_IMPL_COCOA
552 [self setDelegate: self];
553#endif
554 return self;
555}
556
557
558- (void)setFrame: (struct frame *)f
559{
560 frame = f;
561}
562
563
564/* delegate method called when a submenu is being opened: run a 'deep' call
565 to set_frame_menubar */
566- (void)menuNeedsUpdate: (NSMenu *)menu
567{
201949c3
AR
568 NSEvent *event;
569 if (!FRAME_LIVE_P (frame))
570 return;
571 event = [[FRAME_NS_VIEW (frame) window] currentEvent];
edfda783
AR
572 /* HACK: Cocoa/Carbon will request update on every keystroke
573 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
574 since key equivalents are handled through emacs.
575 On Leopard, even keystroke events generate SystemDefined events, but
576 their subtype is 8. */
c96169a0
AR
577 if ([event type] != NSSystemDefined || [event subtype] == 8
578 /* Also, don't try this if from an event picked up asynchronously,
579 as lots of lisp evaluation happens in ns_update_menubar. */
580 || handling_signal != 0)
edfda783
AR
581 return;
582/*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
583 ns_update_menubar (frame, 1, self);
584}
585
586
587- (BOOL)performKeyEquivalent: (NSEvent *)theEvent
588{
589 if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
590 && FRAME_NS_VIEW (SELECTED_FRAME ()))
591 [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
592 return YES;
593}
594
595
c7cef62d
AR
596/* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
597 into an accelerator string. We are only able to display a single character
598 for an accelerator, together with an optional modifier combination. (Under
599 Carbon more control was possible, but in Cocoa multi-char strings passed to
600 NSMenuItem get ignored. For now we try to display a super-single letter
601 combo, and return the others as strings to be appended to the item title.
602 (This is signaled by setting keyEquivModMask to 0 for now.) */
9c5bd55a 603-(NSString *)parseKeyEquiv: (const char *)key
edfda783 604{
9c5bd55a 605 const char *tpos = key;
c7cef62d
AR
606 keyEquivModMask = NSCommandKeyMask;
607
edfda783
AR
608 if (!key || !strlen (key))
609 return @"";
610
611 while (*tpos == ' ' || *tpos == '(')
612 tpos++;
0bae4e09
AR
613 if ((*tpos == 's') && (*(tpos+1) == '-'))
614 {
615 return [NSString stringWithFormat: @"%c", tpos[2]];
616 }
617 keyEquivModMask = 0; /* signal */
618 return [NSString stringWithUTF8String: tpos];
edfda783
AR
619}
620
07b87a10 621
15034960 622- (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
edfda783 623{
15034960 624 NSMenuItem *item;
edfda783
AR
625 widget_value *wv = (widget_value *)wvptr;
626
627 if (name_is_separator (wv->name))
628 {
629 item = [NSMenuItem separatorItem];
630 [self addItem: item];
631 }
632 else
633 {
634 NSString *title, *keyEq;
635 title = [NSString stringWithUTF8String: wv->name];
636 if (title == nil)
637 title = @"< ? >"; /* (get out in the open so we know about it) */
638
639 keyEq = [self parseKeyEquiv: wv->key];
84ee8aba
AR
640#ifdef NS_IMPL_COCOA
641 /* OS X just ignores modifier strings longer than one character */
c7cef62d
AR
642 if (keyEquivModMask == 0)
643 title = [title stringByAppendingFormat: @" (%@)", keyEq];
84ee8aba 644#endif
edfda783
AR
645
646 item = [self addItemWithTitle: (NSString *)title
647 action: @selector (menuDown:)
648 keyEquivalent: keyEq];
c7cef62d 649 [item setKeyEquivalentModifierMask: keyEquivModMask];
edfda783
AR
650
651 [item setEnabled: wv->enabled];
652
653 /* Draw radio buttons and tickboxes */
654 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
655 wv->button_type == BUTTON_TYPE_RADIO))
656 [item setState: NSOnState];
657 else
658 [item setState: NSOffState];
659
f3f08c28 660 [item setTag: (NSInteger)wv->call_data];
edfda783
AR
661 }
662
663 return item;
664}
665
666
667/* convenience */
4ff670a8 668-(void)clear
edfda783
AR
669{
670 int n;
671
672 for (n = [self numberOfItems]-1; n >= 0; n--)
673 {
674 NSMenuItem *item = [self itemAtIndex: n];
675 NSString *title = [item title];
4ff670a8 676 if (([title length] == 0 /* OSX 10.5 */
3988c7d6 677 || [ns_app_name isEqualToString: title] /* from 10.6 on */
4ff670a8 678 || [@"Apple" isEqualToString: title]) /* older */
edfda783
AR
679 && ![item isSeparatorItem])
680 continue;
681 [self removeItemAtIndex: n];
682 }
683}
684
685
686- (void)fillWithWidgetValue: (void *)wvptr
687{
688 widget_value *wv = (widget_value *)wvptr;
689
690 /* clear existing contents */
691 [self setMenuChangedMessagesEnabled: NO];
692 [self clear];
693
694 /* add new contents */
695 for (; wv != NULL; wv = wv->next)
696 {
15034960 697 NSMenuItem *item = [self addItemWithWidgetValue: wv];
edfda783
AR
698
699 if (wv->contents)
700 {
c96169a0 701 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
edfda783
AR
702
703 [self setSubmenu: submenu forItem: item];
704 [submenu fillWithWidgetValue: wv->contents];
705 [submenu release];
706 [item setAction: nil];
707 }
708 }
709
710 [self setMenuChangedMessagesEnabled: YES];
711#ifdef NS_IMPL_GNUSTEP
712 if ([[self window] isVisible])
713 [self sizeToFit];
714#else
715 if ([self supermenu] == nil)
716 [self sizeToFit];
717#endif
718}
719
720
721/* adds an empty submenu and returns it */
9c5bd55a 722- (EmacsMenu *)addSubmenuWithTitle: (const char *)title forFrame: (struct frame *)f
edfda783
AR
723{
724 NSString *titleStr = [NSString stringWithUTF8String: title];
15034960
AR
725 NSMenuItem *item = [self addItemWithTitle: titleStr
726 action: nil /*@selector (menuDown:) */
727 keyEquivalent: @""];
edfda783
AR
728 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
729 [self setSubmenu: submenu forItem: item];
730 [submenu release];
731 return submenu;
732}
733
734/* run a menu in popup mode */
735- (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
736 keymaps: (int)keymaps
737{
738 EmacsView *view = FRAME_NS_VIEW (f);
3d608a86
J
739 NSEvent *e, *event;
740 long retVal;
741
edfda783
AR
742/* p = [view convertPoint:p fromView: nil]; */
743 p.y = NSHeight ([view frame]) - p.y;
3d608a86
J
744 e = [[view window] currentEvent];
745 event = [NSEvent mouseEventWithType: NSRightMouseDown
746 location: p
747 modifierFlags: 0
748 timestamp: [e timestamp]
749 windowNumber: [[view window] windowNumber]
750 context: [e context]
751 eventNumber: 0/*[e eventNumber] */
752 clickCount: 1
753 pressure: 0];
edfda783
AR
754
755 context_menu_value = -1;
756 [NSMenu popUpContextMenu: self withEvent: event forView: view];
757 retVal = context_menu_value;
758 context_menu_value = 0;
facfbbbd
SM
759 return retVal > 0
760 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
761 : Qnil;
edfda783
AR
762}
763
764@end /* EmacsMenu */
765
766
767
768/* ==========================================================================
769
770 Context Menu: implementing functions
771
772 ========================================================================== */
773
ef7417fd
SM
774Lisp_Object
775ns_menu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps,
776 Lisp_Object title, char **error)
edfda783
AR
777{
778 EmacsMenu *pmenu;
edfda783 779 NSPoint p;
ef7417fd
SM
780 Lisp_Object window, tem, keymap;
781 int specpdl_count = SPECPDL_INDEX ();
edfda783
AR
782 widget_value *wv, *first_wv = 0;
783
ef7417fd 784 p.x = x; p.y = y;
edfda783
AR
785
786 /* now parse stage 2 as in ns_update_menubar */
787 wv = xmalloc_widget_value ();
788 wv->name = "contextmenu";
789 wv->value = 0;
790 wv->enabled = 1;
791 wv->button_type = BUTTON_TYPE_NONE;
792 wv->help = Qnil;
793 first_wv = wv;
794
edfda783 795#if 0
df2142db 796 /* FIXME: a couple of one-line differences prevent reuse */
edfda783
AR
797 wv = digest_single_submenu (0, menu_items_used, Qnil);
798#else
799 {
800 widget_value *save_wv = 0, *prev_wv = 0;
801 widget_value **submenu_stack
802 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
803/* Lisp_Object *subprefix_stack
804 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object)); */
805 int submenu_depth = 0;
806 int first_pane = 1;
807 int i;
808
809 /* Loop over all panes and items, filling in the tree. */
810 i = 0;
811 while (i < menu_items_used)
812 {
813 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
814 {
815 submenu_stack[submenu_depth++] = save_wv;
816 save_wv = prev_wv;
817 prev_wv = 0;
818 first_pane = 1;
819 i++;
820 }
821 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
822 {
823 prev_wv = save_wv;
824 save_wv = submenu_stack[--submenu_depth];
825 first_pane = 0;
826 i++;
827 }
828 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
829 && submenu_depth != 0)
830 i += MENU_ITEMS_PANE_LENGTH;
831 /* Ignore a nil in the item list.
832 It's meaningful only for dialog boxes. */
833 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
834 i += 1;
835 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
836 {
837 /* Create a new pane. */
838 Lisp_Object pane_name, prefix;
9c5bd55a 839 const char *pane_string;
edfda783
AR
840
841 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
842 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
843
844#ifndef HAVE_MULTILINGUAL_MENU
845 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
846 {
847 pane_name = ENCODE_MENU_STRING (pane_name);
848 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
849 }
850#endif
851 pane_string = (NILP (pane_name)
852 ? "" : (char *) SDATA (pane_name));
853 /* If there is just one top-level pane, put all its items directly
854 under the top-level menu. */
855 if (menu_items_n_panes == 1)
856 pane_string = "";
857
858 /* If the pane has a meaningful name,
859 make the pane a top-level menu item
860 with its items as a submenu beneath it. */
861 if (!keymaps && strcmp (pane_string, ""))
862 {
863 wv = xmalloc_widget_value ();
864 if (save_wv)
865 save_wv->next = wv;
866 else
867 first_wv->contents = wv;
868 wv->name = pane_string;
869 if (keymaps && !NILP (prefix))
870 wv->name++;
871 wv->value = 0;
872 wv->enabled = 1;
873 wv->button_type = BUTTON_TYPE_NONE;
874 wv->help = Qnil;
875 save_wv = wv;
876 prev_wv = 0;
877 }
878 else if (first_pane)
879 {
880 save_wv = wv;
881 prev_wv = 0;
882 }
883 first_pane = 0;
884 i += MENU_ITEMS_PANE_LENGTH;
885 }
886 else
887 {
888 /* Create a new item within current pane. */
889 Lisp_Object item_name, enable, descrip, def, type, selected, help;
890 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
891 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
892 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
893 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
894 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
895 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
896 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
897
898#ifndef HAVE_MULTILINGUAL_MENU
899 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
900 {
901 item_name = ENCODE_MENU_STRING (item_name);
902 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
903 }
904
905 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
906 {
907 descrip = ENCODE_MENU_STRING (descrip);
908 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
909 }
910#endif /* not HAVE_MULTILINGUAL_MENU */
911
912 wv = xmalloc_widget_value ();
913 if (prev_wv)
914 prev_wv->next = wv;
915 else
916 save_wv->contents = wv;
917 wv->name = (char *) SDATA (item_name);
918 if (!NILP (descrip))
919 wv->key = (char *) SDATA (descrip);
920 wv->value = 0;
921 /* If this item has a null value,
922 make the call_data null so that it won't display a box
923 when the mouse is on it. */
facfbbbd
SM
924 wv->call_data
925 = !NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0;
edfda783
AR
926 wv->enabled = !NILP (enable);
927
928 if (NILP (type))
929 wv->button_type = BUTTON_TYPE_NONE;
930 else if (EQ (type, QCtoggle))
931 wv->button_type = BUTTON_TYPE_TOGGLE;
932 else if (EQ (type, QCradio))
933 wv->button_type = BUTTON_TYPE_RADIO;
934 else
935 abort ();
936
937 wv->selected = !NILP (selected);
938
939 if (! STRINGP (help))
940 help = Qnil;
941
942 wv->help = help;
943
944 prev_wv = wv;
945
946 i += MENU_ITEMS_ITEM_LENGTH;
947 }
948 }
949 }
950#endif
951
952 if (!NILP (title))
953 {
954 widget_value *wv_title = xmalloc_widget_value ();
955 widget_value *wv_sep = xmalloc_widget_value ();
956
957 /* Maybe replace this separator with a bitmap or owner-draw item
958 so that it looks better. Having two separators looks odd. */
959 wv_sep->name = "--";
960 wv_sep->next = first_wv->contents;
961 wv_sep->help = Qnil;
962
963#ifndef HAVE_MULTILINGUAL_MENU
964 if (STRING_MULTIBYTE (title))
965 title = ENCODE_MENU_STRING (title);
966#endif
967
968 wv_title->name = (char *) SDATA (title);
15034960 969 wv_title->enabled = NO;
edfda783
AR
970 wv_title->button_type = BUTTON_TYPE_NONE;
971 wv_title->help = Qnil;
972 wv_title->next = wv_sep;
973 first_wv->contents = wv_title;
974 }
975
976 pmenu = [[EmacsMenu alloc] initWithTitle:
977 [NSString stringWithUTF8String: SDATA (title)]];
978 [pmenu fillWithWidgetValue: first_wv->contents];
979 free_menubar_widget_value_tree (first_wv);
ef7417fd 980 unbind_to (specpdl_count, Qnil);
edfda783 981
07b87a10 982 popup_activated_flag = 1;
edfda783 983 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
07b87a10 984 popup_activated_flag = 0;
edfda783
AR
985 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
986
edfda783
AR
987 return tem;
988}
989
990
edfda783
AR
991/* ==========================================================================
992
993 Toolbar: externally-called functions
994
995 ========================================================================== */
996
997void
998free_frame_tool_bar (FRAME_PTR f)
999/* --------------------------------------------------------------------------
1000 Under NS we just hide the toolbar until it might be needed again.
1001 -------------------------------------------------------------------------- */
1002{
1f984e12 1003 BLOCK_INPUT;
edfda783 1004 [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
581a8100 1005 FRAME_TOOLBAR_HEIGHT (f) = 0;
1f984e12 1006 UNBLOCK_INPUT;
edfda783
AR
1007}
1008
1009void
1010update_frame_tool_bar (FRAME_PTR f)
1011/* --------------------------------------------------------------------------
1012 Update toolbar contents
1013 -------------------------------------------------------------------------- */
1014{
1015 int i;
581a8100
J
1016 EmacsView *view = FRAME_NS_VIEW (f);
1017 NSWindow *window = [view window];
1018 EmacsToolbar *toolbar = [view toolbar];
edfda783 1019
1f984e12 1020 BLOCK_INPUT;
edfda783
AR
1021 [toolbar clearActive];
1022
1023 /* update EmacsToolbar as in GtkUtils, build items list */
1024 for (i = 0; i < f->n_tool_bar_items; ++i)
1025 {
1026#define TOOLPROP(IDX) AREF (f->tool_bar_items, \
1027 i * TOOL_BAR_ITEM_NSLOTS + (IDX))
1028
1029 BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
1030 BOOL selected_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_SELECTED_P));
1031 int idx;
1032 int img_id;
1033 struct image *img;
1034 Lisp_Object image;
1035 Lisp_Object helpObj;
9c5bd55a 1036 const char *helpText;
edfda783
AR
1037
1038 /* If image is a vector, choose the image according to the
1039 button state. */
1040 image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
1041 if (VECTORP (image))
1042 {
1043 /* NS toolbar auto-computes disabled and selected images */
1044 idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
1045 xassert (ASIZE (image) >= idx);
1046 image = AREF (image, idx);
1047 }
1048 else
1049 {
1050 idx = -1;
1051 }
1052 /* Ignore invalid image specifications. */
1053 if (!valid_image_p (image))
1054 {
1055 NSLog (@"Invalid image for toolbar item");
1056 continue;
1057 }
1058
1059 img_id = lookup_image (f, image);
1060 img = IMAGE_FROM_ID (f, img_id);
1061 prepare_image_for_display (f, img);
1062
1063 if (img->load_failed_p || img->pixmap == nil)
1064 {
1065 NSLog (@"Could not prepare toolbar image for display.");
1066 continue;
1067 }
1068
1069 helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
1070 if (NILP (helpObj))
1071 helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
1072 helpText = NILP (helpObj) ? "" : (char *)SDATA (helpObj);
1073
1074 [toolbar addDisplayItemWithImage: img->pixmap idx: i helpText: helpText
1075 enabled: enabled_p];
1076#undef TOOLPROP
1077 }
1078
1079 if (![toolbar isVisible])
1080 [toolbar setVisible: YES];
1081
1082 if ([toolbar changed])
1083 {
1084 /* inform app that toolbar has changed */
1085 NSDictionary *dict = [toolbar configurationDictionary];
1086 NSMutableDictionary *newDict = [dict mutableCopy];
1087 NSEnumerator *keys = [[dict allKeys] objectEnumerator];
1088 NSObject *key;
1089 while ((key = [keys nextObject]) != nil)
1090 {
1091 NSObject *val = [dict objectForKey: key];
1092 if ([val isKindOfClass: [NSArray class]])
1093 {
1094 [newDict setObject:
1095 [toolbar toolbarDefaultItemIdentifiers: toolbar]
1096 forKey: key];
1097 break;
1098 }
1099 }
1100 [toolbar setConfigurationFromDictionary: newDict];
1101 [newDict release];
1102 }
1103
581a8100
J
1104 FRAME_TOOLBAR_HEIGHT (f) =
1105 NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1106 - FRAME_NS_TITLEBAR_HEIGHT (f);
1f984e12 1107 UNBLOCK_INPUT;
edfda783
AR
1108}
1109
1110
1111/* ==========================================================================
1112
1113 Toolbar: class implementation
1114
1115 ========================================================================== */
1116
1117@implementation EmacsToolbar
1118
1119- initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
1120{
1121 self = [super initWithIdentifier: identifier];
1122 emacsView = view;
1123 [self setDisplayMode: NSToolbarDisplayModeIconOnly];
1124 [self setSizeMode: NSToolbarSizeModeSmall];
1125 [self setDelegate: self];
1126 identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
1127 activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
1128 prevEnablement = enablement = 0L;
1129 return self;
1130}
1131
1132- (void)dealloc
1133{
1134 [prevIdentifiers release];
1135 [activeIdentifiers release];
1136 [identifierToItem release];
1137 [super dealloc];
1138}
1139
1140- (void) clearActive
1141{
1142 [prevIdentifiers release];
1143 prevIdentifiers = [activeIdentifiers copy];
1144 [activeIdentifiers removeAllObjects];
1145 prevEnablement = enablement;
1146 enablement = 0L;
1147}
1148
1149- (BOOL) changed
1150{
1151 return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
1152 enablement == prevEnablement ? NO : YES;
1153}
1154
1155- (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx
9c5bd55a 1156 helpText: (const char *)help enabled: (BOOL)enabled
edfda783
AR
1157{
1158 /* 1) come up w/identifier */
facfbbbd
SM
1159 NSString *identifier
1160 = [NSString stringWithFormat: @"%u", [img hash]];
edfda783
AR
1161
1162 /* 2) create / reuse item */
1163 NSToolbarItem *item = [identifierToItem objectForKey: identifier];
1164 if (item == nil)
1165 {
1166 item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
1167 autorelease];
1168 [item setImage: img];
f3f08c28 1169 [item setToolTip: [NSString stringWithUTF8String: help]];
edfda783
AR
1170 [item setTarget: emacsView];
1171 [item setAction: @selector (toolbarClicked:)];
1172 }
1173
1174 [item setTag: idx];
1175 [item setEnabled: enabled];
1176
1177 /* 3) update state */
1178 [identifierToItem setObject: item forKey: identifier];
1179 [activeIdentifiers addObject: identifier];
1180 enablement = (enablement << 1) | (enabled == YES);
1181}
1182
1183/* This overrides super's implementation, which automatically sets
1184 all items to enabled state (for some reason). */
1185- (void)validateVisibleItems { }
1186
1187
1188/* delegate methods */
1189
1190- (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
1191 itemForItemIdentifier: (NSString *)itemIdentifier
1192 willBeInsertedIntoToolbar: (BOOL)flag
1193{
1194 /* look up NSToolbarItem by identifier and return... */
1195 return [identifierToItem objectForKey: itemIdentifier];
1196}
1197
1198- (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
1199{
1200 /* return entire set.. */
1201 return activeIdentifiers;
1202}
1203
1204/* for configuration palette (not yet supported) */
1205- (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
1206{
1207 /* return entire set... */
1208 return [identifierToItem allKeys];
1209}
1210
1211/* optional and unneeded */
1212/* - toolbarWillAddItem: (NSNotification *)notification { } */
1213/* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1214/* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1215
1216@end /* EmacsToolbar */
1217
1218
1219
1220/* ==========================================================================
1221
1222 Tooltip: class implementation
1223
1224 ========================================================================== */
1225
1226/* Needed because NeXTstep does not provide enough control over tooltip
1227 display. */
1228@implementation EmacsTooltip
1229
1230- init
1231{
1232 NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
1233 blue: 0.792 alpha: 0.95];
1234 NSFont *font = [NSFont toolTipsFontOfSize: 0];
1235 NSFont *sfont = [font screenFont];
1236 int height = [sfont ascender] - [sfont descender];
1237/*[font boundingRectForFont].size.height; */
1238 NSRect r = NSMakeRect (0, 0, 100, height+6);
1239
1240 textField = [[NSTextField alloc] initWithFrame: r];
1241 [textField setFont: font];
1242 [textField setBackgroundColor: col];
1243
1244 [textField setEditable: NO];
1245 [textField setSelectable: NO];
1246 [textField setBordered: YES];
1247 [textField setBezeled: YES];
1248 [textField setDrawsBackground: YES];
1249
1250 win = [[NSWindow alloc]
1251 initWithContentRect: [textField frame]
1252 styleMask: 0
1253 backing: NSBackingStoreBuffered
1254 defer: YES];
1255 [win setReleasedWhenClosed: NO];
1256 [win setDelegate: self];
1257 [[win contentView] addSubview: textField];
1258/* [win setBackgroundColor: col]; */
1259 [win setOpaque: NO];
1260
1261 return self;
1262}
1263
1264- (void) dealloc
1265{
1266 [win close];
1267 [win release];
1268 [textField release];
1269 [super dealloc];
1270}
1271
1272- (void) setText: (char *)text
1273{
1274 NSString *str = [NSString stringWithUTF8String: text];
1275 NSRect r = [textField frame];
5ee6f629
DR
1276 NSSize textSize = [str sizeWithAttributes:
1277 [NSDictionary dictionaryWithObject: [[textField font] screenFont]
1278 forKey: NSFontAttributeName]];
1279 NSSize padSize = [[[textField font] screenFont]
1280 boundingRectForFont].size;
1281
1282 r.size.width = textSize.width + padSize.width/2;
1283 r.size.height = textSize.height + padSize.height/2;
edfda783
AR
1284 [textField setFrame: r];
1285 [textField setStringValue: str];
1286}
1287
1288- (void) showAtX: (int)x Y: (int)y for: (int)seconds
1289{
1290 NSRect wr = [win frame];
1291
1292 wr.origin = NSMakePoint (x, y);
1293 wr.size = [textField frame].size;
1294
1295 [win setFrame: wr display: YES];
1296 [win orderFront: self];
1297 [win display];
1298 timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
1299 selector: @selector (hide)
1300 userInfo: nil repeats: NO];
1301 [timer retain];
1302}
1303
1304- (void) hide
1305{
1306 [win close];
1307 if (timer != nil)
1308 {
1309 if ([timer isValid])
1310 [timer invalidate];
1311 [timer release];
1312 timer = nil;
1313 }
1314}
1315
1316- (BOOL) isActive
1317{
1318 return timer != nil;
1319}
1320
1321- (NSRect) frame
1322{
1323 return [textField frame];
1324}
1325
1326@end /* EmacsTooltip */
1327
1328
1329
1330/* ==========================================================================
1331
1332 Popup Dialog: implementing functions
1333
1334 ========================================================================== */
1335
c96169a0
AR
1336
1337static Lisp_Object
1338pop_down_menu (Lisp_Object arg)
1339{
1340 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
ba301db3
AR
1341 if (popup_activated_flag)
1342 {
1343 popup_activated_flag = 0;
1344 BLOCK_INPUT;
1345 [NSApp endModalSession: popupSession];
1346 [((EmacsDialogPanel *) (p->pointer)) close];
1347 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
1348 UNBLOCK_INPUT;
1349 }
c96169a0
AR
1350 return Qnil;
1351}
1352
1353
edfda783
AR
1354Lisp_Object
1355ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1356{
1357 id dialog;
1358 Lisp_Object window, tem;
1359 struct frame *f;
1360 NSPoint p;
1361 BOOL isQ;
1362
1363 NSTRACE (x-popup-dialog);
1364
1365 check_ns ();
1366
1367 isQ = NILP (header);
1368
8612b71a
AR
1369 if (EQ (position, Qt)
1370 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1371 || EQ (XCAR (position), Qtool_bar))))
edfda783
AR
1372 {
1373 window = selected_window;
1374 }
1375 else if (CONSP (position))
1376 {
1377 Lisp_Object tem;
1378 tem = Fcar (position);
1379 if (XTYPE (tem) == Lisp_Cons)
1380 window = Fcar (Fcdr (position));
1381 else
1382 {
1383 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1384 window = Fcar (tem); /* POSN_WINDOW (tem) */
1385 }
1386 }
8612b71a 1387 else if (WINDOWP (position) || FRAMEP (position))
edfda783
AR
1388 {
1389 window = position;
1390 }
1391 else
8612b71a
AR
1392 window = Qnil;
1393
edfda783
AR
1394 if (FRAMEP (window))
1395 f = XFRAME (window);
8612b71a 1396 else if (WINDOWP (window))
edfda783
AR
1397 {
1398 CHECK_LIVE_WINDOW (window);
1399 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1400 }
8612b71a
AR
1401 else
1402 CHECK_WINDOW (window);
1403
edfda783
AR
1404 p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
1405 p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
3175b12a
AR
1406
1407 BLOCK_INPUT;
edfda783
AR
1408 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1409 isQuestion: isQ];
c96169a0
AR
1410 {
1411 int specpdl_count = SPECPDL_INDEX ();
1412 record_unwind_protect (pop_down_menu, make_save_value (dialog, 0));
1413 popup_activated_flag = 1;
1414 tem = [dialog runDialogAt: p];
ba301db3 1415 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
c96169a0 1416 }
3175b12a 1417 UNBLOCK_INPUT;
c96169a0 1418
edfda783
AR
1419 return tem;
1420}
1421
1422
1423/* ==========================================================================
1424
1425 Popup Dialog: class implementation
1426
1427 ========================================================================== */
1428
1429@interface FlippedView : NSView
1430{
1431}
1432@end
1433
1434@implementation FlippedView
1435- (BOOL)isFlipped
1436{
1437 return YES;
1438}
1439@end
1440
1441@implementation EmacsDialogPanel
1442
1443#define SPACER 8.0
1444#define ICONSIZE 64.0
1445#define TEXTHEIGHT 20.0
1446#define MINCELLWIDTH 90.0
1447
f3f08c28 1448- initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
edfda783
AR
1449 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1450{
1451 NSSize spacing = {SPACER, SPACER};
1452 NSRect area;
1453 char this_cmd_name[80];
facfbbbd 1454 id cell;
edfda783
AR
1455 static NSImageView *imgView;
1456 static FlippedView *contentView;
1457
1458 if (imgView == nil)
1459 {
1460 NSImage *img;
1461 area.origin.x = 3*SPACER;
1462 area.origin.y = 2*SPACER;
1463 area.size.width = ICONSIZE;
1464 area.size.height= ICONSIZE;
1465 img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
1466 [img setScalesWhenResized: YES];
1467 [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
1468 imgView = [[NSImageView alloc] initWithFrame: area];
1469 [imgView setImage: img];
1470 [imgView setEditable: NO];
1471 [img release];
1472 }
1473
1474 aStyle = NSTitledWindowMask;
1475 flag = YES;
1476 rows = 0;
1477 cols = 1;
1478 [super initWithContentRect: contentRect styleMask: aStyle
1479 backing: backingType defer: flag];
1480 contentView = [[FlippedView alloc] initWithFrame: [[self contentView] frame]];
1481 [self setContentView: contentView];
1482
1483 [[self contentView] setAutoresizesSubviews: YES];
1484
1485 [[self contentView] addSubview: imgView];
1486 [self setTitle: @""];
1487
1488 area.origin.x += ICONSIZE+2*SPACER;
1489/* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1490 area.size.width = 400;
1491 area.size.height= TEXTHEIGHT;
1492 command = [[[NSTextField alloc] initWithFrame: area] autorelease];
1493 [[self contentView] addSubview: command];
3988c7d6 1494 [command setStringValue: ns_app_name];
edfda783
AR
1495 [command setDrawsBackground: NO];
1496 [command setBezeled: NO];
1497 [command setSelectable: NO];
1498 [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
1499
1500/* area.origin.x = ICONSIZE+2*SPACER;
1501 area.origin.y = TEXTHEIGHT + 2*SPACER;
1502 area.size.width = 400;
1503 area.size.height= 2;
1504 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1505 [[self contentView] addSubview: tem];
1506 [tem setTitlePosition: NSNoTitle];
1507 [tem setAutoresizingMask: NSViewWidthSizable];*/
1508
1509/* area.origin.x = ICONSIZE+2*SPACER; */
1510 area.origin.y += TEXTHEIGHT+SPACER;
1511 area.size.width = 400;
1512 area.size.height= TEXTHEIGHT;
1513 title = [[[NSTextField alloc] initWithFrame: area] autorelease];
1514 [[self contentView] addSubview: title];
1515 [title setDrawsBackground: NO];
1516 [title setBezeled: NO];
1517 [title setSelectable: NO];
1518 [title setFont: [NSFont systemFontOfSize: 11.0]];
1519
1520 cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
1521 [cell setBordered: NO];
1522 [cell setEnabled: NO];
1523 [cell setCellAttribute: NSCellIsInsetButton to: 8];
1524 [cell setBezelStyle: NSRoundedBezelStyle];
1525
1526 matrix = [[NSMatrix alloc] initWithFrame: contentRect
1527 mode: NSHighlightModeMatrix
1528 prototype: cell
1529 numberOfRows: 0
1530 numberOfColumns: 1];
1531 [[self contentView] addSubview: matrix];
1532 [matrix release];
1533 [matrix setFrameOrigin: NSMakePoint (area.origin.x,
1534 area.origin.y + (TEXTHEIGHT+3*SPACER))];
1535 [matrix setIntercellSpacing: spacing];
1536
1537 [self setOneShot: YES];
1538 [self setReleasedWhenClosed: YES];
1539 [self setHidesOnDeactivate: YES];
1540 return self;
1541}
1542
1543
1544- (BOOL)windowShouldClose: (id)sender
1545{
facfbbbd 1546 [NSApp stopModalWithCode: XHASH (Qnil)]; // FIXME: BIG UGLY HACK!!
edfda783
AR
1547 return NO;
1548}
1549
1550
1551void process_dialog (id window, Lisp_Object list)
1552{
1553 Lisp_Object item;
1554 int row = 0;
1555
1556 for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
1557 {
1558 item = XCAR (list);
1559 if (XTYPE (item) == Lisp_String)
1560 {
86fa089e 1561 [window addString: SDATA (item) row: row++];
edfda783
AR
1562 }
1563 else if (XTYPE (item) == Lisp_Cons)
1564 {
86fa089e 1565 [window addButton: SDATA (XCAR (item))
edfda783
AR
1566 value: XCDR (item) row: row++];
1567 }
1568 else if (NILP (item))
1569 {
1570 [window addSplit];
1571 row = 0;
1572 }
1573 }
1574}
1575
1576
1577- addButton: (char *)str value: (Lisp_Object)val row: (int)row
1578{
1579 id cell;
1580
1581 if (row >= rows)
1582 {
1583 [matrix addRow];
1584 rows++;
1585 }
1586 cell = [matrix cellAtRow: row column: cols-1];
1587 [cell setTarget: self];
1588 [cell setAction: @selector (clicked: )];
1589 [cell setTitle: [NSString stringWithUTF8String: str]];
facfbbbd 1590 [cell setTag: XHASH (val)]; // FIXME: BIG UGLY HACK!!
edfda783
AR
1591 [cell setBordered: YES];
1592 [cell setEnabled: YES];
1593
1594 return self;
1595}
1596
1597
1598- addString: (char *)str row: (int)row
1599{
1600 id cell;
1601
1602 if (row >= rows)
1603 {
1604 [matrix addRow];
1605 rows++;
1606 }
1607 cell = [matrix cellAtRow: row column: cols-1];
1608 [cell setTitle: [NSString stringWithUTF8String: str]];
1609 [cell setBordered: YES];
1610 [cell setEnabled: NO];
1611
1612 return self;
1613}
1614
1615
1616- addSplit
1617{
1618 [matrix addColumn];
1619 cols++;
1620 return self;
1621}
1622
1623
1624- clicked: sender
1625{
1626 NSArray *sellist = nil;
facfbbbd 1627 EMACS_INT seltag;
edfda783
AR
1628
1629 sellist = [sender selectedCells];
1630 if ([sellist count]<1)
1631 return self;
1632
facfbbbd 1633 seltag = [[sellist objectAtIndex: 0] tag];
4e622592 1634 if (seltag != XHASH (Qundefined)) // FIXME: BIG UGLY HACK!!
edfda783
AR
1635 [NSApp stopModalWithCode: seltag];
1636 return self;
1637}
1638
1639
1640- initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
1641{
1642 Lisp_Object head;
1643 [super init];
1644
1645 if (XTYPE (contents) == Lisp_Cons)
1646 {
1647 head = Fcar (contents);
1648 process_dialog (self, Fcdr (contents));
1649 }
1650 else
1651 head = contents;
1652
1653 if (XTYPE (head) == Lisp_String)
1654 [title setStringValue:
86fa089e 1655 [NSString stringWithUTF8String: SDATA (head)]];
edfda783
AR
1656 else if (isQ == YES)
1657 [title setStringValue: @"Question"];
1658 else
1659 [title setStringValue: @"Information"];
1660
1661 {
1662 int i;
1663 NSRect r, s, t;
1664
1665 if (cols == 1 && rows > 1) /* Never told where to split */
1666 {
1667 [matrix addColumn];
1668 for (i = 0; i<rows/2; i++)
1669 {
1670 [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
1671 atRow: i column: 1];
1672 [matrix removeRow: (rows+1)/2];
1673 }
1674 }
1675
1676 [matrix sizeToFit];
1677 {
1678 NSSize csize = [matrix cellSize];
1679 if (csize.width < MINCELLWIDTH)
1680 {
1681 csize.width = MINCELLWIDTH;
1682 [matrix setCellSize: csize];
1683 [matrix sizeToCells];
1684 }
1685 }
1686
1687 [title sizeToFit];
1688 [command sizeToFit];
1689
1690 t = [matrix frame];
1691 r = [title frame];
1692 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1693 {
1694 t.origin.x = r.origin.x;
1695 t.size.width = r.size.width;
1696 }
1697 r = [command frame];
1698 if (r.size.width+r.origin.x > t.size.width+t.origin.x)
1699 {
1700 t.origin.x = r.origin.x;
1701 t.size.width = r.size.width;
1702 }
1703
1704 r = [self frame];
1705 s = [(NSView *)[self contentView] frame];
1706 r.size.width += t.origin.x+t.size.width +2*SPACER-s.size.width;
1707 r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
1708 [self setFrame: r display: NO];
1709 }
1710
1711 return self;
1712}
1713
1714
1715- (void)dealloc
1716{
1717 { [super dealloc]; return; };
1718}
1719
1720
1721- (Lisp_Object)runDialogAt: (NSPoint)p
1722{
b2e40a75 1723 NSInteger ret;
641d87f5 1724 extern EMACS_TIME timer_check (int do_it_now); /* TODO: add to a header */
edfda783 1725
ba301db3 1726 /* initiate a session that will be ended by pop_down_menu */
3175b12a 1727 popupSession = [NSApp beginModalSessionForWindow: self];
c96169a0 1728 while (popup_activated_flag
3175b12a
AR
1729 && (ret = [NSApp runModalSession: popupSession])
1730 == NSRunContinuesResponse)
edfda783 1731 {
3175b12a
AR
1732 /* Run this for timers.el, indep of atimers; might not return.
1733 TODO: use return value to avoid calling every iteration. */
1734 timer_check (1);
1735 [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
edfda783 1736 }
edfda783 1737
3175b12a 1738 { /* FIXME: BIG UGLY HACK!!! */
facfbbbd
SM
1739 Lisp_Object tmp;
1740 *(EMACS_INT*)(&tmp) = ret;
1741 return tmp;
1742 }
edfda783
AR
1743}
1744
1745@end
1746
1747
edfda783
AR
1748/* ==========================================================================
1749
1750 Lisp definitions
1751
1752 ========================================================================== */
1753
1754DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
eb6f7ed0 1755 doc: /* Cause the NS menu to be re-calculated. */)
5842a27b 1756 (void)
edfda783
AR
1757{
1758 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1759 return Qnil;
1760}
1761
1762
edfda783
AR
1763DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1764 doc: /* Pop up a dialog box and return user's selection.
1765POSITION specifies which frame to use.
1766This is normally a mouse button event or a window or frame.
1767If POSITION is t, it means to use the frame the mouse is on.
1768The dialog box appears in the middle of the specified frame.
1769
1770CONTENTS specifies the alternatives to display in the dialog box.
1771It is a list of the form (DIALOG ITEM1 ITEM2...).
1772Each ITEM is a cons cell (STRING . VALUE).
1773The return value is VALUE from the chosen item.
1774
1775An ITEM may also be just a string--that makes a nonselectable item.
1776An ITEM may also be nil--that means to put all preceding items
1777on the left of the dialog box and all following items on the right.
1778\(By default, approximately half appear on each side.)
1779
1780If HEADER is non-nil, the frame title for the box is "Information",
1781otherwise it is "Question".
1782
1783If the user gets rid of the dialog box without making a valid choice,
1784for instance using the window manager, then this produces a quit and
1785`x-popup-dialog' does not return. */)
5842a27b 1786 (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
edfda783
AR
1787{
1788 return ns_popup_dialog (position, contents, header);
1789}
1790
07b87a10
AR
1791DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1792 doc: /* Return t if a menu or popup dialog is active. */)
5842a27b 1793 (void)
07b87a10
AR
1794{
1795 return popup_activated () ? Qt : Qnil;
1796}
edfda783
AR
1797
1798/* ==========================================================================
1799
1800 Lisp interface declaration
1801
1802 ========================================================================== */
1803
1804void
3d608a86 1805syms_of_nsmenu (void)
edfda783 1806{
edfda783
AR
1807 defsubr (&Sx_popup_dialog);
1808 defsubr (&Sns_reset_menu);
07b87a10 1809 defsubr (&Smenu_or_popup_active_p);
edfda783
AR
1810
1811 Qdebug_on_next_call = intern ("debug-on-next-call");
1812 staticpro (&Qdebug_on_next_call);
1813}
0ae1e5e5 1814
2f8e74bb 1815// arch-tag: 75773656-52e5-4c44-a398-47bd87b32619