*** empty log message ***
[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,
aaef169d 3 2005, 2006 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
9the Free Software Foundation; either version 2, or (at your option)
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
80#define HAVE_DIALOGS 1
81
82#undef HAVE_MULTILINGUAL_MENU
e0f712ba 83#undef HAVE_DIALOGS /* TODO: Implement native dialogs. */
1a578e9b
AC
84
85/******************************************************************/
86/* Definitions copied from lwlib.h */
87
88typedef void * XtPointer;
89
1a578e9b
AC
90enum button_type
91{
92 BUTTON_TYPE_NONE,
93 BUTTON_TYPE_TOGGLE,
94 BUTTON_TYPE_RADIO
95};
96
e0f712ba
AC
97/* This structure is based on the one in ../lwlib/lwlib.h, modified
98 for Mac OS. */
1a578e9b
AC
99typedef struct _widget_value
100{
101 /* name of widget */
16ceacc2 102 Lisp_Object lname;
1a578e9b
AC
103 char* name;
104 /* value (meaning depend on widget type) */
105 char* value;
177c0ea7 106 /* keyboard equivalent. no implications for XtTranslations */
16ceacc2 107 Lisp_Object lkey;
1a578e9b 108 char* key;
e0f712ba
AC
109 /* Help string or nil if none.
110 GC finds this string through the frame's menu_bar_vector
111 or through menu_items. */
112 Lisp_Object help;
1a578e9b
AC
113 /* true if enabled */
114 Boolean enabled;
115 /* true if selected */
116 Boolean selected;
117 /* The type of a button. */
118 enum button_type button_type;
119 /* true if menu title */
120 Boolean title;
121#if 0
122 /* true if was edited (maintained by get_value) */
123 Boolean edited;
124 /* true if has changed (maintained by lw library) */
125 change_type change;
126 /* true if this widget itself has changed,
127 but not counting the other widgets found in the `next' field. */
128 change_type this_one_change;
129#endif
130 /* Contents of the sub-widgets, also selected slot for checkbox */
131 struct _widget_value* contents;
132 /* data passed to callback */
133 XtPointer call_data;
134 /* next one in the list */
135 struct _widget_value* next;
136#if 0
137 /* slot for the toolkit dependent part. Always initialize to NULL. */
138 void* toolkit_data;
139 /* tell us if we should free the toolkit data slot when freeing the
140 widget_value itself. */
141 Boolean free_toolkit_data;
142
143 /* we resource the widget_value structures; this points to the next
144 one on the free list if this one has been deallocated.
145 */
146 struct _widget_value *free_list;
147#endif
148} widget_value;
149
150/* Assumed by other routines to zero area returned. */
151#define malloc_widget_value() (void *)memset (xmalloc (sizeof (widget_value)),\
152 0, (sizeof (widget_value)))
153#define free_widget_value(wv) xfree (wv)
154
155/******************************************************************/
156
1a578e9b
AC
157#ifndef TRUE
158#define TRUE 1
159#define FALSE 0
160#endif /* no TRUE */
177c0ea7 161
1a578e9b
AC
162Lisp_Object Qdebug_on_next_call;
163
0e9ffc04
YM
164extern Lisp_Object Vmenu_updating_frame;
165
4036ffb9 166extern Lisp_Object Qmenu_bar, Qmac_apple_event;
1a578e9b
AC
167
168extern Lisp_Object QCtoggle, QCradio;
169
170extern Lisp_Object Voverriding_local_map;
171extern Lisp_Object Voverriding_local_map_menu_flag;
172
173extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
174
175extern Lisp_Object Qmenu_bar_update_hook;
176
4036ffb9
YM
177void set_frame_menubar P_ ((FRAME_PTR, int, int));
178
383418e5
ST
179#if TARGET_API_MAC_CARBON
180#define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
181#else
182#define ENCODE_MENU_STRING(str) ENCODE_SYSTEM (str)
183#endif
184
1a578e9b
AC
185static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
186 Lisp_Object, Lisp_Object, Lisp_Object,
187 Lisp_Object, Lisp_Object));
e0f712ba 188#ifdef HAVE_DIALOGS
4036ffb9
YM
189static Lisp_Object mac_dialog_show P_ ((FRAME_PTR, int, Lisp_Object,
190 Lisp_Object, char **));
e0f712ba 191#endif
4036ffb9
YM
192static Lisp_Object mac_menu_show P_ ((struct frame *, int, int, int, int,
193 Lisp_Object, char **));
194static void keymap_panes P_ ((Lisp_Object *, int, int));
195static void single_keymap_panes P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
196 int, int));
197static void list_of_panes P_ ((Lisp_Object));
198static void list_of_items P_ ((Lisp_Object));
1a578e9b 199
b0da976d
YM
200static void find_and_call_menu_selection P_ ((FRAME_PTR, int, Lisp_Object,
201 void *));
e2e206ae 202static int fill_menu P_ ((MenuHandle, widget_value *, enum mac_menu_kind, int));
0f745bcf 203static void fill_menubar P_ ((widget_value *, int));
e2e206ae 204static void dispose_menus P_ ((enum mac_menu_kind, int));
1a578e9b
AC
205
206\f
207/* This holds a Lisp vector that holds the results of decoding
208 the keymaps or alist-of-alists that specify a menu.
209
210 It describes the panes and items within the panes.
211
212 Each pane is described by 3 elements in the vector:
213 t, the pane name, the pane's prefix key.
214 Then follow the pane's items, with 5 elements per item:
215 the item string, the enable flag, the item's value,
216 the definition, and the equivalent keyboard key's description string.
217
218 In some cases, multiple levels of menus may be described.
219 A single vector slot containing nil indicates the start of a submenu.
220 A single vector slot containing lambda indicates the end of a submenu.
221 The submenu follows a menu item which is the way to reach the submenu.
222
223 A single vector slot containing quote indicates that the
224 following items should appear on the right of a dialog box.
225
226 Using a Lisp vector to hold this information while we decode it
227 takes care of protecting all the data from GC. */
228
229#define MENU_ITEMS_PANE_NAME 1
230#define MENU_ITEMS_PANE_PREFIX 2
231#define MENU_ITEMS_PANE_LENGTH 3
232
233enum menu_item_idx
234{
235 MENU_ITEMS_ITEM_NAME = 0,
236 MENU_ITEMS_ITEM_ENABLE,
237 MENU_ITEMS_ITEM_VALUE,
238 MENU_ITEMS_ITEM_EQUIV_KEY,
239 MENU_ITEMS_ITEM_DEFINITION,
240 MENU_ITEMS_ITEM_TYPE,
241 MENU_ITEMS_ITEM_SELECTED,
242 MENU_ITEMS_ITEM_HELP,
243 MENU_ITEMS_ITEM_LENGTH
244};
245
246static Lisp_Object menu_items;
247
248/* Number of slots currently allocated in menu_items. */
249static int menu_items_allocated;
250
251/* This is the index in menu_items of the first empty slot. */
252static int menu_items_used;
253
254/* The number of panes currently recorded in menu_items,
255 excluding those within submenus. */
256static int menu_items_n_panes;
257
258/* Current depth within submenus. */
259static int menu_items_submenu_depth;
260
1a578e9b
AC
261/* This is set nonzero after the user activates the menu bar, and set
262 to zero again after the menu bars are redisplayed by prepare_menu_bar.
263 While it is nonzero, all calls to set_frame_menubar go deep.
264
265 I don't understand why this is needed, but it does seem to be
266 needed on Motif, according to Marcus Daniels <marcus@sysc.pdx.edu>. */
267
268int pending_menu_activation;
269\f
270/* Initialize the menu_items structure if we haven't already done so.
271 Also mark it as currently empty. */
272
273static void
274init_menu_items ()
275{
276 if (NILP (menu_items))
277 {
278 menu_items_allocated = 60;
279 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
280 }
281
282 menu_items_used = 0;
283 menu_items_n_panes = 0;
284 menu_items_submenu_depth = 0;
285}
286
4036ffb9 287/* Call at the end of generating the data in menu_items. */
1a578e9b
AC
288
289static void
290finish_menu_items ()
291{
292}
293
294/* Call when finished using the data for the current menu
295 in menu_items. */
296
297static void
298discard_menu_items ()
299{
300 /* Free the structure if it is especially large.
301 Otherwise, hold on to it, to save time. */
302 if (menu_items_allocated > 200)
303 {
304 menu_items = Qnil;
305 menu_items_allocated = 0;
306 }
307}
308
f1d7196a
YM
309/* This undoes save_menu_items, and it is called by the specpdl unwind
310 mechanism. */
311
312static Lisp_Object
313restore_menu_items (saved)
314 Lisp_Object saved;
315{
316 menu_items = XCAR (saved);
317 menu_items_allocated = (VECTORP (menu_items) ? ASIZE (menu_items) : 0);
318 saved = XCDR (saved);
319 menu_items_used = XINT (XCAR (saved));
320 saved = XCDR (saved);
321 menu_items_n_panes = XINT (XCAR (saved));
74e537fb 322 saved = XCDR (saved);
f1d7196a 323 menu_items_submenu_depth = XINT (XCAR (saved));
0e530e3b 324 return Qnil;
f1d7196a
YM
325}
326
327/* Push the whole state of menu_items processing onto the specpdl.
328 It will be restored when the specpdl is unwound. */
329
330static void
331save_menu_items ()
332{
333 Lisp_Object saved = list4 (menu_items,
334 make_number (menu_items_used),
335 make_number (menu_items_n_panes),
336 make_number (menu_items_submenu_depth));
337 record_unwind_protect (restore_menu_items, saved);
338 menu_items = Qnil;
339}
340\f
1a578e9b
AC
341/* Make the menu_items vector twice as large. */
342
343static void
344grow_menu_items ()
345{
346 Lisp_Object old;
347 int old_size = menu_items_allocated;
348 old = menu_items;
349
350 menu_items_allocated *= 2;
f1d7196a 351
1a578e9b
AC
352 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
353 bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents,
354 old_size * sizeof (Lisp_Object));
355}
356
357/* Begin a submenu. */
358
359static void
360push_submenu_start ()
361{
362 if (menu_items_used + 1 > menu_items_allocated)
363 grow_menu_items ();
364
365 XVECTOR (menu_items)->contents[menu_items_used++] = Qnil;
366 menu_items_submenu_depth++;
367}
368
369/* End a submenu. */
370
371static void
372push_submenu_end ()
373{
374 if (menu_items_used + 1 > menu_items_allocated)
375 grow_menu_items ();
376
377 XVECTOR (menu_items)->contents[menu_items_used++] = Qlambda;
378 menu_items_submenu_depth--;
379}
380
381/* Indicate boundary between left and right. */
382
383static void
384push_left_right_boundary ()
385{
386 if (menu_items_used + 1 > menu_items_allocated)
387 grow_menu_items ();
388
389 XVECTOR (menu_items)->contents[menu_items_used++] = Qquote;
390}
391
e0f712ba 392/* Start a new menu pane in menu_items.
1a578e9b
AC
393 NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */
394
395static void
396push_menu_pane (name, prefix_vec)
397 Lisp_Object name, prefix_vec;
398{
399 if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated)
400 grow_menu_items ();
401
402 if (menu_items_submenu_depth == 0)
403 menu_items_n_panes++;
404 XVECTOR (menu_items)->contents[menu_items_used++] = Qt;
405 XVECTOR (menu_items)->contents[menu_items_used++] = name;
406 XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec;
407}
408
409/* Push one menu item into the current pane. NAME is the string to
410 display. ENABLE if non-nil means this item can be selected. KEY
411 is the key generated by choosing this item, or nil if this item
412 doesn't really have a definition. DEF is the definition of this
413 item. EQUIV is the textual description of the keyboard equivalent
414 for this item (or nil if none). TYPE is the type of this menu
415 item, one of nil, `toggle' or `radio'. */
416
417static void
418push_menu_item (name, enable, key, def, equiv, type, selected, help)
419 Lisp_Object name, enable, key, def, equiv, type, selected, help;
420{
421 if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
422 grow_menu_items ();
423
424 XVECTOR (menu_items)->contents[menu_items_used++] = name;
425 XVECTOR (menu_items)->contents[menu_items_used++] = enable;
426 XVECTOR (menu_items)->contents[menu_items_used++] = key;
427 XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
428 XVECTOR (menu_items)->contents[menu_items_used++] = def;
429 XVECTOR (menu_items)->contents[menu_items_used++] = type;
430 XVECTOR (menu_items)->contents[menu_items_used++] = selected;
431 XVECTOR (menu_items)->contents[menu_items_used++] = help;
432}
433\f
434/* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
435 and generate menu panes for them in menu_items.
436 If NOTREAL is nonzero,
437 don't bother really computing whether an item is enabled. */
438
439static void
440keymap_panes (keymaps, nmaps, notreal)
441 Lisp_Object *keymaps;
442 int nmaps;
443 int notreal;
444{
445 int mapno;
446
447 init_menu_items ();
448
449 /* Loop over the given keymaps, making a pane for each map.
450 But don't make a pane that is empty--ignore that map instead.
451 P is the number of panes we have made so far. */
452 for (mapno = 0; mapno < nmaps; mapno++)
e0f712ba 453 single_keymap_panes (keymaps[mapno],
4036ffb9 454 Fkeymap_prompt (keymaps[mapno]), Qnil, notreal, 10);
1a578e9b
AC
455
456 finish_menu_items ();
457}
458
4036ffb9
YM
459/* Args passed between single_keymap_panes and single_menu_item. */
460struct skp
461 {
462 Lisp_Object pending_maps;
463 int maxdepth, notreal;
464 };
465
466static void single_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
467 void *));
468
1a578e9b
AC
469/* This is a recursive subroutine of keymap_panes.
470 It handles one keymap, KEYMAP.
471 The other arguments are passed along
472 or point to local variables of the previous function.
473 If NOTREAL is nonzero, only check for equivalent key bindings, don't
474 evaluate expressions in menu items and don't make any menu.
475
476 If we encounter submenus deeper than MAXDEPTH levels, ignore them. */
477
478static void
479single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
480 Lisp_Object keymap;
481 Lisp_Object pane_name;
482 Lisp_Object prefix;
483 int notreal;
484 int maxdepth;
485{
4036ffb9
YM
486 struct skp skp;
487 struct gcpro gcpro1;
488
489 skp.pending_maps = Qnil;
490 skp.maxdepth = maxdepth;
491 skp.notreal = notreal;
1a578e9b
AC
492
493 if (maxdepth <= 0)
494 return;
495
496 push_menu_pane (pane_name, prefix);
497
4036ffb9
YM
498 GCPRO1 (skp.pending_maps);
499 map_keymap (keymap, single_menu_item, Qnil, &skp, 1);
500 UNGCPRO;
1a578e9b
AC
501
502 /* Process now any submenus which want to be panes at this level. */
4036ffb9 503 while (CONSP (skp.pending_maps))
1a578e9b
AC
504 {
505 Lisp_Object elt, eltcdr, string;
4036ffb9 506 elt = XCAR (skp.pending_maps);
1a578e9b
AC
507 eltcdr = XCDR (elt);
508 string = XCAR (eltcdr);
509 /* We no longer discard the @ from the beginning of the string here.
510 Instead, we do this in mac_menu_show. */
511 single_keymap_panes (Fcar (elt), string,
512 XCDR (eltcdr), notreal, maxdepth - 1);
4036ffb9 513 skp.pending_maps = XCDR (skp.pending_maps);
1a578e9b
AC
514 }
515}
516\f
517/* This is a subroutine of single_keymap_panes that handles one
518 keymap entry.
177c0ea7 519 KEY is a key in a keymap and ITEM is its binding.
4036ffb9 520 SKP->PENDING_MAPS_PTR is a list of keymaps waiting to be made into
1a578e9b 521 separate panes.
4036ffb9 522 If SKP->NOTREAL is nonzero, only check for equivalent key bindings, don't
1a578e9b 523 evaluate expressions in menu items and don't make any menu.
4036ffb9 524 If we encounter submenus deeper than SKP->MAXDEPTH levels, ignore them. */
1a578e9b
AC
525
526static void
4036ffb9
YM
527single_menu_item (key, item, dummy, skp_v)
528 Lisp_Object key, item, dummy;
529 void *skp_v;
1a578e9b
AC
530{
531 Lisp_Object map, item_string, enabled;
532 struct gcpro gcpro1, gcpro2;
533 int res;
4036ffb9 534 struct skp *skp = skp_v;
177c0ea7 535
1a578e9b
AC
536 /* Parse the menu item and leave the result in item_properties. */
537 GCPRO2 (key, item);
4036ffb9 538 res = parse_menu_item (item, skp->notreal, 0);
1a578e9b
AC
539 UNGCPRO;
540 if (!res)
541 return; /* Not a menu item. */
542
543 map = XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP];
177c0ea7 544
4036ffb9 545 if (skp->notreal)
1a578e9b
AC
546 {
547 /* We don't want to make a menu, just traverse the keymaps to
548 precompute equivalent key bindings. */
549 if (!NILP (map))
4036ffb9 550 single_keymap_panes (map, Qnil, key, 1, skp->maxdepth - 1);
1a578e9b
AC
551 return;
552 }
553
554 enabled = XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE];
177c0ea7 555 item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME];
1a578e9b 556
d5db4077 557 if (!NILP (map) && SREF (item_string, 0) == '@')
1a578e9b
AC
558 {
559 if (!NILP (enabled))
560 /* An enabled separate pane. Remember this to handle it later. */
4036ffb9
YM
561 skp->pending_maps = Fcons (Fcons (map, Fcons (item_string, key)),
562 skp->pending_maps);
1a578e9b
AC
563 return;
564 }
565
566 push_menu_item (item_string, enabled, key,
567 XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF],
568 XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ],
569 XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE],
4036ffb9
YM
570 XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED],
571 XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]);
1a578e9b
AC
572
573 /* Display a submenu using the toolkit. */
574 if (! (NILP (map) || NILP (enabled)))
575 {
576 push_submenu_start ();
4036ffb9 577 single_keymap_panes (map, Qnil, key, 0, skp->maxdepth - 1);
1a578e9b
AC
578 push_submenu_end ();
579 }
580}
581\f
582/* Push all the panes and items of a menu described by the
583 alist-of-alists MENU.
584 This handles old-fashioned calls to x-popup-menu. */
585
586static void
587list_of_panes (menu)
588 Lisp_Object menu;
589{
590 Lisp_Object tail;
591
592 init_menu_items ();
593
4036ffb9 594 for (tail = menu; CONSP (tail); tail = XCDR (tail))
1a578e9b
AC
595 {
596 Lisp_Object elt, pane_name, pane_data;
4036ffb9 597 elt = XCAR (tail);
1a578e9b 598 pane_name = Fcar (elt);
e0f712ba 599 CHECK_STRING (pane_name);
4036ffb9 600 push_menu_pane (ENCODE_MENU_STRING (pane_name), Qnil);
1a578e9b 601 pane_data = Fcdr (elt);
e0f712ba 602 CHECK_CONS (pane_data);
1a578e9b
AC
603 list_of_items (pane_data);
604 }
605
606 finish_menu_items ();
607}
608
609/* Push the items in a single pane defined by the alist PANE. */
610
611static void
612list_of_items (pane)
613 Lisp_Object pane;
614{
615 Lisp_Object tail, item, item1;
616
4036ffb9 617 for (tail = pane; CONSP (tail); tail = XCDR (tail))
1a578e9b 618 {
4036ffb9 619 item = XCAR (tail);
1a578e9b 620 if (STRINGP (item))
4036ffb9
YM
621 push_menu_item (ENCODE_MENU_STRING (item), Qnil, Qnil, Qt,
622 Qnil, Qnil, Qnil, Qnil);
623 else if (CONSP (item))
1a578e9b 624 {
4036ffb9 625 item1 = XCAR (item);
e0f712ba 626 CHECK_STRING (item1);
4036ffb9
YM
627 push_menu_item (ENCODE_MENU_STRING (item1), Qt, XCDR (item),
628 Qt, Qnil, Qnil, Qnil, Qnil);
1a578e9b 629 }
4036ffb9
YM
630 else
631 push_left_right_boundary ();
632
1a578e9b
AC
633 }
634}
635\f
b8987570
JD
636static Lisp_Object
637cleanup_popup_menu (arg)
638 Lisp_Object arg;
639{
640 discard_menu_items ();
0e530e3b 641 return Qnil;
b8987570
JD
642}
643
1a578e9b 644DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
e0f712ba 645 doc: /* Pop up a deck-of-cards menu and return user's selection.
50971a12
YM
646POSITION is a position specification. This is either a mouse button event
647or a list ((XOFFSET YOFFSET) WINDOW)
648where XOFFSET and YOFFSET are positions in pixels from the top left
649corner of WINDOW. (WINDOW may be a window or a frame object.)
650This controls the position of the top left of the menu as a whole.
651If POSITION is t, it means to use the current mouse position.
e0f712ba
AC
652
653MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
654The menu items come from key bindings that have a menu string as well as
50971a12 655a definition; actually, the "definition" in such a key binding looks like
e0f712ba
AC
656\(STRING . REAL-DEFINITION). To give the menu a title, put a string into
657the keymap as a top-level element.
658
659If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
660Otherwise, REAL-DEFINITION should be a valid key binding definition.
661
50971a12
YM
662You can also use a list of keymaps as MENU.
663 Then each keymap makes a separate pane.
e0f712ba 664
50971a12
YM
665When MENU is a keymap or a list of keymaps, the return value is the
666list of events corresponding to the user's choice. Note that
667`x-popup-menu' does not actually execute the command bound to that
668sequence of events.
669
670Alternatively, you can specify a menu of multiple panes
671 with a list of the form (TITLE PANE1 PANE2...),
672where each pane is a list of form (TITLE ITEM1 ITEM2...).
673Each ITEM is normally a cons cell (STRING . VALUE);
674but a string can appear as an item--that makes a nonselectable line
675in the menu.
e0f712ba
AC
676With this form of menu, the return value is VALUE from the chosen item.
677
678If POSITION is nil, don't display the menu at all, just precalculate the
50971a12
YM
679cached information about equivalent key sequences.
680
681If the user gets rid of the menu without making a valid choice, for
682instance by clicking the mouse away from a valid choice or by typing
683keyboard input, then this normally results in a quit and
684`x-popup-menu' does not return. But if POSITION is a mouse button
685event (indicating that the user invoked the menu with the mouse) then
686no quit occurs and `x-popup-menu' returns nil. */)
687 (position, menu)
1a578e9b
AC
688 Lisp_Object position, menu;
689{
690 Lisp_Object keymap, tem;
e0f712ba 691 int xpos = 0, ypos = 0;
1a578e9b 692 Lisp_Object title;
4036ffb9 693 char *error_name = NULL;
1a578e9b 694 Lisp_Object selection;
e0f712ba 695 FRAME_PTR f = NULL;
1a578e9b
AC
696 Lisp_Object x, y, window;
697 int keymaps = 0;
698 int for_click = 0;
b8987570 699 int specpdl_count = SPECPDL_INDEX ();
4036ffb9 700 struct gcpro gcpro1;
1a578e9b
AC
701
702#ifdef HAVE_MENUS
703 if (! NILP (position))
704 {
705 check_mac ();
706
707 /* Decode the first argument: find the window and the coordinates. */
708 if (EQ (position, Qt)
e0f712ba 709 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
4036ffb9
YM
710 || EQ (XCAR (position), Qtool_bar)
711 || EQ (XCAR (position), Qmac_apple_event))))
1a578e9b
AC
712 {
713 /* Use the mouse's current position. */
714 FRAME_PTR new_f = SELECTED_FRAME ();
715 Lisp_Object bar_window;
716 enum scroll_bar_part part;
717 unsigned long time;
718
719 if (mouse_position_hook)
720 (*mouse_position_hook) (&new_f, 1, &bar_window,
721 &part, &x, &y, &time);
722 if (new_f != 0)
723 XSETFRAME (window, new_f);
724 else
725 {
726 window = selected_window;
727 XSETFASTINT (x, 0);
728 XSETFASTINT (y, 0);
729 }
730 }
731 else
732 {
733 tem = Fcar (position);
734 if (CONSP (tem))
735 {
736 window = Fcar (Fcdr (position));
4036ffb9
YM
737 x = XCAR (tem);
738 y = Fcar (XCDR (tem));
1a578e9b
AC
739 }
740 else
741 {
742 for_click = 1;
743 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
744 window = Fcar (tem); /* POSN_WINDOW (tem) */
745 tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
746 x = Fcar (tem);
747 y = Fcdr (tem);
748 }
749 }
750
e0f712ba
AC
751 CHECK_NUMBER (x);
752 CHECK_NUMBER (y);
1a578e9b
AC
753
754 /* Decode where to put the menu. */
755
756 if (FRAMEP (window))
757 {
758 f = XFRAME (window);
759 xpos = 0;
760 ypos = 0;
761 }
762 else if (WINDOWP (window))
763 {
e0f712ba 764 CHECK_LIVE_WINDOW (window);
1a578e9b
AC
765 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
766
90022f5a
KS
767 xpos = WINDOW_LEFT_EDGE_X (XWINDOW (window));
768 ypos = WINDOW_TOP_EDGE_Y (XWINDOW (window));
1a578e9b
AC
769 }
770 else
771 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
772 but I don't want to make one now. */
e0f712ba 773 CHECK_WINDOW (window);
1a578e9b
AC
774
775 xpos += XINT (x);
776 ypos += XINT (y);
777
778 XSETFRAME (Vmenu_updating_frame, f);
779 }
c1d9dffd
JL
780 else
781 Vmenu_updating_frame = Qnil;
1a578e9b
AC
782#endif /* HAVE_MENUS */
783
784 title = Qnil;
785 GCPRO1 (title);
786
787 /* Decode the menu items from what was specified. */
788
e0f712ba
AC
789 keymap = get_keymap (menu, 0, 0);
790 if (CONSP (keymap))
1a578e9b
AC
791 {
792 /* We were given a keymap. Extract menu info from the keymap. */
793 Lisp_Object prompt;
1a578e9b
AC
794
795 /* Extract the detailed info to make one pane. */
796 keymap_panes (&menu, 1, NILP (position));
797
798 /* Search for a string appearing directly as an element of the keymap.
799 That string is the title of the menu. */
0c5b23f8 800 prompt = Fkeymap_prompt (keymap);
1a578e9b
AC
801 if (NILP (title) && !NILP (prompt))
802 title = prompt;
803
804 /* Make that be the pane title of the first pane. */
805 if (!NILP (prompt) && menu_items_n_panes >= 0)
806 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
807
808 keymaps = 1;
809 }
e0f712ba 810 else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
1a578e9b
AC
811 {
812 /* We were given a list of keymaps. */
813 int nmaps = XFASTINT (Flength (menu));
814 Lisp_Object *maps
815 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
816 int i;
817
818 title = Qnil;
819
820 /* The first keymap that has a prompt string
821 supplies the menu title. */
4036ffb9 822 for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
1a578e9b
AC
823 {
824 Lisp_Object prompt;
825
4036ffb9 826 maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
1a578e9b 827
0c5b23f8 828 prompt = Fkeymap_prompt (keymap);
1a578e9b
AC
829 if (NILP (title) && !NILP (prompt))
830 title = prompt;
831 }
832
833 /* Extract the detailed info to make one pane. */
834 keymap_panes (maps, nmaps, NILP (position));
835
836 /* Make the title be the pane title of the first pane. */
837 if (!NILP (title) && menu_items_n_panes >= 0)
838 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
839
840 keymaps = 1;
841 }
842 else
843 {
844 /* We were given an old-fashioned menu. */
845 title = Fcar (menu);
e0f712ba 846 CHECK_STRING (title);
1a578e9b
AC
847
848 list_of_panes (Fcdr (menu));
849
850 keymaps = 0;
851 }
177c0ea7 852
1a578e9b
AC
853 if (NILP (position))
854 {
855 discard_menu_items ();
856 UNGCPRO;
857 return Qnil;
858 }
859
860#ifdef HAVE_MENUS
861 /* Display them in a menu. */
b8987570 862 record_unwind_protect (cleanup_popup_menu, Qnil);
1a578e9b
AC
863 BLOCK_INPUT;
864
865 selection = mac_menu_show (f, xpos, ypos, for_click,
866 keymaps, title, &error_name);
867 UNBLOCK_INPUT;
b8987570 868 unbind_to (specpdl_count, Qnil);
1a578e9b
AC
869
870 UNGCPRO;
871#endif /* HAVE_MENUS */
872
873 if (error_name) error (error_name);
874 return selection;
875}
876
877#ifdef HAVE_MENUS
878
16b12668 879DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
e0f712ba
AC
880 doc: /* Pop up a dialog box and return user's selection.
881POSITION specifies which frame to use.
882This is normally a mouse button event or a window or frame.
883If POSITION is t, it means to use the frame the mouse is on.
884The dialog box appears in the middle of the specified frame.
885
886CONTENTS specifies the alternatives to display in the dialog box.
50971a12 887It is a list of the form (DIALOG ITEM1 ITEM2...).
e0f712ba
AC
888Each ITEM is a cons cell (STRING . VALUE).
889The return value is VALUE from the chosen item.
890
891An ITEM may also be just a string--that makes a nonselectable item.
892An ITEM may also be nil--that means to put all preceding items
893on the left of the dialog box and all following items on the right.
3f297536
NR
894\(By default, approximately half appear on each side.)
895
896If HEADER is non-nil, the frame title for the box is "Information",
50971a12
YM
897otherwise it is "Question".
898
899If the user gets rid of the dialog box without making a valid choice,
900for instance using the window manager, then this produces a quit and
901`x-popup-dialog' does not return. */)
902 (position, contents, header)
3f297536 903 Lisp_Object position, contents, header;
1a578e9b 904{
e0f712ba 905 FRAME_PTR f = NULL;
1a578e9b
AC
906 Lisp_Object window;
907
908 check_mac ();
909
910 /* Decode the first argument: find the window or frame to use. */
911 if (EQ (position, Qt)
e0f712ba 912 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
4036ffb9
YM
913 || EQ (XCAR (position), Qtool_bar)
914 || EQ (XCAR (position), Qmac_apple_event))))
1a578e9b
AC
915 {
916#if 0 /* Using the frame the mouse is on may not be right. */
917 /* Use the mouse's current position. */
918 FRAME_PTR new_f = SELECTED_FRAME ();
919 Lisp_Object bar_window;
e0f712ba 920 enum scroll_bar_part part;
1a578e9b
AC
921 unsigned long time;
922 Lisp_Object x, y;
923
924 (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
925
926 if (new_f != 0)
927 XSETFRAME (window, new_f);
928 else
929 window = selected_window;
930#endif
931 window = selected_window;
932 }
933 else if (CONSP (position))
934 {
935 Lisp_Object tem;
936 tem = Fcar (position);
937 if (CONSP (tem))
938 window = Fcar (Fcdr (position));
939 else
940 {
941 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
942 window = Fcar (tem); /* POSN_WINDOW (tem) */
943 }
944 }
945 else if (WINDOWP (position) || FRAMEP (position))
946 window = position;
947 else
948 window = Qnil;
949
950 /* Decode where to put the menu. */
951
952 if (FRAMEP (window))
953 f = XFRAME (window);
954 else if (WINDOWP (window))
955 {
e0f712ba 956 CHECK_LIVE_WINDOW (window);
1a578e9b
AC
957 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
958 }
959 else
960 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
961 but I don't want to make one now. */
e0f712ba 962 CHECK_WINDOW (window);
1a578e9b
AC
963
964#ifndef HAVE_DIALOGS
965 /* Display a menu with these alternatives
966 in the middle of frame F. */
967 {
968 Lisp_Object x, y, frame, newpos;
969 XSETFRAME (frame, f);
970 XSETINT (x, x_pixel_width (f) / 2);
971 XSETINT (y, x_pixel_height (f) / 2);
972 newpos = Fcons (Fcons (x, Fcons (y, Qnil)), Fcons (frame, Qnil));
973
974 return Fx_popup_menu (newpos,
975 Fcons (Fcar (contents), Fcons (contents, Qnil)));
976 }
977#else /* HAVE_DIALOGS */
978 {
979 Lisp_Object title;
980 char *error_name;
981 Lisp_Object selection;
4036ffb9 982 int specpdl_count = SPECPDL_INDEX ();
1a578e9b
AC
983
984 /* Decode the dialog items from what was specified. */
985 title = Fcar (contents);
e0f712ba 986 CHECK_STRING (title);
1a578e9b
AC
987
988 list_of_panes (Fcons (contents, Qnil));
989
990 /* Display them in a dialog box. */
4036ffb9 991 record_unwind_protect (cleanup_popup_menu, Qnil);
1a578e9b 992 BLOCK_INPUT;
3f297536 993 selection = mac_dialog_show (f, 0, title, header, &error_name);
1a578e9b 994 UNBLOCK_INPUT;
4036ffb9 995 unbind_to (specpdl_count, Qnil);
1a578e9b
AC
996
997 if (error_name) error (error_name);
998 return selection;
999 }
1000#endif /* HAVE_DIALOGS */
1001}
1002
1003/* Activate the menu bar of frame F.
1004 This is called from keyboard.c when it gets the
b3e8cc4d 1005 MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
1a578e9b 1006
4036ffb9
YM
1007 To activate the menu bar, we use the button-press event location
1008 that was saved in saved_menu_event_location.
1a578e9b
AC
1009
1010 But first we recompute the menu bar contents (the whole tree).
1011
4036ffb9
YM
1012 The reason for saving the button event until here, instead of
1013 passing it to the toolkit right away, is that we can safely
1014 execute Lisp code. */
177c0ea7 1015
1a578e9b
AC
1016void
1017x_activate_menubar (f)
1018 FRAME_PTR f;
1019{
1020 SInt32 menu_choice;
b0da976d 1021 SInt16 menu_id, menu_item;
1a578e9b
AC
1022 extern Point saved_menu_event_location;
1023
1024 set_frame_menubar (f, 0, 1);
1025 BLOCK_INPUT;
1026
1027 menu_choice = MenuSelect (saved_menu_event_location);
b0da976d
YM
1028 menu_id = HiWord (menu_choice);
1029 menu_item = LoWord (menu_choice);
1030
1031#if !TARGET_API_MAC_CARBON
1032 if (menu_id == min_menu_id[MAC_MENU_M_APPLE])
1033 do_apple_menu (menu_item);
1034 else
1035#endif
1036 if (menu_id)
1037 {
1038 MenuHandle menu = GetMenuHandle (menu_id);
1039
1040 if (menu)
1041 {
1042 UInt32 refcon;
1043
1044 GetMenuItemRefCon (menu, menu_item, &refcon);
1045 find_and_call_menu_selection (f, f->menu_bar_items_used,
1046 f->menu_bar_vector, (void *) refcon);
1047 }
1048 }
1049
1050 HiliteMenu (0);
1a578e9b
AC
1051
1052 UNBLOCK_INPUT;
1053}
1054
b0da976d
YM
1055/* Find the menu selection and store it in the keyboard buffer.
1056 F is the frame the menu is on.
1057 MENU_BAR_ITEMS_USED is the length of VECTOR.
1058 VECTOR is an array of menu events for the whole menu. */
1a578e9b 1059
b0da976d
YM
1060static void
1061find_and_call_menu_selection (f, menu_bar_items_used, vector, client_data)
1062 FRAME_PTR f;
1063 int menu_bar_items_used;
1064 Lisp_Object vector;
1065 void *client_data;
1a578e9b
AC
1066{
1067 Lisp_Object prefix, entry;
1a578e9b
AC
1068 Lisp_Object *subprefix_stack;
1069 int submenu_depth = 0;
1070 int i;
1071
e0f712ba 1072 entry = Qnil;
b0da976d 1073 subprefix_stack = (Lisp_Object *) alloca (menu_bar_items_used * sizeof (Lisp_Object));
1a578e9b
AC
1074 prefix = Qnil;
1075 i = 0;
b0da976d
YM
1076
1077 while (i < menu_bar_items_used)
1a578e9b
AC
1078 {
1079 if (EQ (XVECTOR (vector)->contents[i], Qnil))
1080 {
1081 subprefix_stack[submenu_depth++] = prefix;
1082 prefix = entry;
1083 i++;
1084 }
1085 else if (EQ (XVECTOR (vector)->contents[i], Qlambda))
1086 {
1087 prefix = subprefix_stack[--submenu_depth];
1088 i++;
1089 }
1090 else if (EQ (XVECTOR (vector)->contents[i], Qt))
1091 {
1092 prefix = XVECTOR (vector)->contents[i + MENU_ITEMS_PANE_PREFIX];
1093 i += MENU_ITEMS_PANE_LENGTH;
1094 }
1095 else
1096 {
1097 entry = XVECTOR (vector)->contents[i + MENU_ITEMS_ITEM_VALUE];
e0f712ba
AC
1098 /* The EMACS_INT cast avoids a warning. There's no problem
1099 as long as pointers have enough bits to hold small integers. */
1100 if ((int) (EMACS_INT) client_data == i)
1a578e9b
AC
1101 {
1102 int j;
1103 struct input_event buf;
1104 Lisp_Object frame;
f456401b 1105 EVENT_INIT (buf);
1a578e9b
AC
1106
1107 XSETFRAME (frame, f);
1108 buf.kind = MENU_BAR_EVENT;
1109 buf.frame_or_window = frame;
1110 buf.arg = frame;
1111 kbd_buffer_store_event (&buf);
1112
1113 for (j = 0; j < submenu_depth; j++)
1114 if (!NILP (subprefix_stack[j]))
1115 {
1116 buf.kind = MENU_BAR_EVENT;
1117 buf.frame_or_window = frame;
1118 buf.arg = subprefix_stack[j];
1119 kbd_buffer_store_event (&buf);
1120 }
1121
1122 if (!NILP (prefix))
1123 {
1124 buf.kind = MENU_BAR_EVENT;
1125 buf.frame_or_window = frame;
1126 buf.arg = prefix;
1127 kbd_buffer_store_event (&buf);
1128 }
1129
1130 buf.kind = MENU_BAR_EVENT;
1131 buf.frame_or_window = frame;
1132 buf.arg = entry;
1133 kbd_buffer_store_event (&buf);
1134
1a578e9b
AC
1135 return;
1136 }
1137 i += MENU_ITEMS_ITEM_LENGTH;
1138 }
1139 }
1140}
1141
1142/* Allocate a widget_value, blocking input. */
1143
1144widget_value *
1145xmalloc_widget_value ()
1146{
1147 widget_value *value;
1148
1149 BLOCK_INPUT;
1150 value = malloc_widget_value ();
1151 UNBLOCK_INPUT;
1152
1153 return value;
1154}
1155
1156/* This recursively calls free_widget_value on the tree of widgets.
1157 It must free all data that was malloc'ed for these widget_values.
1158 In Emacs, many slots are pointers into the data of Lisp_Strings, and
1159 must be left alone. */
1160
1161void
1162free_menubar_widget_value_tree (wv)
1163 widget_value *wv;
1164{
1165 if (! wv) return;
1166
1167 wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
1168
1169 if (wv->contents && (wv->contents != (widget_value*)1))
1170 {
1171 free_menubar_widget_value_tree (wv->contents);
1172 wv->contents = (widget_value *) 0xDEADBEEF;
1173 }
1174 if (wv->next)
1175 {
1176 free_menubar_widget_value_tree (wv->next);
1177 wv->next = (widget_value *) 0xDEADBEEF;
1178 }
1179 BLOCK_INPUT;
1180 free_widget_value (wv);
1181 UNBLOCK_INPUT;
1182}
1183\f
4036ffb9 1184/* Set up data in menu_items for a menu bar item
1a578e9b
AC
1185 whose event type is ITEM_KEY (with string ITEM_NAME)
1186 and whose contents come from the list of keymaps MAPS. */
1187
4036ffb9
YM
1188static int
1189parse_single_submenu (item_key, item_name, maps)
1a578e9b
AC
1190 Lisp_Object item_key, item_name, maps;
1191{
1a578e9b
AC
1192 Lisp_Object length;
1193 int len;
1194 Lisp_Object *mapvec;
4036ffb9 1195 int i;
1a578e9b
AC
1196 int top_level_items = 0;
1197
1198 length = Flength (maps);
1199 len = XINT (length);
1200
1201 /* Convert the list MAPS into a vector MAPVEC. */
1202 mapvec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
1203 for (i = 0; i < len; i++)
1204 {
1205 mapvec[i] = Fcar (maps);
1206 maps = Fcdr (maps);
1207 }
1208
1a578e9b
AC
1209 /* Loop over the given keymaps, making a pane for each map.
1210 But don't make a pane that is empty--ignore that map instead. */
1211 for (i = 0; i < len; i++)
1212 {
4036ffb9 1213 if (!KEYMAPP (mapvec[i]))
1a578e9b
AC
1214 {
1215 /* Here we have a command at top level in the menu bar
1216 as opposed to a submenu. */
1217 top_level_items = 1;
1218 push_menu_pane (Qnil, Qnil);
1219 push_menu_item (item_name, Qt, item_key, mapvec[i],
4036ffb9 1220 Qnil, Qnil, Qnil, Qnil);
1a578e9b
AC
1221 }
1222 else
4036ffb9
YM
1223 {
1224 Lisp_Object prompt;
1225 prompt = Fkeymap_prompt (mapvec[i]);
1226 single_keymap_panes (mapvec[i],
1227 !NILP (prompt) ? prompt : item_name,
1228 item_key, 0, 10);
1229 }
1a578e9b
AC
1230 }
1231
4036ffb9
YM
1232 return top_level_items;
1233}
1234
1235/* Create a tree of widget_value objects
1236 representing the panes and items
1237 in menu_items starting at index START, up to index END. */
1238
1239static widget_value *
1240digest_single_submenu (start, end, top_level_items)
1241 int start, end, top_level_items;
1242{
1243 widget_value *wv, *prev_wv, *save_wv, *first_wv;
1244 int i;
1245 int submenu_depth = 0;
1246 widget_value **submenu_stack;
f1d7196a 1247 int panes_seen = 0;
1a578e9b
AC
1248
1249 submenu_stack
1250 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
1251 wv = xmalloc_widget_value ();
1252 wv->name = "menu";
1253 wv->value = 0;
1254 wv->enabled = 1;
1255 wv->button_type = BUTTON_TYPE_NONE;
e0f712ba 1256 wv->help = Qnil;
1a578e9b
AC
1257 first_wv = wv;
1258 save_wv = 0;
1259 prev_wv = 0;
177c0ea7 1260
4036ffb9
YM
1261 /* Loop over all panes and items made by the preceding call
1262 to parse_single_submenu and construct a tree of widget_value objects.
1263 Ignore the panes and items used by previous calls to
1264 digest_single_submenu, even though those are also in menu_items. */
1265 i = start;
1266 while (i < end)
1a578e9b
AC
1267 {
1268 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1269 {
1270 submenu_stack[submenu_depth++] = save_wv;
1271 save_wv = prev_wv;
1272 prev_wv = 0;
1273 i++;
1274 }
1275 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1276 {
1277 prev_wv = save_wv;
1278 save_wv = submenu_stack[--submenu_depth];
1279 i++;
1280 }
1281 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
1282 && submenu_depth != 0)
1283 i += MENU_ITEMS_PANE_LENGTH;
1284 /* Ignore a nil in the item list.
1285 It's meaningful only for dialog boxes. */
1286 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1287 i += 1;
1288 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1289 {
1290 /* Create a new pane. */
1291 Lisp_Object pane_name, prefix;
1292 char *pane_string;
e0f712ba 1293
f1d7196a
YM
1294 panes_seen++;
1295
1a578e9b
AC
1296 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1297 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
e0f712ba 1298
1a578e9b
AC
1299#ifndef HAVE_MULTILINGUAL_MENU
1300 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
e0f712ba 1301 {
4036ffb9 1302 pane_name = ENCODE_MENU_STRING (pane_name);
e0f712ba
AC
1303 AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
1304 }
1a578e9b
AC
1305#endif
1306 pane_string = (NILP (pane_name)
d5db4077 1307 ? "" : (char *) SDATA (pane_name));
1a578e9b
AC
1308 /* If there is just one top-level pane, put all its items directly
1309 under the top-level menu. */
1310 if (menu_items_n_panes == 1)
1311 pane_string = "";
1312
1313 /* If the pane has a meaningful name,
1314 make the pane a top-level menu item
1315 with its items as a submenu beneath it. */
1316 if (strcmp (pane_string, ""))
1317 {
1318 wv = xmalloc_widget_value ();
1319 if (save_wv)
1320 save_wv->next = wv;
1321 else
1322 first_wv->contents = wv;
16ceacc2
JD
1323 wv->lname = pane_name;
1324 /* Set value to 1 so update_submenu_strings can handle '@' */
1325 wv->value = (char *)1;
1a578e9b
AC
1326 wv->enabled = 1;
1327 wv->button_type = BUTTON_TYPE_NONE;
e0f712ba 1328 wv->help = Qnil;
e188aa29 1329 save_wv = wv;
1a578e9b 1330 }
e188aa29
YM
1331 else
1332 save_wv = first_wv;
1333
1a578e9b
AC
1334 prev_wv = 0;
1335 i += MENU_ITEMS_PANE_LENGTH;
1336 }
1337 else
1338 {
1339 /* Create a new item within current pane. */
1340 Lisp_Object item_name, enable, descrip, def, type, selected;
4036ffb9 1341 Lisp_Object help;
1a578e9b 1342
f1d7196a
YM
1343 /* All items should be contained in panes. */
1344 if (panes_seen == 0)
1345 abort ();
1346
e0f712ba
AC
1347 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
1348 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
1349 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
1350 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
1351 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
1352 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
1353 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
1a578e9b
AC
1354
1355#ifndef HAVE_MULTILINGUAL_MENU
4036ffb9 1356 if (STRING_MULTIBYTE (item_name))
e0f712ba 1357 {
383418e5 1358 item_name = ENCODE_MENU_STRING (item_name);
e0f712ba
AC
1359 AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
1360 }
1361
4036ffb9 1362 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
e0f712ba 1363 {
383418e5 1364 descrip = ENCODE_MENU_STRING (descrip);
e0f712ba
AC
1365 AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
1366 }
1367#endif /* not HAVE_MULTILINGUAL_MENU */
1a578e9b
AC
1368
1369 wv = xmalloc_widget_value ();
177c0ea7 1370 if (prev_wv)
1a578e9b
AC
1371 prev_wv->next = wv;
1372 else
1373 save_wv->contents = wv;
1374
16ceacc2 1375 wv->lname = item_name;
1a578e9b 1376 if (!NILP (descrip))
16ceacc2 1377 wv->lkey = descrip;
1a578e9b
AC
1378 wv->value = 0;
1379 /* The EMACS_INT cast avoids a warning. There's no problem
1380 as long as pointers have enough bits to hold small integers. */
1381 wv->call_data = (!NILP (def) ? (void *) (EMACS_INT) i : 0);
1382 wv->enabled = !NILP (enable);
1383
1384 if (NILP (type))
1385 wv->button_type = BUTTON_TYPE_NONE;
1386 else if (EQ (type, QCradio))
1387 wv->button_type = BUTTON_TYPE_RADIO;
1388 else if (EQ (type, QCtoggle))
1389 wv->button_type = BUTTON_TYPE_TOGGLE;
1390 else
1391 abort ();
1392
1393 wv->selected = !NILP (selected);
4036ffb9 1394 if (! STRINGP (help))
e0f712ba
AC
1395 help = Qnil;
1396
1397 wv->help = help;
1a578e9b
AC
1398
1399 prev_wv = wv;
1400
1401 i += MENU_ITEMS_ITEM_LENGTH;
1402 }
1403 }
1404
1405 /* If we have just one "menu item"
1406 that was originally a button, return it by itself. */
1407 if (top_level_items && first_wv->contents && first_wv->contents->next == 0)
1408 {
1409 wv = first_wv->contents;
1410 free_widget_value (first_wv);
1411 return wv;
1412 }
1413
1414 return first_wv;
1415}
4036ffb9 1416
16ceacc2
JD
1417/* Walk through the widget_value tree starting at FIRST_WV and update
1418 the char * pointers from the corresponding lisp values.
1419 We do this after building the whole tree, since GC may happen while the
1420 tree is constructed, and small strings are relocated. So we must wait
1421 until no GC can happen before storing pointers into lisp values. */
1422static void
1423update_submenu_strings (first_wv)
1424 widget_value *first_wv;
1425{
1426 widget_value *wv;
1427
1428 for (wv = first_wv; wv; wv = wv->next)
1429 {
bf06c82f 1430 if (STRINGP (wv->lname))
16ceacc2
JD
1431 {
1432 wv->name = SDATA (wv->lname);
1433
1434 /* Ignore the @ that means "separate pane".
1435 This is a kludge, but this isn't worth more time. */
1436 if (wv->value == (char *)1)
1437 {
1438 if (wv->name[0] == '@')
1439 wv->name++;
1440 wv->value = 0;
1441 }
1442 }
1443
bf06c82f 1444 if (STRINGP (wv->lkey))
16ceacc2
JD
1445 wv->key = SDATA (wv->lkey);
1446
1447 if (wv->contents)
1448 update_submenu_strings (wv->contents);
1449 }
1450}
1451
1a578e9b 1452\f
f5f870c0
JD
1453/* Event handler function that pops down a menu on C-g. We can only pop
1454 down menus if CancelMenuTracking is present (OSX 10.3 or later). */
1455
74e537fb 1456#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
f5f870c0
JD
1457static pascal OSStatus
1458menu_quit_handler (nextHandler, theEvent, userData)
1459 EventHandlerCallRef nextHandler;
1460 EventRef theEvent;
1461 void* userData;
1462{
e2e206ae 1463 OSStatus err;
f5f870c0
JD
1464 UInt32 keyCode;
1465 UInt32 keyModifiers;
1466 extern int mac_quit_char_modifiers;
1467 extern int mac_quit_char_keycode;
1468
e2e206ae
YM
1469 err = GetEventParameter (theEvent, kEventParamKeyCode,
1470 typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
f5f870c0 1471
e2e206ae
YM
1472 if (err == noErr)
1473 err = GetEventParameter (theEvent, kEventParamKeyModifiers,
1474 typeUInt32, NULL, sizeof(UInt32),
1475 NULL, &keyModifiers);
f5f870c0 1476
e2e206ae 1477 if (err == noErr && keyCode == mac_quit_char_keycode
f5f870c0
JD
1478 && keyModifiers == mac_quit_char_modifiers)
1479 {
1480 MenuRef menu = userData != 0
1481 ? (MenuRef)userData : AcquireRootMenu ();
1482
1483 CancelMenuTracking (menu, true, 0);
1484 if (!userData) ReleaseMenu (menu);
1485 return noErr;
1486 }
1487
1488 return CallNextEventHandler (nextHandler, theEvent);
1489}
74e537fb 1490#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
f5f870c0 1491
e2e206ae
YM
1492/* Add event handler to all menus that belong to KIND so we can detect C-g.
1493 MENU_HANDLE is the root menu of the tracking session to dismiss
1494 when C-g is detected. NULL means the menu bar.
f5f870c0
JD
1495 If CancelMenuTracking isn't available, do nothing. */
1496
1497static void
e2e206ae
YM
1498install_menu_quit_handler (kind, menu_handle)
1499 enum mac_menu_kind kind;
1500 MenuHandle menu_handle;
f5f870c0 1501{
74e537fb 1502#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
37a39780
YM
1503 static const EventTypeSpec typesList[] =
1504 {{kEventClassKeyboard, kEventRawKeyDown}};
e2e206ae 1505 int id;
89f2614d 1506
74e537fb
YM
1507#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
1508 if (CancelMenuTracking == NULL)
1509 return;
1510#endif
e2e206ae 1511 for (id = min_menu_id[kind]; id < min_menu_id[kind + 1]; id++)
f5f870c0 1512 {
e2e206ae 1513 MenuHandle menu = GetMenuHandle (id);
4036ffb9 1514
e2e206ae
YM
1515 if (menu == NULL)
1516 break;
4036ffb9
YM
1517 InstallMenuEventHandler (menu, menu_quit_handler,
1518 GetEventTypeCount (typesList),
74e537fb 1519 typesList, menu_handle, NULL);
4036ffb9 1520 }
74e537fb 1521#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
f5f870c0
JD
1522}
1523
1a578e9b
AC
1524/* Set the contents of the menubar widgets of frame F.
1525 The argument FIRST_TIME is currently ignored;
1526 it is set the first time this is called, from initialize_frame_menubar. */
1527
1528void
1529set_frame_menubar (f, first_time, deep_p)
1530 FRAME_PTR f;
1531 int first_time;
1532 int deep_p;
1533{
1534 int menubar_widget = f->output_data.mac->menubar_widget;
1535 Lisp_Object items;
1536 widget_value *wv, *first_wv, *prev_wv = 0;
4036ffb9
YM
1537 int i, last_i = 0;
1538 int *submenu_start, *submenu_end;
1539 int *submenu_top_level_items, *submenu_n_panes;
1a578e9b
AC
1540
1541 XSETFRAME (Vmenu_updating_frame, f);
1542
e0f712ba
AC
1543 if (! menubar_widget)
1544 deep_p = 1;
1545 else if (pending_menu_activation && !deep_p)
1546 deep_p = 1;
1547
e0f712ba
AC
1548 if (deep_p)
1549 {
1550 /* Make a widget-value tree representing the entire menu trees. */
1551
1552 struct buffer *prev = current_buffer;
1553 Lisp_Object buffer;
aed13378 1554 int specpdl_count = SPECPDL_INDEX ();
e0f712ba
AC
1555 int previous_menu_items_used = f->menu_bar_items_used;
1556 Lisp_Object *previous_items
1557 = (Lisp_Object *) alloca (previous_menu_items_used
1558 * sizeof (Lisp_Object));
1559
1560 /* If we are making a new widget, its contents are empty,
1561 do always reinitialize them. */
1562 if (! menubar_widget)
1563 previous_menu_items_used = 0;
1564
1565 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
1566 specbind (Qinhibit_quit, Qt);
1567 /* Don't let the debugger step into this code
1568 because it is not reentrant. */
1569 specbind (Qdebug_on_next_call, Qnil);
1570
89f2614d 1571 record_unwind_save_match_data ();
e0f712ba
AC
1572 if (NILP (Voverriding_local_map_menu_flag))
1573 {
1574 specbind (Qoverriding_terminal_local_map, Qnil);
1575 specbind (Qoverriding_local_map, Qnil);
1576 }
1a578e9b 1577
e0f712ba 1578 set_buffer_internal_1 (XBUFFER (buffer));
1a578e9b 1579
e0f712ba
AC
1580 /* Run the Lucid hook. */
1581 safe_run_hooks (Qactivate_menubar_hook);
4036ffb9 1582
e0f712ba
AC
1583 /* If it has changed current-menubar from previous value,
1584 really recompute the menubar from the value. */
1585 if (! NILP (Vlucid_menu_bar_dirty_flag))
1586 call0 (Qrecompute_lucid_menubar);
1587 safe_run_hooks (Qmenu_bar_update_hook);
1588 FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
1a578e9b 1589
e0f712ba 1590 items = FRAME_MENU_BAR_ITEMS (f);
1a578e9b 1591
e0f712ba
AC
1592 /* Save the frame's previous menu bar contents data. */
1593 if (previous_menu_items_used)
1594 bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
1595 previous_menu_items_used * sizeof (Lisp_Object));
1a578e9b 1596
4036ffb9
YM
1597 /* Fill in menu_items with the current menu bar contents.
1598 This can evaluate Lisp code. */
f1d7196a
YM
1599 save_menu_items ();
1600
e0f712ba
AC
1601 menu_items = f->menu_bar_vector;
1602 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
4036ffb9
YM
1603 submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
1604 submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
1605 submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int));
1606 submenu_top_level_items
1607 = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
e0f712ba
AC
1608 init_menu_items ();
1609 for (i = 0; i < XVECTOR (items)->size; i += 4)
1610 {
1611 Lisp_Object key, string, maps;
1612
4036ffb9
YM
1613 last_i = i;
1614
e0f712ba
AC
1615 key = XVECTOR (items)->contents[i];
1616 string = XVECTOR (items)->contents[i + 1];
1617 maps = XVECTOR (items)->contents[i + 2];
1618 if (NILP (string))
1619 break;
1620
4036ffb9
YM
1621 submenu_start[i] = menu_items_used;
1622
1623 menu_items_n_panes = 0;
1624 submenu_top_level_items[i]
1625 = parse_single_submenu (key, string, maps);
1626 submenu_n_panes[i] = menu_items_n_panes;
1627
1628 submenu_end[i] = menu_items_used;
1629 }
1630
1631 finish_menu_items ();
1632
1633 /* Convert menu_items into widget_value trees
1634 to display the menu. This cannot evaluate Lisp code. */
1635
1636 wv = xmalloc_widget_value ();
1637 wv->name = "menubar";
1638 wv->value = 0;
1639 wv->enabled = 1;
1640 wv->button_type = BUTTON_TYPE_NONE;
1641 wv->help = Qnil;
1642 first_wv = wv;
1643
1644 for (i = 0; i < last_i; i += 4)
1645 {
1646 menu_items_n_panes = submenu_n_panes[i];
1647 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
1648 submenu_top_level_items[i]);
177c0ea7 1649 if (prev_wv)
e0f712ba
AC
1650 prev_wv->next = wv;
1651 else
1652 first_wv->contents = wv;
1653 /* Don't set wv->name here; GC during the loop might relocate it. */
1654 wv->enabled = 1;
1655 wv->button_type = BUTTON_TYPE_NONE;
1656 prev_wv = wv;
1657 }
1658
e0f712ba 1659 set_buffer_internal_1 (prev);
e0f712ba
AC
1660
1661 /* If there has been no change in the Lisp-level contents
1662 of the menu bar, skip redisplaying it. Just exit. */
1663
f1d7196a 1664 /* Compare the new menu items with the ones computed last time. */
e0f712ba
AC
1665 for (i = 0; i < previous_menu_items_used; i++)
1666 if (menu_items_used == i
4036ffb9 1667 || (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i])))
1a578e9b 1668 break;
e0f712ba
AC
1669 if (i == menu_items_used && i == previous_menu_items_used && i != 0)
1670 {
f1d7196a
YM
1671 /* The menu items have not changed. Don't bother updating
1672 the menus in any form, since it would be a no-op. */
e0f712ba 1673 free_menubar_widget_value_tree (first_wv);
4036ffb9 1674 discard_menu_items ();
f1d7196a 1675 unbind_to (specpdl_count, Qnil);
e0f712ba
AC
1676 return;
1677 }
1a578e9b 1678
f1d7196a
YM
1679 /* The menu items are different, so store them in the frame. */
1680 f->menu_bar_vector = menu_items;
1681 f->menu_bar_items_used = menu_items_used;
1682
1683 /* This calls restore_menu_items to restore menu_items, etc.,
1684 as they were outside. */
1685 unbind_to (specpdl_count, Qnil);
1686
e0f712ba 1687 /* Now GC cannot happen during the lifetime of the widget_value,
4036ffb9 1688 so it's safe to store data from a Lisp_String. */
e0f712ba
AC
1689 wv = first_wv->contents;
1690 for (i = 0; i < XVECTOR (items)->size; i += 4)
1691 {
1692 Lisp_Object string;
1693 string = XVECTOR (items)->contents[i + 1];
1694 if (NILP (string))
1695 break;
d5db4077 1696 wv->name = (char *) SDATA (string);
16ceacc2 1697 update_submenu_strings (wv->contents);
e0f712ba
AC
1698 wv = wv->next;
1699 }
1a578e9b 1700
e0f712ba
AC
1701 }
1702 else
1703 {
1704 /* Make a widget-value tree containing
1705 just the top level menu bar strings. */
1a578e9b 1706
4036ffb9
YM
1707 wv = xmalloc_widget_value ();
1708 wv->name = "menubar";
1709 wv->value = 0;
1710 wv->enabled = 1;
1711 wv->button_type = BUTTON_TYPE_NONE;
1712 wv->help = Qnil;
1713 first_wv = wv;
1714
e0f712ba
AC
1715 items = FRAME_MENU_BAR_ITEMS (f);
1716 for (i = 0; i < XVECTOR (items)->size; i += 4)
1717 {
1718 Lisp_Object string;
1a578e9b 1719
e0f712ba
AC
1720 string = XVECTOR (items)->contents[i + 1];
1721 if (NILP (string))
1722 break;
1a578e9b 1723
e0f712ba 1724 wv = xmalloc_widget_value ();
d5db4077 1725 wv->name = (char *) SDATA (string);
e0f712ba
AC
1726 wv->value = 0;
1727 wv->enabled = 1;
1728 wv->button_type = BUTTON_TYPE_NONE;
1729 wv->help = Qnil;
1730 /* This prevents lwlib from assuming this
1731 menu item is really supposed to be empty. */
1732 /* The EMACS_INT cast avoids a warning.
1733 This value just has to be different from small integers. */
1734 wv->call_data = (void *) (EMACS_INT) (-1);
1a578e9b 1735
177c0ea7 1736 if (prev_wv)
e0f712ba
AC
1737 prev_wv->next = wv;
1738 else
1739 first_wv->contents = wv;
1740 prev_wv = wv;
1741 }
1a578e9b 1742
e0f712ba
AC
1743 /* Forget what we thought we knew about what is in the
1744 detailed contents of the menu bar menus.
1745 Changing the top level always destroys the contents. */
1746 f->menu_bar_items_used = 0;
1747 }
1a578e9b
AC
1748
1749 /* Create or update the menu bar widget. */
1750
1751 BLOCK_INPUT;
1752
e0f712ba
AC
1753 /* Non-null value to indicate menubar has already been "created". */
1754 f->output_data.mac->menubar_widget = 1;
1a578e9b 1755
0f745bcf 1756 fill_menubar (first_wv->contents, deep_p);
177c0ea7 1757
f5f870c0 1758 /* Add event handler so we can detect C-g. */
e2e206ae
YM
1759 install_menu_quit_handler (MAC_MENU_MENU_BAR, NULL);
1760 install_menu_quit_handler (MAC_MENU_MENU_BAR_SUB, NULL);
1a578e9b
AC
1761 free_menubar_widget_value_tree (first_wv);
1762
1763 UNBLOCK_INPUT;
1764}
1765
1a578e9b
AC
1766/* Get rid of the menu bar of frame F, and free its storage.
1767 This is used when deleting a frame, and when turning off the menu bar. */
1768
1769void
1770free_frame_menubar (f)
1771 FRAME_PTR f;
1772{
4036ffb9 1773 f->output_data.mac->menubar_widget = 0;
1a578e9b
AC
1774}
1775
1776\f
f5f870c0
JD
1777static Lisp_Object
1778pop_down_menu (arg)
1779 Lisp_Object arg;
1780{
0f745bcf
YM
1781 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
1782 FRAME_PTR f = p->pointer;
e2e206ae 1783 MenuHandle menu = GetMenuHandle (min_menu_id[MAC_MENU_POPUP]);
f5f870c0
JD
1784
1785 BLOCK_INPUT;
1786
1787 /* Must reset this manually because the button release event is not
1788 passed to Emacs event loop. */
1789 FRAME_MAC_DISPLAY_INFO (f)->grabbed = 0;
1790
1791 /* delete all menus */
e2e206ae
YM
1792 dispose_menus (MAC_MENU_POPUP_SUB, 0);
1793 DeleteMenu (min_menu_id[MAC_MENU_POPUP]);
0f745bcf 1794 DisposeMenu (menu);
f5f870c0
JD
1795
1796 UNBLOCK_INPUT;
1797
1798 return Qnil;
1799}
1800
1801/* Mac_menu_show actually displays a menu using the panes and items in
1a578e9b
AC
1802 menu_items and returns the value selected from it; we assume input
1803 is blocked by the caller. */
1804
1805/* F is the frame the menu is for.
1806 X and Y are the frame-relative specified position,
1807 relative to the inside upper left corner of the frame F.
1808 FOR_CLICK is nonzero if this menu was invoked for a mouse click.
1809 KEYMAPS is 1 if this menu was specified with keymaps;
1810 in that case, we return a list containing the chosen item's value
1811 and perhaps also the pane's prefix.
1812 TITLE is the specified menu title.
1813 ERROR is a place to store an error message string in case of failure.
1814 (We return nil on failure, but the value doesn't actually matter.) */
1815
1816static Lisp_Object
1817mac_menu_show (f, x, y, for_click, keymaps, title, error)
1818 FRAME_PTR f;
1819 int x;
1820 int y;
1821 int for_click;
1822 int keymaps;
1823 Lisp_Object title;
1824 char **error;
1825{
1826 int i;
61f1d295
ST
1827 UInt32 refcon;
1828 int menu_item_choice;
1a578e9b
AC
1829 int menu_item_selection;
1830 MenuHandle menu;
1831 Point pos;
1832 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
1833 widget_value **submenu_stack
1834 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
1835 Lisp_Object *subprefix_stack
1836 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
1837 int submenu_depth = 0;
4036ffb9 1838
1a578e9b 1839 int first_pane;
f5f870c0 1840 int specpdl_count = SPECPDL_INDEX ();
1a578e9b
AC
1841
1842 *error = NULL;
1843
1844 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
1845 {
1846 *error = "Empty menu";
1847 return Qnil;
1848 }
1849
1850 /* Create a tree of widget_value objects
1851 representing the panes and their items. */
1852 wv = xmalloc_widget_value ();
1853 wv->name = "menu";
1854 wv->value = 0;
1855 wv->enabled = 1;
1856 wv->button_type = BUTTON_TYPE_NONE;
e0f712ba 1857 wv->help = Qnil;
1a578e9b
AC
1858 first_wv = wv;
1859 first_pane = 1;
177c0ea7 1860
1a578e9b
AC
1861 /* Loop over all panes and items, filling in the tree. */
1862 i = 0;
1863 while (i < menu_items_used)
1864 {
1865 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1866 {
1867 submenu_stack[submenu_depth++] = save_wv;
1868 save_wv = prev_wv;
1869 prev_wv = 0;
1870 first_pane = 1;
1871 i++;
1872 }
1873 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1874 {
1875 prev_wv = save_wv;
1876 save_wv = submenu_stack[--submenu_depth];
1877 first_pane = 0;
1878 i++;
1879 }
1880 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
1881 && submenu_depth != 0)
1882 i += MENU_ITEMS_PANE_LENGTH;
1883 /* Ignore a nil in the item list.
1884 It's meaningful only for dialog boxes. */
1885 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1886 i += 1;
1887 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1888 {
1889 /* Create a new pane. */
1890 Lisp_Object pane_name, prefix;
1891 char *pane_string;
4036ffb9 1892
e0f712ba
AC
1893 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
1894 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
4036ffb9 1895
1a578e9b
AC
1896#ifndef HAVE_MULTILINGUAL_MENU
1897 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
e0f712ba 1898 {
4036ffb9 1899 pane_name = ENCODE_MENU_STRING (pane_name);
e0f712ba
AC
1900 AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
1901 }
1a578e9b
AC
1902#endif
1903 pane_string = (NILP (pane_name)
d5db4077 1904 ? "" : (char *) SDATA (pane_name));
1a578e9b
AC
1905 /* If there is just one top-level pane, put all its items directly
1906 under the top-level menu. */
1907 if (menu_items_n_panes == 1)
1908 pane_string = "";
1909
1910 /* If the pane has a meaningful name,
1911 make the pane a top-level menu item
1912 with its items as a submenu beneath it. */
1913 if (!keymaps && strcmp (pane_string, ""))
1914 {
1915 wv = xmalloc_widget_value ();
1916 if (save_wv)
1917 save_wv->next = wv;
1918 else
1919 first_wv->contents = wv;
1920 wv->name = pane_string;
1921 if (keymaps && !NILP (prefix))
1922 wv->name++;
1923 wv->value = 0;
1924 wv->enabled = 1;
1925 wv->button_type = BUTTON_TYPE_NONE;
e0f712ba 1926 wv->help = Qnil;
1a578e9b
AC
1927 save_wv = wv;
1928 prev_wv = 0;
1929 }
1930 else if (first_pane)
1931 {
1932 save_wv = wv;
1933 prev_wv = 0;
1934 }
1935 first_pane = 0;
1936 i += MENU_ITEMS_PANE_LENGTH;
1937 }
1938 else
1939 {
1940 /* Create a new item within current pane. */
1941 Lisp_Object item_name, enable, descrip, def, type, selected, help;
e0f712ba
AC
1942 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
1943 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
1944 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
1945 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
1946 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
1947 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
4036ffb9 1948 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
1a578e9b
AC
1949
1950#ifndef HAVE_MULTILINGUAL_MENU
e0f712ba
AC
1951 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
1952 {
383418e5 1953 item_name = ENCODE_MENU_STRING (item_name);
e0f712ba
AC
1954 AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
1955 }
4036ffb9 1956
1a578e9b 1957 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
4036ffb9 1958 {
383418e5 1959 descrip = ENCODE_MENU_STRING (descrip);
e0f712ba
AC
1960 AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
1961 }
1962#endif /* not HAVE_MULTILINGUAL_MENU */
1a578e9b
AC
1963
1964 wv = xmalloc_widget_value ();
177c0ea7 1965 if (prev_wv)
1a578e9b 1966 prev_wv->next = wv;
177c0ea7 1967 else
1a578e9b 1968 save_wv->contents = wv;
d5db4077 1969 wv->name = (char *) SDATA (item_name);
1a578e9b 1970 if (!NILP (descrip))
d5db4077 1971 wv->key = (char *) SDATA (descrip);
1a578e9b
AC
1972 wv->value = 0;
1973 /* Use the contents index as call_data, since we are
e0f712ba 1974 restricted to 16-bits. */
1a578e9b
AC
1975 wv->call_data = !NILP (def) ? (void *) (EMACS_INT) i : 0;
1976 wv->enabled = !NILP (enable);
1977
1978 if (NILP (type))
1979 wv->button_type = BUTTON_TYPE_NONE;
1980 else if (EQ (type, QCtoggle))
1981 wv->button_type = BUTTON_TYPE_TOGGLE;
1982 else if (EQ (type, QCradio))
1983 wv->button_type = BUTTON_TYPE_RADIO;
1984 else
1985 abort ();
1986
1987 wv->selected = !NILP (selected);
4036ffb9
YM
1988
1989 if (! STRINGP (help))
e0f712ba 1990 help = Qnil;
1a578e9b 1991
e0f712ba 1992 wv->help = help;
1a578e9b
AC
1993
1994 prev_wv = wv;
1995
1996 i += MENU_ITEMS_ITEM_LENGTH;
1997 }
1998 }
1999
2000 /* Deal with the title, if it is non-nil. */
2001 if (!NILP (title))
2002 {
2003 widget_value *wv_title = xmalloc_widget_value ();
2004 widget_value *wv_sep = xmalloc_widget_value ();
2005
2006 /* Maybe replace this separator with a bitmap or owner-draw item
2007 so that it looks better. Having two separators looks odd. */
2008 wv_sep->name = "--";
2009 wv_sep->next = first_wv->contents;
e0f712ba 2010 wv_sep->help = Qnil;
1a578e9b
AC
2011
2012#ifndef HAVE_MULTILINGUAL_MENU
2013 if (STRING_MULTIBYTE (title))
383418e5 2014 title = ENCODE_MENU_STRING (title);
1a578e9b 2015#endif
4036ffb9 2016
d5db4077 2017 wv_title->name = (char *) SDATA (title);
f5f870c0 2018 wv_title->enabled = FALSE;
e0f712ba 2019 wv_title->title = TRUE;
1a578e9b 2020 wv_title->button_type = BUTTON_TYPE_NONE;
e0f712ba 2021 wv_title->help = Qnil;
1a578e9b
AC
2022 wv_title->next = wv_sep;
2023 first_wv->contents = wv_title;
2024 }
2025
2026 /* Actually create the menu. */
e2e206ae 2027 menu = NewMenu (min_menu_id[MAC_MENU_POPUP], "\p");
0f745bcf 2028 InsertMenu (menu, -1);
e2e206ae
YM
2029 fill_menu (menu, first_wv->contents, MAC_MENU_POPUP_SUB,
2030 min_menu_id[MAC_MENU_POPUP_SUB]);
1a578e9b 2031
f5f870c0
JD
2032 /* Free the widget_value objects we used to specify the
2033 contents. */
2034 free_menubar_widget_value_tree (first_wv);
2035
1a578e9b
AC
2036 /* Adjust coordinates to be root-window-relative. */
2037 pos.h = x;
2038 pos.v = y;
e0f712ba 2039
50bf7673 2040 SetPortWindowPort (FRAME_MAC_WINDOW (f));
1a578e9b
AC
2041 LocalToGlobal (&pos);
2042
e0f712ba 2043 /* No selection has been chosen yet. */
61f1d295 2044 menu_item_choice = 0;
e0f712ba
AC
2045 menu_item_selection = 0;
2046
0f745bcf 2047 record_unwind_protect (pop_down_menu, make_save_value (f, 0));
f5f870c0
JD
2048
2049 /* Add event handler so we can detect C-g. */
e2e206ae
YM
2050 install_menu_quit_handler (MAC_MENU_POPUP, menu);
2051 install_menu_quit_handler (MAC_MENU_POPUP_SUB, menu);
89f2614d 2052
1a578e9b 2053 /* Display the menu. */
61f1d295
ST
2054 menu_item_choice = PopUpMenuSelect (menu, pos.v, pos.h, 0);
2055 menu_item_selection = LoWord (menu_item_choice);
1a578e9b 2056
f5f870c0 2057 /* Get the refcon to find the correct item */
177c0ea7 2058 if (menu_item_selection)
61f1d295 2059 {
11715f92
ST
2060 MenuHandle sel_menu = GetMenuHandle (HiWord (menu_item_choice));
2061 if (sel_menu) {
2062 GetMenuItemRefCon (sel_menu, menu_item_selection, &refcon);
61f1d295
ST
2063 }
2064 }
f5f870c0
JD
2065 else if (! for_click)
2066 /* Make "Cancel" equivalent to C-g unless this menu was popped up by
2067 a mouse press. */
2068 Fsignal (Qquit, Qnil);
1a578e9b 2069
e0f712ba
AC
2070 /* Find the selected item, and its pane, to return
2071 the proper value. */
1a578e9b
AC
2072 if (menu_item_selection != 0)
2073 {
2074 Lisp_Object prefix, entry;
2075
e0f712ba 2076 prefix = entry = Qnil;
1a578e9b
AC
2077 i = 0;
2078 while (i < menu_items_used)
2079 {
2080 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
2081 {
2082 subprefix_stack[submenu_depth++] = prefix;
2083 prefix = entry;
2084 i++;
2085 }
2086 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
2087 {
2088 prefix = subprefix_stack[--submenu_depth];
2089 i++;
2090 }
2091 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
2092 {
2093 prefix
2094 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2095 i += MENU_ITEMS_PANE_LENGTH;
2096 }
e0f712ba
AC
2097 /* Ignore a nil in the item list.
2098 It's meaningful only for dialog boxes. */
1a578e9b
AC
2099 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
2100 i += 1;
2101 else
2102 {
2103 entry
2104 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
61f1d295 2105 if ((int) (EMACS_INT) refcon == i)
1a578e9b
AC
2106 {
2107 if (keymaps != 0)
2108 {
2109 int j;
2110
2111 entry = Fcons (entry, Qnil);
2112 if (!NILP (prefix))
2113 entry = Fcons (prefix, entry);
2114 for (j = submenu_depth - 1; j >= 0; j--)
2115 if (!NILP (subprefix_stack[j]))
2116 entry = Fcons (subprefix_stack[j], entry);
2117 }
2118 return entry;
2119 }
2120 i += MENU_ITEMS_ITEM_LENGTH;
2121 }
2122 }
2123 }
b8987570
JD
2124 else if (!for_click)
2125 /* Make "Cancel" equivalent to C-g. */
2126 Fsignal (Qquit, Qnil);
1a578e9b 2127
f5f870c0
JD
2128 unbind_to (specpdl_count, Qnil);
2129
1a578e9b
AC
2130 return Qnil;
2131}
2132\f
2133
e0f712ba 2134#ifdef HAVE_DIALOGS
1a578e9b
AC
2135/* Construct native Mac OS menubar based on widget_value tree. */
2136
2137static int
2138mac_dialog (widget_value *wv)
2139{
2140 char *dialog_name;
2141 char *prompt;
2142 char **button_labels;
2143 UInt32 *ref_cons;
2144 int nb_buttons;
2145 int left_count;
2146 int i;
2147 int dialog_width;
2148 Rect rect;
2149 WindowPtr window_ptr;
2150 ControlHandle ch;
2151 int left;
2152 EventRecord event_record;
2153 SInt16 part_code;
2154 int control_part_code;
2155 Point mouse;
177c0ea7 2156
1a578e9b
AC
2157 dialog_name = wv->name;
2158 nb_buttons = dialog_name[1] - '0';
2159 left_count = nb_buttons - (dialog_name[4] - '0');
2160 button_labels = (char **) alloca (sizeof (char *) * nb_buttons);
e0f712ba 2161 ref_cons = (UInt32 *) alloca (sizeof (UInt32) * nb_buttons);
177c0ea7 2162
1a578e9b
AC
2163 wv = wv->contents;
2164 prompt = (char *) alloca (strlen (wv->value) + 1);
2165 strcpy (prompt, wv->value);
2166 c2pstr (prompt);
2167
2168 wv = wv->next;
2169 for (i = 0; i < nb_buttons; i++)
2170 {
2171 button_labels[i] = wv->value;
2172 button_labels[i] = (char *) alloca (strlen (wv->value) + 1);
2173 strcpy (button_labels[i], wv->value);
2174 c2pstr (button_labels[i]);
2175 ref_cons[i] = (UInt32) wv->call_data;
2176 wv = wv->next;
2177 }
2178
2179 window_ptr = GetNewCWindow (DIALOG_WINDOW_RESOURCE, NULL, (WindowPtr) -1);
e0f712ba 2180
50bf7673 2181 SetPortWindowPort (window_ptr);
177c0ea7 2182
1a578e9b
AC
2183 TextFont (0);
2184 /* Left and right margins in the dialog are 13 pixels each.*/
2185 dialog_width = 14;
2186 /* Calculate width of dialog box: 8 pixels on each side of the text
2187 label in each button, 12 pixels between buttons. */
2188 for (i = 0; i < nb_buttons; i++)
2189 dialog_width += StringWidth (button_labels[i]) + 16 + 12;
2190
2191 if (left_count != 0 && nb_buttons - left_count != 0)
2192 dialog_width += 12;
2193
2194 dialog_width = max (dialog_width, StringWidth (prompt) + 26);
2195
2196 SizeWindow (window_ptr, dialog_width, 78, 0);
2197 ShowWindow (window_ptr);
2198
50bf7673 2199 SetPortWindowPort (window_ptr);
177c0ea7 2200
1a578e9b
AC
2201 TextFont (0);
2202
2203 MoveTo (13, 29);
2204 DrawString (prompt);
2205
2206 left = 13;
2207 for (i = 0; i < nb_buttons; i++)
2208 {
2209 int button_width = StringWidth (button_labels[i]) + 16;
2210 SetRect (&rect, left, 45, left + button_width, 65);
2211 ch = NewControl (window_ptr, &rect, button_labels[i], 1, 0, 0, 0,
2212 kControlPushButtonProc, ref_cons[i]);
2213 left += button_width + 12;
2214 if (i == left_count - 1)
2215 left += 12;
2216 }
2217
2218 i = 0;
2219 while (!i)
2220 {
2221 if (WaitNextEvent (mDownMask, &event_record, 10, NULL))
2222 if (event_record.what == mouseDown)
2223 {
2224 part_code = FindWindow (event_record.where, &window_ptr);
2225 if (part_code == inContent)
2226 {
2227 mouse = event_record.where;
2228 GlobalToLocal (&mouse);
2229 control_part_code = FindControl (mouse, window_ptr, &ch);
2230 if (control_part_code == kControlButtonPart)
2231 if (TrackControl (ch, mouse, NULL))
2232 i = GetControlReference (ch);
2233 }
2234 }
2235 }
2236
2237 DisposeWindow (window_ptr);
177c0ea7 2238
1a578e9b
AC
2239 return i;
2240}
2241
2242static char * button_names [] = {
2243 "button1", "button2", "button3", "button4", "button5",
2244 "button6", "button7", "button8", "button9", "button10" };
2245
2246static Lisp_Object
4036ffb9 2247mac_dialog_show (f, keymaps, title, header, error_name)
1a578e9b
AC
2248 FRAME_PTR f;
2249 int keymaps;
3f297536 2250 Lisp_Object title, header;
4036ffb9 2251 char **error_name;
1a578e9b
AC
2252{
2253 int i, nb_buttons=0;
2254 char dialog_name[6];
2255 int menu_item_selection;
2256
e0f712ba 2257 widget_value *wv, *first_wv = 0, *prev_wv = 0;
1a578e9b
AC
2258
2259 /* Number of elements seen so far, before boundary. */
2260 int left_count = 0;
e0f712ba 2261 /* 1 means we've seen the boundary between left-hand elts and right-hand. */
1a578e9b
AC
2262 int boundary_seen = 0;
2263
4036ffb9 2264 *error_name = NULL;
1a578e9b
AC
2265
2266 if (menu_items_n_panes > 1)
2267 {
4036ffb9 2268 *error_name = "Multiple panes in dialog box";
1a578e9b
AC
2269 return Qnil;
2270 }
2271
e0f712ba
AC
2272 /* Create a tree of widget_value objects
2273 representing the text label and buttons. */
1a578e9b
AC
2274 {
2275 Lisp_Object pane_name, prefix;
2276 char *pane_string;
2277 pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
2278 prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
2279 pane_string = (NILP (pane_name)
177c0ea7 2280 ? "" : (char *) SDATA (pane_name));
1a578e9b
AC
2281 prev_wv = xmalloc_widget_value ();
2282 prev_wv->value = pane_string;
2283 if (keymaps && !NILP (prefix))
2284 prev_wv->name++;
2285 prev_wv->enabled = 1;
2286 prev_wv->name = "message";
e0f712ba 2287 prev_wv->help = Qnil;
1a578e9b 2288 first_wv = prev_wv;
177c0ea7 2289
1a578e9b
AC
2290 /* Loop over all panes and items, filling in the tree. */
2291 i = MENU_ITEMS_PANE_LENGTH;
2292 while (i < menu_items_used)
2293 {
177c0ea7 2294
1a578e9b 2295 /* Create a new item within current pane. */
4036ffb9 2296 Lisp_Object item_name, enable, descrip;
1a578e9b
AC
2297 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
2298 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
2299 descrip
2300 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
177c0ea7 2301
1a578e9b
AC
2302 if (NILP (item_name))
2303 {
2304 free_menubar_widget_value_tree (first_wv);
4036ffb9 2305 *error_name = "Submenu in dialog items";
1a578e9b
AC
2306 return Qnil;
2307 }
2308 if (EQ (item_name, Qquote))
2309 {
e0f712ba
AC
2310 /* This is the boundary between left-side elts
2311 and right-side elts. Stop incrementing right_count. */
1a578e9b
AC
2312 boundary_seen = 1;
2313 i++;
2314 continue;
2315 }
2316 if (nb_buttons >= 9)
2317 {
2318 free_menubar_widget_value_tree (first_wv);
4036ffb9 2319 *error_name = "Too many dialog items";
1a578e9b
AC
2320 return Qnil;
2321 }
2322
2323 wv = xmalloc_widget_value ();
2324 prev_wv->next = wv;
2325 wv->name = (char *) button_names[nb_buttons];
2326 if (!NILP (descrip))
d5db4077
KR
2327 wv->key = (char *) SDATA (descrip);
2328 wv->value = (char *) SDATA (item_name);
1a578e9b
AC
2329 wv->call_data = (void *) i;
2330 /* menu item is identified by its index in menu_items table */
2331 wv->enabled = !NILP (enable);
e0f712ba 2332 wv->help = Qnil;
1a578e9b
AC
2333 prev_wv = wv;
2334
2335 if (! boundary_seen)
2336 left_count++;
2337
2338 nb_buttons++;
2339 i += MENU_ITEMS_ITEM_LENGTH;
2340 }
2341
e0f712ba
AC
2342 /* If the boundary was not specified,
2343 by default put half on the left and half on the right. */
1a578e9b
AC
2344 if (! boundary_seen)
2345 left_count = nb_buttons - nb_buttons / 2;
2346
2347 wv = xmalloc_widget_value ();
2348 wv->name = dialog_name;
e0f712ba 2349 wv->help = Qnil;
1a578e9b 2350
3f297536
NR
2351 /* Frame title: 'Q' = Question, 'I' = Information.
2352 Can also have 'E' = Error if, one day, we want
2353 a popup for errors. */
2354 if (NILP(header))
2355 dialog_name[0] = 'Q';
2356 else
2357 dialog_name[0] = 'I';
2358
e0f712ba
AC
2359 /* Dialog boxes use a really stupid name encoding
2360 which specifies how many buttons to use
3f297536 2361 and how many buttons are on the right. */
1a578e9b
AC
2362 dialog_name[1] = '0' + nb_buttons;
2363 dialog_name[2] = 'B';
2364 dialog_name[3] = 'R';
2365 /* Number of buttons to put on the right. */
2366 dialog_name[4] = '0' + nb_buttons - left_count;
2367 dialog_name[5] = 0;
2368 wv->contents = first_wv;
2369 first_wv = wv;
2370 }
2371
2372 /* Actually create the dialog. */
2373#ifdef HAVE_DIALOGS
2374 menu_item_selection = mac_dialog (first_wv);
2375#else
2376 menu_item_selection = 0;
2377#endif
2378
e0f712ba 2379 /* Free the widget_value objects we used to specify the contents. */
1a578e9b
AC
2380 free_menubar_widget_value_tree (first_wv);
2381
4036ffb9
YM
2382 /* Find the selected item, and its pane, to return
2383 the proper value. */
1a578e9b
AC
2384 if (menu_item_selection != 0)
2385 {
2386 Lisp_Object prefix;
2387
2388 prefix = Qnil;
2389 i = 0;
2390 while (i < menu_items_used)
2391 {
2392 Lisp_Object entry;
2393
2394 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
2395 {
2396 prefix
2397 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2398 i += MENU_ITEMS_PANE_LENGTH;
2399 }
4036ffb9
YM
2400 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
2401 {
2402 /* This is the boundary between left-side elts and
2403 right-side elts. */
2404 ++i;
2405 }
1a578e9b
AC
2406 else
2407 {
2408 entry
2409 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
2410 if (menu_item_selection == i)
2411 {
2412 if (keymaps != 0)
2413 {
2414 entry = Fcons (entry, Qnil);
2415 if (!NILP (prefix))
2416 entry = Fcons (prefix, entry);
2417 }
2418 return entry;
2419 }
2420 i += MENU_ITEMS_ITEM_LENGTH;
2421 }
2422 }
2423 }
4036ffb9
YM
2424 else
2425 /* Make "Cancel" equivalent to C-g. */
2426 Fsignal (Qquit, Qnil);
1a578e9b
AC
2427
2428 return Qnil;
2429}
e0f712ba 2430#endif /* HAVE_DIALOGS */
1a578e9b
AC
2431\f
2432
2433/* Is this item a separator? */
2434static int
2435name_is_separator (name)
37a39780 2436 const char *name;
1a578e9b 2437{
37a39780 2438 const char *start = name;
e0f712ba
AC
2439
2440 /* Check if name string consists of only dashes ('-'). */
1a578e9b 2441 while (*name == '-') name++;
e0f712ba
AC
2442 /* Separators can also be of the form "--:TripleSuperMegaEtched"
2443 or "--deep-shadow". We don't implement them yet, se we just treat
2444 them like normal separators. */
2445 return (*name == '\0' || start + 2 == name);
1a578e9b
AC
2446}
2447
2448static void
0f745bcf
YM
2449add_menu_item (menu, pos, wv)
2450 MenuHandle menu;
2451 int pos;
2452 widget_value *wv;
1a578e9b 2453{
4036ffb9
YM
2454#if TARGET_API_MAC_CARBON
2455 CFStringRef item_name;
2456#else
1a578e9b 2457 Str255 item_name;
4036ffb9 2458#endif
1a578e9b
AC
2459
2460 if (name_is_separator (wv->name))
2461 AppendMenu (menu, "\p-");
177c0ea7 2462 else
1a578e9b
AC
2463 {
2464 AppendMenu (menu, "\pX");
177c0ea7 2465
e0f712ba 2466#if TARGET_API_MAC_CARBON
4036ffb9
YM
2467 item_name = cfstring_create_with_utf8_cstring (wv->name);
2468
1a578e9b
AC
2469 if (wv->key != NULL)
2470 {
4036ffb9
YM
2471 CFStringRef name, key;
2472
2473 name = item_name;
2474 key = cfstring_create_with_utf8_cstring (wv->key);
2475 item_name = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@ %@"),
2476 name, key);
2477 CFRelease (name);
2478 CFRelease (key);
1a578e9b 2479 }
d8f96db8 2480
4036ffb9
YM
2481 SetMenuItemTextWithCFString (menu, pos, item_name);
2482 CFRelease (item_name);
2483
0f745bcf 2484 if (wv->enabled)
4036ffb9
YM
2485 EnableMenuItem (menu, pos);
2486 else
2487 DisableMenuItem (menu, pos);
2488#else /* ! TARGET_API_MAC_CARBON */
4036ffb9
YM
2489 item_name[sizeof (item_name) - 1] = '\0';
2490 strncpy (item_name, wv->name, sizeof (item_name) - 1);
2491 if (wv->key != NULL)
2492 {
2493 int len = strlen (item_name);
2494
2495 strncpy (item_name + len, " ", sizeof (item_name) - 1 - len);
2496 len = strlen (item_name);
2497 strncpy (item_name + len, wv->key, sizeof (item_name) - 1 - len);
2498 }
1a578e9b
AC
2499 c2pstr (item_name);
2500 SetMenuItemText (menu, pos, item_name);
2501
0f745bcf 2502 if (wv->enabled)
e0f712ba 2503 EnableItem (menu, pos);
1a578e9b 2504 else
e0f712ba 2505 DisableItem (menu, pos);
4036ffb9 2506#endif /* ! TARGET_API_MAC_CARBON */
1a578e9b
AC
2507
2508 /* Draw radio buttons and tickboxes. */
1a578e9b
AC
2509 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
2510 wv->button_type == BUTTON_TYPE_RADIO))
2511 SetItemMark (menu, pos, checkMark);
2512 else
2513 SetItemMark (menu, pos, noMark);
1a578e9b 2514
a8e08014
ST
2515 SetMenuItemRefCon (menu, pos, (UInt32) wv->call_data);
2516 }
1a578e9b
AC
2517}
2518
0f745bcf 2519/* Construct native Mac OS menu based on widget_value tree. */
1a578e9b 2520
0f745bcf 2521static int
e2e206ae 2522fill_menu (menu, wv, kind, submenu_id)
0f745bcf
YM
2523 MenuHandle menu;
2524 widget_value *wv;
e2e206ae 2525 enum mac_menu_kind kind;
0f745bcf 2526 int submenu_id;
1a578e9b 2527{
0f745bcf 2528 int pos;
1a578e9b 2529
0f745bcf
YM
2530 for (pos = 1; wv != NULL; wv = wv->next, pos++)
2531 {
2532 add_menu_item (menu, pos, wv);
e2e206ae 2533 if (wv->contents && submenu_id < min_menu_id[kind + 1])
0f745bcf
YM
2534 {
2535 MenuHandle submenu = NewMenu (submenu_id, "\pX");
1a578e9b 2536
0f745bcf
YM
2537 InsertMenu (submenu, -1);
2538 SetMenuItemHierarchicalID (menu, pos, submenu_id);
e2e206ae 2539 submenu_id = fill_menu (submenu, wv->contents, kind, submenu_id + 1);
0f745bcf
YM
2540 }
2541 }
1a578e9b 2542
0f745bcf 2543 return submenu_id;
1a578e9b
AC
2544}
2545
2546/* Construct native Mac OS menubar based on widget_value tree. */
2547
2548static void
0f745bcf
YM
2549fill_menubar (wv, deep_p)
2550 widget_value *wv;
2551 int deep_p;
1a578e9b 2552{
0f745bcf
YM
2553 int id, submenu_id;
2554 MenuHandle menu;
2555 Str255 title;
2556#if !TARGET_API_MAC_CARBON
2557 int title_changed_p = 0;
2558#endif
1a578e9b 2559
0f745bcf
YM
2560 /* Clean up the menu bar when filled by the entire menu trees. */
2561 if (deep_p)
2562 {
e2e206ae
YM
2563 dispose_menus (MAC_MENU_MENU_BAR, 0);
2564 dispose_menus (MAC_MENU_MENU_BAR_SUB, 0);
0f745bcf
YM
2565#if !TARGET_API_MAC_CARBON
2566 title_changed_p = 1;
2567#endif
2568 }
1a578e9b 2569
0f745bcf
YM
2570 /* Fill menu bar titles and submenus. Reuse the existing menu bar
2571 titles as much as possible to minimize redraw (if !deep_p). */
e2e206ae
YM
2572 submenu_id = min_menu_id[MAC_MENU_MENU_BAR_SUB];
2573 for (id = min_menu_id[MAC_MENU_MENU_BAR];
2574 wv != NULL && id < min_menu_id[MAC_MENU_MENU_BAR + 1];
2575 wv = wv->next, id++)
1a578e9b 2576 {
72742a99 2577 strncpy (title, wv->name, 255);
0f745bcf 2578 title[255] = '\0';
1a578e9b 2579 c2pstr (title);
0f745bcf
YM
2580
2581 menu = GetMenuHandle (id);
2582 if (menu)
2583 {
2584#if TARGET_API_MAC_CARBON
2585 Str255 old_title;
2586
2587 GetMenuTitle (menu, old_title);
2588 if (!EqualString (title, old_title, false, false))
2589 SetMenuTitle (menu, title);
2590#else /* !TARGET_API_MAC_CARBON */
2591 if (!EqualString (title, (*menu)->menuData, false, false))
2592 {
2593 DeleteMenu (id);
2594 DisposeMenu (menu);
2595 menu = NewMenu (id, title);
2596 InsertMenu (menu, GetMenuHandle (id + 1) ? id + 1 : 0);
2597 title_changed_p = 1;
2598 }
2599#endif /* !TARGET_API_MAC_CARBON */
2600 }
2601 else
2602 {
2603 menu = NewMenu (id, title);
2604 InsertMenu (menu, 0);
2605#if !TARGET_API_MAC_CARBON
2606 title_changed_p = 1;
2607#endif
2608 }
1a578e9b
AC
2609
2610 if (wv->contents)
e2e206ae
YM
2611 submenu_id = fill_menu (menu, wv->contents, MAC_MENU_MENU_BAR_SUB,
2612 submenu_id);
0f745bcf 2613 }
177c0ea7 2614
e2e206ae 2615 if (id < min_menu_id[MAC_MENU_MENU_BAR + 1] && GetMenuHandle (id))
0f745bcf 2616 {
e2e206ae 2617 dispose_menus (MAC_MENU_MENU_BAR, id);
0f745bcf
YM
2618#if !TARGET_API_MAC_CARBON
2619 title_changed_p = 1;
2620#endif
2621 }
2622
2623#if !TARGET_API_MAC_CARBON
2624 if (title_changed_p)
2625 InvalMenuBar ();
2626#endif
2627}
2628
e2e206ae
YM
2629/* Dispose of menus that belong to KIND, and remove them from the menu
2630 list. ID is the lower bound of menu IDs that will be processed. */
2631
0f745bcf 2632static void
e2e206ae
YM
2633dispose_menus (kind, id)
2634 enum mac_menu_kind kind;
0f745bcf
YM
2635 int id;
2636{
e2e206ae 2637 for (id = max (id, min_menu_id[kind]); id < min_menu_id[kind + 1]; id++)
0f745bcf 2638 {
e2e206ae
YM
2639 MenuHandle menu = GetMenuHandle (id);
2640
2641 if (menu == NULL)
2642 break;
0f745bcf
YM
2643 DeleteMenu (id);
2644 DisposeMenu (menu);
1a578e9b
AC
2645 }
2646}
2647
2648#endif /* HAVE_MENUS */
c1f043a0
CY
2649
2650/* The following is used by delayed window autoselection. */
2651
2652DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
2653 doc: /* Return t if a menu or popup dialog is active. */)
2654 ()
2655{
2656 /* Always return Qnil since menu selection functions do not return
2657 until a selection has been made or cancelled. */
2658 return Qnil;
2659}
1a578e9b
AC
2660\f
2661void
2662syms_of_macmenu ()
2663{
2664 staticpro (&menu_items);
2665 menu_items = Qnil;
2666
2667 Qdebug_on_next_call = intern ("debug-on-next-call");
2668 staticpro (&Qdebug_on_next_call);
2669
1a578e9b 2670 defsubr (&Sx_popup_menu);
0ec2e29e 2671 defsubr (&Smenu_or_popup_active_p);
1a578e9b
AC
2672#ifdef HAVE_MENUS
2673 defsubr (&Sx_popup_dialog);
2674#endif
2675}
ab5796a9
MB
2676
2677/* arch-tag: 40b2c6c7-b8a9-4a49-b930-1b2707184cce
2678 (do not change this comment) */