1 /* Menu support for GNU Emacs on the for Mac OS.
2 Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 /* Contributed by Andrew Choi (akochoi@mac.com). */
27 #include "termhooks.h"
32 #include "blockinput.h"
37 #if !TARGET_API_MAC_CARBON
40 #include <QuickDraw.h>
41 #include <ToolUtils.h>
46 #if defined (__MRC__) || (__MSL__ >= 0x6000)
47 #include <ControlDefinitions.h>
49 #endif /* not TARGET_API_MAC_CARBON */
51 /* This may include sys/types.h, and that somehow loses
52 if this is not done before the other system files. */
55 /* Load sys/types.h if not already loaded.
56 In some systems loading it twice is suicidal. */
58 #include <sys/types.h>
61 #include "dispextern.h"
63 #define POPUP_SUBMENU_ID 235
64 #define MIN_POPUP_SUBMENU_ID 512
65 #define MIN_MENU_ID 256
66 #define MIN_SUBMENU_ID 1
68 #define DIALOG_WINDOW_RESOURCE 130
70 #define HAVE_DIALOGS 1
72 #undef HAVE_MULTILINGUAL_MENU
73 #undef HAVE_DIALOGS /* TODO: Implement native dialogs. */
75 /******************************************************************/
76 /* Definitions copied from lwlib.h */
78 typedef void * XtPointer
;
87 /* This structure is based on the one in ../lwlib/lwlib.h, modified
89 typedef struct _widget_value
94 /* value (meaning depend on widget type) */
96 /* keyboard equivalent. no implications for XtTranslations */
99 /* Help string or nil if none.
100 GC finds this string through the frame's menu_bar_vector
101 or through menu_items. */
103 /* true if enabled */
105 /* true if selected */
107 /* The type of a button. */
108 enum button_type button_type
;
109 /* true if menu title */
112 /* true if was edited (maintained by get_value) */
114 /* true if has changed (maintained by lw library) */
116 /* true if this widget itself has changed,
117 but not counting the other widgets found in the `next' field. */
118 change_type this_one_change
;
120 /* Contents of the sub-widgets, also selected slot for checkbox */
121 struct _widget_value
* contents
;
122 /* data passed to callback */
124 /* next one in the list */
125 struct _widget_value
* next
;
127 /* slot for the toolkit dependent part. Always initialize to NULL. */
129 /* tell us if we should free the toolkit data slot when freeing the
130 widget_value itself. */
131 Boolean free_toolkit_data
;
133 /* we resource the widget_value structures; this points to the next
134 one on the free list if this one has been deallocated.
136 struct _widget_value
*free_list
;
140 /* Assumed by other routines to zero area returned. */
141 #define malloc_widget_value() (void *)memset (xmalloc (sizeof (widget_value)),\
142 0, (sizeof (widget_value)))
143 #define free_widget_value(wv) xfree (wv)
145 /******************************************************************/
152 Lisp_Object Vmenu_updating_frame
;
154 Lisp_Object Qdebug_on_next_call
;
156 extern Lisp_Object Qmenu_bar
;
158 extern Lisp_Object QCtoggle
, QCradio
;
160 extern Lisp_Object Voverriding_local_map
;
161 extern Lisp_Object Voverriding_local_map_menu_flag
;
163 extern Lisp_Object Qoverriding_local_map
, Qoverriding_terminal_local_map
;
165 extern Lisp_Object Qmenu_bar_update_hook
;
167 #if TARGET_API_MAC_CARBON
168 #define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
170 #define ENCODE_MENU_STRING(str) ENCODE_SYSTEM (str)
173 void set_frame_menubar ();
175 static void push_menu_item
P_ ((Lisp_Object
, Lisp_Object
, Lisp_Object
,
176 Lisp_Object
, Lisp_Object
, Lisp_Object
,
177 Lisp_Object
, Lisp_Object
));
179 static Lisp_Object
mac_dialog_show ();
181 static Lisp_Object
mac_menu_show ();
183 static void keymap_panes ();
184 static void single_keymap_panes ();
185 static void single_menu_item ();
186 static void list_of_panes ();
187 static void list_of_items ();
189 static void fill_submenu (MenuHandle
, widget_value
*);
190 static void fill_menubar (widget_value
*);
193 /* This holds a Lisp vector that holds the results of decoding
194 the keymaps or alist-of-alists that specify a menu.
196 It describes the panes and items within the panes.
198 Each pane is described by 3 elements in the vector:
199 t, the pane name, the pane's prefix key.
200 Then follow the pane's items, with 5 elements per item:
201 the item string, the enable flag, the item's value,
202 the definition, and the equivalent keyboard key's description string.
204 In some cases, multiple levels of menus may be described.
205 A single vector slot containing nil indicates the start of a submenu.
206 A single vector slot containing lambda indicates the end of a submenu.
207 The submenu follows a menu item which is the way to reach the submenu.
209 A single vector slot containing quote indicates that the
210 following items should appear on the right of a dialog box.
212 Using a Lisp vector to hold this information while we decode it
213 takes care of protecting all the data from GC. */
215 #define MENU_ITEMS_PANE_NAME 1
216 #define MENU_ITEMS_PANE_PREFIX 2
217 #define MENU_ITEMS_PANE_LENGTH 3
221 MENU_ITEMS_ITEM_NAME
= 0,
222 MENU_ITEMS_ITEM_ENABLE
,
223 MENU_ITEMS_ITEM_VALUE
,
224 MENU_ITEMS_ITEM_EQUIV_KEY
,
225 MENU_ITEMS_ITEM_DEFINITION
,
226 MENU_ITEMS_ITEM_TYPE
,
227 MENU_ITEMS_ITEM_SELECTED
,
228 MENU_ITEMS_ITEM_HELP
,
229 MENU_ITEMS_ITEM_LENGTH
232 static Lisp_Object menu_items
;
234 /* Number of slots currently allocated in menu_items. */
235 static int menu_items_allocated
;
237 /* This is the index in menu_items of the first empty slot. */
238 static int menu_items_used
;
240 /* The number of panes currently recorded in menu_items,
241 excluding those within submenus. */
242 static int menu_items_n_panes
;
244 /* Current depth within submenus. */
245 static int menu_items_submenu_depth
;
247 /* Flag which when set indicates a dialog or menu has been posted by
248 Xt on behalf of one of the widget sets. */
249 static int popup_activated_flag
;
251 /* Index of the next submenu */
252 static int submenu_id
;
254 static int next_menubar_widget_id
;
256 /* This is set nonzero after the user activates the menu bar, and set
257 to zero again after the menu bars are redisplayed by prepare_menu_bar.
258 While it is nonzero, all calls to set_frame_menubar go deep.
260 I don't understand why this is needed, but it does seem to be
261 needed on Motif, according to Marcus Daniels <marcus@sysc.pdx.edu>. */
263 int pending_menu_activation
;
265 /* Initialize the menu_items structure if we haven't already done so.
266 Also mark it as currently empty. */
271 if (NILP (menu_items
))
273 menu_items_allocated
= 60;
274 menu_items
= Fmake_vector (make_number (menu_items_allocated
), Qnil
);
278 menu_items_n_panes
= 0;
279 menu_items_submenu_depth
= 0;
282 /* Call at the end of generating the data in menu_items.
283 This fills in the number of items in the last pane. */
290 /* Call when finished using the data for the current menu
294 discard_menu_items ()
296 /* Free the structure if it is especially large.
297 Otherwise, hold on to it, to save time. */
298 if (menu_items_allocated
> 200)
301 menu_items_allocated
= 0;
305 /* Make the menu_items vector twice as large. */
311 int old_size
= menu_items_allocated
;
314 menu_items_allocated
*= 2;
315 menu_items
= Fmake_vector (make_number (menu_items_allocated
), Qnil
);
316 bcopy (XVECTOR (old
)->contents
, XVECTOR (menu_items
)->contents
,
317 old_size
* sizeof (Lisp_Object
));
320 /* Begin a submenu. */
323 push_submenu_start ()
325 if (menu_items_used
+ 1 > menu_items_allocated
)
328 XVECTOR (menu_items
)->contents
[menu_items_used
++] = Qnil
;
329 menu_items_submenu_depth
++;
337 if (menu_items_used
+ 1 > menu_items_allocated
)
340 XVECTOR (menu_items
)->contents
[menu_items_used
++] = Qlambda
;
341 menu_items_submenu_depth
--;
344 /* Indicate boundary between left and right. */
347 push_left_right_boundary ()
349 if (menu_items_used
+ 1 > menu_items_allocated
)
352 XVECTOR (menu_items
)->contents
[menu_items_used
++] = Qquote
;
355 /* Start a new menu pane in menu_items.
356 NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */
359 push_menu_pane (name
, prefix_vec
)
360 Lisp_Object name
, prefix_vec
;
362 if (menu_items_used
+ MENU_ITEMS_PANE_LENGTH
> menu_items_allocated
)
365 if (menu_items_submenu_depth
== 0)
366 menu_items_n_panes
++;
367 XVECTOR (menu_items
)->contents
[menu_items_used
++] = Qt
;
368 XVECTOR (menu_items
)->contents
[menu_items_used
++] = name
;
369 XVECTOR (menu_items
)->contents
[menu_items_used
++] = prefix_vec
;
372 /* Push one menu item into the current pane. NAME is the string to
373 display. ENABLE if non-nil means this item can be selected. KEY
374 is the key generated by choosing this item, or nil if this item
375 doesn't really have a definition. DEF is the definition of this
376 item. EQUIV is the textual description of the keyboard equivalent
377 for this item (or nil if none). TYPE is the type of this menu
378 item, one of nil, `toggle' or `radio'. */
381 push_menu_item (name
, enable
, key
, def
, equiv
, type
, selected
, help
)
382 Lisp_Object name
, enable
, key
, def
, equiv
, type
, selected
, help
;
384 if (menu_items_used
+ MENU_ITEMS_ITEM_LENGTH
> menu_items_allocated
)
387 XVECTOR (menu_items
)->contents
[menu_items_used
++] = name
;
388 XVECTOR (menu_items
)->contents
[menu_items_used
++] = enable
;
389 XVECTOR (menu_items
)->contents
[menu_items_used
++] = key
;
390 XVECTOR (menu_items
)->contents
[menu_items_used
++] = equiv
;
391 XVECTOR (menu_items
)->contents
[menu_items_used
++] = def
;
392 XVECTOR (menu_items
)->contents
[menu_items_used
++] = type
;
393 XVECTOR (menu_items
)->contents
[menu_items_used
++] = selected
;
394 XVECTOR (menu_items
)->contents
[menu_items_used
++] = help
;
397 /* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
398 and generate menu panes for them in menu_items.
399 If NOTREAL is nonzero,
400 don't bother really computing whether an item is enabled. */
403 keymap_panes (keymaps
, nmaps
, notreal
)
404 Lisp_Object
*keymaps
;
412 /* Loop over the given keymaps, making a pane for each map.
413 But don't make a pane that is empty--ignore that map instead.
414 P is the number of panes we have made so far. */
415 for (mapno
= 0; mapno
< nmaps
; mapno
++)
416 single_keymap_panes (keymaps
[mapno
],
417 Fkeymap_prompt (keymaps
[mapno
]), Qnil
, notreal
, 10);
419 finish_menu_items ();
422 /* This is a recursive subroutine of keymap_panes.
423 It handles one keymap, KEYMAP.
424 The other arguments are passed along
425 or point to local variables of the previous function.
426 If NOTREAL is nonzero, only check for equivalent key bindings, don't
427 evaluate expressions in menu items and don't make any menu.
429 If we encounter submenus deeper than MAXDEPTH levels, ignore them. */
432 single_keymap_panes (keymap
, pane_name
, prefix
, notreal
, maxdepth
)
434 Lisp_Object pane_name
;
439 Lisp_Object pending_maps
= Qnil
;
440 Lisp_Object tail
, item
;
441 struct gcpro gcpro1
, gcpro2
;
446 push_menu_pane (pane_name
, prefix
);
448 for (tail
= keymap
; CONSP (tail
); tail
= XCDR (tail
))
450 GCPRO2 (keymap
, pending_maps
);
451 /* Look at each key binding, and if it is a menu item add it
455 single_menu_item (XCAR (item
), XCDR (item
),
456 &pending_maps
, notreal
, maxdepth
);
457 else if (VECTORP (item
))
459 /* Loop over the char values represented in the vector. */
460 int len
= XVECTOR (item
)->size
;
462 for (c
= 0; c
< len
; c
++)
464 Lisp_Object character
;
465 XSETFASTINT (character
, c
);
466 single_menu_item (character
, XVECTOR (item
)->contents
[c
],
467 &pending_maps
, notreal
, maxdepth
);
473 /* Process now any submenus which want to be panes at this level. */
474 while (!NILP (pending_maps
))
476 Lisp_Object elt
, eltcdr
, string
;
477 elt
= Fcar (pending_maps
);
479 string
= XCAR (eltcdr
);
480 /* We no longer discard the @ from the beginning of the string here.
481 Instead, we do this in mac_menu_show. */
482 single_keymap_panes (Fcar (elt
), string
,
483 XCDR (eltcdr
), notreal
, maxdepth
- 1);
484 pending_maps
= Fcdr (pending_maps
);
488 /* This is a subroutine of single_keymap_panes that handles one
490 KEY is a key in a keymap and ITEM is its binding.
491 PENDING_MAPS_PTR points to a list of keymaps waiting to be made into
493 If NOTREAL is nonzero, only check for equivalent key bindings, don't
494 evaluate expressions in menu items and don't make any menu.
495 If we encounter submenus deeper than MAXDEPTH levels, ignore them. */
498 single_menu_item (key
, item
, pending_maps_ptr
, notreal
, maxdepth
)
499 Lisp_Object key
, item
;
500 Lisp_Object
*pending_maps_ptr
;
501 int maxdepth
, notreal
;
503 Lisp_Object map
, item_string
, enabled
;
504 struct gcpro gcpro1
, gcpro2
;
507 /* Parse the menu item and leave the result in item_properties. */
509 res
= parse_menu_item (item
, notreal
, 0);
512 return; /* Not a menu item. */
514 map
= XVECTOR (item_properties
)->contents
[ITEM_PROPERTY_MAP
];
518 /* We don't want to make a menu, just traverse the keymaps to
519 precompute equivalent key bindings. */
521 single_keymap_panes (map
, Qnil
, key
, 1, maxdepth
- 1);
525 enabled
= XVECTOR (item_properties
)->contents
[ITEM_PROPERTY_ENABLE
];
526 item_string
= XVECTOR (item_properties
)->contents
[ITEM_PROPERTY_NAME
];
528 if (!NILP (map
) && SREF (item_string
, 0) == '@')
531 /* An enabled separate pane. Remember this to handle it later. */
532 *pending_maps_ptr
= Fcons (Fcons (map
, Fcons (item_string
, key
)),
537 push_menu_item (item_string
, enabled
, key
,
538 XVECTOR (item_properties
)->contents
[ITEM_PROPERTY_DEF
],
539 XVECTOR (item_properties
)->contents
[ITEM_PROPERTY_KEYEQ
],
540 XVECTOR (item_properties
)->contents
[ITEM_PROPERTY_TYPE
],
541 XVECTOR (item_properties
)->contents
[ITEM_PROPERTY_SELECTED
],
542 XVECTOR (item_properties
)->contents
[ITEM_PROPERTY_HELP
]);
544 /* Display a submenu using the toolkit. */
545 if (! (NILP (map
) || NILP (enabled
)))
547 push_submenu_start ();
548 single_keymap_panes (map
, Qnil
, key
, 0, maxdepth
- 1);
553 /* Push all the panes and items of a menu described by the
554 alist-of-alists MENU.
555 This handles old-fashioned calls to x-popup-menu. */
565 for (tail
= menu
; !NILP (tail
); tail
= Fcdr (tail
))
567 Lisp_Object elt
, pane_name
, pane_data
;
569 pane_name
= Fcar (elt
);
570 CHECK_STRING (pane_name
);
571 push_menu_pane (pane_name
, Qnil
);
572 pane_data
= Fcdr (elt
);
573 CHECK_CONS (pane_data
);
574 list_of_items (pane_data
);
577 finish_menu_items ();
580 /* Push the items in a single pane defined by the alist PANE. */
586 Lisp_Object tail
, item
, item1
;
588 for (tail
= pane
; !NILP (tail
); tail
= Fcdr (tail
))
592 push_menu_item (item
, Qnil
, Qnil
, Qt
, Qnil
, Qnil
, Qnil
, Qnil
);
593 else if (NILP (item
))
594 push_left_right_boundary ();
599 CHECK_STRING (item1
);
600 push_menu_item (item1
, Qt
, Fcdr (item
), Qt
, Qnil
, Qnil
, Qnil
, Qnil
);
606 cleanup_popup_menu (arg
)
609 discard_menu_items ();
612 DEFUN ("x-popup-menu", Fx_popup_menu
, Sx_popup_menu
, 2, 2, 0,
613 doc
: /* Pop up a deck-of-cards menu and return user's selection.
614 POSITION is a position specification. This is either a mouse button
615 event or a list ((XOFFSET YOFFSET) WINDOW) where XOFFSET and YOFFSET
616 are positions in pixels from the top left corner of WINDOW's frame
617 \(WINDOW may be a frame object instead of a window). This controls the
618 position of the center of the first line in the first pane of the
619 menu, not the top left of the menu as a whole. If POSITION is t, it
620 means to use the current mouse position.
622 MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
623 The menu items come from key bindings that have a menu string as well as
624 a definition; actually, the \"definition\" in such a key binding looks like
625 \(STRING . REAL-DEFINITION). To give the menu a title, put a string into
626 the keymap as a top-level element.
628 If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
629 Otherwise, REAL-DEFINITION should be a valid key binding definition.
631 You can also use a list of keymaps as MENU. Then each keymap makes a
632 separate pane. When MENU is a keymap or a list of keymaps, the return
633 value is a list of events.
635 Alternatively, you can specify a menu of multiple panes with a list of
636 the form (TITLE PANE1 PANE2...), where each pane is a list of
637 form (TITLE ITEM1 ITEM2...).
638 Each ITEM is normally a cons cell (STRING . VALUE); but a string can
639 appear as an item--that makes a nonselectable line in the menu.
640 With this form of menu, the return value is VALUE from the chosen item.
642 If POSITION is nil, don't display the menu at all, just precalculate the
643 cached information about equivalent key sequences. */)
645 Lisp_Object position
, menu
;
647 Lisp_Object keymap
, tem
;
648 int xpos
= 0, ypos
= 0;
651 Lisp_Object selection
;
653 Lisp_Object x
, y
, window
;
657 int specpdl_count
= SPECPDL_INDEX ();
661 if (! NILP (position
))
665 /* Decode the first argument: find the window and the coordinates. */
666 if (EQ (position
, Qt
)
667 || (CONSP (position
) && (EQ (XCAR (position
), Qmenu_bar
)
668 || EQ (XCAR (position
), Qtool_bar
))))
670 /* Use the mouse's current position. */
671 FRAME_PTR new_f
= SELECTED_FRAME ();
672 Lisp_Object bar_window
;
673 enum scroll_bar_part part
;
676 if (mouse_position_hook
)
677 (*mouse_position_hook
) (&new_f
, 1, &bar_window
,
678 &part
, &x
, &y
, &time
);
680 XSETFRAME (window
, new_f
);
683 window
= selected_window
;
690 tem
= Fcar (position
);
693 window
= Fcar (Fcdr (position
));
695 y
= Fcar (Fcdr (tem
));
700 tem
= Fcar (Fcdr (position
)); /* EVENT_START (position) */
701 window
= Fcar (tem
); /* POSN_WINDOW (tem) */
702 tem
= Fcar (Fcdr (Fcdr (tem
))); /* POSN_WINDOW_POSN (tem) */
711 /* Decode where to put the menu. */
719 else if (WINDOWP (window
))
721 CHECK_LIVE_WINDOW (window
);
722 f
= XFRAME (WINDOW_FRAME (XWINDOW (window
)));
724 xpos
= WINDOW_LEFT_EDGE_X (XWINDOW (window
));
725 ypos
= WINDOW_TOP_EDGE_Y (XWINDOW (window
));
728 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
729 but I don't want to make one now. */
730 CHECK_WINDOW (window
);
735 XSETFRAME (Vmenu_updating_frame
, f
);
737 Vmenu_updating_frame
= Qnil
;
738 #endif /* HAVE_MENUS */
743 /* Decode the menu items from what was specified. */
745 keymap
= get_keymap (menu
, 0, 0);
748 /* We were given a keymap. Extract menu info from the keymap. */
751 /* Extract the detailed info to make one pane. */
752 keymap_panes (&menu
, 1, NILP (position
));
754 /* Search for a string appearing directly as an element of the keymap.
755 That string is the title of the menu. */
756 prompt
= Fkeymap_prompt (keymap
);
757 if (NILP (title
) && !NILP (prompt
))
760 /* Make that be the pane title of the first pane. */
761 if (!NILP (prompt
) && menu_items_n_panes
>= 0)
762 XVECTOR (menu_items
)->contents
[MENU_ITEMS_PANE_NAME
] = prompt
;
766 else if (CONSP (menu
) && KEYMAPP (XCAR (menu
)))
768 /* We were given a list of keymaps. */
769 int nmaps
= XFASTINT (Flength (menu
));
771 = (Lisp_Object
*) alloca (nmaps
* sizeof (Lisp_Object
));
776 /* The first keymap that has a prompt string
777 supplies the menu title. */
778 for (tem
= menu
, i
= 0; CONSP (tem
); tem
= Fcdr (tem
))
782 maps
[i
++] = keymap
= get_keymap (Fcar (tem
), 1, 0);
784 prompt
= Fkeymap_prompt (keymap
);
785 if (NILP (title
) && !NILP (prompt
))
789 /* Extract the detailed info to make one pane. */
790 keymap_panes (maps
, nmaps
, NILP (position
));
792 /* Make the title be the pane title of the first pane. */
793 if (!NILP (title
) && menu_items_n_panes
>= 0)
794 XVECTOR (menu_items
)->contents
[MENU_ITEMS_PANE_NAME
] = title
;
800 /* We were given an old-fashioned menu. */
802 CHECK_STRING (title
);
804 list_of_panes (Fcdr (menu
));
811 discard_menu_items ();
817 /* Display them in a menu. */
818 record_unwind_protect (cleanup_popup_menu
, Qnil
);
821 selection
= mac_menu_show (f
, xpos
, ypos
, for_click
,
822 keymaps
, title
, &error_name
);
824 unbind_to (specpdl_count
, Qnil
);
827 #endif /* HAVE_MENUS */
829 if (error_name
) error (error_name
);
835 DEFUN ("x-popup-dialog", Fx_popup_dialog
, Sx_popup_dialog
, 2, 3, 0,
836 doc
: /* Pop up a dialog box and return user's selection.
837 POSITION specifies which frame to use.
838 This is normally a mouse button event or a window or frame.
839 If POSITION is t, it means to use the frame the mouse is on.
840 The dialog box appears in the middle of the specified frame.
842 CONTENTS specifies the alternatives to display in the dialog box.
843 It is a list of the form (TITLE ITEM1 ITEM2...).
844 Each ITEM is a cons cell (STRING . VALUE).
845 The return value is VALUE from the chosen item.
847 An ITEM may also be just a string--that makes a nonselectable item.
848 An ITEM may also be nil--that means to put all preceding items
849 on the left of the dialog box and all following items on the right.
850 \(By default, approximately half appear on each side.)
852 If HEADER is non-nil, the frame title for the box is "Information",
853 otherwise it is "Question". */)
854 (position
, contents
, header
)
855 Lisp_Object position
, contents
, header
;
862 /* Decode the first argument: find the window or frame to use. */
863 if (EQ (position
, Qt
)
864 || (CONSP (position
) && (EQ (XCAR (position
), Qmenu_bar
)
865 || EQ (XCAR (position
), Qtool_bar
))))
867 #if 0 /* Using the frame the mouse is on may not be right. */
868 /* Use the mouse's current position. */
869 FRAME_PTR new_f
= SELECTED_FRAME ();
870 Lisp_Object bar_window
;
871 enum scroll_bar_part part
;
875 (*mouse_position_hook
) (&new_f
, 1, &bar_window
, &part
, &x
, &y
, &time
);
878 XSETFRAME (window
, new_f
);
880 window
= selected_window
;
882 window
= selected_window
;
884 else if (CONSP (position
))
887 tem
= Fcar (position
);
889 window
= Fcar (Fcdr (position
));
892 tem
= Fcar (Fcdr (position
)); /* EVENT_START (position) */
893 window
= Fcar (tem
); /* POSN_WINDOW (tem) */
896 else if (WINDOWP (position
) || FRAMEP (position
))
901 /* Decode where to put the menu. */
905 else if (WINDOWP (window
))
907 CHECK_LIVE_WINDOW (window
);
908 f
= XFRAME (WINDOW_FRAME (XWINDOW (window
)));
911 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
912 but I don't want to make one now. */
913 CHECK_WINDOW (window
);
916 /* Display a menu with these alternatives
917 in the middle of frame F. */
919 Lisp_Object x
, y
, frame
, newpos
;
920 XSETFRAME (frame
, f
);
921 XSETINT (x
, x_pixel_width (f
) / 2);
922 XSETINT (y
, x_pixel_height (f
) / 2);
923 newpos
= Fcons (Fcons (x
, Fcons (y
, Qnil
)), Fcons (frame
, Qnil
));
925 return Fx_popup_menu (newpos
,
926 Fcons (Fcar (contents
), Fcons (contents
, Qnil
)));
928 #else /* HAVE_DIALOGS */
932 Lisp_Object selection
;
934 /* Decode the dialog items from what was specified. */
935 title
= Fcar (contents
);
936 CHECK_STRING (title
);
938 list_of_panes (Fcons (contents
, Qnil
));
940 /* Display them in a dialog box. */
942 selection
= mac_dialog_show (f
, 0, title
, header
, &error_name
);
945 discard_menu_items ();
947 if (error_name
) error (error_name
);
950 #endif /* HAVE_DIALOGS */
953 /* Activate the menu bar of frame F.
954 This is called from keyboard.c when it gets the
955 MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
957 To activate the menu bar, we signal to the input thread that it can
958 return from the WM_INITMENU message, allowing the normal Windows
959 processing of the menus.
961 But first we recompute the menu bar contents (the whole tree).
963 This way we can safely execute Lisp code. */
966 x_activate_menubar (f
)
970 extern Point saved_menu_event_location
;
972 set_frame_menubar (f
, 0, 1);
975 menu_choice
= MenuSelect (saved_menu_event_location
);
976 do_menu_choice (menu_choice
);
981 /* This callback is called from the menu bar pulldown menu
982 when the user makes a selection.
983 Figure out what the user chose
984 and put the appropriate events into the keyboard buffer. */
987 menubar_selection_callback (FRAME_PTR f
, int client_data
)
989 Lisp_Object prefix
, entry
;
991 Lisp_Object
*subprefix_stack
;
992 int submenu_depth
= 0;
998 subprefix_stack
= (Lisp_Object
*) alloca (f
->menu_bar_items_used
* sizeof (Lisp_Object
));
999 vector
= f
->menu_bar_vector
;
1002 while (i
< f
->menu_bar_items_used
)
1004 if (EQ (XVECTOR (vector
)->contents
[i
], Qnil
))
1006 subprefix_stack
[submenu_depth
++] = prefix
;
1010 else if (EQ (XVECTOR (vector
)->contents
[i
], Qlambda
))
1012 prefix
= subprefix_stack
[--submenu_depth
];
1015 else if (EQ (XVECTOR (vector
)->contents
[i
], Qt
))
1017 prefix
= XVECTOR (vector
)->contents
[i
+ MENU_ITEMS_PANE_PREFIX
];
1018 i
+= MENU_ITEMS_PANE_LENGTH
;
1022 entry
= XVECTOR (vector
)->contents
[i
+ MENU_ITEMS_ITEM_VALUE
];
1023 /* The EMACS_INT cast avoids a warning. There's no problem
1024 as long as pointers have enough bits to hold small integers. */
1025 if ((int) (EMACS_INT
) client_data
== i
)
1028 struct input_event buf
;
1032 XSETFRAME (frame
, f
);
1033 buf
.kind
= MENU_BAR_EVENT
;
1034 buf
.frame_or_window
= frame
;
1036 kbd_buffer_store_event (&buf
);
1038 for (j
= 0; j
< submenu_depth
; j
++)
1039 if (!NILP (subprefix_stack
[j
]))
1041 buf
.kind
= MENU_BAR_EVENT
;
1042 buf
.frame_or_window
= frame
;
1043 buf
.arg
= subprefix_stack
[j
];
1044 kbd_buffer_store_event (&buf
);
1049 buf
.kind
= MENU_BAR_EVENT
;
1050 buf
.frame_or_window
= frame
;
1052 kbd_buffer_store_event (&buf
);
1055 buf
.kind
= MENU_BAR_EVENT
;
1056 buf
.frame_or_window
= frame
;
1058 kbd_buffer_store_event (&buf
);
1060 f
->output_data
.mac
->menu_command_in_progress
= 0;
1061 f
->output_data
.mac
->menubar_active
= 0;
1064 i
+= MENU_ITEMS_ITEM_LENGTH
;
1067 f
->output_data
.mac
->menu_command_in_progress
= 0;
1068 f
->output_data
.mac
->menubar_active
= 0;
1071 /* Allocate a widget_value, blocking input. */
1074 xmalloc_widget_value ()
1076 widget_value
*value
;
1079 value
= malloc_widget_value ();
1085 /* This recursively calls free_widget_value on the tree of widgets.
1086 It must free all data that was malloc'ed for these widget_values.
1087 In Emacs, many slots are pointers into the data of Lisp_Strings, and
1088 must be left alone. */
1091 free_menubar_widget_value_tree (wv
)
1096 wv
->name
= wv
->value
= wv
->key
= (char *) 0xDEADBEEF;
1098 if (wv
->contents
&& (wv
->contents
!= (widget_value
*)1))
1100 free_menubar_widget_value_tree (wv
->contents
);
1101 wv
->contents
= (widget_value
*) 0xDEADBEEF;
1105 free_menubar_widget_value_tree (wv
->next
);
1106 wv
->next
= (widget_value
*) 0xDEADBEEF;
1109 free_widget_value (wv
);
1113 /* Return a tree of widget_value structures for a menu bar item
1114 whose event type is ITEM_KEY (with string ITEM_NAME)
1115 and whose contents come from the list of keymaps MAPS. */
1117 static widget_value
*
1118 single_submenu (item_key
, item_name
, maps
)
1119 Lisp_Object item_key
, item_name
, maps
;
1121 widget_value
*wv
, *prev_wv
, *save_wv
, *first_wv
;
1123 int submenu_depth
= 0;
1126 Lisp_Object
*mapvec
;
1127 widget_value
**submenu_stack
;
1128 int previous_items
= menu_items_used
;
1129 int top_level_items
= 0;
1131 length
= Flength (maps
);
1132 len
= XINT (length
);
1134 /* Convert the list MAPS into a vector MAPVEC. */
1135 mapvec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
1136 for (i
= 0; i
< len
; i
++)
1138 mapvec
[i
] = Fcar (maps
);
1142 menu_items_n_panes
= 0;
1144 /* Loop over the given keymaps, making a pane for each map.
1145 But don't make a pane that is empty--ignore that map instead. */
1146 for (i
= 0; i
< len
; i
++)
1148 if (SYMBOLP (mapvec
[i
])
1149 || (CONSP (mapvec
[i
]) && !KEYMAPP (mapvec
[i
])))
1151 /* Here we have a command at top level in the menu bar
1152 as opposed to a submenu. */
1153 top_level_items
= 1;
1154 push_menu_pane (Qnil
, Qnil
);
1155 push_menu_item (item_name
, Qt
, item_key
, mapvec
[i
],
1156 Qnil
, Qnil
, Qnil
, Qnil
);
1159 single_keymap_panes (mapvec
[i
], item_name
, item_key
, 0, 10);
1162 /* Create a tree of widget_value objects
1163 representing the panes and their items. */
1166 = (widget_value
**) alloca (menu_items_used
* sizeof (widget_value
*));
1167 wv
= xmalloc_widget_value ();
1171 wv
->button_type
= BUTTON_TYPE_NONE
;
1177 /* Loop over all panes and items made during this call
1178 and construct a tree of widget_value objects.
1179 Ignore the panes and items made by previous calls to
1180 single_submenu, even though those are also in menu_items. */
1182 while (i
< menu_items_used
)
1184 if (EQ (XVECTOR (menu_items
)->contents
[i
], Qnil
))
1186 submenu_stack
[submenu_depth
++] = save_wv
;
1191 else if (EQ (XVECTOR (menu_items
)->contents
[i
], Qlambda
))
1194 save_wv
= submenu_stack
[--submenu_depth
];
1197 else if (EQ (XVECTOR (menu_items
)->contents
[i
], Qt
)
1198 && submenu_depth
!= 0)
1199 i
+= MENU_ITEMS_PANE_LENGTH
;
1200 /* Ignore a nil in the item list.
1201 It's meaningful only for dialog boxes. */
1202 else if (EQ (XVECTOR (menu_items
)->contents
[i
], Qquote
))
1204 else if (EQ (XVECTOR (menu_items
)->contents
[i
], Qt
))
1206 /* Create a new pane. */
1207 Lisp_Object pane_name
, prefix
;
1210 pane_name
= XVECTOR (menu_items
)->contents
[i
+ MENU_ITEMS_PANE_NAME
];
1211 prefix
= XVECTOR (menu_items
)->contents
[i
+ MENU_ITEMS_PANE_PREFIX
];
1213 #ifndef HAVE_MULTILINGUAL_MENU
1214 if (STRINGP (pane_name
) && STRING_MULTIBYTE (pane_name
))
1216 pane_name
= ENCODE_SYSTEM (pane_name
);
1217 AREF (menu_items
, i
+ MENU_ITEMS_PANE_NAME
) = pane_name
;
1220 pane_string
= (NILP (pane_name
)
1221 ? "" : (char *) SDATA (pane_name
));
1222 /* If there is just one top-level pane, put all its items directly
1223 under the top-level menu. */
1224 if (menu_items_n_panes
== 1)
1227 /* If the pane has a meaningful name,
1228 make the pane a top-level menu item
1229 with its items as a submenu beneath it. */
1230 if (strcmp (pane_string
, ""))
1232 wv
= xmalloc_widget_value ();
1236 first_wv
->contents
= wv
;
1237 wv
->lname
= pane_name
;
1238 /* Set value to 1 so update_submenu_strings can handle '@' */
1239 wv
->value
= (char *)1;
1241 wv
->button_type
= BUTTON_TYPE_NONE
;
1246 i
+= MENU_ITEMS_PANE_LENGTH
;
1250 /* Create a new item within current pane. */
1251 Lisp_Object item_name
, enable
, descrip
, def
, type
, selected
;
1254 item_name
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_NAME
);
1255 enable
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_ENABLE
);
1256 descrip
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_EQUIV_KEY
);
1257 def
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_DEFINITION
);
1258 type
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_TYPE
);
1259 selected
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_SELECTED
);
1260 help
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_HELP
);
1262 #ifndef HAVE_MULTILINGUAL_MENU
1263 if (STRING_MULTIBYTE (item_name
))
1265 item_name
= ENCODE_MENU_STRING (item_name
);
1266 AREF (menu_items
, i
+ MENU_ITEMS_ITEM_NAME
) = item_name
;
1269 if (STRINGP (descrip
) && STRING_MULTIBYTE (descrip
))
1271 descrip
= ENCODE_MENU_STRING (descrip
);
1272 AREF (menu_items
, i
+ MENU_ITEMS_ITEM_EQUIV_KEY
) = descrip
;
1274 #endif /* not HAVE_MULTILINGUAL_MENU */
1276 wv
= xmalloc_widget_value ();
1280 save_wv
->contents
= wv
;
1282 wv
->lname
= item_name
;
1283 if (!NILP (descrip
))
1286 /* The EMACS_INT cast avoids a warning. There's no problem
1287 as long as pointers have enough bits to hold small integers. */
1288 wv
->call_data
= (!NILP (def
) ? (void *) (EMACS_INT
) i
: 0);
1289 wv
->enabled
= !NILP (enable
);
1292 wv
->button_type
= BUTTON_TYPE_NONE
;
1293 else if (EQ (type
, QCradio
))
1294 wv
->button_type
= BUTTON_TYPE_RADIO
;
1295 else if (EQ (type
, QCtoggle
))
1296 wv
->button_type
= BUTTON_TYPE_TOGGLE
;
1300 wv
->selected
= !NILP (selected
);
1301 if (!STRINGP (help
))
1308 i
+= MENU_ITEMS_ITEM_LENGTH
;
1312 /* If we have just one "menu item"
1313 that was originally a button, return it by itself. */
1314 if (top_level_items
&& first_wv
->contents
&& first_wv
->contents
->next
== 0)
1316 wv
= first_wv
->contents
;
1317 free_widget_value (first_wv
);
1323 /* Walk through the widget_value tree starting at FIRST_WV and update
1324 the char * pointers from the corresponding lisp values.
1325 We do this after building the whole tree, since GC may happen while the
1326 tree is constructed, and small strings are relocated. So we must wait
1327 until no GC can happen before storing pointers into lisp values. */
1329 update_submenu_strings (first_wv
)
1330 widget_value
*first_wv
;
1334 for (wv
= first_wv
; wv
; wv
= wv
->next
)
1336 if (STRINGP (wv
->lname
))
1338 wv
->name
= SDATA (wv
->lname
);
1340 /* Ignore the @ that means "separate pane".
1341 This is a kludge, but this isn't worth more time. */
1342 if (wv
->value
== (char *)1)
1344 if (wv
->name
[0] == '@')
1350 if (STRINGP (wv
->lkey
))
1351 wv
->key
= SDATA (wv
->lkey
);
1354 update_submenu_strings (wv
->contents
);
1359 /* Event handler function that pops down a menu on C-g. We can only pop
1360 down menus if CancelMenuTracking is present (OSX 10.3 or later). */
1362 #ifdef HAVE_CANCELMENUTRACKING
1363 static pascal OSStatus
1364 menu_quit_handler (nextHandler
, theEvent
, userData
)
1365 EventHandlerCallRef nextHandler
;
1370 UInt32 keyModifiers
;
1371 extern int mac_quit_char_modifiers
;
1372 extern int mac_quit_char_keycode
;
1374 GetEventParameter (theEvent
, kEventParamKeyCode
,
1375 typeUInt32
, NULL
, sizeof(UInt32
), NULL
, &keyCode
);
1377 GetEventParameter (theEvent
, kEventParamKeyModifiers
,
1378 typeUInt32
, NULL
, sizeof(UInt32
),
1379 NULL
, &keyModifiers
);
1381 if (keyCode
== mac_quit_char_keycode
1382 && keyModifiers
== mac_quit_char_modifiers
)
1384 MenuRef menu
= userData
!= 0
1385 ? (MenuRef
)userData
: AcquireRootMenu ();
1387 CancelMenuTracking (menu
, true, 0);
1388 if (!userData
) ReleaseMenu (menu
);
1392 return CallNextEventHandler (nextHandler
, theEvent
);
1394 #endif /* HAVE_CANCELMENUTRACKING */
1396 /* Add event handler for MENU_HANDLE so we can detect C-g.
1397 If MENU_HANDLE is NULL, install handler for all menus in the menu bar.
1398 If CancelMenuTracking isn't available, do nothing. */
1401 install_menu_quit_handler (MenuHandle menu_handle
)
1403 #ifdef HAVE_CANCELMENUTRACKING
1404 EventHandlerUPP handler
= NewEventHandlerUPP(menu_quit_handler
);
1405 UInt32 numTypes
= 1;
1406 EventTypeSpec typesList
[] = { { kEventClassKeyboard
, kEventRawKeyDown
} };
1407 int i
= MIN_MENU_ID
;
1408 MenuHandle menu
= menu_handle
? menu_handle
: GetMenuHandle (i
);
1410 while (menu
!= NULL
)
1412 InstallMenuEventHandler (menu
, handler
, GetEventTypeCount (typesList
),
1413 typesList
, menu_handle
, NULL
);
1414 if (menu_handle
) break;
1415 menu
= GetMenuHandle (++i
);
1417 DisposeEventHandlerUPP (handler
);
1418 #endif /* HAVE_CANCELMENUTRACKING */
1421 /* Set the contents of the menubar widgets of frame F.
1422 The argument FIRST_TIME is currently ignored;
1423 it is set the first time this is called, from initialize_frame_menubar. */
1426 set_frame_menubar (f
, first_time
, deep_p
)
1431 int menubar_widget
= f
->output_data
.mac
->menubar_widget
;
1433 widget_value
*wv
, *first_wv
, *prev_wv
= 0;
1436 /* We must not change the menubar when actually in use. */
1437 if (f
->output_data
.mac
->menubar_active
)
1440 XSETFRAME (Vmenu_updating_frame
, f
);
1442 if (! menubar_widget
)
1444 else if (pending_menu_activation
&& !deep_p
)
1447 wv
= xmalloc_widget_value ();
1448 wv
->name
= "menubar";
1451 wv
->button_type
= BUTTON_TYPE_NONE
;
1457 /* Make a widget-value tree representing the entire menu trees. */
1459 struct buffer
*prev
= current_buffer
;
1461 int specpdl_count
= SPECPDL_INDEX ();
1462 int previous_menu_items_used
= f
->menu_bar_items_used
;
1463 Lisp_Object
*previous_items
1464 = (Lisp_Object
*) alloca (previous_menu_items_used
1465 * sizeof (Lisp_Object
));
1467 /* If we are making a new widget, its contents are empty,
1468 do always reinitialize them. */
1469 if (! menubar_widget
)
1470 previous_menu_items_used
= 0;
1472 buffer
= XWINDOW (FRAME_SELECTED_WINDOW (f
))->buffer
;
1473 specbind (Qinhibit_quit
, Qt
);
1474 /* Don't let the debugger step into this code
1475 because it is not reentrant. */
1476 specbind (Qdebug_on_next_call
, Qnil
);
1478 record_unwind_save_match_data ();
1479 if (NILP (Voverriding_local_map_menu_flag
))
1481 specbind (Qoverriding_terminal_local_map
, Qnil
);
1482 specbind (Qoverriding_local_map
, Qnil
);
1485 set_buffer_internal_1 (XBUFFER (buffer
));
1487 /* Run the Lucid hook. */
1488 safe_run_hooks (Qactivate_menubar_hook
);
1489 /* If it has changed current-menubar from previous value,
1490 really recompute the menubar from the value. */
1491 if (! NILP (Vlucid_menu_bar_dirty_flag
))
1492 call0 (Qrecompute_lucid_menubar
);
1493 safe_run_hooks (Qmenu_bar_update_hook
);
1494 FRAME_MENU_BAR_ITEMS (f
) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f
));
1496 items
= FRAME_MENU_BAR_ITEMS (f
);
1498 /* Save the frame's previous menu bar contents data. */
1499 if (previous_menu_items_used
)
1500 bcopy (XVECTOR (f
->menu_bar_vector
)->contents
, previous_items
,
1501 previous_menu_items_used
* sizeof (Lisp_Object
));
1503 /* Fill in the current menu bar contents. */
1504 menu_items
= f
->menu_bar_vector
;
1505 menu_items_allocated
= VECTORP (menu_items
) ? ASIZE (menu_items
) : 0;
1507 for (i
= 0; i
< XVECTOR (items
)->size
; i
+= 4)
1509 Lisp_Object key
, string
, maps
;
1511 key
= XVECTOR (items
)->contents
[i
];
1512 string
= XVECTOR (items
)->contents
[i
+ 1];
1513 maps
= XVECTOR (items
)->contents
[i
+ 2];
1517 wv
= single_submenu (key
, string
, maps
);
1521 first_wv
->contents
= wv
;
1522 /* Don't set wv->name here; GC during the loop might relocate it. */
1524 wv
->button_type
= BUTTON_TYPE_NONE
;
1528 finish_menu_items ();
1530 set_buffer_internal_1 (prev
);
1531 unbind_to (specpdl_count
, Qnil
);
1533 /* If there has been no change in the Lisp-level contents
1534 of the menu bar, skip redisplaying it. Just exit. */
1536 for (i
= 0; i
< previous_menu_items_used
; i
++)
1537 if (menu_items_used
== i
1538 || (NILP (Fequal (previous_items
[i
],
1539 XVECTOR (menu_items
)->contents
[i
]))))
1541 if (i
== menu_items_used
&& i
== previous_menu_items_used
&& i
!= 0)
1543 free_menubar_widget_value_tree (first_wv
);
1549 /* Now GC cannot happen during the lifetime of the widget_value,
1550 so it's safe to store data from a Lisp_String, as long as
1551 local copies are made when the actual menu is created.
1552 Windows takes care of this for normal string items, but
1553 not for owner-drawn items or additional item-info. */
1554 wv
= first_wv
->contents
;
1555 for (i
= 0; i
< XVECTOR (items
)->size
; i
+= 4)
1558 string
= XVECTOR (items
)->contents
[i
+ 1];
1561 wv
->name
= (char *) SDATA (string
);
1562 update_submenu_strings (wv
->contents
);
1566 f
->menu_bar_vector
= menu_items
;
1567 f
->menu_bar_items_used
= menu_items_used
;
1572 /* Make a widget-value tree containing
1573 just the top level menu bar strings. */
1575 items
= FRAME_MENU_BAR_ITEMS (f
);
1576 for (i
= 0; i
< XVECTOR (items
)->size
; i
+= 4)
1580 string
= XVECTOR (items
)->contents
[i
+ 1];
1584 wv
= xmalloc_widget_value ();
1585 wv
->name
= (char *) SDATA (string
);
1588 wv
->button_type
= BUTTON_TYPE_NONE
;
1590 /* This prevents lwlib from assuming this
1591 menu item is really supposed to be empty. */
1592 /* The EMACS_INT cast avoids a warning.
1593 This value just has to be different from small integers. */
1594 wv
->call_data
= (void *) (EMACS_INT
) (-1);
1599 first_wv
->contents
= wv
;
1603 /* Forget what we thought we knew about what is in the
1604 detailed contents of the menu bar menus.
1605 Changing the top level always destroys the contents. */
1606 f
->menu_bar_items_used
= 0;
1609 /* Create or update the menu bar widget. */
1613 /* Non-null value to indicate menubar has already been "created". */
1614 f
->output_data
.mac
->menubar_widget
= 1;
1617 int i
= MIN_MENU_ID
;
1618 MenuHandle menu
= GetMenuHandle (i
);
1619 while (menu
!= NULL
)
1623 menu
= GetMenuHandle (++i
);
1627 menu
= GetMenuHandle (i
);
1628 while (menu
!= NULL
)
1632 menu
= GetMenuHandle (++i
);
1636 fill_menubar (first_wv
->contents
);
1640 /* Add event handler so we can detect C-g. */
1641 install_menu_quit_handler (NULL
);
1642 free_menubar_widget_value_tree (first_wv
);
1647 /* Called from Fx_create_frame to create the initial menubar of a frame
1648 before it is mapped, so that the window is mapped with the menubar already
1649 there instead of us tacking it on later and thrashing the window after it
1653 initialize_frame_menubar (f
)
1656 /* This function is called before the first chance to redisplay
1657 the frame. It has to be, so the frame will have the right size. */
1658 FRAME_MENU_BAR_ITEMS (f
) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f
));
1659 set_frame_menubar (f
, 1, 1);
1662 /* Get rid of the menu bar of frame F, and free its storage.
1663 This is used when deleting a frame, and when turning off the menu bar. */
1666 free_frame_menubar (f
)
1669 f
->output_data
.mac
->menubar_widget
= NULL
;
1677 struct Lisp_Save_Value
*p1
= XSAVE_VALUE (Fcar (arg
));
1678 struct Lisp_Save_Value
*p2
= XSAVE_VALUE (Fcdr (arg
));
1680 FRAME_PTR f
= p1
->pointer
;
1681 MenuHandle
*menu
= p2
->pointer
;
1685 /* Must reset this manually because the button release event is not
1686 passed to Emacs event loop. */
1687 FRAME_MAC_DISPLAY_INFO (f
)->grabbed
= 0;
1689 /* delete all menus */
1691 int i
= MIN_POPUP_SUBMENU_ID
;
1692 MenuHandle submenu
= GetMenuHandle (i
);
1693 while (submenu
!= NULL
)
1696 DisposeMenu (submenu
);
1697 submenu
= GetMenuHandle (++i
);
1701 DeleteMenu (POPUP_SUBMENU_ID
);
1702 DisposeMenu (*menu
);
1709 /* Mac_menu_show actually displays a menu using the panes and items in
1710 menu_items and returns the value selected from it; we assume input
1711 is blocked by the caller. */
1713 /* F is the frame the menu is for.
1714 X and Y are the frame-relative specified position,
1715 relative to the inside upper left corner of the frame F.
1716 FOR_CLICK is nonzero if this menu was invoked for a mouse click.
1717 KEYMAPS is 1 if this menu was specified with keymaps;
1718 in that case, we return a list containing the chosen item's value
1719 and perhaps also the pane's prefix.
1720 TITLE is the specified menu title.
1721 ERROR is a place to store an error message string in case of failure.
1722 (We return nil on failure, but the value doesn't actually matter.) */
1725 mac_menu_show (f
, x
, y
, for_click
, keymaps
, title
, error
)
1736 int menu_item_choice
;
1737 int menu_item_selection
;
1740 widget_value
*wv
, *save_wv
= 0, *first_wv
= 0, *prev_wv
= 0;
1741 widget_value
**submenu_stack
1742 = (widget_value
**) alloca (menu_items_used
* sizeof (widget_value
*));
1743 Lisp_Object
*subprefix_stack
1744 = (Lisp_Object
*) alloca (menu_items_used
* sizeof (Lisp_Object
));
1745 int submenu_depth
= 0;
1747 int specpdl_count
= SPECPDL_INDEX ();
1751 if (menu_items_used
<= MENU_ITEMS_PANE_LENGTH
)
1753 *error
= "Empty menu";
1757 /* Create a tree of widget_value objects
1758 representing the panes and their items. */
1759 wv
= xmalloc_widget_value ();
1763 wv
->button_type
= BUTTON_TYPE_NONE
;
1768 /* Loop over all panes and items, filling in the tree. */
1770 while (i
< menu_items_used
)
1772 if (EQ (XVECTOR (menu_items
)->contents
[i
], Qnil
))
1774 submenu_stack
[submenu_depth
++] = save_wv
;
1780 else if (EQ (XVECTOR (menu_items
)->contents
[i
], Qlambda
))
1783 save_wv
= submenu_stack
[--submenu_depth
];
1787 else if (EQ (XVECTOR (menu_items
)->contents
[i
], Qt
)
1788 && submenu_depth
!= 0)
1789 i
+= MENU_ITEMS_PANE_LENGTH
;
1790 /* Ignore a nil in the item list.
1791 It's meaningful only for dialog boxes. */
1792 else if (EQ (XVECTOR (menu_items
)->contents
[i
], Qquote
))
1794 else if (EQ (XVECTOR (menu_items
)->contents
[i
], Qt
))
1796 /* Create a new pane. */
1797 Lisp_Object pane_name
, prefix
;
1799 pane_name
= AREF (menu_items
, i
+ MENU_ITEMS_PANE_NAME
);
1800 prefix
= AREF (menu_items
, i
+ MENU_ITEMS_PANE_PREFIX
);
1801 #ifndef HAVE_MULTILINGUAL_MENU
1802 if (STRINGP (pane_name
) && STRING_MULTIBYTE (pane_name
))
1804 pane_name
= ENCODE_SYSTEM (pane_name
);
1805 AREF (menu_items
, i
+ MENU_ITEMS_PANE_NAME
) = pane_name
;
1808 pane_string
= (NILP (pane_name
)
1809 ? "" : (char *) SDATA (pane_name
));
1810 /* If there is just one top-level pane, put all its items directly
1811 under the top-level menu. */
1812 if (menu_items_n_panes
== 1)
1815 /* If the pane has a meaningful name,
1816 make the pane a top-level menu item
1817 with its items as a submenu beneath it. */
1818 if (!keymaps
&& strcmp (pane_string
, ""))
1820 wv
= xmalloc_widget_value ();
1824 first_wv
->contents
= wv
;
1825 wv
->name
= pane_string
;
1826 if (keymaps
&& !NILP (prefix
))
1830 wv
->button_type
= BUTTON_TYPE_NONE
;
1835 else if (first_pane
)
1841 i
+= MENU_ITEMS_PANE_LENGTH
;
1845 /* Create a new item within current pane. */
1846 Lisp_Object item_name
, enable
, descrip
, def
, type
, selected
, help
;
1848 item_name
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_NAME
);
1849 enable
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_ENABLE
);
1850 descrip
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_EQUIV_KEY
);
1851 def
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_DEFINITION
);
1852 type
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_TYPE
);
1853 selected
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_SELECTED
);
1854 help
= AREF (menu_items
, i
+ MENU_ITEMS_ITEM_HELP
);
1856 #ifndef HAVE_MULTILINGUAL_MENU
1857 if (STRINGP (item_name
) && STRING_MULTIBYTE (item_name
))
1859 item_name
= ENCODE_MENU_STRING (item_name
);
1860 AREF (menu_items
, i
+ MENU_ITEMS_ITEM_NAME
) = item_name
;
1862 if (STRINGP (descrip
) && STRING_MULTIBYTE (descrip
))
1864 descrip
= ENCODE_MENU_STRING (descrip
);
1865 AREF (menu_items
, i
+ MENU_ITEMS_ITEM_EQUIV_KEY
) = descrip
;
1867 #endif /* not HAVE_MULTILINGUAL_MENU */
1869 wv
= xmalloc_widget_value ();
1873 save_wv
->contents
= wv
;
1874 wv
->name
= (char *) SDATA (item_name
);
1875 if (!NILP (descrip
))
1876 wv
->key
= (char *) SDATA (descrip
);
1878 /* Use the contents index as call_data, since we are
1879 restricted to 16-bits. */
1880 wv
->call_data
= !NILP (def
) ? (void *) (EMACS_INT
) i
: 0;
1881 wv
->enabled
= !NILP (enable
);
1884 wv
->button_type
= BUTTON_TYPE_NONE
;
1885 else if (EQ (type
, QCtoggle
))
1886 wv
->button_type
= BUTTON_TYPE_TOGGLE
;
1887 else if (EQ (type
, QCradio
))
1888 wv
->button_type
= BUTTON_TYPE_RADIO
;
1892 wv
->selected
= !NILP (selected
);
1893 if (!STRINGP (help
))
1900 i
+= MENU_ITEMS_ITEM_LENGTH
;
1904 /* Deal with the title, if it is non-nil. */
1907 widget_value
*wv_title
= xmalloc_widget_value ();
1908 widget_value
*wv_sep
= xmalloc_widget_value ();
1910 /* Maybe replace this separator with a bitmap or owner-draw item
1911 so that it looks better. Having two separators looks odd. */
1912 wv_sep
->name
= "--";
1913 wv_sep
->next
= first_wv
->contents
;
1914 wv_sep
->help
= Qnil
;
1916 #ifndef HAVE_MULTILINGUAL_MENU
1917 if (STRING_MULTIBYTE (title
))
1918 title
= ENCODE_MENU_STRING (title
);
1920 wv_title
->name
= (char *) SDATA (title
);
1921 wv_title
->enabled
= FALSE
;
1922 wv_title
->title
= TRUE
;
1923 wv_title
->button_type
= BUTTON_TYPE_NONE
;
1924 wv_title
->help
= Qnil
;
1925 wv_title
->next
= wv_sep
;
1926 first_wv
->contents
= wv_title
;
1929 /* Actually create the menu. */
1930 menu
= NewMenu (POPUP_SUBMENU_ID
, "\p");
1931 submenu_id
= MIN_POPUP_SUBMENU_ID
;
1932 fill_submenu (menu
, first_wv
->contents
);
1934 /* Free the widget_value objects we used to specify the
1936 free_menubar_widget_value_tree (first_wv
);
1938 /* Adjust coordinates to be root-window-relative. */
1942 SetPortWindowPort (FRAME_MAC_WINDOW (f
));
1944 LocalToGlobal (&pos
);
1946 /* No selection has been chosen yet. */
1947 menu_item_choice
= 0;
1948 menu_item_selection
= 0;
1950 InsertMenu (menu
, -1);
1952 record_unwind_protect (pop_down_menu
,
1953 Fcons (make_save_value (f
, 0),
1954 make_save_value (&menu
, 0)));
1956 /* Add event handler so we can detect C-g. */
1957 install_menu_quit_handler (menu
);
1959 /* Display the menu. */
1960 menu_item_choice
= PopUpMenuSelect (menu
, pos
.v
, pos
.h
, 0);
1961 menu_item_selection
= LoWord (menu_item_choice
);
1963 /* Get the refcon to find the correct item */
1964 if (menu_item_selection
)
1966 MenuHandle sel_menu
= GetMenuHandle (HiWord (menu_item_choice
));
1968 GetMenuItemRefCon (sel_menu
, menu_item_selection
, &refcon
);
1971 else if (! for_click
)
1972 /* Make "Cancel" equivalent to C-g unless this menu was popped up by
1974 Fsignal (Qquit
, Qnil
);
1976 /* Find the selected item, and its pane, to return
1977 the proper value. */
1978 if (menu_item_selection
!= 0)
1980 Lisp_Object prefix
, entry
;
1982 prefix
= entry
= Qnil
;
1984 while (i
< menu_items_used
)
1986 if (EQ (XVECTOR (menu_items
)->contents
[i
], Qnil
))
1988 subprefix_stack
[submenu_depth
++] = prefix
;
1992 else if (EQ (XVECTOR (menu_items
)->contents
[i
], Qlambda
))
1994 prefix
= subprefix_stack
[--submenu_depth
];
1997 else if (EQ (XVECTOR (menu_items
)->contents
[i
], Qt
))
2000 = XVECTOR (menu_items
)->contents
[i
+ MENU_ITEMS_PANE_PREFIX
];
2001 i
+= MENU_ITEMS_PANE_LENGTH
;
2003 /* Ignore a nil in the item list.
2004 It's meaningful only for dialog boxes. */
2005 else if (EQ (XVECTOR (menu_items
)->contents
[i
], Qquote
))
2010 = XVECTOR (menu_items
)->contents
[i
+ MENU_ITEMS_ITEM_VALUE
];
2011 if ((int) (EMACS_INT
) refcon
== i
)
2017 entry
= Fcons (entry
, Qnil
);
2019 entry
= Fcons (prefix
, entry
);
2020 for (j
= submenu_depth
- 1; j
>= 0; j
--)
2021 if (!NILP (subprefix_stack
[j
]))
2022 entry
= Fcons (subprefix_stack
[j
], entry
);
2026 i
+= MENU_ITEMS_ITEM_LENGTH
;
2030 else if (!for_click
)
2031 /* Make "Cancel" equivalent to C-g. */
2032 Fsignal (Qquit
, Qnil
);
2034 unbind_to (specpdl_count
, Qnil
);
2041 /* Construct native Mac OS menubar based on widget_value tree. */
2044 mac_dialog (widget_value
*wv
)
2048 char **button_labels
;
2055 WindowPtr window_ptr
;
2058 EventRecord event_record
;
2060 int control_part_code
;
2063 dialog_name
= wv
->name
;
2064 nb_buttons
= dialog_name
[1] - '0';
2065 left_count
= nb_buttons
- (dialog_name
[4] - '0');
2066 button_labels
= (char **) alloca (sizeof (char *) * nb_buttons
);
2067 ref_cons
= (UInt32
*) alloca (sizeof (UInt32
) * nb_buttons
);
2070 prompt
= (char *) alloca (strlen (wv
->value
) + 1);
2071 strcpy (prompt
, wv
->value
);
2075 for (i
= 0; i
< nb_buttons
; i
++)
2077 button_labels
[i
] = wv
->value
;
2078 button_labels
[i
] = (char *) alloca (strlen (wv
->value
) + 1);
2079 strcpy (button_labels
[i
], wv
->value
);
2080 c2pstr (button_labels
[i
]);
2081 ref_cons
[i
] = (UInt32
) wv
->call_data
;
2085 window_ptr
= GetNewCWindow (DIALOG_WINDOW_RESOURCE
, NULL
, (WindowPtr
) -1);
2087 SetPortWindowPort (window_ptr
);
2090 /* Left and right margins in the dialog are 13 pixels each.*/
2092 /* Calculate width of dialog box: 8 pixels on each side of the text
2093 label in each button, 12 pixels between buttons. */
2094 for (i
= 0; i
< nb_buttons
; i
++)
2095 dialog_width
+= StringWidth (button_labels
[i
]) + 16 + 12;
2097 if (left_count
!= 0 && nb_buttons
- left_count
!= 0)
2100 dialog_width
= max (dialog_width
, StringWidth (prompt
) + 26);
2102 SizeWindow (window_ptr
, dialog_width
, 78, 0);
2103 ShowWindow (window_ptr
);
2105 SetPortWindowPort (window_ptr
);
2110 DrawString (prompt
);
2113 for (i
= 0; i
< nb_buttons
; i
++)
2115 int button_width
= StringWidth (button_labels
[i
]) + 16;
2116 SetRect (&rect
, left
, 45, left
+ button_width
, 65);
2117 ch
= NewControl (window_ptr
, &rect
, button_labels
[i
], 1, 0, 0, 0,
2118 kControlPushButtonProc
, ref_cons
[i
]);
2119 left
+= button_width
+ 12;
2120 if (i
== left_count
- 1)
2127 if (WaitNextEvent (mDownMask
, &event_record
, 10, NULL
))
2128 if (event_record
.what
== mouseDown
)
2130 part_code
= FindWindow (event_record
.where
, &window_ptr
);
2131 if (part_code
== inContent
)
2133 mouse
= event_record
.where
;
2134 GlobalToLocal (&mouse
);
2135 control_part_code
= FindControl (mouse
, window_ptr
, &ch
);
2136 if (control_part_code
== kControlButtonPart
)
2137 if (TrackControl (ch
, mouse
, NULL
))
2138 i
= GetControlReference (ch
);
2143 DisposeWindow (window_ptr
);
2148 static char * button_names
[] = {
2149 "button1", "button2", "button3", "button4", "button5",
2150 "button6", "button7", "button8", "button9", "button10" };
2153 mac_dialog_show (f
, keymaps
, title
, header
, error
)
2156 Lisp_Object title
, header
;
2159 int i
, nb_buttons
=0;
2160 char dialog_name
[6];
2161 int menu_item_selection
;
2163 widget_value
*wv
, *first_wv
= 0, *prev_wv
= 0;
2165 /* Number of elements seen so far, before boundary. */
2167 /* 1 means we've seen the boundary between left-hand elts and right-hand. */
2168 int boundary_seen
= 0;
2172 if (menu_items_n_panes
> 1)
2174 *error
= "Multiple panes in dialog box";
2178 /* Create a tree of widget_value objects
2179 representing the text label and buttons. */
2181 Lisp_Object pane_name
, prefix
;
2183 pane_name
= XVECTOR (menu_items
)->contents
[MENU_ITEMS_PANE_NAME
];
2184 prefix
= XVECTOR (menu_items
)->contents
[MENU_ITEMS_PANE_PREFIX
];
2185 pane_string
= (NILP (pane_name
)
2186 ? "" : (char *) SDATA (pane_name
));
2187 prev_wv
= xmalloc_widget_value ();
2188 prev_wv
->value
= pane_string
;
2189 if (keymaps
&& !NILP (prefix
))
2191 prev_wv
->enabled
= 1;
2192 prev_wv
->name
= "message";
2193 prev_wv
->help
= Qnil
;
2196 /* Loop over all panes and items, filling in the tree. */
2197 i
= MENU_ITEMS_PANE_LENGTH
;
2198 while (i
< menu_items_used
)
2201 /* Create a new item within current pane. */
2202 Lisp_Object item_name
, enable
, descrip
, help
;
2204 item_name
= XVECTOR (menu_items
)->contents
[i
+ MENU_ITEMS_ITEM_NAME
];
2205 enable
= XVECTOR (menu_items
)->contents
[i
+ MENU_ITEMS_ITEM_ENABLE
];
2207 = XVECTOR (menu_items
)->contents
[i
+ MENU_ITEMS_ITEM_EQUIV_KEY
];
2208 help
= XVECTOR (menu_items
)->contents
[i
+ MENU_ITEMS_ITEM_HELP
];
2210 if (NILP (item_name
))
2212 free_menubar_widget_value_tree (first_wv
);
2213 *error
= "Submenu in dialog items";
2216 if (EQ (item_name
, Qquote
))
2218 /* This is the boundary between left-side elts
2219 and right-side elts. Stop incrementing right_count. */
2224 if (nb_buttons
>= 9)
2226 free_menubar_widget_value_tree (first_wv
);
2227 *error
= "Too many dialog items";
2231 wv
= xmalloc_widget_value ();
2233 wv
->name
= (char *) button_names
[nb_buttons
];
2234 if (!NILP (descrip
))
2235 wv
->key
= (char *) SDATA (descrip
);
2236 wv
->value
= (char *) SDATA (item_name
);
2237 wv
->call_data
= (void *) i
;
2238 /* menu item is identified by its index in menu_items table */
2239 wv
->enabled
= !NILP (enable
);
2243 if (! boundary_seen
)
2247 i
+= MENU_ITEMS_ITEM_LENGTH
;
2250 /* If the boundary was not specified,
2251 by default put half on the left and half on the right. */
2252 if (! boundary_seen
)
2253 left_count
= nb_buttons
- nb_buttons
/ 2;
2255 wv
= xmalloc_widget_value ();
2256 wv
->name
= dialog_name
;
2259 /* Frame title: 'Q' = Question, 'I' = Information.
2260 Can also have 'E' = Error if, one day, we want
2261 a popup for errors. */
2263 dialog_name
[0] = 'Q';
2265 dialog_name
[0] = 'I';
2267 /* Dialog boxes use a really stupid name encoding
2268 which specifies how many buttons to use
2269 and how many buttons are on the right. */
2270 dialog_name
[1] = '0' + nb_buttons
;
2271 dialog_name
[2] = 'B';
2272 dialog_name
[3] = 'R';
2273 /* Number of buttons to put on the right. */
2274 dialog_name
[4] = '0' + nb_buttons
- left_count
;
2276 wv
->contents
= first_wv
;
2280 /* Actually create the dialog. */
2282 menu_item_selection
= mac_dialog (first_wv
);
2284 menu_item_selection
= 0;
2287 /* Free the widget_value objects we used to specify the contents. */
2288 free_menubar_widget_value_tree (first_wv
);
2290 /* Find the selected item, and its pane, to return the proper
2292 if (menu_item_selection
!= 0)
2298 while (i
< menu_items_used
)
2302 if (EQ (XVECTOR (menu_items
)->contents
[i
], Qt
))
2305 = XVECTOR (menu_items
)->contents
[i
+ MENU_ITEMS_PANE_PREFIX
];
2306 i
+= MENU_ITEMS_PANE_LENGTH
;
2311 = XVECTOR (menu_items
)->contents
[i
+ MENU_ITEMS_ITEM_VALUE
];
2312 if (menu_item_selection
== i
)
2316 entry
= Fcons (entry
, Qnil
);
2318 entry
= Fcons (prefix
, entry
);
2322 i
+= MENU_ITEMS_ITEM_LENGTH
;
2329 #endif /* HAVE_DIALOGS */
2332 /* Is this item a separator? */
2334 name_is_separator (name
)
2339 /* Check if name string consists of only dashes ('-'). */
2340 while (*name
== '-') name
++;
2341 /* Separators can also be of the form "--:TripleSuperMegaEtched"
2342 or "--deep-shadow". We don't implement them yet, se we just treat
2343 them like normal separators. */
2344 return (*name
== '\0' || start
+ 2 == name
);
2348 add_menu_item (MenuHandle menu
, widget_value
*wv
, int submenu
,
2354 if (name_is_separator (wv
->name
))
2355 AppendMenu (menu
, "\p-");
2358 AppendMenu (menu
, "\pX");
2360 #if TARGET_API_MAC_CARBON
2361 pos
= CountMenuItems (menu
);
2363 pos
= CountMItems (menu
);
2366 strcpy (item_name
, "");
2367 strncat (item_name
, wv
->name
, 255);
2368 if (wv
->key
!= NULL
)
2370 strncat (item_name
, " ", 255);
2371 strncat (item_name
, wv
->key
, 255);
2374 #if TARGET_API_MAC_CARBON
2376 CFStringRef string
= cfstring_create_with_utf8_cstring (item_name
);
2378 SetMenuItemTextWithCFString (menu
, pos
, string
);
2383 SetMenuItemText (menu
, pos
, item_name
);
2386 if (wv
->enabled
&& !force_disable
)
2387 #if TARGET_API_MAC_CARBON
2388 EnableMenuItem (menu
, pos
);
2390 EnableItem (menu
, pos
);
2393 #if TARGET_API_MAC_CARBON
2394 DisableMenuItem (menu
, pos
);
2396 DisableItem (menu
, pos
);
2399 /* Draw radio buttons and tickboxes. */
2401 if (wv
->selected
&& (wv
->button_type
== BUTTON_TYPE_TOGGLE
||
2402 wv
->button_type
== BUTTON_TYPE_RADIO
))
2403 SetItemMark (menu
, pos
, checkMark
);
2405 SetItemMark (menu
, pos
, noMark
);
2408 SetMenuItemRefCon (menu
, pos
, (UInt32
) wv
->call_data
);
2411 if (submenu
!= NULL
)
2412 SetMenuItemHierarchicalID (menu
, pos
, submenu
);
2415 /* Construct native Mac OS menubar based on widget_value tree. */
2418 fill_submenu (MenuHandle menu
, widget_value
*wv
)
2420 for ( ; wv
!= NULL
; wv
= wv
->next
)
2423 int cur_submenu
= submenu_id
++;
2424 MenuHandle submenu
= NewMenu (cur_submenu
, "\pX");
2425 fill_submenu (submenu
, wv
->contents
);
2426 InsertMenu (submenu
, -1);
2427 add_menu_item (menu
, wv
, cur_submenu
, 0);
2430 add_menu_item (menu
, wv
, NULL
, 0);
2434 /* Construct native Mac OS menu based on widget_value tree. */
2437 fill_menu (MenuHandle menu
, widget_value
*wv
)
2439 for ( ; wv
!= NULL
; wv
= wv
->next
)
2442 int cur_submenu
= submenu_id
++;
2443 MenuHandle submenu
= NewMenu (cur_submenu
, "\pX");
2444 fill_submenu (submenu
, wv
->contents
);
2445 InsertMenu (submenu
, -1);
2446 add_menu_item (menu
, wv
, cur_submenu
, 0);
2449 add_menu_item (menu
, wv
, NULL
, 0);
2452 /* Construct native Mac OS menubar based on widget_value tree. */
2455 fill_menubar (widget_value
*wv
)
2459 submenu_id
= MIN_SUBMENU_ID
;
2461 for (id
= MIN_MENU_ID
; wv
!= NULL
; wv
= wv
->next
, id
++)
2466 strncpy (title
, wv
->name
, 255);
2469 menu
= NewMenu (id
, title
);
2472 fill_menu (menu
, wv
->contents
);
2474 InsertMenu (menu
, 0);
2478 #endif /* HAVE_MENUS */
2484 staticpro (&menu_items
);
2487 Qdebug_on_next_call
= intern ("debug-on-next-call");
2488 staticpro (&Qdebug_on_next_call
);
2490 DEFVAR_LISP ("menu-updating-frame", &Vmenu_updating_frame
,
2491 doc
: /* Frame for which we are updating a menu.
2492 The enable predicate for a menu command should check this variable. */);
2493 Vmenu_updating_frame
= Qnil
;
2495 defsubr (&Sx_popup_menu
);
2497 defsubr (&Sx_popup_dialog
);
2501 /* arch-tag: 40b2c6c7-b8a9-4a49-b930-1b2707184cce
2502 (do not change this comment) */