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