Merge from emacs--rel--22
[bpt/emacs.git] / src / macmenu.c
CommitLineData
4036ffb9 1/* Menu support for GNU Emacs on Mac OS.
0b5538bd 2 Copyright (C) 2000, 2001, 2002, 2003, 2004,
4e6835db 3 2005, 2006, 2007 Free Software Foundation, Inc.
1a578e9b
AC
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
684d6f5b 9the Free Software Foundation; either version 3, or (at your option)
1a578e9b
AC
10any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs; see the file COPYING. If not, write to
4fc5845f
LK
19the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20Boston, MA 02110-1301, USA. */
1a578e9b 21
e0f712ba 22/* Contributed by Andrew Choi (akochoi@mac.com). */
1a578e9b
AC
23
24#include <config.h>
1a578e9b
AC
25
26#include <stdio.h>
4036ffb9 27
1a578e9b
AC
28#include "lisp.h"
29#include "termhooks.h"
e0f712ba
AC
30#include "keyboard.h"
31#include "keymap.h"
1a578e9b
AC
32#include "frame.h"
33#include "window.h"
1a578e9b
AC
34#include "blockinput.h"
35#include "buffer.h"
36#include "charset.h"
37#include "coding.h"
38
04b5475b 39#if !TARGET_API_MAC_CARBON
1a578e9b
AC
40#include <MacTypes.h>
41#include <Menus.h>
42#include <QuickDraw.h>
43#include <ToolUtils.h>
44#include <Fonts.h>
45#include <Controls.h>
46#include <Windows.h>
47#include <Events.h>
e0f712ba 48#if defined (__MRC__) || (__MSL__ >= 0x6000)
1a578e9b
AC
49#include <ControlDefinitions.h>
50#endif
04b5475b 51#endif /* not TARGET_API_MAC_CARBON */
1a578e9b
AC
52
53/* This may include sys/types.h, and that somehow loses
54 if this is not done before the other system files. */
55#include "macterm.h"
56
57/* Load sys/types.h if not already loaded.
58 In some systems loading it twice is suicidal. */
59#ifndef makedev
60#include <sys/types.h>
61#endif
62
63#include "dispextern.h"
64
e2e206ae
YM
65enum mac_menu_kind { /* Menu ID range */
66 MAC_MENU_APPLE, /* 0 (Reserved by Apple) */
b0da976d
YM
67 MAC_MENU_MENU_BAR, /* 1 .. 233 */
68 MAC_MENU_M_APPLE, /* 234 (== M_APPLE) */
e2e206ae
YM
69 MAC_MENU_POPUP, /* 235 */
70 MAC_MENU_DRIVER, /* 236 .. 255 (Reserved) */
71 MAC_MENU_MENU_BAR_SUB, /* 256 .. 16383 */
72 MAC_MENU_POPUP_SUB, /* 16384 .. 32767 */
73 MAC_MENU_END /* 32768 */
74};
75
b0da976d 76static const int min_menu_id[] = {0, 1, 234, 235, 236, 256, 16384, 32768};
1a578e9b
AC
77
78#define DIALOG_WINDOW_RESOURCE 130
79
28eabd14 80#if TARGET_API_MAC_CARBON
1a578e9b 81#define HAVE_DIALOGS 1
28eabd14 82#endif
1a578e9b
AC
83
84#undef HAVE_MULTILINGUAL_MENU
85
86/******************************************************************/
87/* Definitions copied from lwlib.h */
88
89typedef void * XtPointer;
90
1a578e9b
AC
91enum button_type
92{
93 BUTTON_TYPE_NONE,
94 BUTTON_TYPE_TOGGLE,
95 BUTTON_TYPE_RADIO
96};
97
e0f712ba
AC
98/* This structure is based on the one in ../lwlib/lwlib.h, modified
99 for Mac OS. */
1a578e9b
AC
100typedef struct _widget_value
101{
102 /* name of widget */
16ceacc2 103 Lisp_Object lname;
1a578e9b
AC
104 char* name;
105 /* value (meaning depend on widget type) */
106 char* value;
177c0ea7 107 /* keyboard equivalent. no implications for XtTranslations */
16ceacc2 108 Lisp_Object lkey;
1a578e9b 109 char* key;
e0f712ba
AC
110 /* Help string or nil if none.
111 GC finds this string through the frame's menu_bar_vector
112 or through menu_items. */
113 Lisp_Object help;
1a578e9b
AC
114 /* true if enabled */
115 Boolean enabled;
116 /* true if selected */
117 Boolean selected;
118 /* The type of a button. */
119 enum button_type button_type;
120 /* true if menu title */
121 Boolean title;
122#if 0
123 /* true if was edited (maintained by get_value) */
124 Boolean edited;
125 /* true if has changed (maintained by lw library) */
126 change_type change;
127 /* true if this widget itself has changed,
128 but not counting the other widgets found in the `next' field. */
129 change_type this_one_change;
130#endif
131 /* Contents of the sub-widgets, also selected slot for checkbox */
132 struct _widget_value* contents;
133 /* data passed to callback */
134 XtPointer call_data;
135 /* next one in the list */
136 struct _widget_value* next;
137#if 0
138 /* slot for the toolkit dependent part. Always initialize to NULL. */
139 void* toolkit_data;
140 /* tell us if we should free the toolkit data slot when freeing the
141 widget_value itself. */
142 Boolean free_toolkit_data;
143
144 /* we resource the widget_value structures; this points to the next
145 one on the free list if this one has been deallocated.
146 */
147 struct _widget_value *free_list;
148#endif
149} widget_value;
150
151/* Assumed by other routines to zero area returned. */
152#define malloc_widget_value() (void *)memset (xmalloc (sizeof (widget_value)),\
153 0, (sizeof (widget_value)))
154#define free_widget_value(wv) xfree (wv)
155
156/******************************************************************/
157
1a578e9b
AC
158#ifndef TRUE
159#define TRUE 1
160#define FALSE 0
161#endif /* no TRUE */
177c0ea7 162
1a578e9b
AC
163Lisp_Object Qdebug_on_next_call;
164
0e9ffc04
YM
165extern Lisp_Object Vmenu_updating_frame;
166
4036ffb9 167extern Lisp_Object Qmenu_bar, Qmac_apple_event;
1a578e9b
AC
168
169extern Lisp_Object QCtoggle, QCradio;
170
171extern Lisp_Object Voverriding_local_map;
172extern Lisp_Object Voverriding_local_map_menu_flag;
173
174extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
175
176extern Lisp_Object Qmenu_bar_update_hook;
177
4036ffb9
YM
178void set_frame_menubar P_ ((FRAME_PTR, int, int));
179
383418e5
ST
180#if TARGET_API_MAC_CARBON
181#define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
182#else
183#define ENCODE_MENU_STRING(str) ENCODE_SYSTEM (str)
184#endif
185
1a578e9b
AC
186static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
187 Lisp_Object, Lisp_Object, Lisp_Object,
188 Lisp_Object, Lisp_Object));
e0f712ba 189#ifdef HAVE_DIALOGS
4036ffb9
YM
190static Lisp_Object mac_dialog_show P_ ((FRAME_PTR, int, Lisp_Object,
191 Lisp_Object, char **));
e0f712ba 192#endif
4036ffb9
YM
193static Lisp_Object mac_menu_show P_ ((struct frame *, int, int, int, int,
194 Lisp_Object, char **));
195static void keymap_panes P_ ((Lisp_Object *, int, int));
196static void single_keymap_panes P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
197 int, int));
198static void list_of_panes P_ ((Lisp_Object));
199static void list_of_items P_ ((Lisp_Object));
1a578e9b 200
b0da976d
YM
201static void find_and_call_menu_selection P_ ((FRAME_PTR, int, Lisp_Object,
202 void *));
477f75a5 203static int fill_menu P_ ((MenuRef, widget_value *, enum mac_menu_kind, int));
0f745bcf 204static void fill_menubar P_ ((widget_value *, int));
e2e206ae 205static void dispose_menus P_ ((enum mac_menu_kind, int));
1a578e9b
AC
206
207\f
208/* This holds a Lisp vector that holds the results of decoding
209 the keymaps or alist-of-alists that specify a menu.
210
211 It describes the panes and items within the panes.
212
213 Each pane is described by 3 elements in the vector:
214 t, the pane name, the pane's prefix key.
215 Then follow the pane's items, with 5 elements per item:
216 the item string, the enable flag, the item's value,
217 the definition, and the equivalent keyboard key's description string.
218
219 In some cases, multiple levels of menus may be described.
220 A single vector slot containing nil indicates the start of a submenu.
221 A single vector slot containing lambda indicates the end of a submenu.
222 The submenu follows a menu item which is the way to reach the submenu.
223
224 A single vector slot containing quote indicates that the
225 following items should appear on the right of a dialog box.
226
227 Using a Lisp vector to hold this information while we decode it
228 takes care of protecting all the data from GC. */
229
230#define MENU_ITEMS_PANE_NAME 1
231#define MENU_ITEMS_PANE_PREFIX 2
232#define MENU_ITEMS_PANE_LENGTH 3
233
234enum menu_item_idx
235{
236 MENU_ITEMS_ITEM_NAME = 0,
237 MENU_ITEMS_ITEM_ENABLE,
238 MENU_ITEMS_ITEM_VALUE,
239 MENU_ITEMS_ITEM_EQUIV_KEY,
240 MENU_ITEMS_ITEM_DEFINITION,
241 MENU_ITEMS_ITEM_TYPE,
242 MENU_ITEMS_ITEM_SELECTED,
243 MENU_ITEMS_ITEM_HELP,
244 MENU_ITEMS_ITEM_LENGTH
245};
246
247static Lisp_Object menu_items;
248
249/* Number of slots currently allocated in menu_items. */
250static int menu_items_allocated;
251
252/* This is the index in menu_items of the first empty slot. */
253static int menu_items_used;
254
255/* The number of panes currently recorded in menu_items,
256 excluding those within submenus. */
257static int menu_items_n_panes;
258
259/* Current depth within submenus. */
260static int menu_items_submenu_depth;
261
a83c3e31
YM
262/* Nonzero means a menu is currently active. */
263static int popup_activated_flag;
264
1a578e9b
AC
265/* This is set nonzero after the user activates the menu bar, and set
266 to zero again after the menu bars are redisplayed by prepare_menu_bar.
267 While it is nonzero, all calls to set_frame_menubar go deep.
268
269 I don't understand why this is needed, but it does seem to be
270 needed on Motif, according to Marcus Daniels <marcus@sysc.pdx.edu>. */
271
272int pending_menu_activation;
273\f
274/* Initialize the menu_items structure if we haven't already done so.
275 Also mark it as currently empty. */
276
277static void
278init_menu_items ()
279{
280 if (NILP (menu_items))
281 {
282 menu_items_allocated = 60;
283 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
284 }
285
286 menu_items_used = 0;
287 menu_items_n_panes = 0;
288 menu_items_submenu_depth = 0;
289}
290
4036ffb9 291/* Call at the end of generating the data in menu_items. */
1a578e9b
AC
292
293static void
294finish_menu_items ()
295{
296}
297
298/* Call when finished using the data for the current menu
299 in menu_items. */
300
301static void
302discard_menu_items ()
303{
304 /* Free the structure if it is especially large.
305 Otherwise, hold on to it, to save time. */
306 if (menu_items_allocated > 200)
307 {
308 menu_items = Qnil;
309 menu_items_allocated = 0;
310 }
311}
312
f1d7196a
YM
313/* This undoes save_menu_items, and it is called by the specpdl unwind
314 mechanism. */
315
316static Lisp_Object
317restore_menu_items (saved)
318 Lisp_Object saved;
319{
320 menu_items = XCAR (saved);
321 menu_items_allocated = (VECTORP (menu_items) ? ASIZE (menu_items) : 0);
322 saved = XCDR (saved);
323 menu_items_used = XINT (XCAR (saved));
324 saved = XCDR (saved);
325 menu_items_n_panes = XINT (XCAR (saved));
74e537fb 326 saved = XCDR (saved);
f1d7196a 327 menu_items_submenu_depth = XINT (XCAR (saved));
0e530e3b 328 return Qnil;
f1d7196a
YM
329}
330
331/* Push the whole state of menu_items processing onto the specpdl.
332 It will be restored when the specpdl is unwound. */
333
334static void
335save_menu_items ()
336{
337 Lisp_Object saved = list4 (menu_items,
338 make_number (menu_items_used),
339 make_number (menu_items_n_panes),
340 make_number (menu_items_submenu_depth));
341 record_unwind_protect (restore_menu_items, saved);
342 menu_items = Qnil;
343}
344\f
1a578e9b
AC
345/* Make the menu_items vector twice as large. */
346
347static void
348grow_menu_items ()
349{
350 Lisp_Object old;
351 int old_size = menu_items_allocated;
352 old = menu_items;
353
354 menu_items_allocated *= 2;
f1d7196a 355
1a578e9b
AC
356 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
357 bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents,
358 old_size * sizeof (Lisp_Object));
359}
360
361/* Begin a submenu. */
362
363static void
364push_submenu_start ()
365{
366 if (menu_items_used + 1 > menu_items_allocated)
367 grow_menu_items ();
368
369 XVECTOR (menu_items)->contents[menu_items_used++] = Qnil;
370 menu_items_submenu_depth++;
371}
372
373/* End a submenu. */
374
375static void
376push_submenu_end ()
377{
378 if (menu_items_used + 1 > menu_items_allocated)
379 grow_menu_items ();
380
381 XVECTOR (menu_items)->contents[menu_items_used++] = Qlambda;
382 menu_items_submenu_depth--;
383}
384
385/* Indicate boundary between left and right. */
386
387static void
388push_left_right_boundary ()
389{
390 if (menu_items_used + 1 > menu_items_allocated)
391 grow_menu_items ();
392
393 XVECTOR (menu_items)->contents[menu_items_used++] = Qquote;
394}
395
e0f712ba 396/* Start a new menu pane in menu_items.
1a578e9b
AC
397 NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */
398
399static void
400push_menu_pane (name, prefix_vec)
401 Lisp_Object name, prefix_vec;
402{
403 if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated)
404 grow_menu_items ();
405
406 if (menu_items_submenu_depth == 0)
407 menu_items_n_panes++;
408 XVECTOR (menu_items)->contents[menu_items_used++] = Qt;
409 XVECTOR (menu_items)->contents[menu_items_used++] = name;
410 XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec;
411}
412
413/* Push one menu item into the current pane. NAME is the string to
414 display. ENABLE if non-nil means this item can be selected. KEY
415 is the key generated by choosing this item, or nil if this item
416 doesn't really have a definition. DEF is the definition of this
417 item. EQUIV is the textual description of the keyboard equivalent
418 for this item (or nil if none). TYPE is the type of this menu
419 item, one of nil, `toggle' or `radio'. */
420
421static void
422push_menu_item (name, enable, key, def, equiv, type, selected, help)
423 Lisp_Object name, enable, key, def, equiv, type, selected, help;
424{
425 if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
426 grow_menu_items ();
427
428 XVECTOR (menu_items)->contents[menu_items_used++] = name;
429 XVECTOR (menu_items)->contents[menu_items_used++] = enable;
430 XVECTOR (menu_items)->contents[menu_items_used++] = key;
431 XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
432 XVECTOR (menu_items)->contents[menu_items_used++] = def;
433 XVECTOR (menu_items)->contents[menu_items_used++] = type;
434 XVECTOR (menu_items)->contents[menu_items_used++] = selected;
435 XVECTOR (menu_items)->contents[menu_items_used++] = help;
436}
437\f
438/* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
439 and generate menu panes for them in menu_items.
440 If NOTREAL is nonzero,
441 don't bother really computing whether an item is enabled. */
442
443static void
444keymap_panes (keymaps, nmaps, notreal)
445 Lisp_Object *keymaps;
446 int nmaps;
447 int notreal;
448{
449 int mapno;
450
451 init_menu_items ();
452
453 /* Loop over the given keymaps, making a pane for each map.
454 But don't make a pane that is empty--ignore that map instead.
455 P is the number of panes we have made so far. */
456 for (mapno = 0; mapno < nmaps; mapno++)
e0f712ba 457 single_keymap_panes (keymaps[mapno],
4036ffb9 458 Fkeymap_prompt (keymaps[mapno]), Qnil, notreal, 10);
1a578e9b
AC
459
460 finish_menu_items ();
461}
462
4036ffb9
YM
463/* Args passed between single_keymap_panes and single_menu_item. */
464struct skp
465 {
466 Lisp_Object pending_maps;
467 int maxdepth, notreal;
468 };
469
470static void single_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
471 void *));
472
1a578e9b
AC
473/* This is a recursive subroutine of keymap_panes.
474 It handles one keymap, KEYMAP.
475 The other arguments are passed along
476 or point to local variables of the previous function.
477 If NOTREAL is nonzero, only check for equivalent key bindings, don't
478 evaluate expressions in menu items and don't make any menu.
479
480 If we encounter submenus deeper than MAXDEPTH levels, ignore them. */
481
482static void
483single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
484 Lisp_Object keymap;
485 Lisp_Object pane_name;
486 Lisp_Object prefix;
487 int notreal;
488 int maxdepth;
489{
4036ffb9
YM
490 struct skp skp;
491 struct gcpro gcpro1;
492
493 skp.pending_maps = Qnil;
494 skp.maxdepth = maxdepth;
495 skp.notreal = notreal;
1a578e9b
AC
496
497 if (maxdepth <= 0)
498 return;
499
500 push_menu_pane (pane_name, prefix);
501
4036ffb9
YM
502 GCPRO1 (skp.pending_maps);
503 map_keymap (keymap, single_menu_item, Qnil, &skp, 1);
504 UNGCPRO;
1a578e9b
AC
505
506 /* Process now any submenus which want to be panes at this level. */
4036ffb9 507 while (CONSP (skp.pending_maps))
1a578e9b
AC
508 {
509 Lisp_Object elt, eltcdr, string;
4036ffb9 510 elt = XCAR (skp.pending_maps);
1a578e9b
AC
511 eltcdr = XCDR (elt);
512 string = XCAR (eltcdr);
513 /* We no longer discard the @ from the beginning of the string here.
514 Instead, we do this in mac_menu_show. */
515 single_keymap_panes (Fcar (elt), string,
516 XCDR (eltcdr), notreal, maxdepth - 1);
4036ffb9 517 skp.pending_maps = XCDR (skp.pending_maps);
1a578e9b
AC
518 }
519}
520\f
521/* This is a subroutine of single_keymap_panes that handles one
522 keymap entry.
177c0ea7 523 KEY is a key in a keymap and ITEM is its binding.
4036ffb9 524 SKP->PENDING_MAPS_PTR is a list of keymaps waiting to be made into
1a578e9b 525 separate panes.
4036ffb9 526 If SKP->NOTREAL is nonzero, only check for equivalent key bindings, don't
1a578e9b 527 evaluate expressions in menu items and don't make any menu.
4036ffb9 528 If we encounter submenus deeper than SKP->MAXDEPTH levels, ignore them. */
1a578e9b
AC
529
530static void
4036ffb9
YM
531single_menu_item (key, item, dummy, skp_v)
532 Lisp_Object key, item, dummy;
533 void *skp_v;
1a578e9b
AC
534{
535 Lisp_Object map, item_string, enabled;
536 struct gcpro gcpro1, gcpro2;
537 int res;
4036ffb9 538 struct skp *skp = skp_v;
177c0ea7 539
1a578e9b
AC
540 /* Parse the menu item and leave the result in item_properties. */
541 GCPRO2 (key, item);
4036ffb9 542 res = parse_menu_item (item, skp->notreal, 0);
1a578e9b
AC
543 UNGCPRO;
544 if (!res)
545 return; /* Not a menu item. */
546
547 map = XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP];
177c0ea7 548
4036ffb9 549 if (skp->notreal)
1a578e9b
AC
550 {
551 /* We don't want to make a menu, just traverse the keymaps to
552 precompute equivalent key bindings. */
553 if (!NILP (map))
4036ffb9 554 single_keymap_panes (map, Qnil, key, 1, skp->maxdepth - 1);
1a578e9b
AC
555 return;
556 }
557
558 enabled = XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE];
177c0ea7 559 item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME];
1a578e9b 560
d5db4077 561 if (!NILP (map) && SREF (item_string, 0) == '@')
1a578e9b
AC
562 {
563 if (!NILP (enabled))
564 /* An enabled separate pane. Remember this to handle it later. */
4036ffb9
YM
565 skp->pending_maps = Fcons (Fcons (map, Fcons (item_string, key)),
566 skp->pending_maps);
1a578e9b
AC
567 return;
568 }
569
570 push_menu_item (item_string, enabled, key,
571 XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF],
572 XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ],
573 XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE],
4036ffb9
YM
574 XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED],
575 XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]);
1a578e9b
AC
576
577 /* Display a submenu using the toolkit. */
578 if (! (NILP (map) || NILP (enabled)))
579 {
580 push_submenu_start ();
4036ffb9 581 single_keymap_panes (map, Qnil, key, 0, skp->maxdepth - 1);
1a578e9b
AC
582 push_submenu_end ();
583 }
584}
585\f
586/* Push all the panes and items of a menu described by the
587 alist-of-alists MENU.
588 This handles old-fashioned calls to x-popup-menu. */
589
590static void
591list_of_panes (menu)
592 Lisp_Object menu;
593{
594 Lisp_Object tail;
595
596 init_menu_items ();
597
4036ffb9 598 for (tail = menu; CONSP (tail); tail = XCDR (tail))
1a578e9b
AC
599 {
600 Lisp_Object elt, pane_name, pane_data;
4036ffb9 601 elt = XCAR (tail);
1a578e9b 602 pane_name = Fcar (elt);
e0f712ba 603 CHECK_STRING (pane_name);
4036ffb9 604 push_menu_pane (ENCODE_MENU_STRING (pane_name), Qnil);
1a578e9b 605 pane_data = Fcdr (elt);
e0f712ba 606 CHECK_CONS (pane_data);
1a578e9b
AC
607 list_of_items (pane_data);
608 }
609
610 finish_menu_items ();
611}
612
613/* Push the items in a single pane defined by the alist PANE. */
614
615static void
616list_of_items (pane)
617 Lisp_Object pane;
618{
619 Lisp_Object tail, item, item1;
620
4036ffb9 621 for (tail = pane; CONSP (tail); tail = XCDR (tail))
1a578e9b 622 {
4036ffb9 623 item = XCAR (tail);
1a578e9b 624 if (STRINGP (item))
4036ffb9
YM
625 push_menu_item (ENCODE_MENU_STRING (item), Qnil, Qnil, Qt,
626 Qnil, Qnil, Qnil, Qnil);
627 else if (CONSP (item))
1a578e9b 628 {
4036ffb9 629 item1 = XCAR (item);
e0f712ba 630 CHECK_STRING (item1);
4036ffb9
YM
631 push_menu_item (ENCODE_MENU_STRING (item1), Qt, XCDR (item),
632 Qt, Qnil, Qnil, Qnil, Qnil);
1a578e9b 633 }
4036ffb9
YM
634 else
635 push_left_right_boundary ();
636
1a578e9b
AC
637 }
638}
639\f
b8987570
JD
640static Lisp_Object
641cleanup_popup_menu (arg)
642 Lisp_Object arg;
643{
644 discard_menu_items ();
0e530e3b 645 return Qnil;
b8987570
JD
646}
647
1a578e9b 648DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
e0f712ba 649 doc: /* Pop up a deck-of-cards menu and return user's selection.
50971a12
YM
650POSITION is a position specification. This is either a mouse button event
651or a list ((XOFFSET YOFFSET) WINDOW)
652where XOFFSET and YOFFSET are positions in pixels from the top left
653corner of WINDOW. (WINDOW may be a window or a frame object.)
654This controls the position of the top left of the menu as a whole.
655If POSITION is t, it means to use the current mouse position.
e0f712ba
AC
656
657MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
658The menu items come from key bindings that have a menu string as well as
50971a12 659a definition; actually, the "definition" in such a key binding looks like
e0f712ba
AC
660\(STRING . REAL-DEFINITION). To give the menu a title, put a string into
661the keymap as a top-level element.
662
663If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
664Otherwise, REAL-DEFINITION should be a valid key binding definition.
665
50971a12
YM
666You can also use a list of keymaps as MENU.
667 Then each keymap makes a separate pane.
e0f712ba 668
50971a12
YM
669When MENU is a keymap or a list of keymaps, the return value is the
670list of events corresponding to the user's choice. Note that
671`x-popup-menu' does not actually execute the command bound to that
672sequence of events.
673
674Alternatively, you can specify a menu of multiple panes
675 with a list of the form (TITLE PANE1 PANE2...),
676where each pane is a list of form (TITLE ITEM1 ITEM2...).
677Each ITEM is normally a cons cell (STRING . VALUE);
678but a string can appear as an item--that makes a nonselectable line
679in the menu.
e0f712ba
AC
680With this form of menu, the return value is VALUE from the chosen item.
681
682If POSITION is nil, don't display the menu at all, just precalculate the
50971a12
YM
683cached information about equivalent key sequences.
684
685If the user gets rid of the menu without making a valid choice, for
686instance by clicking the mouse away from a valid choice or by typing
687keyboard input, then this normally results in a quit and
688`x-popup-menu' does not return. But if POSITION is a mouse button
689event (indicating that the user invoked the menu with the mouse) then
690no quit occurs and `x-popup-menu' returns nil. */)
691 (position, menu)
1a578e9b
AC
692 Lisp_Object position, menu;
693{
694 Lisp_Object keymap, tem;
e0f712ba 695 int xpos = 0, ypos = 0;
1a578e9b 696 Lisp_Object title;
4036ffb9 697 char *error_name = NULL;
1a578e9b 698 Lisp_Object selection;
e0f712ba 699 FRAME_PTR f = NULL;
1a578e9b
AC
700 Lisp_Object x, y, window;
701 int keymaps = 0;
702 int for_click = 0;
b8987570 703 int specpdl_count = SPECPDL_INDEX ();
4036ffb9 704 struct gcpro gcpro1;
1a578e9b
AC
705
706#ifdef HAVE_MENUS
707 if (! NILP (position))
708 {
709 check_mac ();
710
711 /* Decode the first argument: find the window and the coordinates. */
712 if (EQ (position, Qt)
e0f712ba 713 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
4036ffb9
YM
714 || EQ (XCAR (position), Qtool_bar)
715 || EQ (XCAR (position), Qmac_apple_event))))
1a578e9b
AC
716 {
717 /* Use the mouse's current position. */
718 FRAME_PTR new_f = SELECTED_FRAME ();
719 Lisp_Object bar_window;
720 enum scroll_bar_part part;
721 unsigned long time;
722
723 if (mouse_position_hook)
724 (*mouse_position_hook) (&new_f, 1, &bar_window,
725 &part, &x, &y, &time);
726 if (new_f != 0)
727 XSETFRAME (window, new_f);
728 else
729 {
730 window = selected_window;
731 XSETFASTINT (x, 0);
732 XSETFASTINT (y, 0);
733 }
734 }
735 else
736 {
737 tem = Fcar (position);
738 if (CONSP (tem))
739 {
740 window = Fcar (Fcdr (position));
4036ffb9
YM
741 x = XCAR (tem);
742 y = Fcar (XCDR (tem));
1a578e9b
AC
743 }
744 else
745 {
746 for_click = 1;
747 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
748 window = Fcar (tem); /* POSN_WINDOW (tem) */
749 tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
750 x = Fcar (tem);
751 y = Fcdr (tem);
752 }
753 }
754
e0f712ba
AC
755 CHECK_NUMBER (x);
756 CHECK_NUMBER (y);
1a578e9b
AC
757
758 /* Decode where to put the menu. */
759
760 if (FRAMEP (window))
761 {
762 f = XFRAME (window);
763 xpos = 0;
764 ypos = 0;
765 }
766 else if (WINDOWP (window))
767 {
e0f712ba 768 CHECK_LIVE_WINDOW (window);
1a578e9b
AC
769 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
770
90022f5a
KS
771 xpos = WINDOW_LEFT_EDGE_X (XWINDOW (window));
772 ypos = WINDOW_TOP_EDGE_Y (XWINDOW (window));
1a578e9b
AC
773 }
774 else
775 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
776 but I don't want to make one now. */
e0f712ba 777 CHECK_WINDOW (window);
1a578e9b
AC
778
779 xpos += XINT (x);
780 ypos += XINT (y);
781
782 XSETFRAME (Vmenu_updating_frame, f);
783 }
c1d9dffd
JL
784 else
785 Vmenu_updating_frame = Qnil;
1a578e9b
AC
786#endif /* HAVE_MENUS */
787
788 title = Qnil;
789 GCPRO1 (title);
790
791 /* Decode the menu items from what was specified. */
792
e0f712ba
AC
793 keymap = get_keymap (menu, 0, 0);
794 if (CONSP (keymap))
1a578e9b
AC
795 {
796 /* We were given a keymap. Extract menu info from the keymap. */
797 Lisp_Object prompt;
1a578e9b
AC
798
799 /* Extract the detailed info to make one pane. */
800 keymap_panes (&menu, 1, NILP (position));
801
802 /* Search for a string appearing directly as an element of the keymap.
803 That string is the title of the menu. */
0c5b23f8 804 prompt = Fkeymap_prompt (keymap);
1a578e9b
AC
805 if (NILP (title) && !NILP (prompt))
806 title = prompt;
807
808 /* Make that be the pane title of the first pane. */
809 if (!NILP (prompt) && menu_items_n_panes >= 0)
810 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
811
812 keymaps = 1;
813 }
e0f712ba 814 else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
1a578e9b
AC
815 {
816 /* We were given a list of keymaps. */
817 int nmaps = XFASTINT (Flength (menu));
818 Lisp_Object *maps
819 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
820 int i;
821
822 title = Qnil;
823
824 /* The first keymap that has a prompt string
825 supplies the menu title. */
4036ffb9 826 for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
1a578e9b
AC
827 {
828 Lisp_Object prompt;
829
4036ffb9 830 maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
1a578e9b 831
0c5b23f8 832 prompt = Fkeymap_prompt (keymap);
1a578e9b
AC
833 if (NILP (title) && !NILP (prompt))
834 title = prompt;
835 }
836
837 /* Extract the detailed info to make one pane. */
838 keymap_panes (maps, nmaps, NILP (position));
839
840 /* Make the title be the pane title of the first pane. */
841 if (!NILP (title) && menu_items_n_panes >= 0)
842 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
843
844 keymaps = 1;
845 }
846 else
847 {
848 /* We were given an old-fashioned menu. */
849 title = Fcar (menu);
e0f712ba 850 CHECK_STRING (title);
1a578e9b
AC
851
852 list_of_panes (Fcdr (menu));
853
854 keymaps = 0;
855 }
177c0ea7 856
1a578e9b
AC
857 if (NILP (position))
858 {
859 discard_menu_items ();
860 UNGCPRO;
861 return Qnil;
862 }
863
864#ifdef HAVE_MENUS
865 /* Display them in a menu. */
b8987570 866 record_unwind_protect (cleanup_popup_menu, Qnil);
1a578e9b
AC
867 BLOCK_INPUT;
868
869 selection = mac_menu_show (f, xpos, ypos, for_click,
870 keymaps, title, &error_name);
871 UNBLOCK_INPUT;
b8987570 872 unbind_to (specpdl_count, Qnil);
1a578e9b
AC
873
874 UNGCPRO;
875#endif /* HAVE_MENUS */
876
877 if (error_name) error (error_name);
878 return selection;
879}
880
881#ifdef HAVE_MENUS
882
7dcebea4
YM
883/* Regard ESC and C-g as Cancel even without the Cancel button. */
884
512eb161 885#if 0 /* defined (MAC_OSX) */
7dcebea4
YM
886static Boolean
887mac_dialog_modal_filter (dialog, event, item_hit)
888 DialogRef dialog;
889 EventRecord *event;
890 DialogItemIndex *item_hit;
891{
892 Boolean result;
893
894 result = StdFilterProc (dialog, event, item_hit);
895 if (result == false
896 && (event->what == keyDown || event->what == autoKey)
897 && ((event->message & charCodeMask) == kEscapeCharCode
898 || mac_quit_char_key_p (event->modifiers,
899 (event->message & keyCodeMask) >> 8)))
900 {
901 *item_hit = kStdCancelItemIndex;
902 return true;
903 }
904
905 return result;
906}
907#endif
908
16b12668 909DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
e0f712ba
AC
910 doc: /* Pop up a dialog box and return user's selection.
911POSITION specifies which frame to use.
912This is normally a mouse button event or a window or frame.
913If POSITION is t, it means to use the frame the mouse is on.
914The dialog box appears in the middle of the specified frame.
915
916CONTENTS specifies the alternatives to display in the dialog box.
50971a12 917It is a list of the form (DIALOG ITEM1 ITEM2...).
e0f712ba
AC
918Each ITEM is a cons cell (STRING . VALUE).
919The return value is VALUE from the chosen item.
920
921An ITEM may also be just a string--that makes a nonselectable item.
922An ITEM may also be nil--that means to put all preceding items
923on the left of the dialog box and all following items on the right.
3f297536
NR
924\(By default, approximately half appear on each side.)
925
926If HEADER is non-nil, the frame title for the box is "Information",
50971a12
YM
927otherwise it is "Question".
928
929If the user gets rid of the dialog box without making a valid choice,
930for instance using the window manager, then this produces a quit and
931`x-popup-dialog' does not return. */)
932 (position, contents, header)
3f297536 933 Lisp_Object position, contents, header;
1a578e9b 934{
e0f712ba 935 FRAME_PTR f = NULL;
1a578e9b
AC
936 Lisp_Object window;
937
938 check_mac ();
939
940 /* Decode the first argument: find the window or frame to use. */
941 if (EQ (position, Qt)
e0f712ba 942 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
4036ffb9
YM
943 || EQ (XCAR (position), Qtool_bar)
944 || EQ (XCAR (position), Qmac_apple_event))))
1a578e9b
AC
945 {
946#if 0 /* Using the frame the mouse is on may not be right. */
947 /* Use the mouse's current position. */
948 FRAME_PTR new_f = SELECTED_FRAME ();
949 Lisp_Object bar_window;
e0f712ba 950 enum scroll_bar_part part;
1a578e9b
AC
951 unsigned long time;
952 Lisp_Object x, y;
953
954 (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
955
956 if (new_f != 0)
957 XSETFRAME (window, new_f);
958 else
959 window = selected_window;
960#endif
961 window = selected_window;
962 }
963 else if (CONSP (position))
964 {
965 Lisp_Object tem;
966 tem = Fcar (position);
967 if (CONSP (tem))
968 window = Fcar (Fcdr (position));
969 else
970 {
971 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
972 window = Fcar (tem); /* POSN_WINDOW (tem) */
973 }
974 }
975 else if (WINDOWP (position) || FRAMEP (position))
976 window = position;
977 else
978 window = Qnil;
979
980 /* Decode where to put the menu. */
981
982 if (FRAMEP (window))
983 f = XFRAME (window);
984 else if (WINDOWP (window))
985 {
e0f712ba 986 CHECK_LIVE_WINDOW (window);
1a578e9b
AC
987 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
988 }
989 else
990 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
991 but I don't want to make one now. */
e0f712ba 992 CHECK_WINDOW (window);
1a578e9b 993
512eb161 994#if 0 /* defined (MAC_OSX) */
7dcebea4
YM
995 /* Special treatment for Fmessage_box, Fyes_or_no_p, and Fy_or_n_p. */
996 if (EQ (position, Qt)
997 && STRINGP (Fcar (contents))
998 && ((!NILP (Fequal (XCDR (contents),
999 Fcons (Fcons (build_string ("OK"), Qt), Qnil)))
1000 && EQ (header, Qt))
1001 || (!NILP (Fequal (XCDR (contents),
1002 Fcons (Fcons (build_string ("Yes"), Qt),
1003 Fcons (Fcons (build_string ("No"), Qnil),
1004 Qnil))))
1005 && NILP (header))))
1006 {
1007 OSStatus err = noErr;
1008 AlertStdCFStringAlertParamRec param;
1009 CFStringRef error_string, explanation_string;
1010 DialogRef alert;
1011 DialogItemIndex item_hit;
1012 Lisp_Object tem;
1013
b28d7564
YM
1014 /* Force a redisplay before showing the dialog. If a frame is
1015 created just before showing the dialog, its contents may not
1016 have been fully drawn. */
1017 Fredisplay (Qt);
1018
7dcebea4
YM
1019 tem = Fstring_match (concat3 (build_string ("\\("),
1020 call0 (intern ("sentence-end")),
1021 build_string ("\\)\n")),
1022 XCAR (contents), Qnil);
1023 BLOCK_INPUT;
1024 if (NILP (tem))
1025 {
1026 error_string = cfstring_create_with_string (XCAR (contents));
1027 if (error_string == NULL)
1028 err = memFullErr;
1029 explanation_string = NULL;
1030 }
1031 else
1032 {
1033 tem = Fmatch_end (make_number (1));
1034 error_string =
1035 cfstring_create_with_string (Fsubstring (XCAR (contents),
1036 make_number (0), tem));
1037 if (error_string == NULL)
1038 err = memFullErr;
1039 else
1040 {
1041 XSETINT (tem, XINT (tem) + 1);
1042 explanation_string =
1043 cfstring_create_with_string (Fsubstring (XCAR (contents),
1044 tem, Qnil));
1045 if (explanation_string == NULL)
1046 {
1047 CFRelease (error_string);
1048 err = memFullErr;
1049 }
1050 }
1051 }
1052 if (err == noErr)
1053 err = GetStandardAlertDefaultParams (&param,
1054 kStdCFStringAlertVersionOne);
1055 if (err == noErr)
1056 {
1057 param.movable = true;
1058 param.position = kWindowAlertPositionParentWindow;
1059 if (NILP (header))
1060 {
1061 param.defaultText = CFSTR ("Yes");
1062 param.otherText = CFSTR ("No");
1063#if 0
1064 param.cancelText = CFSTR ("Cancel");
1065 param.cancelButton = kAlertStdAlertCancelButton;
1066#endif
1067 }
1068 err = CreateStandardAlert (kAlertNoteAlert, error_string,
1069 explanation_string, &param, &alert);
1070 CFRelease (error_string);
1071 if (explanation_string)
1072 CFRelease (explanation_string);
1073 }
1074 if (err == noErr)
1075 err = RunStandardAlert (alert, mac_dialog_modal_filter, &item_hit);
1076 UNBLOCK_INPUT;
1077
1078 if (err == noErr)
1079 {
1080 if (item_hit == kStdCancelItemIndex)
1081 Fsignal (Qquit, Qnil);
1082 else if (item_hit == kStdOkItemIndex)
1083 return Qt;
1084 else
1085 return Qnil;
1086 }
1087 }
1088#endif
1a578e9b
AC
1089#ifndef HAVE_DIALOGS
1090 /* Display a menu with these alternatives
1091 in the middle of frame F. */
1092 {
1093 Lisp_Object x, y, frame, newpos;
1094 XSETFRAME (frame, f);
1095 XSETINT (x, x_pixel_width (f) / 2);
1096 XSETINT (y, x_pixel_height (f) / 2);
1097 newpos = Fcons (Fcons (x, Fcons (y, Qnil)), Fcons (frame, Qnil));
1098
1099 return Fx_popup_menu (newpos,
1100 Fcons (Fcar (contents), Fcons (contents, Qnil)));
1101 }
1102#else /* HAVE_DIALOGS */
1103 {
1104 Lisp_Object title;
1105 char *error_name;
1106 Lisp_Object selection;
4036ffb9 1107 int specpdl_count = SPECPDL_INDEX ();
1a578e9b
AC
1108
1109 /* Decode the dialog items from what was specified. */
1110 title = Fcar (contents);
e0f712ba 1111 CHECK_STRING (title);
1a578e9b
AC
1112
1113 list_of_panes (Fcons (contents, Qnil));
1114
1115 /* Display them in a dialog box. */
4036ffb9 1116 record_unwind_protect (cleanup_popup_menu, Qnil);
1a578e9b 1117 BLOCK_INPUT;
3f297536 1118 selection = mac_dialog_show (f, 0, title, header, &error_name);
1a578e9b 1119 UNBLOCK_INPUT;
4036ffb9 1120 unbind_to (specpdl_count, Qnil);
1a578e9b
AC
1121
1122 if (error_name) error (error_name);
1123 return selection;
1124 }
1125#endif /* HAVE_DIALOGS */
1126}
1127
1128/* Activate the menu bar of frame F.
1129 This is called from keyboard.c when it gets the
b3e8cc4d 1130 MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
1a578e9b 1131
4036ffb9
YM
1132 To activate the menu bar, we use the button-press event location
1133 that was saved in saved_menu_event_location.
1a578e9b
AC
1134
1135 But first we recompute the menu bar contents (the whole tree).
1136
4036ffb9
YM
1137 The reason for saving the button event until here, instead of
1138 passing it to the toolkit right away, is that we can safely
1139 execute Lisp code. */
177c0ea7 1140
1a578e9b
AC
1141void
1142x_activate_menubar (f)
1143 FRAME_PTR f;
1144{
1145 SInt32 menu_choice;
b0da976d 1146 SInt16 menu_id, menu_item;
1a578e9b
AC
1147 extern Point saved_menu_event_location;
1148
1149 set_frame_menubar (f, 0, 1);
1150 BLOCK_INPUT;
1151
a83c3e31 1152 popup_activated_flag = 1;
1a578e9b 1153 menu_choice = MenuSelect (saved_menu_event_location);
a83c3e31 1154 popup_activated_flag = 0;
b0da976d
YM
1155 menu_id = HiWord (menu_choice);
1156 menu_item = LoWord (menu_choice);
1157
1158#if !TARGET_API_MAC_CARBON
1159 if (menu_id == min_menu_id[MAC_MENU_M_APPLE])
1160 do_apple_menu (menu_item);
1161 else
1162#endif
1163 if (menu_id)
1164 {
477f75a5 1165 MenuRef menu = GetMenuRef (menu_id);
b0da976d
YM
1166
1167 if (menu)
1168 {
1169 UInt32 refcon;
1170
1171 GetMenuItemRefCon (menu, menu_item, &refcon);
1172 find_and_call_menu_selection (f, f->menu_bar_items_used,
1173 f->menu_bar_vector, (void *) refcon);
1174 }
1175 }
1176
1177 HiliteMenu (0);
1a578e9b
AC
1178
1179 UNBLOCK_INPUT;
1180}
1181
b0da976d
YM
1182/* Find the menu selection and store it in the keyboard buffer.
1183 F is the frame the menu is on.
1184 MENU_BAR_ITEMS_USED is the length of VECTOR.
1185 VECTOR is an array of menu events for the whole menu. */
1a578e9b 1186
b0da976d
YM
1187static void
1188find_and_call_menu_selection (f, menu_bar_items_used, vector, client_data)
1189 FRAME_PTR f;
1190 int menu_bar_items_used;
1191 Lisp_Object vector;
1192 void *client_data;
1a578e9b
AC
1193{
1194 Lisp_Object prefix, entry;
1a578e9b
AC
1195 Lisp_Object *subprefix_stack;
1196 int submenu_depth = 0;
1197 int i;
1198
e0f712ba 1199 entry = Qnil;
b0da976d 1200 subprefix_stack = (Lisp_Object *) alloca (menu_bar_items_used * sizeof (Lisp_Object));
1a578e9b
AC
1201 prefix = Qnil;
1202 i = 0;
b0da976d
YM
1203
1204 while (i < menu_bar_items_used)
1a578e9b
AC
1205 {
1206 if (EQ (XVECTOR (vector)->contents[i], Qnil))
1207 {
1208 subprefix_stack[submenu_depth++] = prefix;
1209 prefix = entry;
1210 i++;
1211 }
1212 else if (EQ (XVECTOR (vector)->contents[i], Qlambda))
1213 {
1214 prefix = subprefix_stack[--submenu_depth];
1215 i++;
1216 }
1217 else if (EQ (XVECTOR (vector)->contents[i], Qt))
1218 {
1219 prefix = XVECTOR (vector)->contents[i + MENU_ITEMS_PANE_PREFIX];
1220 i += MENU_ITEMS_PANE_LENGTH;
1221 }
1222 else
1223 {
1224 entry = XVECTOR (vector)->contents[i + MENU_ITEMS_ITEM_VALUE];
e0f712ba
AC
1225 /* The EMACS_INT cast avoids a warning. There's no problem
1226 as long as pointers have enough bits to hold small integers. */
1227 if ((int) (EMACS_INT) client_data == i)
1a578e9b
AC
1228 {
1229 int j;
1230 struct input_event buf;
1231 Lisp_Object frame;
f456401b 1232 EVENT_INIT (buf);
1a578e9b
AC
1233
1234 XSETFRAME (frame, f);
1235 buf.kind = MENU_BAR_EVENT;
1236 buf.frame_or_window = frame;
1237 buf.arg = frame;
1238 kbd_buffer_store_event (&buf);
1239
1240 for (j = 0; j < submenu_depth; j++)
1241 if (!NILP (subprefix_stack[j]))
1242 {
1243 buf.kind = MENU_BAR_EVENT;
1244 buf.frame_or_window = frame;
1245 buf.arg = subprefix_stack[j];
1246 kbd_buffer_store_event (&buf);
1247 }
1248
1249 if (!NILP (prefix))
1250 {
1251 buf.kind = MENU_BAR_EVENT;
1252 buf.frame_or_window = frame;
1253 buf.arg = prefix;
1254 kbd_buffer_store_event (&buf);
1255 }
1256
1257 buf.kind = MENU_BAR_EVENT;
1258 buf.frame_or_window = frame;
1259 buf.arg = entry;
1260 kbd_buffer_store_event (&buf);
1261
1a578e9b
AC
1262 return;
1263 }
1264 i += MENU_ITEMS_ITEM_LENGTH;
1265 }
1266 }
1267}
1268
1269/* Allocate a widget_value, blocking input. */
1270
1271widget_value *
1272xmalloc_widget_value ()
1273{
1274 widget_value *value;
1275
1276 BLOCK_INPUT;
1277 value = malloc_widget_value ();
1278 UNBLOCK_INPUT;
1279
1280 return value;
1281}
1282
1283/* This recursively calls free_widget_value on the tree of widgets.
1284 It must free all data that was malloc'ed for these widget_values.
1285 In Emacs, many slots are pointers into the data of Lisp_Strings, and
1286 must be left alone. */
1287
1288void
1289free_menubar_widget_value_tree (wv)
1290 widget_value *wv;
1291{
1292 if (! wv) return;
1293
1294 wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
1295
1296 if (wv->contents && (wv->contents != (widget_value*)1))
1297 {
1298 free_menubar_widget_value_tree (wv->contents);
1299 wv->contents = (widget_value *) 0xDEADBEEF;
1300 }
1301 if (wv->next)
1302 {
1303 free_menubar_widget_value_tree (wv->next);
1304 wv->next = (widget_value *) 0xDEADBEEF;
1305 }
1306 BLOCK_INPUT;
1307 free_widget_value (wv);
1308 UNBLOCK_INPUT;
1309}
1310\f
4036ffb9 1311/* Set up data in menu_items for a menu bar item
1a578e9b
AC
1312 whose event type is ITEM_KEY (with string ITEM_NAME)
1313 and whose contents come from the list of keymaps MAPS. */
1314
4036ffb9
YM
1315static int
1316parse_single_submenu (item_key, item_name, maps)
1a578e9b
AC
1317 Lisp_Object item_key, item_name, maps;
1318{
1a578e9b
AC
1319 Lisp_Object length;
1320 int len;
1321 Lisp_Object *mapvec;
4036ffb9 1322 int i;
1a578e9b
AC
1323 int top_level_items = 0;
1324
1325 length = Flength (maps);
1326 len = XINT (length);
1327
1328 /* Convert the list MAPS into a vector MAPVEC. */
1329 mapvec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
1330 for (i = 0; i < len; i++)
1331 {
1332 mapvec[i] = Fcar (maps);
1333 maps = Fcdr (maps);
1334 }
1335
1a578e9b
AC
1336 /* Loop over the given keymaps, making a pane for each map.
1337 But don't make a pane that is empty--ignore that map instead. */
1338 for (i = 0; i < len; i++)
1339 {
4036ffb9 1340 if (!KEYMAPP (mapvec[i]))
1a578e9b
AC
1341 {
1342 /* Here we have a command at top level in the menu bar
1343 as opposed to a submenu. */
1344 top_level_items = 1;
1345 push_menu_pane (Qnil, Qnil);
1346 push_menu_item (item_name, Qt, item_key, mapvec[i],
4036ffb9 1347 Qnil, Qnil, Qnil, Qnil);
1a578e9b
AC
1348 }
1349 else
4036ffb9
YM
1350 {
1351 Lisp_Object prompt;
1352 prompt = Fkeymap_prompt (mapvec[i]);
1353 single_keymap_panes (mapvec[i],
1354 !NILP (prompt) ? prompt : item_name,
1355 item_key, 0, 10);
1356 }
1a578e9b
AC
1357 }
1358
4036ffb9
YM
1359 return top_level_items;
1360}
1361
1362/* Create a tree of widget_value objects
1363 representing the panes and items
1364 in menu_items starting at index START, up to index END. */
1365
1366static widget_value *
1367digest_single_submenu (start, end, top_level_items)
1368 int start, end, top_level_items;
1369{
1370 widget_value *wv, *prev_wv, *save_wv, *first_wv;
1371 int i;
1372 int submenu_depth = 0;
1373 widget_value **submenu_stack;
f1d7196a 1374 int panes_seen = 0;
1a578e9b
AC
1375
1376 submenu_stack
1377 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
1378 wv = xmalloc_widget_value ();
1379 wv->name = "menu";
1380 wv->value = 0;
1381 wv->enabled = 1;
1382 wv->button_type = BUTTON_TYPE_NONE;
e0f712ba 1383 wv->help = Qnil;
1a578e9b
AC
1384 first_wv = wv;
1385 save_wv = 0;
1386 prev_wv = 0;
177c0ea7 1387
4036ffb9
YM
1388 /* Loop over all panes and items made by the preceding call
1389 to parse_single_submenu and construct a tree of widget_value objects.
1390 Ignore the panes and items used by previous calls to
1391 digest_single_submenu, even though those are also in menu_items. */
1392 i = start;
1393 while (i < end)
1a578e9b
AC
1394 {
1395 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1396 {
1397 submenu_stack[submenu_depth++] = save_wv;
1398 save_wv = prev_wv;
1399 prev_wv = 0;
1400 i++;
1401 }
1402 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1403 {
1404 prev_wv = save_wv;
1405 save_wv = submenu_stack[--submenu_depth];
1406 i++;
1407 }
1408 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
1409 && submenu_depth != 0)
1410 i += MENU_ITEMS_PANE_LENGTH;
1411 /* Ignore a nil in the item list.
1412 It's meaningful only for dialog boxes. */
1413 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1414 i += 1;
1415 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1416 {
1417 /* Create a new pane. */
1418 Lisp_Object pane_name, prefix;
1419 char *pane_string;
e0f712ba 1420
f1d7196a
YM
1421 panes_seen++;
1422
1a578e9b
AC
1423 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1424 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
e0f712ba 1425
1a578e9b
AC
1426#ifndef HAVE_MULTILINGUAL_MENU
1427 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
e0f712ba 1428 {
4036ffb9 1429 pane_name = ENCODE_MENU_STRING (pane_name);
e0f712ba
AC
1430 AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
1431 }
1a578e9b
AC
1432#endif
1433 pane_string = (NILP (pane_name)
d5db4077 1434 ? "" : (char *) SDATA (pane_name));
1a578e9b
AC
1435 /* If there is just one top-level pane, put all its items directly
1436 under the top-level menu. */
1437 if (menu_items_n_panes == 1)
1438 pane_string = "";
1439
1440 /* If the pane has a meaningful name,
1441 make the pane a top-level menu item
1442 with its items as a submenu beneath it. */
1443 if (strcmp (pane_string, ""))
1444 {
1445 wv = xmalloc_widget_value ();
1446 if (save_wv)
1447 save_wv->next = wv;
1448 else
1449 first_wv->contents = wv;
16ceacc2
JD
1450 wv->lname = pane_name;
1451 /* Set value to 1 so update_submenu_strings can handle '@' */
1452 wv->value = (char *)1;
1a578e9b
AC
1453 wv->enabled = 1;
1454 wv->button_type = BUTTON_TYPE_NONE;
e0f712ba 1455 wv->help = Qnil;
e188aa29 1456 save_wv = wv;
1a578e9b 1457 }
e188aa29
YM
1458 else
1459 save_wv = first_wv;
1460
1a578e9b
AC
1461 prev_wv = 0;
1462 i += MENU_ITEMS_PANE_LENGTH;
1463 }
1464 else
1465 {
1466 /* Create a new item within current pane. */
1467 Lisp_Object item_name, enable, descrip, def, type, selected;
4036ffb9 1468 Lisp_Object help;
1a578e9b 1469
f1d7196a
YM
1470 /* All items should be contained in panes. */
1471 if (panes_seen == 0)
1472 abort ();
1473
e0f712ba
AC
1474 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
1475 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
1476 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
1477 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
1478 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
1479 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
1480 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
1a578e9b
AC
1481
1482#ifndef HAVE_MULTILINGUAL_MENU
4036ffb9 1483 if (STRING_MULTIBYTE (item_name))
e0f712ba 1484 {
383418e5 1485 item_name = ENCODE_MENU_STRING (item_name);
e0f712ba
AC
1486 AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
1487 }
1488
4036ffb9 1489 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
e0f712ba 1490 {
383418e5 1491 descrip = ENCODE_MENU_STRING (descrip);
e0f712ba
AC
1492 AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
1493 }
1494#endif /* not HAVE_MULTILINGUAL_MENU */
1a578e9b
AC
1495
1496 wv = xmalloc_widget_value ();
177c0ea7 1497 if (prev_wv)
1a578e9b
AC
1498 prev_wv->next = wv;
1499 else
1500 save_wv->contents = wv;
1501
16ceacc2 1502 wv->lname = item_name;
1a578e9b 1503 if (!NILP (descrip))
16ceacc2 1504 wv->lkey = descrip;
1a578e9b
AC
1505 wv->value = 0;
1506 /* The EMACS_INT cast avoids a warning. There's no problem
1507 as long as pointers have enough bits to hold small integers. */
1508 wv->call_data = (!NILP (def) ? (void *) (EMACS_INT) i : 0);
1509 wv->enabled = !NILP (enable);
1510
1511 if (NILP (type))
1512 wv->button_type = BUTTON_TYPE_NONE;
1513 else if (EQ (type, QCradio))
1514 wv->button_type = BUTTON_TYPE_RADIO;
1515 else if (EQ (type, QCtoggle))
1516 wv->button_type = BUTTON_TYPE_TOGGLE;
1517 else
1518 abort ();
1519
1520 wv->selected = !NILP (selected);
4036ffb9 1521 if (! STRINGP (help))
e0f712ba
AC
1522 help = Qnil;
1523
1524 wv->help = help;
1a578e9b
AC
1525
1526 prev_wv = wv;
1527
1528 i += MENU_ITEMS_ITEM_LENGTH;
1529 }
1530 }
1531
1532 /* If we have just one "menu item"
1533 that was originally a button, return it by itself. */
1534 if (top_level_items && first_wv->contents && first_wv->contents->next == 0)
1535 {
1536 wv = first_wv->contents;
1537 free_widget_value (first_wv);
1538 return wv;
1539 }
1540
1541 return first_wv;
1542}
4036ffb9 1543
16ceacc2
JD
1544/* Walk through the widget_value tree starting at FIRST_WV and update
1545 the char * pointers from the corresponding lisp values.
1546 We do this after building the whole tree, since GC may happen while the
1547 tree is constructed, and small strings are relocated. So we must wait
1548 until no GC can happen before storing pointers into lisp values. */
1549static void
1550update_submenu_strings (first_wv)
1551 widget_value *first_wv;
1552{
1553 widget_value *wv;
1554
1555 for (wv = first_wv; wv; wv = wv->next)
1556 {
bf06c82f 1557 if (STRINGP (wv->lname))
16ceacc2
JD
1558 {
1559 wv->name = SDATA (wv->lname);
1560
1561 /* Ignore the @ that means "separate pane".
1562 This is a kludge, but this isn't worth more time. */
1563 if (wv->value == (char *)1)
1564 {
1565 if (wv->name[0] == '@')
1566 wv->name++;
1567 wv->value = 0;
1568 }
1569 }
1570
bf06c82f 1571 if (STRINGP (wv->lkey))
16ceacc2
JD
1572 wv->key = SDATA (wv->lkey);
1573
1574 if (wv->contents)
1575 update_submenu_strings (wv->contents);
1576 }
1577}
1578
1a578e9b 1579\f
0aea47c1
YM
1580#if TARGET_API_MAC_CARBON
1581extern Lisp_Object Vshow_help_function;
1582
1583static Lisp_Object
1584restore_show_help_function (old_show_help_function)
1585 Lisp_Object old_show_help_function;
1586{
1587 Vshow_help_function = old_show_help_function;
1588
1589 return Qnil;
1590}
1591
1592static pascal OSStatus
1593menu_target_item_handler (next_handler, event, data)
1594 EventHandlerCallRef next_handler;
1595 EventRef event;
1596 void *data;
1597{
b52ee0bd 1598 OSStatus err;
0aea47c1
YM
1599 MenuRef menu;
1600 MenuItemIndex menu_item;
1601 Lisp_Object help;
1602 GrafPtr port;
1603 int specpdl_count = SPECPDL_INDEX ();
1604
1bd0c761
YM
1605 /* Don't be bothered with the overflowed toolbar items menu. */
1606 if (!popup_activated ())
1607 return eventNotHandledErr;
1608
0aea47c1
YM
1609 err = GetEventParameter (event, kEventParamDirectObject, typeMenuRef,
1610 NULL, sizeof (MenuRef), NULL, &menu);
1611 if (err == noErr)
1612 err = GetEventParameter (event, kEventParamMenuItemIndex,
1613 typeMenuItemIndex, NULL,
1614 sizeof (MenuItemIndex), NULL, &menu_item);
1615 if (err == noErr)
1616 err = GetMenuItemProperty (menu, menu_item,
1617 MAC_EMACS_CREATOR_CODE, 'help',
1618 sizeof (Lisp_Object), NULL, &help);
1619 if (err != noErr)
1620 help = Qnil;
1621
1622 /* Temporarily bind Vshow_help_function to Qnil because we don't
1623 want tooltips during menu tracking. */
1624 record_unwind_protect (restore_show_help_function, Vshow_help_function);
1625 Vshow_help_function = Qnil;
1626 GetPort (&port);
1627 show_help_echo (help, Qnil, Qnil, Qnil, 1);
1628 SetPort (port);
1629 unbind_to (specpdl_count, Qnil);
1630
b52ee0bd 1631 return err == noErr ? noErr : eventNotHandledErr;
0aea47c1 1632}
0aea47c1
YM
1633
1634OSStatus
b52ee0bd 1635install_menu_target_item_handler ()
0aea47c1 1636{
0aea47c1
YM
1637 static const EventTypeSpec specs[] =
1638 {{kEventClassMenu, kEventMenuTargetItem}};
0aea47c1 1639
b52ee0bd
YM
1640 return InstallApplicationEventHandler (NewEventHandlerUPP
1641 (menu_target_item_handler),
1642 GetEventTypeCount (specs),
1643 specs, NULL, NULL);
0aea47c1 1644}
b52ee0bd 1645#endif /* TARGET_API_MAC_CARBON */
0aea47c1 1646
f5f870c0
JD
1647/* Event handler function that pops down a menu on C-g. We can only pop
1648 down menus if CancelMenuTracking is present (OSX 10.3 or later). */
1649
74e537fb 1650#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
f5f870c0
JD
1651static pascal OSStatus
1652menu_quit_handler (nextHandler, theEvent, userData)
1653 EventHandlerCallRef nextHandler;
1654 EventRef theEvent;
1655 void* userData;
1656{
e2e206ae 1657 OSStatus err;
f5f870c0
JD
1658 UInt32 keyCode;
1659 UInt32 keyModifiers;
f5f870c0 1660
e2e206ae
YM
1661 err = GetEventParameter (theEvent, kEventParamKeyCode,
1662 typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
f5f870c0 1663
e2e206ae
YM
1664 if (err == noErr)
1665 err = GetEventParameter (theEvent, kEventParamKeyModifiers,
1666 typeUInt32, NULL, sizeof(UInt32),
1667 NULL, &keyModifiers);
f5f870c0 1668
7dcebea4 1669 if (err == noErr && mac_quit_char_key_p (keyModifiers, keyCode))
f5f870c0
JD
1670 {
1671 MenuRef menu = userData != 0
1672 ? (MenuRef)userData : AcquireRootMenu ();
1673
1674 CancelMenuTracking (menu, true, 0);
1675 if (!userData) ReleaseMenu (menu);
1676 return noErr;
1677 }
1678
1679 return CallNextEventHandler (nextHandler, theEvent);
1680}
74e537fb 1681#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
f5f870c0 1682
477f75a5
YM
1683/* Add event handler to all menus that belong to KIND so we can detect
1684 C-g. ROOT_MENU is the root menu of the tracking session to dismiss
1685 when C-g is detected. NULL means the menu bar. If
1686 CancelMenuTracking isn't available, do nothing. */
f5f870c0
JD
1687
1688static void
477f75a5 1689install_menu_quit_handler (kind, root_menu)
e2e206ae 1690 enum mac_menu_kind kind;
477f75a5 1691 MenuRef root_menu;
f5f870c0 1692{
74e537fb 1693#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
37a39780
YM
1694 static const EventTypeSpec typesList[] =
1695 {{kEventClassKeyboard, kEventRawKeyDown}};
e2e206ae 1696 int id;
89f2614d 1697
74e537fb
YM
1698#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
1699 if (CancelMenuTracking == NULL)
1700 return;
1701#endif
e2e206ae 1702 for (id = min_menu_id[kind]; id < min_menu_id[kind + 1]; id++)
f5f870c0 1703 {
477f75a5 1704 MenuRef menu = GetMenuRef (id);
4036ffb9 1705
e2e206ae
YM
1706 if (menu == NULL)
1707 break;
4036ffb9
YM
1708 InstallMenuEventHandler (menu, menu_quit_handler,
1709 GetEventTypeCount (typesList),
477f75a5 1710 typesList, root_menu, NULL);
4036ffb9 1711 }
74e537fb 1712#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
f5f870c0
JD
1713}
1714
1a578e9b
AC
1715/* Set the contents of the menubar widgets of frame F.
1716 The argument FIRST_TIME is currently ignored;
1717 it is set the first time this is called, from initialize_frame_menubar. */
1718
1719void
1720set_frame_menubar (f, first_time, deep_p)
1721 FRAME_PTR f;
1722 int first_time;
1723 int deep_p;
1724{
1725 int menubar_widget = f->output_data.mac->menubar_widget;
1726 Lisp_Object items;
1727 widget_value *wv, *first_wv, *prev_wv = 0;
4036ffb9
YM
1728 int i, last_i = 0;
1729 int *submenu_start, *submenu_end;
1730 int *submenu_top_level_items, *submenu_n_panes;
1a578e9b
AC
1731
1732 XSETFRAME (Vmenu_updating_frame, f);
1733
b52ee0bd
YM
1734 /* This seems to be unnecessary for Carbon. */
1735#if 0
e0f712ba
AC
1736 if (! menubar_widget)
1737 deep_p = 1;
1738 else if (pending_menu_activation && !deep_p)
1739 deep_p = 1;
b52ee0bd 1740#endif
e0f712ba 1741
e0f712ba
AC
1742 if (deep_p)
1743 {
1744 /* Make a widget-value tree representing the entire menu trees. */
1745
1746 struct buffer *prev = current_buffer;
1747 Lisp_Object buffer;
aed13378 1748 int specpdl_count = SPECPDL_INDEX ();
e0f712ba
AC
1749 int previous_menu_items_used = f->menu_bar_items_used;
1750 Lisp_Object *previous_items
1751 = (Lisp_Object *) alloca (previous_menu_items_used
1752 * sizeof (Lisp_Object));
1753
1754 /* If we are making a new widget, its contents are empty,
1755 do always reinitialize them. */
1756 if (! menubar_widget)
1757 previous_menu_items_used = 0;
1758
1759 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
1760 specbind (Qinhibit_quit, Qt);
1761 /* Don't let the debugger step into this code
1762 because it is not reentrant. */
1763 specbind (Qdebug_on_next_call, Qnil);
1764
89f2614d 1765 record_unwind_save_match_data ();
e0f712ba
AC
1766 if (NILP (Voverriding_local_map_menu_flag))
1767 {
1768 specbind (Qoverriding_terminal_local_map, Qnil);
1769 specbind (Qoverriding_local_map, Qnil);
1770 }
1a578e9b 1771
e0f712ba 1772 set_buffer_internal_1 (XBUFFER (buffer));
1a578e9b 1773
e0f712ba
AC
1774 /* Run the Lucid hook. */
1775 safe_run_hooks (Qactivate_menubar_hook);
4036ffb9 1776
e0f712ba
AC
1777 /* If it has changed current-menubar from previous value,
1778 really recompute the menubar from the value. */
1779 if (! NILP (Vlucid_menu_bar_dirty_flag))
1780 call0 (Qrecompute_lucid_menubar);
1781 safe_run_hooks (Qmenu_bar_update_hook);
1782 FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
1a578e9b 1783
e0f712ba 1784 items = FRAME_MENU_BAR_ITEMS (f);
1a578e9b 1785
e0f712ba
AC
1786 /* Save the frame's previous menu bar contents data. */
1787 if (previous_menu_items_used)
1788 bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
1789 previous_menu_items_used * sizeof (Lisp_Object));
1a578e9b 1790
4036ffb9
YM
1791 /* Fill in menu_items with the current menu bar contents.
1792 This can evaluate Lisp code. */
f1d7196a
YM
1793 save_menu_items ();
1794
e0f712ba
AC
1795 menu_items = f->menu_bar_vector;
1796 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
4036ffb9
YM
1797 submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
1798 submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
1799 submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int));
1800 submenu_top_level_items
1801 = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
e0f712ba
AC
1802 init_menu_items ();
1803 for (i = 0; i < XVECTOR (items)->size; i += 4)
1804 {
1805 Lisp_Object key, string, maps;
1806
4036ffb9
YM
1807 last_i = i;
1808
e0f712ba
AC
1809 key = XVECTOR (items)->contents[i];
1810 string = XVECTOR (items)->contents[i + 1];
1811 maps = XVECTOR (items)->contents[i + 2];
1812 if (NILP (string))
1813 break;
1814
4036ffb9
YM
1815 submenu_start[i] = menu_items_used;
1816
1817 menu_items_n_panes = 0;
1818 submenu_top_level_items[i]
1819 = parse_single_submenu (key, string, maps);
1820 submenu_n_panes[i] = menu_items_n_panes;
1821
1822 submenu_end[i] = menu_items_used;
1823 }
1824
1825 finish_menu_items ();
1826
1827 /* Convert menu_items into widget_value trees
1828 to display the menu. This cannot evaluate Lisp code. */
1829
1830 wv = xmalloc_widget_value ();
1831 wv->name = "menubar";
1832 wv->value = 0;
1833 wv->enabled = 1;
1834 wv->button_type = BUTTON_TYPE_NONE;
1835 wv->help = Qnil;
1836 first_wv = wv;
1837
1838 for (i = 0; i < last_i; i += 4)
1839 {
1840 menu_items_n_panes = submenu_n_panes[i];
1841 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
1842 submenu_top_level_items[i]);
177c0ea7 1843 if (prev_wv)
e0f712ba
AC
1844 prev_wv->next = wv;
1845 else
1846 first_wv->contents = wv;
1847 /* Don't set wv->name here; GC during the loop might relocate it. */
1848 wv->enabled = 1;
1849 wv->button_type = BUTTON_TYPE_NONE;
1850 prev_wv = wv;
1851 }
1852
e0f712ba 1853 set_buffer_internal_1 (prev);
e0f712ba
AC
1854
1855 /* If there has been no change in the Lisp-level contents
1856 of the menu bar, skip redisplaying it. Just exit. */
1857
f1d7196a 1858 /* Compare the new menu items with the ones computed last time. */
e0f712ba
AC
1859 for (i = 0; i < previous_menu_items_used; i++)
1860 if (menu_items_used == i
4036ffb9 1861 || (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i])))
1a578e9b 1862 break;
e0f712ba
AC
1863 if (i == menu_items_used && i == previous_menu_items_used && i != 0)
1864 {
f1d7196a
YM
1865 /* The menu items have not changed. Don't bother updating
1866 the menus in any form, since it would be a no-op. */
e0f712ba 1867 free_menubar_widget_value_tree (first_wv);
4036ffb9 1868 discard_menu_items ();
f1d7196a 1869 unbind_to (specpdl_count, Qnil);
e0f712ba
AC
1870 return;
1871 }
1a578e9b 1872
f1d7196a
YM
1873 /* The menu items are different, so store them in the frame. */
1874 f->menu_bar_vector = menu_items;
1875 f->menu_bar_items_used = menu_items_used;
1876
1877 /* This calls restore_menu_items to restore menu_items, etc.,
1878 as they were outside. */
1879 unbind_to (specpdl_count, Qnil);
1880
e0f712ba 1881 /* Now GC cannot happen during the lifetime of the widget_value,
4036ffb9 1882 so it's safe to store data from a Lisp_String. */
e0f712ba
AC
1883 wv = first_wv->contents;
1884 for (i = 0; i < XVECTOR (items)->size; i += 4)
1885 {
1886 Lisp_Object string;
1887 string = XVECTOR (items)->contents[i + 1];
1888 if (NILP (string))
1889 break;
d5db4077 1890 wv->name = (char *) SDATA (string);
16ceacc2 1891 update_submenu_strings (wv->contents);
e0f712ba
AC
1892 wv = wv->next;
1893 }
1a578e9b 1894
e0f712ba
AC
1895 }
1896 else
1897 {
1898 /* Make a widget-value tree containing
1899 just the top level menu bar strings. */
1a578e9b 1900
4036ffb9
YM
1901 wv = xmalloc_widget_value ();
1902 wv->name = "menubar";
1903 wv->value = 0;
1904 wv->enabled = 1;
1905 wv->button_type = BUTTON_TYPE_NONE;
1906 wv->help = Qnil;
1907 first_wv = wv;
1908
e0f712ba
AC
1909 items = FRAME_MENU_BAR_ITEMS (f);
1910 for (i = 0; i < XVECTOR (items)->size; i += 4)
1911 {
1912 Lisp_Object string;
1a578e9b 1913
e0f712ba
AC
1914 string = XVECTOR (items)->contents[i + 1];
1915 if (NILP (string))
1916 break;
1a578e9b 1917
e0f712ba 1918 wv = xmalloc_widget_value ();
d5db4077 1919 wv->name = (char *) SDATA (string);
e0f712ba
AC
1920 wv->value = 0;
1921 wv->enabled = 1;
1922 wv->button_type = BUTTON_TYPE_NONE;
1923 wv->help = Qnil;
1924 /* This prevents lwlib from assuming this
1925 menu item is really supposed to be empty. */
1926 /* The EMACS_INT cast avoids a warning.
1927 This value just has to be different from small integers. */
1928 wv->call_data = (void *) (EMACS_INT) (-1);
1a578e9b 1929
177c0ea7 1930 if (prev_wv)
e0f712ba
AC
1931 prev_wv->next = wv;
1932 else
1933 first_wv->contents = wv;
1934 prev_wv = wv;
1935 }
1a578e9b 1936
e0f712ba
AC
1937 /* Forget what we thought we knew about what is in the
1938 detailed contents of the menu bar menus.
1939 Changing the top level always destroys the contents. */
1940 f->menu_bar_items_used = 0;
1941 }
1a578e9b
AC
1942
1943 /* Create or update the menu bar widget. */
1944
1945 BLOCK_INPUT;
1946
e0f712ba
AC
1947 /* Non-null value to indicate menubar has already been "created". */
1948 f->output_data.mac->menubar_widget = 1;
1a578e9b 1949
0f745bcf 1950 fill_menubar (first_wv->contents, deep_p);
177c0ea7 1951
f5f870c0 1952 /* Add event handler so we can detect C-g. */
e2e206ae
YM
1953 install_menu_quit_handler (MAC_MENU_MENU_BAR, NULL);
1954 install_menu_quit_handler (MAC_MENU_MENU_BAR_SUB, NULL);
1a578e9b
AC
1955 free_menubar_widget_value_tree (first_wv);
1956
1957 UNBLOCK_INPUT;
1958}
1959
1a578e9b
AC
1960/* Get rid of the menu bar of frame F, and free its storage.
1961 This is used when deleting a frame, and when turning off the menu bar. */
1962
1963void
1964free_frame_menubar (f)
1965 FRAME_PTR f;
1966{
4036ffb9 1967 f->output_data.mac->menubar_widget = 0;
1a578e9b
AC
1968}
1969
1970\f
f5f870c0
JD
1971static Lisp_Object
1972pop_down_menu (arg)
1973 Lisp_Object arg;
1974{
0f745bcf
YM
1975 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
1976 FRAME_PTR f = p->pointer;
477f75a5 1977 MenuRef menu = GetMenuRef (min_menu_id[MAC_MENU_POPUP]);
f5f870c0
JD
1978
1979 BLOCK_INPUT;
1980
1981 /* Must reset this manually because the button release event is not
1982 passed to Emacs event loop. */
1983 FRAME_MAC_DISPLAY_INFO (f)->grabbed = 0;
1984
1985 /* delete all menus */
e2e206ae
YM
1986 dispose_menus (MAC_MENU_POPUP_SUB, 0);
1987 DeleteMenu (min_menu_id[MAC_MENU_POPUP]);
0f745bcf 1988 DisposeMenu (menu);
f5f870c0
JD
1989
1990 UNBLOCK_INPUT;
1991
1992 return Qnil;
1993}
1994
1995/* Mac_menu_show actually displays a menu using the panes and items in
1a578e9b
AC
1996 menu_items and returns the value selected from it; we assume input
1997 is blocked by the caller. */
1998
1999/* F is the frame the menu is for.
2000 X and Y are the frame-relative specified position,
2001 relative to the inside upper left corner of the frame F.
2002 FOR_CLICK is nonzero if this menu was invoked for a mouse click.
2003 KEYMAPS is 1 if this menu was specified with keymaps;
2004 in that case, we return a list containing the chosen item's value
2005 and perhaps also the pane's prefix.
2006 TITLE is the specified menu title.
2007 ERROR is a place to store an error message string in case of failure.
2008 (We return nil on failure, but the value doesn't actually matter.) */
2009
2010static Lisp_Object
2011mac_menu_show (f, x, y, for_click, keymaps, title, error)
2012 FRAME_PTR f;
2013 int x;
2014 int y;
2015 int for_click;
2016 int keymaps;
2017 Lisp_Object title;
2018 char **error;
2019{
2020 int i;
61f1d295 2021 int menu_item_choice;
356ce658 2022 UInt32 menu_item_selection;
477f75a5 2023 MenuRef menu;
1a578e9b
AC
2024 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
2025 widget_value **submenu_stack
2026 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
2027 Lisp_Object *subprefix_stack
2028 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
2029 int submenu_depth = 0;
4036ffb9 2030
1a578e9b 2031 int first_pane;
f5f870c0 2032 int specpdl_count = SPECPDL_INDEX ();
1a578e9b
AC
2033
2034 *error = NULL;
2035
2036 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
2037 {
2038 *error = "Empty menu";
2039 return Qnil;
2040 }
2041
2042 /* Create a tree of widget_value objects
2043 representing the panes and their items. */
2044 wv = xmalloc_widget_value ();
2045 wv->name = "menu";
2046 wv->value = 0;
2047 wv->enabled = 1;
2048 wv->button_type = BUTTON_TYPE_NONE;
e0f712ba 2049 wv->help = Qnil;
1a578e9b
AC
2050 first_wv = wv;
2051 first_pane = 1;
177c0ea7 2052
1a578e9b
AC
2053 /* Loop over all panes and items, filling in the tree. */
2054 i = 0;
2055 while (i < menu_items_used)
2056 {
2057 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
2058 {
2059 submenu_stack[submenu_depth++] = save_wv;
2060 save_wv = prev_wv;
2061 prev_wv = 0;
2062 first_pane = 1;
2063 i++;
2064 }
2065 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
2066 {
2067 prev_wv = save_wv;
2068 save_wv = submenu_stack[--submenu_depth];
2069 first_pane = 0;
2070 i++;
2071 }
2072 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
2073 && submenu_depth != 0)
2074 i += MENU_ITEMS_PANE_LENGTH;
2075 /* Ignore a nil in the item list.
2076 It's meaningful only for dialog boxes. */
2077 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
2078 i += 1;
2079 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
2080 {
2081 /* Create a new pane. */
2082 Lisp_Object pane_name, prefix;
2083 char *pane_string;
4036ffb9 2084
e0f712ba
AC
2085 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
2086 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
4036ffb9 2087
1a578e9b
AC
2088#ifndef HAVE_MULTILINGUAL_MENU
2089 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
e0f712ba 2090 {
4036ffb9 2091 pane_name = ENCODE_MENU_STRING (pane_name);
e0f712ba
AC
2092 AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
2093 }
1a578e9b
AC
2094#endif
2095 pane_string = (NILP (pane_name)
d5db4077 2096 ? "" : (char *) SDATA (pane_name));
1a578e9b
AC
2097 /* If there is just one top-level pane, put all its items directly
2098 under the top-level menu. */
2099 if (menu_items_n_panes == 1)
2100 pane_string = "";
2101
2102 /* If the pane has a meaningful name,
2103 make the pane a top-level menu item
2104 with its items as a submenu beneath it. */
2105 if (!keymaps && strcmp (pane_string, ""))
2106 {
2107 wv = xmalloc_widget_value ();
2108 if (save_wv)
2109 save_wv->next = wv;
2110 else
2111 first_wv->contents = wv;
2112 wv->name = pane_string;
2113 if (keymaps && !NILP (prefix))
2114 wv->name++;
2115 wv->value = 0;
2116 wv->enabled = 1;
2117 wv->button_type = BUTTON_TYPE_NONE;
e0f712ba 2118 wv->help = Qnil;
1a578e9b
AC
2119 save_wv = wv;
2120 prev_wv = 0;
2121 }
2122 else if (first_pane)
2123 {
2124 save_wv = wv;
2125 prev_wv = 0;
2126 }
2127 first_pane = 0;
2128 i += MENU_ITEMS_PANE_LENGTH;
2129 }
2130 else
2131 {
2132 /* Create a new item within current pane. */
2133 Lisp_Object item_name, enable, descrip, def, type, selected, help;
e0f712ba
AC
2134 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
2135 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
2136 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
2137 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
2138 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
2139 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
4036ffb9 2140 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
1a578e9b
AC
2141
2142#ifndef HAVE_MULTILINGUAL_MENU
e0f712ba
AC
2143 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
2144 {
383418e5 2145 item_name = ENCODE_MENU_STRING (item_name);
e0f712ba
AC
2146 AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
2147 }
4036ffb9 2148
1a578e9b 2149 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
4036ffb9 2150 {
383418e5 2151 descrip = ENCODE_MENU_STRING (descrip);
e0f712ba
AC
2152 AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
2153 }
2154#endif /* not HAVE_MULTILINGUAL_MENU */
1a578e9b
AC
2155
2156 wv = xmalloc_widget_value ();
177c0ea7 2157 if (prev_wv)
1a578e9b 2158 prev_wv->next = wv;
177c0ea7 2159 else
1a578e9b 2160 save_wv->contents = wv;
d5db4077 2161 wv->name = (char *) SDATA (item_name);
1a578e9b 2162 if (!NILP (descrip))
d5db4077 2163 wv->key = (char *) SDATA (descrip);
1a578e9b
AC
2164 wv->value = 0;
2165 /* Use the contents index as call_data, since we are
e0f712ba 2166 restricted to 16-bits. */
1a578e9b
AC
2167 wv->call_data = !NILP (def) ? (void *) (EMACS_INT) i : 0;
2168 wv->enabled = !NILP (enable);
2169
2170 if (NILP (type))
2171 wv->button_type = BUTTON_TYPE_NONE;
2172 else if (EQ (type, QCtoggle))
2173 wv->button_type = BUTTON_TYPE_TOGGLE;
2174 else if (EQ (type, QCradio))
2175 wv->button_type = BUTTON_TYPE_RADIO;
2176 else
2177 abort ();
2178
2179 wv->selected = !NILP (selected);
4036ffb9
YM
2180
2181 if (! STRINGP (help))
e0f712ba 2182 help = Qnil;
1a578e9b 2183
e0f712ba 2184 wv->help = help;
1a578e9b
AC
2185
2186 prev_wv = wv;
2187
2188 i += MENU_ITEMS_ITEM_LENGTH;
2189 }
2190 }
2191
2192 /* Deal with the title, if it is non-nil. */
2193 if (!NILP (title))
2194 {
2195 widget_value *wv_title = xmalloc_widget_value ();
2196 widget_value *wv_sep = xmalloc_widget_value ();
2197
2198 /* Maybe replace this separator with a bitmap or owner-draw item
2199 so that it looks better. Having two separators looks odd. */
2200 wv_sep->name = "--";
2201 wv_sep->next = first_wv->contents;
e0f712ba 2202 wv_sep->help = Qnil;
1a578e9b
AC
2203
2204#ifndef HAVE_MULTILINGUAL_MENU
2205 if (STRING_MULTIBYTE (title))
383418e5 2206 title = ENCODE_MENU_STRING (title);
1a578e9b 2207#endif
4036ffb9 2208
d5db4077 2209 wv_title->name = (char *) SDATA (title);
f5f870c0 2210 wv_title->enabled = FALSE;
e0f712ba 2211 wv_title->title = TRUE;
1a578e9b 2212 wv_title->button_type = BUTTON_TYPE_NONE;
e0f712ba 2213 wv_title->help = Qnil;
1a578e9b
AC
2214 wv_title->next = wv_sep;
2215 first_wv->contents = wv_title;
2216 }
2217
2218 /* Actually create the menu. */
e2e206ae 2219 menu = NewMenu (min_menu_id[MAC_MENU_POPUP], "\p");
0f745bcf 2220 InsertMenu (menu, -1);
e2e206ae
YM
2221 fill_menu (menu, first_wv->contents, MAC_MENU_POPUP_SUB,
2222 min_menu_id[MAC_MENU_POPUP_SUB]);
1a578e9b 2223
f5f870c0
JD
2224 /* Free the widget_value objects we used to specify the
2225 contents. */
2226 free_menubar_widget_value_tree (first_wv);
2227
1a578e9b 2228 /* Adjust coordinates to be root-window-relative. */
b52ee0bd
YM
2229 x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
2230 y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
1a578e9b 2231
e0f712ba
AC
2232 /* No selection has been chosen yet. */
2233 menu_item_selection = 0;
2234
0f745bcf 2235 record_unwind_protect (pop_down_menu, make_save_value (f, 0));
f5f870c0
JD
2236
2237 /* Add event handler so we can detect C-g. */
e2e206ae
YM
2238 install_menu_quit_handler (MAC_MENU_POPUP, menu);
2239 install_menu_quit_handler (MAC_MENU_POPUP_SUB, menu);
89f2614d 2240
1a578e9b 2241 /* Display the menu. */
a83c3e31 2242 popup_activated_flag = 1;
b52ee0bd 2243 menu_item_choice = PopUpMenuSelect (menu, y, x, 0);
a83c3e31 2244 popup_activated_flag = 0;
1a578e9b 2245
f5f870c0 2246 /* Get the refcon to find the correct item */
356ce658 2247 if (menu_item_choice)
61f1d295 2248 {
477f75a5 2249 MenuRef sel_menu = GetMenuRef (HiWord (menu_item_choice));
356ce658
YM
2250
2251 if (sel_menu)
2252 GetMenuItemRefCon (sel_menu, LoWord (menu_item_choice),
2253 &menu_item_selection);
61f1d295 2254 }
356ce658
YM
2255
2256 unbind_to (specpdl_count, Qnil);
1a578e9b 2257
e0f712ba
AC
2258 /* Find the selected item, and its pane, to return
2259 the proper value. */
1a578e9b
AC
2260 if (menu_item_selection != 0)
2261 {
2262 Lisp_Object prefix, entry;
2263
e0f712ba 2264 prefix = entry = Qnil;
1a578e9b
AC
2265 i = 0;
2266 while (i < menu_items_used)
2267 {
2268 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
2269 {
2270 subprefix_stack[submenu_depth++] = prefix;
2271 prefix = entry;
2272 i++;
2273 }
2274 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
2275 {
2276 prefix = subprefix_stack[--submenu_depth];
2277 i++;
2278 }
2279 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
2280 {
2281 prefix
2282 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2283 i += MENU_ITEMS_PANE_LENGTH;
2284 }
e0f712ba
AC
2285 /* Ignore a nil in the item list.
2286 It's meaningful only for dialog boxes. */
1a578e9b
AC
2287 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
2288 i += 1;
2289 else
2290 {
2291 entry
2292 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
356ce658 2293 if (menu_item_selection == i)
1a578e9b
AC
2294 {
2295 if (keymaps != 0)
2296 {
2297 int j;
2298
2299 entry = Fcons (entry, Qnil);
2300 if (!NILP (prefix))
2301 entry = Fcons (prefix, entry);
2302 for (j = submenu_depth - 1; j >= 0; j--)
2303 if (!NILP (subprefix_stack[j]))
2304 entry = Fcons (subprefix_stack[j], entry);
2305 }
2306 return entry;
2307 }
2308 i += MENU_ITEMS_ITEM_LENGTH;
2309 }
2310 }
2311 }
b8987570
JD
2312 else if (!for_click)
2313 /* Make "Cancel" equivalent to C-g. */
2314 Fsignal (Qquit, Qnil);
1a578e9b
AC
2315
2316 return Qnil;
2317}
2318\f
2319
e0f712ba 2320#ifdef HAVE_DIALOGS
28eabd14
YM
2321/* Construct native Mac OS dialog based on widget_value tree. */
2322
2323#if TARGET_API_MAC_CARBON
2324
b52ee0bd
YM
2325#define DIALOG_BUTTON_COMMAND_ID_OFFSET 'Bt\0\0'
2326#define DIALOG_BUTTON_COMMAND_ID_P(id) \
2327 (((id) & ~0xffff) == DIALOG_BUTTON_COMMAND_ID_OFFSET)
2328#define DIALOG_BUTTON_COMMAND_ID_VALUE(id) \
2329 ((id) - DIALOG_BUTTON_COMMAND_ID_OFFSET)
2330#define DIALOG_BUTTON_MAKE_COMMAND_ID(value) \
2331 ((value) + DIALOG_BUTTON_COMMAND_ID_OFFSET)
2332
512eb161 2333extern EMACS_TIME timer_check P_ ((int));
cbc179a3 2334static int quit_dialog_event_loop;
512eb161 2335
28eabd14
YM
2336static pascal OSStatus
2337mac_handle_dialog_event (next_handler, event, data)
2338 EventHandlerCallRef next_handler;
2339 EventRef event;
2340 void *data;
2341{
512eb161 2342 OSStatus err, result = eventNotHandledErr;
28eabd14
YM
2343 WindowRef window = (WindowRef) data;
2344
2345 switch (GetEventClass (event))
2346 {
2347 case kEventClassCommand:
2348 {
2349 HICommand command;
2350
2351 err = GetEventParameter (event, kEventParamDirectObject,
2352 typeHICommand, NULL, sizeof (HICommand),
2353 NULL, &command);
2354 if (err == noErr)
b52ee0bd 2355 if (DIALOG_BUTTON_COMMAND_ID_P (command.commandID))
28eabd14
YM
2356 {
2357 SetWRefCon (window, command.commandID);
cbc179a3 2358 quit_dialog_event_loop = 1;
512eb161 2359 break;
28eabd14
YM
2360 }
2361
512eb161 2362 result = CallNextEventHandler (next_handler, event);
28eabd14
YM
2363 }
2364 break;
2365
2366 case kEventClassKeyboard:
2367 {
2368 OSStatus result;
2369 char char_code;
2370
2371 result = CallNextEventHandler (next_handler, event);
512eb161
YM
2372 if (result != eventNotHandledErr)
2373 break;
28eabd14
YM
2374
2375 err = GetEventParameter (event, kEventParamKeyMacCharCodes,
2376 typeChar, NULL, sizeof (char),
2377 NULL, &char_code);
2378 if (err == noErr)
2379 switch (char_code)
2380 {
2381 case kEscapeCharCode:
cbc179a3 2382 quit_dialog_event_loop = 1;
28eabd14
YM
2383 break;
2384
2385 default:
2386 {
2387 UInt32 modifiers, key_code;
2388
2389 err = GetEventParameter (event, kEventParamKeyModifiers,
2390 typeUInt32, NULL, sizeof (UInt32),
2391 NULL, &modifiers);
2392 if (err == noErr)
2393 err = GetEventParameter (event, kEventParamKeyCode,
2394 typeUInt32, NULL, sizeof (UInt32),
2395 NULL, &key_code);
2396 if (err == noErr)
512eb161 2397 if (mac_quit_char_key_p (modifiers, key_code))
cbc179a3 2398 quit_dialog_event_loop = 1;
28eabd14
YM
2399 }
2400 break;
2401 }
28eabd14
YM
2402 }
2403 break;
2404
2405 default:
2406 abort ();
2407 }
512eb161 2408
cbc179a3 2409 if (quit_dialog_event_loop)
512eb161
YM
2410 {
2411 err = QuitEventLoop (GetCurrentEventLoop ());
2412 if (err == noErr)
2413 result = noErr;
2414 }
2415
2416 return result;
28eabd14
YM
2417}
2418
2419static OSStatus
2420install_dialog_event_handler (window)
2421 WindowRef window;
2422{
2423 static const EventTypeSpec specs[] =
2424 {{kEventClassCommand, kEventCommandProcess},
2425 {kEventClassKeyboard, kEventRawKeyDown}};
2426 static EventHandlerUPP handle_dialog_eventUPP = NULL;
2427
2428 if (handle_dialog_eventUPP == NULL)
2429 handle_dialog_eventUPP = NewEventHandlerUPP (mac_handle_dialog_event);
2430 return InstallWindowEventHandler (window, handle_dialog_eventUPP,
2431 GetEventTypeCount (specs), specs,
2432 window, NULL);
2433}
2434
2435#define DIALOG_LEFT_MARGIN (112)
2436#define DIALOG_TOP_MARGIN (24)
2437#define DIALOG_RIGHT_MARGIN (24)
2438#define DIALOG_BOTTOM_MARGIN (20)
2439#define DIALOG_MIN_INNER_WIDTH (338)
2440#define DIALOG_MAX_INNER_WIDTH (564)
2441#define DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE (12)
2442#define DIALOG_BUTTON_BUTTON_VERTICAL_SPACE (12)
2443#define DIALOG_BUTTON_MIN_WIDTH (68)
2444#define DIALOG_TEXT_MIN_HEIGHT (50)
2445#define DIALOG_TEXT_BUTTONS_VERTICAL_SPACE (10)
2446#define DIALOG_ICON_WIDTH (64)
2447#define DIALOG_ICON_HEIGHT (64)
2448#define DIALOG_ICON_LEFT_MARGIN (24)
2449#define DIALOG_ICON_TOP_MARGIN (15)
1a578e9b 2450
512eb161
YM
2451static Lisp_Object
2452pop_down_dialog (arg)
2453 Lisp_Object arg;
2454{
2455 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
2456 WindowRef window = p->pointer;
2457
2458 BLOCK_INPUT;
2459
2460 if (popup_activated_flag)
2461 EndAppModalStateForWindow (window);
2462 DisposeWindow (window);
2463 popup_activated_flag = 0;
2464
2465 UNBLOCK_INPUT;
2466
2467 return Qnil;
2468}
2469
28eabd14
YM
2470static int
2471create_and_show_dialog (f, first_wv)
2472 FRAME_PTR f;
2473 widget_value *first_wv;
2474{
2475 OSStatus err;
2476 char *dialog_name, *message;
2477 int nb_buttons, first_group_count, i, result = 0;
2478 widget_value *wv;
2479 short buttons_height, text_height, inner_width, inner_height;
2480 Rect empty_rect, *rects;
2481 WindowRef window = NULL;
28c96b4d 2482 ControlRef *buttons, default_button = NULL, text;
512eb161 2483 int specpdl_count = SPECPDL_INDEX ();
28eabd14
YM
2484
2485 dialog_name = first_wv->name;
2486 nb_buttons = dialog_name[1] - '0';
2487 first_group_count = nb_buttons - (dialog_name[4] - '0');
2488
2489 wv = first_wv->contents;
2490 message = wv->value;
2491
2492 wv = wv->next;
2493 SetRect (&empty_rect, 0, 0, 0, 0);
2494
2495 /* Create dialog window. */
9a5a6add 2496 err = CreateNewWindow (kMovableModalWindowClass,
28eabd14
YM
2497 kWindowStandardHandlerAttribute,
2498 &empty_rect, &window);
2499 if (err == noErr)
512eb161
YM
2500 {
2501 record_unwind_protect (pop_down_dialog, make_save_value (window, 0));
2502 err = SetThemeWindowBackground (window, kThemeBrushMovableModalBackground,
2503 true);
2504 }
28eabd14
YM
2505 if (err == noErr)
2506 err = SetWindowTitleWithCFString (window, (dialog_name[0] == 'Q'
2507 ? CFSTR ("Question")
2508 : CFSTR ("Information")));
2509
2510 /* Create button controls and measure their optimal bounds. */
2511 if (err == noErr)
2512 {
2513 buttons = alloca (sizeof (ControlRef) * nb_buttons);
2514 rects = alloca (sizeof (Rect) * nb_buttons);
2515 for (i = 0; i < nb_buttons; i++)
2516 {
2517 CFStringRef label = cfstring_create_with_utf8_cstring (wv->value);
2518
2519 if (label == NULL)
2520 err = memFullErr;
2521 else
2522 {
2523 err = CreatePushButtonControl (window, &empty_rect,
2524 label, &buttons[i]);
2525 CFRelease (label);
2526 }
28c96b4d
YM
2527 if (err == noErr)
2528 {
2529 if (!wv->enabled)
9a5a6add
YM
2530 {
2531#ifdef MAC_OSX
2532 err = DisableControl (buttons[i]);
2533#else
2534 err = DeactivateControl (buttons[i]);
2535#endif
2536 }
28c96b4d
YM
2537 else if (default_button == NULL)
2538 default_button = buttons[i];
2539 }
28eabd14
YM
2540 if (err == noErr)
2541 {
2542 SInt16 unused;
2543
2544 rects[i] = empty_rect;
2545 err = GetBestControlRect (buttons[i], &rects[i], &unused);
2546 }
2547 if (err == noErr)
2548 {
b52ee0bd
YM
2549 UInt32 command_id;
2550
28eabd14
YM
2551 OffsetRect (&rects[i], -rects[i].left, -rects[i].top);
2552 if (rects[i].right < DIALOG_BUTTON_MIN_WIDTH)
2553 rects[i].right = DIALOG_BUTTON_MIN_WIDTH;
2554 else if (rects[i].right > DIALOG_MAX_INNER_WIDTH)
2555 rects[i].right = DIALOG_MAX_INNER_WIDTH;
2556
b52ee0bd
YM
2557 command_id = DIALOG_BUTTON_MAKE_COMMAND_ID ((int) wv->call_data);
2558 err = SetControlCommandID (buttons[i], command_id);
28eabd14
YM
2559 }
2560 if (err != noErr)
2561 break;
2562 wv = wv->next;
2563 }
2564 }
2565
2566 /* Layout buttons. rects[i] is set relative to the bottom-right
2567 corner of the inner box. */
2568 if (err == noErr)
2569 {
2570 short bottom, right, max_height, left_align_shift;
2571
2572 inner_width = DIALOG_MIN_INNER_WIDTH;
2573 bottom = right = max_height = 0;
2574 for (i = 0; i < nb_buttons; i++)
2575 {
2576 if (right - rects[i].right < - inner_width)
2577 {
2578 if (i != first_group_count
2579 && right - rects[i].right >= - DIALOG_MAX_INNER_WIDTH)
2580 inner_width = - (right - rects[i].right);
2581 else
2582 {
2583 bottom -= max_height + DIALOG_BUTTON_BUTTON_VERTICAL_SPACE;
2584 right = max_height = 0;
2585 }
2586 }
2587 if (max_height < rects[i].bottom)
2588 max_height = rects[i].bottom;
2589 OffsetRect (&rects[i], right - rects[i].right,
2590 bottom - rects[i].bottom);
2591 right = rects[i].left - DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE;
2592 if (i == first_group_count - 1)
2593 right -= DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE;
2594 }
2595 buttons_height = - (bottom - max_height);
2596
2597 left_align_shift = - (inner_width + rects[nb_buttons - 1].left);
2598 for (i = nb_buttons - 1; i >= first_group_count; i--)
2599 {
2600 if (bottom != rects[i].bottom)
2601 {
2602 left_align_shift = - (inner_width + rects[i].left);
2603 bottom = rects[i].bottom;
2604 }
2605 OffsetRect (&rects[i], left_align_shift, 0);
2606 }
2607 }
2608
2609 /* Create a static text control and measure its bounds. */
2610 if (err == noErr)
2611 {
2612 CFStringRef message_string;
2613 Rect bounds;
2614
2615 message_string = cfstring_create_with_utf8_cstring (message);
2616 if (message_string == NULL)
2617 err = memFullErr;
2618 else
2619 {
2620 ControlFontStyleRec text_style;
2621
2622 text_style.flags = 0;
2623 SetRect (&bounds, 0, 0, inner_width, 0);
2624 err = CreateStaticTextControl (window, &bounds, message_string,
2625 &text_style, &text);
2626 CFRelease (message_string);
2627 }
2628 if (err == noErr)
2629 {
2630 SInt16 unused;
2631
2632 bounds = empty_rect;
2633 err = GetBestControlRect (text, &bounds, &unused);
2634 }
2635 if (err == noErr)
2636 {
2637 text_height = bounds.bottom - bounds.top;
2638 if (text_height < DIALOG_TEXT_MIN_HEIGHT)
2639 text_height = DIALOG_TEXT_MIN_HEIGHT;
2640 }
2641 }
2642
2643 /* Place buttons. */
2644 if (err == noErr)
2645 {
2646 inner_height = (text_height + DIALOG_TEXT_BUTTONS_VERTICAL_SPACE
2647 + buttons_height);
2648
2649 for (i = 0; i < nb_buttons; i++)
2650 {
2651 OffsetRect (&rects[i], DIALOG_LEFT_MARGIN + inner_width,
2652 DIALOG_TOP_MARGIN + inner_height);
2653 SetControlBounds (buttons[i], &rects[i]);
2654 }
2655 }
2656
2657 /* Place text. */
2658 if (err == noErr)
2659 {
2660 Rect bounds;
2661
2662 SetRect (&bounds, DIALOG_LEFT_MARGIN, DIALOG_TOP_MARGIN,
2663 DIALOG_LEFT_MARGIN + inner_width,
2664 DIALOG_TOP_MARGIN + text_height);
2665 SetControlBounds (text, &bounds);
2666 }
2667
2668 /* Create the application icon at the upper-left corner. */
2669 if (err == noErr)
2670 {
28c96b4d 2671 ControlButtonContentInfo content;
28eabd14 2672 ControlRef icon;
28c96b4d
YM
2673 static const ProcessSerialNumber psn = {0, kCurrentProcess};
2674#ifdef MAC_OSX
2675 FSRef app_location;
2676#else
2677 ProcessInfoRec pinfo;
2678 FSSpec app_spec;
2679#endif
2680 SInt16 unused;
28eabd14 2681
28c96b4d
YM
2682 content.contentType = kControlContentIconRef;
2683#ifdef MAC_OSX
2684 err = GetProcessBundleLocation (&psn, &app_location);
2685 if (err == noErr)
2686 err = GetIconRefFromFileInfo (&app_location, 0, NULL, 0, NULL,
2687 kIconServicesNormalUsageFlag,
2688 &content.u.iconRef, &unused);
2689#else
2690 bzero (&pinfo, sizeof (ProcessInfoRec));
2691 pinfo.processInfoLength = sizeof (ProcessInfoRec);
2692 pinfo.processAppSpec = &app_spec;
2693 err = GetProcessInformation (&psn, &pinfo);
2694 if (err == noErr)
2695 err = GetIconRefFromFile (&app_spec, &content.u.iconRef, &unused);
2696#endif
28eabd14
YM
2697 if (err == noErr)
2698 {
2699 Rect bounds;
2700
28eabd14
YM
2701 SetRect (&bounds, DIALOG_ICON_LEFT_MARGIN, DIALOG_ICON_TOP_MARGIN,
2702 DIALOG_ICON_LEFT_MARGIN + DIALOG_ICON_WIDTH,
2703 DIALOG_ICON_TOP_MARGIN + DIALOG_ICON_HEIGHT);
28c96b4d
YM
2704 err = CreateIconControl (window, &bounds, &content, true, &icon);
2705 ReleaseIconRef (content.u.iconRef);
28eabd14
YM
2706 }
2707 }
2708
2709 /* Show the dialog window and run event loop. */
2710 if (err == noErr)
28c96b4d
YM
2711 if (default_button)
2712 err = SetWindowDefaultButton (window, default_button);
28eabd14
YM
2713 if (err == noErr)
2714 err = install_dialog_event_handler (window);
2715 if (err == noErr)
2716 {
2717 SizeWindow (window,
2718 DIALOG_LEFT_MARGIN + inner_width + DIALOG_RIGHT_MARGIN,
2719 DIALOG_TOP_MARGIN + inner_height + DIALOG_BOTTOM_MARGIN,
2720 true);
2721 err = RepositionWindow (window, FRAME_MAC_WINDOW (f),
2722 kWindowAlertPositionOnParentWindow);
2723 }
2724 if (err == noErr)
2725 {
2726 SetWRefCon (window, 0);
2727 ShowWindow (window);
2728 BringToFront (window);
512eb161
YM
2729 popup_activated_flag = 1;
2730 err = BeginAppModalStateForWindow (window);
2731 }
2732 if (err == noErr)
2733 {
2734 EventTargetRef toolbox_dispatcher = GetEventDispatcherTarget ();
2735
cbc179a3 2736 quit_dialog_event_loop = 0;
512eb161
YM
2737 while (1)
2738 {
2739 EMACS_TIME next_time = timer_check (1);
2740 long secs = EMACS_SECS (next_time);
2741 long usecs = EMACS_USECS (next_time);
2742 EventTimeout timeout;
2743 EventRef event;
2744
2745 if (secs < 0 || (secs == 0 && usecs == 0))
2746 {
2747 /* Sometimes timer_check returns -1 (no timers) even if
2748 there are timers. So do a timeout anyway. */
2749 secs = 1;
2750 usecs = 0;
2751 }
2752
2753 timeout = (secs * kEventDurationSecond
2754 + usecs * kEventDurationMicrosecond);
2755 err = ReceiveNextEvent (0, NULL, timeout, kEventRemoveFromQueue,
2756 &event);
2757 if (err == noErr)
2758 {
2759 SendEventToEventTarget (event, toolbox_dispatcher);
2760 ReleaseEvent (event);
2761 }
cbc179a3 2762#ifdef MAC_OSX
512eb161
YM
2763 else if (err != eventLoopTimedOutErr)
2764 {
2765 if (err == eventLoopQuitErr)
2766 err = noErr;
2767 break;
2768 }
cbc179a3
YM
2769#else
2770 /* The return value of ReceiveNextEvent seems to be
2771 unreliable. Use our own global variable instead. */
2772 if (quit_dialog_event_loop)
2773 {
2774 err = noErr;
2775 break;
2776 }
2777#endif
512eb161 2778 }
28eabd14
YM
2779 }
2780 if (err == noErr)
2781 {
2782 UInt32 command_id = GetWRefCon (window);
2783
b52ee0bd
YM
2784 if (DIALOG_BUTTON_COMMAND_ID_P (command_id))
2785 result = DIALOG_BUTTON_COMMAND_ID_VALUE (command_id);
28eabd14
YM
2786 }
2787
512eb161 2788 unbind_to (specpdl_count, Qnil);
28eabd14
YM
2789
2790 return result;
2791}
2792#else /* not TARGET_API_MAC_CARBON */
1a578e9b
AC
2793static int
2794mac_dialog (widget_value *wv)
2795{
2796 char *dialog_name;
2797 char *prompt;
2798 char **button_labels;
2799 UInt32 *ref_cons;
2800 int nb_buttons;
2801 int left_count;
2802 int i;
2803 int dialog_width;
2804 Rect rect;
477f75a5
YM
2805 WindowRef window_ptr;
2806 ControlRef ch;
1a578e9b
AC
2807 int left;
2808 EventRecord event_record;
2809 SInt16 part_code;
2810 int control_part_code;
2811 Point mouse;
177c0ea7 2812
1a578e9b
AC
2813 dialog_name = wv->name;
2814 nb_buttons = dialog_name[1] - '0';
2815 left_count = nb_buttons - (dialog_name[4] - '0');
2816 button_labels = (char **) alloca (sizeof (char *) * nb_buttons);
e0f712ba 2817 ref_cons = (UInt32 *) alloca (sizeof (UInt32) * nb_buttons);
177c0ea7 2818
1a578e9b
AC
2819 wv = wv->contents;
2820 prompt = (char *) alloca (strlen (wv->value) + 1);
2821 strcpy (prompt, wv->value);
2822 c2pstr (prompt);
2823
2824 wv = wv->next;
2825 for (i = 0; i < nb_buttons; i++)
2826 {
2827 button_labels[i] = wv->value;
2828 button_labels[i] = (char *) alloca (strlen (wv->value) + 1);
2829 strcpy (button_labels[i], wv->value);
2830 c2pstr (button_labels[i]);
2831 ref_cons[i] = (UInt32) wv->call_data;
2832 wv = wv->next;
2833 }
2834
477f75a5 2835 window_ptr = GetNewCWindow (DIALOG_WINDOW_RESOURCE, NULL, (WindowRef) -1);
e0f712ba 2836
50bf7673 2837 SetPortWindowPort (window_ptr);
177c0ea7 2838
1a578e9b
AC
2839 TextFont (0);
2840 /* Left and right margins in the dialog are 13 pixels each.*/
2841 dialog_width = 14;
2842 /* Calculate width of dialog box: 8 pixels on each side of the text
2843 label in each button, 12 pixels between buttons. */
2844 for (i = 0; i < nb_buttons; i++)
2845 dialog_width += StringWidth (button_labels[i]) + 16 + 12;
2846
2847 if (left_count != 0 && nb_buttons - left_count != 0)
2848 dialog_width += 12;
2849
2850 dialog_width = max (dialog_width, StringWidth (prompt) + 26);
2851
2852 SizeWindow (window_ptr, dialog_width, 78, 0);
2853 ShowWindow (window_ptr);
2854
50bf7673 2855 SetPortWindowPort (window_ptr);
177c0ea7 2856
1a578e9b
AC
2857 TextFont (0);
2858
2859 MoveTo (13, 29);
2860 DrawString (prompt);
2861
2862 left = 13;
2863 for (i = 0; i < nb_buttons; i++)
2864 {
2865 int button_width = StringWidth (button_labels[i]) + 16;
2866 SetRect (&rect, left, 45, left + button_width, 65);
2867 ch = NewControl (window_ptr, &rect, button_labels[i], 1, 0, 0, 0,
2868 kControlPushButtonProc, ref_cons[i]);
2869 left += button_width + 12;
2870 if (i == left_count - 1)
2871 left += 12;
2872 }
2873
2874 i = 0;
2875 while (!i)
2876 {
2877 if (WaitNextEvent (mDownMask, &event_record, 10, NULL))
2878 if (event_record.what == mouseDown)
2879 {
2880 part_code = FindWindow (event_record.where, &window_ptr);
2881 if (part_code == inContent)
2882 {
2883 mouse = event_record.where;
2884 GlobalToLocal (&mouse);
2885 control_part_code = FindControl (mouse, window_ptr, &ch);
2886 if (control_part_code == kControlButtonPart)
2887 if (TrackControl (ch, mouse, NULL))
2888 i = GetControlReference (ch);
2889 }
2890 }
2891 }
2892
2893 DisposeWindow (window_ptr);
177c0ea7 2894
1a578e9b
AC
2895 return i;
2896}
28eabd14 2897#endif /* not TARGET_API_MAC_CARBON */
1a578e9b
AC
2898
2899static char * button_names [] = {
2900 "button1", "button2", "button3", "button4", "button5",
2901 "button6", "button7", "button8", "button9", "button10" };
2902
2903static Lisp_Object
4036ffb9 2904mac_dialog_show (f, keymaps, title, header, error_name)
1a578e9b
AC
2905 FRAME_PTR f;
2906 int keymaps;
3f297536 2907 Lisp_Object title, header;
4036ffb9 2908 char **error_name;
1a578e9b
AC
2909{
2910 int i, nb_buttons=0;
2911 char dialog_name[6];
2912 int menu_item_selection;
2913
e0f712ba 2914 widget_value *wv, *first_wv = 0, *prev_wv = 0;
1a578e9b
AC
2915
2916 /* Number of elements seen so far, before boundary. */
2917 int left_count = 0;
e0f712ba 2918 /* 1 means we've seen the boundary between left-hand elts and right-hand. */
1a578e9b
AC
2919 int boundary_seen = 0;
2920
4036ffb9 2921 *error_name = NULL;
1a578e9b
AC
2922
2923 if (menu_items_n_panes > 1)
2924 {
4036ffb9 2925 *error_name = "Multiple panes in dialog box";
1a578e9b
AC
2926 return Qnil;
2927 }
2928
e0f712ba
AC
2929 /* Create a tree of widget_value objects
2930 representing the text label and buttons. */
1a578e9b
AC
2931 {
2932 Lisp_Object pane_name, prefix;
2933 char *pane_string;
2934 pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
2935 prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
2936 pane_string = (NILP (pane_name)
177c0ea7 2937 ? "" : (char *) SDATA (pane_name));
1a578e9b
AC
2938 prev_wv = xmalloc_widget_value ();
2939 prev_wv->value = pane_string;
2940 if (keymaps && !NILP (prefix))
2941 prev_wv->name++;
2942 prev_wv->enabled = 1;
2943 prev_wv->name = "message";
e0f712ba 2944 prev_wv->help = Qnil;
1a578e9b 2945 first_wv = prev_wv;
177c0ea7 2946
1a578e9b
AC
2947 /* Loop over all panes and items, filling in the tree. */
2948 i = MENU_ITEMS_PANE_LENGTH;
2949 while (i < menu_items_used)
2950 {
177c0ea7 2951
1a578e9b 2952 /* Create a new item within current pane. */
4036ffb9 2953 Lisp_Object item_name, enable, descrip;
1a578e9b
AC
2954 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
2955 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
2956 descrip
2957 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
177c0ea7 2958
1a578e9b
AC
2959 if (NILP (item_name))
2960 {
2961 free_menubar_widget_value_tree (first_wv);
4036ffb9 2962 *error_name = "Submenu in dialog items";
1a578e9b
AC
2963 return Qnil;
2964 }
2965 if (EQ (item_name, Qquote))
2966 {
e0f712ba
AC
2967 /* This is the boundary between left-side elts
2968 and right-side elts. Stop incrementing right_count. */
1a578e9b
AC
2969 boundary_seen = 1;
2970 i++;
2971 continue;
2972 }
2973 if (nb_buttons >= 9)
2974 {
2975 free_menubar_widget_value_tree (first_wv);
4036ffb9 2976 *error_name = "Too many dialog items";
1a578e9b
AC
2977 return Qnil;
2978 }
2979
2980 wv = xmalloc_widget_value ();
2981 prev_wv->next = wv;
2982 wv->name = (char *) button_names[nb_buttons];
2983 if (!NILP (descrip))
d5db4077
KR
2984 wv->key = (char *) SDATA (descrip);
2985 wv->value = (char *) SDATA (item_name);
1a578e9b
AC
2986 wv->call_data = (void *) i;
2987 /* menu item is identified by its index in menu_items table */
2988 wv->enabled = !NILP (enable);
e0f712ba 2989 wv->help = Qnil;
1a578e9b
AC
2990 prev_wv = wv;
2991
2992 if (! boundary_seen)
2993 left_count++;
2994
2995 nb_buttons++;
2996 i += MENU_ITEMS_ITEM_LENGTH;
2997 }
2998
e0f712ba
AC
2999 /* If the boundary was not specified,
3000 by default put half on the left and half on the right. */
1a578e9b
AC
3001 if (! boundary_seen)
3002 left_count = nb_buttons - nb_buttons / 2;
3003
3004 wv = xmalloc_widget_value ();
3005 wv->name = dialog_name;
e0f712ba 3006 wv->help = Qnil;
1a578e9b 3007
3f297536
NR
3008 /* Frame title: 'Q' = Question, 'I' = Information.
3009 Can also have 'E' = Error if, one day, we want
3010 a popup for errors. */
3011 if (NILP(header))
3012 dialog_name[0] = 'Q';
3013 else
3014 dialog_name[0] = 'I';
3015
e0f712ba
AC
3016 /* Dialog boxes use a really stupid name encoding
3017 which specifies how many buttons to use
3f297536 3018 and how many buttons are on the right. */
1a578e9b
AC
3019 dialog_name[1] = '0' + nb_buttons;
3020 dialog_name[2] = 'B';
3021 dialog_name[3] = 'R';
3022 /* Number of buttons to put on the right. */
3023 dialog_name[4] = '0' + nb_buttons - left_count;
3024 dialog_name[5] = 0;
3025 wv->contents = first_wv;
3026 first_wv = wv;
3027 }
3028
b28d7564
YM
3029 /* Force a redisplay before showing the dialog. If a frame is created
3030 just before showing the dialog, its contents may not have been fully
3031 drawn. */
3032 Fredisplay (Qt);
3033
1a578e9b 3034 /* Actually create the dialog. */
28eabd14
YM
3035#if TARGET_API_MAC_CARBON
3036 menu_item_selection = create_and_show_dialog (f, first_wv);
1a578e9b 3037#else
28eabd14 3038 menu_item_selection = mac_dialog (first_wv);
1a578e9b
AC
3039#endif
3040
e0f712ba 3041 /* Free the widget_value objects we used to specify the contents. */
1a578e9b
AC
3042 free_menubar_widget_value_tree (first_wv);
3043
4036ffb9
YM
3044 /* Find the selected item, and its pane, to return
3045 the proper value. */
1a578e9b
AC
3046 if (menu_item_selection != 0)
3047 {
3048 Lisp_Object prefix;
3049
3050 prefix = Qnil;
3051 i = 0;
3052 while (i < menu_items_used)
3053 {
3054 Lisp_Object entry;
3055
3056 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
3057 {
3058 prefix
3059 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
3060 i += MENU_ITEMS_PANE_LENGTH;
3061 }
4036ffb9
YM
3062 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
3063 {
3064 /* This is the boundary between left-side elts and
3065 right-side elts. */
3066 ++i;
3067 }
1a578e9b
AC
3068 else
3069 {
3070 entry
3071 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
3072 if (menu_item_selection == i)
3073 {
3074 if (keymaps != 0)
3075 {
3076 entry = Fcons (entry, Qnil);
3077 if (!NILP (prefix))
3078 entry = Fcons (prefix, entry);
3079 }
3080 return entry;
3081 }
3082 i += MENU_ITEMS_ITEM_LENGTH;
3083 }
3084 }
3085 }
4036ffb9
YM
3086 else
3087 /* Make "Cancel" equivalent to C-g. */
3088 Fsignal (Qquit, Qnil);
1a578e9b
AC
3089
3090 return Qnil;
3091}
e0f712ba 3092#endif /* HAVE_DIALOGS */
1a578e9b
AC
3093\f
3094
3095/* Is this item a separator? */
3096static int
3097name_is_separator (name)
37a39780 3098 const char *name;
1a578e9b 3099{
37a39780 3100 const char *start = name;
e0f712ba
AC
3101
3102 /* Check if name string consists of only dashes ('-'). */
1a578e9b 3103 while (*name == '-') name++;
e0f712ba
AC
3104 /* Separators can also be of the form "--:TripleSuperMegaEtched"
3105 or "--deep-shadow". We don't implement them yet, se we just treat
3106 them like normal separators. */
3107 return (*name == '\0' || start + 2 == name);
1a578e9b
AC
3108}
3109
3110static void
0f745bcf 3111add_menu_item (menu, pos, wv)
477f75a5 3112 MenuRef menu;
0f745bcf
YM
3113 int pos;
3114 widget_value *wv;
1a578e9b 3115{
4036ffb9
YM
3116#if TARGET_API_MAC_CARBON
3117 CFStringRef item_name;
3118#else
1a578e9b 3119 Str255 item_name;
4036ffb9 3120#endif
1a578e9b
AC
3121
3122 if (name_is_separator (wv->name))
3123 AppendMenu (menu, "\p-");
177c0ea7 3124 else
1a578e9b
AC
3125 {
3126 AppendMenu (menu, "\pX");
177c0ea7 3127
e0f712ba 3128#if TARGET_API_MAC_CARBON
4036ffb9
YM
3129 item_name = cfstring_create_with_utf8_cstring (wv->name);
3130
1a578e9b
AC
3131 if (wv->key != NULL)
3132 {
4036ffb9
YM
3133 CFStringRef name, key;
3134
3135 name = item_name;
3136 key = cfstring_create_with_utf8_cstring (wv->key);
3137 item_name = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@ %@"),
3138 name, key);
3139 CFRelease (name);
3140 CFRelease (key);
1a578e9b 3141 }
d8f96db8 3142
4036ffb9
YM
3143 SetMenuItemTextWithCFString (menu, pos, item_name);
3144 CFRelease (item_name);
3145
0f745bcf 3146 if (wv->enabled)
4036ffb9
YM
3147 EnableMenuItem (menu, pos);
3148 else
3149 DisableMenuItem (menu, pos);
0aea47c1
YM
3150
3151 if (STRINGP (wv->help))
3152 SetMenuItemProperty (menu, pos, MAC_EMACS_CREATOR_CODE, 'help',
3153 sizeof (Lisp_Object), &wv->help);
4036ffb9 3154#else /* ! TARGET_API_MAC_CARBON */
4036ffb9
YM
3155 item_name[sizeof (item_name) - 1] = '\0';
3156 strncpy (item_name, wv->name, sizeof (item_name) - 1);
3157 if (wv->key != NULL)
3158 {
3159 int len = strlen (item_name);
3160
3161 strncpy (item_name + len, " ", sizeof (item_name) - 1 - len);
3162 len = strlen (item_name);
3163 strncpy (item_name + len, wv->key, sizeof (item_name) - 1 - len);
3164 }
1a578e9b
AC
3165 c2pstr (item_name);
3166 SetMenuItemText (menu, pos, item_name);
3167
0f745bcf 3168 if (wv->enabled)
e0f712ba 3169 EnableItem (menu, pos);
1a578e9b 3170 else
e0f712ba 3171 DisableItem (menu, pos);
4036ffb9 3172#endif /* ! TARGET_API_MAC_CARBON */
1a578e9b
AC
3173
3174 /* Draw radio buttons and tickboxes. */
1a578e9b
AC
3175 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
3176 wv->button_type == BUTTON_TYPE_RADIO))
3177 SetItemMark (menu, pos, checkMark);
3178 else
3179 SetItemMark (menu, pos, noMark);
1a578e9b 3180
a8e08014
ST
3181 SetMenuItemRefCon (menu, pos, (UInt32) wv->call_data);
3182 }
1a578e9b
AC
3183}
3184
0f745bcf 3185/* Construct native Mac OS menu based on widget_value tree. */
1a578e9b 3186
0f745bcf 3187static int
e2e206ae 3188fill_menu (menu, wv, kind, submenu_id)
477f75a5 3189 MenuRef menu;
0f745bcf 3190 widget_value *wv;
e2e206ae 3191 enum mac_menu_kind kind;
0f745bcf 3192 int submenu_id;
1a578e9b 3193{
0f745bcf 3194 int pos;
1a578e9b 3195
0f745bcf
YM
3196 for (pos = 1; wv != NULL; wv = wv->next, pos++)
3197 {
3198 add_menu_item (menu, pos, wv);
e2e206ae 3199 if (wv->contents && submenu_id < min_menu_id[kind + 1])
0f745bcf 3200 {
477f75a5 3201 MenuRef submenu = NewMenu (submenu_id, "\pX");
1a578e9b 3202
0f745bcf
YM
3203 InsertMenu (submenu, -1);
3204 SetMenuItemHierarchicalID (menu, pos, submenu_id);
e2e206ae 3205 submenu_id = fill_menu (submenu, wv->contents, kind, submenu_id + 1);
0f745bcf
YM
3206 }
3207 }
1a578e9b 3208
0f745bcf 3209 return submenu_id;
1a578e9b
AC
3210}
3211
3212/* Construct native Mac OS menubar based on widget_value tree. */
3213
3214static void
0f745bcf
YM
3215fill_menubar (wv, deep_p)
3216 widget_value *wv;
3217 int deep_p;
1a578e9b 3218{
0f745bcf 3219 int id, submenu_id;
0f745bcf
YM
3220#if !TARGET_API_MAC_CARBON
3221 int title_changed_p = 0;
3222#endif
1a578e9b 3223
0f745bcf
YM
3224 /* Clean up the menu bar when filled by the entire menu trees. */
3225 if (deep_p)
3226 {
e2e206ae
YM
3227 dispose_menus (MAC_MENU_MENU_BAR, 0);
3228 dispose_menus (MAC_MENU_MENU_BAR_SUB, 0);
0f745bcf
YM
3229#if !TARGET_API_MAC_CARBON
3230 title_changed_p = 1;
3231#endif
3232 }
1a578e9b 3233
0f745bcf
YM
3234 /* Fill menu bar titles and submenus. Reuse the existing menu bar
3235 titles as much as possible to minimize redraw (if !deep_p). */
e2e206ae
YM
3236 submenu_id = min_menu_id[MAC_MENU_MENU_BAR_SUB];
3237 for (id = min_menu_id[MAC_MENU_MENU_BAR];
3238 wv != NULL && id < min_menu_id[MAC_MENU_MENU_BAR + 1];
3239 wv = wv->next, id++)
1a578e9b 3240 {
b52ee0bd
YM
3241 OSStatus err = noErr;
3242 MenuRef menu;
3243#if TARGET_API_MAC_CARBON
3244 CFStringRef title;
3245
3246 title = CFStringCreateWithCString (NULL, wv->name,
3247 kCFStringEncodingMacRoman);
3248#else
3249 Str255 title;
3250
72742a99 3251 strncpy (title, wv->name, 255);
0f745bcf 3252 title[255] = '\0';
1a578e9b 3253 c2pstr (title);
b52ee0bd 3254#endif
0f745bcf 3255
477f75a5 3256 menu = GetMenuRef (id);
0f745bcf
YM
3257 if (menu)
3258 {
3259#if TARGET_API_MAC_CARBON
b52ee0bd 3260 CFStringRef old_title;
0f745bcf 3261
b52ee0bd
YM
3262 err = CopyMenuTitleAsCFString (menu, &old_title);
3263 if (err == noErr)
3264 {
3265 if (CFStringCompare (title, old_title, 0) != kCFCompareEqualTo)
3266 err = SetMenuTitleWithCFString (menu, title);
3267 CFRelease (old_title);
3268 }
3269 else
3270 err = SetMenuTitleWithCFString (menu, title);
0f745bcf
YM
3271#else /* !TARGET_API_MAC_CARBON */
3272 if (!EqualString (title, (*menu)->menuData, false, false))
3273 {
3274 DeleteMenu (id);
3275 DisposeMenu (menu);
3276 menu = NewMenu (id, title);
477f75a5 3277 InsertMenu (menu, GetMenuRef (id + 1) ? id + 1 : 0);
0f745bcf
YM
3278 title_changed_p = 1;
3279 }
3280#endif /* !TARGET_API_MAC_CARBON */
3281 }
3282 else
3283 {
b52ee0bd
YM
3284#if TARGET_API_MAC_CARBON
3285 err = CreateNewMenu (id, 0, &menu);
3286 if (err == noErr)
3287 err = SetMenuTitleWithCFString (menu, title);
3288#else
0f745bcf 3289 menu = NewMenu (id, title);
b52ee0bd
YM
3290#endif
3291 if (err == noErr)
3292 {
3293 InsertMenu (menu, 0);
0f745bcf 3294#if !TARGET_API_MAC_CARBON
b52ee0bd 3295 title_changed_p = 1;
0f745bcf 3296#endif
b52ee0bd 3297 }
0f745bcf 3298 }
b52ee0bd
YM
3299#if TARGET_API_MAC_CARBON
3300 CFRelease (title);
3301#endif
1a578e9b 3302
b52ee0bd
YM
3303 if (err == noErr)
3304 if (wv->contents)
3305 submenu_id = fill_menu (menu, wv->contents, MAC_MENU_MENU_BAR_SUB,
3306 submenu_id);
0f745bcf 3307 }
177c0ea7 3308
477f75a5 3309 if (id < min_menu_id[MAC_MENU_MENU_BAR + 1] && GetMenuRef (id))
0f745bcf 3310 {
e2e206ae 3311 dispose_menus (MAC_MENU_MENU_BAR, id);
0f745bcf
YM
3312#if !TARGET_API_MAC_CARBON
3313 title_changed_p = 1;
3314#endif
3315 }
3316
3317#if !TARGET_API_MAC_CARBON
3318 if (title_changed_p)
3319 InvalMenuBar ();
3320#endif
3321}
3322
e2e206ae
YM
3323/* Dispose of menus that belong to KIND, and remove them from the menu
3324 list. ID is the lower bound of menu IDs that will be processed. */
3325
0f745bcf 3326static void
e2e206ae
YM
3327dispose_menus (kind, id)
3328 enum mac_menu_kind kind;
0f745bcf
YM
3329 int id;
3330{
e2e206ae 3331 for (id = max (id, min_menu_id[kind]); id < min_menu_id[kind + 1]; id++)
0f745bcf 3332 {
477f75a5 3333 MenuRef menu = GetMenuRef (id);
e2e206ae
YM
3334
3335 if (menu == NULL)
3336 break;
0f745bcf
YM
3337 DeleteMenu (id);
3338 DisposeMenu (menu);
1a578e9b
AC
3339 }
3340}
3341
3342#endif /* HAVE_MENUS */
c1f043a0 3343
a83c3e31
YM
3344/* Detect if a menu is currently active. */
3345
3346int
3347popup_activated ()
3348{
3349 return popup_activated_flag;
3350}
3351
c1f043a0
CY
3352/* The following is used by delayed window autoselection. */
3353
3354DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
3355 doc: /* Return t if a menu or popup dialog is active. */)
3356 ()
3357{
512eb161
YM
3358#if TARGET_API_MAC_CARBON
3359 return (popup_activated ()) ? Qt : Qnil;
3360#else
c1f043a0
CY
3361 /* Always return Qnil since menu selection functions do not return
3362 until a selection has been made or cancelled. */
3363 return Qnil;
512eb161 3364#endif
c1f043a0 3365}
1a578e9b
AC
3366\f
3367void
3368syms_of_macmenu ()
3369{
3370 staticpro (&menu_items);
3371 menu_items = Qnil;
3372
3373 Qdebug_on_next_call = intern ("debug-on-next-call");
3374 staticpro (&Qdebug_on_next_call);
3375
1a578e9b 3376 defsubr (&Sx_popup_menu);
0ec2e29e 3377 defsubr (&Smenu_or_popup_active_p);
1a578e9b
AC
3378#ifdef HAVE_MENUS
3379 defsubr (&Sx_popup_dialog);
3380#endif
3381}
ab5796a9
MB
3382
3383/* arch-tag: 40b2c6c7-b8a9-4a49-b930-1b2707184cce
3384 (do not change this comment) */