Fix -Wwrite-strings warnings in fakemail.c.
[bpt/emacs.git] / src / nsmenu.m
... / ...
CommitLineData
1/* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
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
17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
18
19/*
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
24/* This should be the first include, as it may set up #defines affecting
25 interpretation of even the system includes. */
26#include "config.h"
27#include <setjmp.h>
28
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"
39#include "menu.h"
40
41#define NSMENUPROFILE 0
42
43#if NSMENUPROFILE
44#include <sys/timeb.h>
45#include <sys/types.h>
46#endif
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
64extern Lisp_Object Qundefined, Qmenu_enable, Qmenu_bar_update_hook;
65extern Lisp_Object QCtoggle, QCradio;
66
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;
72EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
73
74/* Nonzero means a menu is currently active. */
75static int popup_activated_flag;
76static NSModalSession popupSession;
77
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
89/* FIXME: not currently used, but should normalize with other terms. */
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
107int
108popup_activated (void)
109{
110 return popup_activated_flag;
111}
112
113
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 -------------------------------------------------------------------------- */
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
135#if NSMENUPROFILE
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 {
159 menu = [[EmacsMenu alloc] initWithTitle: ns_app_name];
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
169#if NSMENUPROFILE
170 ftime (&tb);
171 t = -(1000*tb.time+tb.millitm);
172#endif
173
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
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. */
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 memcpy (previous_items, XVECTOR (f->menu_bar_vector)->contents,
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
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)))
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 */
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 */
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++)
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 */
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
325#if NSMENUPROFILE
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 */
340 /* FIXME: this is not correct for single-submenu case */
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
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
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 {
416 string = AREF (items, 4*i+1);
417
418 if (EQ (string, make_number (0))) // FIXME: Why??? --Stef
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
484#if NSMENUPROFILE
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 ( const char *name)
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{
568 NSEvent *event;
569 if (!FRAME_LIVE_P (frame))
570 return;
571 event = [[FRAME_NS_VIEW (frame) window] currentEvent];
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. */
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)
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
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.) */
603-(NSString *)parseKeyEquiv: (const char *)key
604{
605 const char *tpos = key;
606 keyEquivModMask = NSCommandKeyMask;
607
608 if (!key || !strlen (key))
609 return @"";
610
611 while (*tpos == ' ' || *tpos == '(')
612 tpos++;
613 if ((*tpos == 's') && (*(tpos+1) == '-'))
614 {
615 return [NSString stringWithFormat: @"%c", tpos[2]];
616 }
617 keyEquivModMask = 0; /* signal */
618 return [NSString stringWithUTF8String: tpos];
619}
620
621
622- (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
623{
624 NSMenuItem *item;
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];
640#ifdef NS_IMPL_COCOA
641 /* OS X just ignores modifier strings longer than one character */
642 if (keyEquivModMask == 0)
643 title = [title stringByAppendingFormat: @" (%@)", keyEq];
644#endif
645
646 item = [self addItemWithTitle: (NSString *)title
647 action: @selector (menuDown:)
648 keyEquivalent: keyEq];
649 [item setKeyEquivalentModifierMask: keyEquivModMask];
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
660 [item setTag: (NSInteger)wv->call_data];
661 }
662
663 return item;
664}
665
666
667/* convenience */
668-(void)clear
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];
676 if (([title length] == 0 /* OSX 10.5 */
677 || [ns_app_name isEqualToString: title] /* from 10.6 on */
678 || [@"Apple" isEqualToString: title]) /* older */
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 {
697 NSMenuItem *item = [self addItemWithWidgetValue: wv];
698
699 if (wv->contents)
700 {
701 EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
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 */
722- (EmacsMenu *)addSubmenuWithTitle: (const char *)title forFrame: (struct frame *)f
723{
724 NSString *titleStr = [NSString stringWithUTF8String: title];
725 NSMenuItem *item = [self addItemWithTitle: titleStr
726 action: nil /*@selector (menuDown:) */
727 keyEquivalent: @""];
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);
739 NSEvent *e, *event;
740 long retVal;
741
742/* p = [view convertPoint:p fromView: nil]; */
743 p.y = NSHeight ([view frame]) - p.y;
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];
754
755 context_menu_value = -1;
756 [NSMenu popUpContextMenu: self withEvent: event forView: view];
757 retVal = context_menu_value;
758 context_menu_value = 0;
759 return retVal > 0
760 ? find_and_return_menu_selection (f, keymaps, (void *)retVal)
761 : Qnil;
762}
763
764@end /* EmacsMenu */
765
766
767
768/* ==========================================================================
769
770 Context Menu: implementing functions
771
772 ========================================================================== */
773
774Lisp_Object
775ns_menu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps,
776 Lisp_Object title, char **error)
777{
778 EmacsMenu *pmenu;
779 NSPoint p;
780 Lisp_Object window, tem, keymap;
781 int specpdl_count = SPECPDL_INDEX ();
782 widget_value *wv, *first_wv = 0;
783
784 p.x = x; p.y = y;
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
795#if 0
796 /* FIXME: a couple of one-line differences prevent reuse */
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;
839 const char *pane_string;
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. */
924 wv->call_data
925 = !NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0;
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);
969 wv_title->enabled = NO;
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);
980 unbind_to (specpdl_count, Qnil);
981
982 popup_activated_flag = 1;
983 tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
984 popup_activated_flag = 0;
985 [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
986
987 return tem;
988}
989
990
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{
1003 BLOCK_INPUT;
1004 [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
1005 FRAME_TOOLBAR_HEIGHT (f) = 0;
1006 UNBLOCK_INPUT;
1007}
1008
1009void
1010update_frame_tool_bar (FRAME_PTR f)
1011/* --------------------------------------------------------------------------
1012 Update toolbar contents
1013 -------------------------------------------------------------------------- */
1014{
1015 int i;
1016 EmacsView *view = FRAME_NS_VIEW (f);
1017 NSWindow *window = [view window];
1018 EmacsToolbar *toolbar = [view toolbar];
1019
1020 BLOCK_INPUT;
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;
1036 const char *helpText;
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
1104 FRAME_TOOLBAR_HEIGHT (f) =
1105 NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
1106 - FRAME_NS_TITLEBAR_HEIGHT (f);
1107 UNBLOCK_INPUT;
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
1156 helpText: (const char *)help enabled: (BOOL)enabled
1157{
1158 /* 1) come up w/identifier */
1159 NSString *identifier
1160 = [NSString stringWithFormat: @"%u", [img hash]];
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];
1169 [item setToolTip: [NSString stringWithUTF8String: help]];
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];
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;
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
1336
1337static Lisp_Object
1338pop_down_menu (Lisp_Object arg)
1339{
1340 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
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 }
1350 return Qnil;
1351}
1352
1353
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
1369 if (EQ (position, Qt)
1370 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1371 || EQ (XCAR (position), Qtool_bar))))
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 }
1387 else if (WINDOWP (position) || FRAMEP (position))
1388 {
1389 window = position;
1390 }
1391 else
1392 window = Qnil;
1393
1394 if (FRAMEP (window))
1395 f = XFRAME (window);
1396 else if (WINDOWP (window))
1397 {
1398 CHECK_LIVE_WINDOW (window);
1399 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1400 }
1401 else
1402 CHECK_WINDOW (window);
1403
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;
1406
1407 BLOCK_INPUT;
1408 dialog = [[EmacsDialogPanel alloc] initFromContents: contents
1409 isQuestion: isQ];
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];
1415 unbind_to (specpdl_count, Qnil); /* calls pop_down_menu */
1416 }
1417 UNBLOCK_INPUT;
1418
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
1448- initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
1449 backing: (NSBackingStoreType)backingType defer: (BOOL)flag
1450{
1451 NSSize spacing = {SPACER, SPACER};
1452 NSRect area;
1453 char this_cmd_name[80];
1454 id cell;
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];
1494 [command setStringValue: ns_app_name];
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{
1546 [NSApp stopModalWithCode: XHASH (Qnil)]; // FIXME: BIG UGLY HACK!!
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 {
1561 [window addString: SDATA (item) row: row++];
1562 }
1563 else if (XTYPE (item) == Lisp_Cons)
1564 {
1565 [window addButton: SDATA (XCAR (item))
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]];
1590 [cell setTag: XHASH (val)]; // FIXME: BIG UGLY HACK!!
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;
1627 EMACS_INT seltag;
1628
1629 sellist = [sender selectedCells];
1630 if ([sellist count]<1)
1631 return self;
1632
1633 seltag = [[sellist objectAtIndex: 0] tag];
1634 if (seltag != XHASH (Qundefined)) // FIXME: BIG UGLY HACK!!
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:
1655 [NSString stringWithUTF8String: SDATA (head)]];
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{
1723 NSInteger ret;
1724 extern EMACS_TIME timer_check (int do_it_now); /* TODO: add to a header */
1725
1726 /* initiate a session that will be ended by pop_down_menu */
1727 popupSession = [NSApp beginModalSessionForWindow: self];
1728 while (popup_activated_flag
1729 && (ret = [NSApp runModalSession: popupSession])
1730 == NSRunContinuesResponse)
1731 {
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]];
1736 }
1737
1738 { /* FIXME: BIG UGLY HACK!!! */
1739 Lisp_Object tmp;
1740 *(EMACS_INT*)(&tmp) = ret;
1741 return tmp;
1742 }
1743}
1744
1745@end
1746
1747
1748/* ==========================================================================
1749
1750 Lisp definitions
1751
1752 ========================================================================== */
1753
1754DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
1755 doc: /* Cause the NS menu to be re-calculated. */)
1756 (void)
1757{
1758 set_frame_menubar (SELECTED_FRAME (), 1, 0);
1759 return Qnil;
1760}
1761
1762
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. */)
1786 (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1787{
1788 return ns_popup_dialog (position, contents, header);
1789}
1790
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. */)
1793 (void)
1794{
1795 return popup_activated () ? Qt : Qnil;
1796}
1797
1798/* ==========================================================================
1799
1800 Lisp interface declaration
1801
1802 ========================================================================== */
1803
1804void
1805syms_of_nsmenu (void)
1806{
1807 defsubr (&Sx_popup_dialog);
1808 defsubr (&Sns_reset_menu);
1809 defsubr (&Smenu_or_popup_active_p);
1810
1811 Qdebug_on_next_call = intern ("debug-on-next-call");
1812 staticpro (&Qdebug_on_next_call);
1813}
1814
1815// arch-tag: 75773656-52e5-4c44-a398-47bd87b32619