This source file includes following definitions.
- have_boxes
- init_menu_items
- finish_menu_items
- unuse_menu_items
- discard_menu_items
- restore_menu_items
- save_menu_items
- ensure_menu_items
- push_submenu_start
- push_submenu_end
- push_left_right_boundary
- push_menu_pane
- push_menu_item
- single_keymap_panes
- single_menu_item
- keymap_panes
- encode_menu_string
- list_of_items
- list_of_panes
- parse_single_submenu
- make_widget_value
- free_menubar_widget_value_tree
- digest_single_submenu
- update_submenu_strings
- find_and_call_menu_selection
- find_and_return_menu_selection
- menu_item_width
- x_popup_menu_1
- emulate_dialog_with_menu
- syms_of_menu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 #include <config.h>
22 #include <limits.h>
23
24 #include "lisp.h"
25 #include "character.h"
26 #include "coding.h"
27 #include "dispextern.h"
28 #include "keyboard.h"
29 #include "keymap.h"
30 #include "frame.h"
31 #include "window.h"
32 #include "termhooks.h"
33 #include "blockinput.h"
34 #include "buffer.h"
35
36 #ifdef HAVE_WINDOW_SYSTEM
37 #include TERM_HEADER
38 #endif
39
40 #ifdef HAVE_NTGUI
41 extern AppendMenuW_Proc unicode_append_menu;
42 #endif
43
44 #include "menu.h"
45
46
47 static bool
48 have_boxes (void)
49 {
50 #if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NTGUI) || defined (HAVE_NS) \
51 || defined (HAVE_HAIKU) || defined (HAVE_ANDROID)
52 if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame)))
53 return 1;
54 #endif
55 return 0;
56 }
57
58 Lisp_Object menu_items;
59
60
61
62 bool menu_items_inuse;
63
64
65 int menu_items_allocated;
66
67
68 int menu_items_used;
69
70
71
72 int menu_items_n_panes;
73
74
75 static int menu_items_submenu_depth;
76
77 void
78 init_menu_items (void)
79 {
80 if (menu_items_inuse)
81 error ("Trying to use a menu from within a menu-entry");
82
83 if (NILP (menu_items))
84 {
85 menu_items_allocated = 60;
86 menu_items = make_nil_vector (menu_items_allocated);
87 }
88
89 menu_items_inuse = true;
90 menu_items_used = 0;
91 menu_items_n_panes = 0;
92 menu_items_submenu_depth = 0;
93 }
94
95
96
97 void
98 finish_menu_items (void)
99 {
100 }
101
102 void
103 unuse_menu_items (void)
104 {
105 menu_items_inuse = false;
106 }
107
108
109
110
111 void
112 discard_menu_items (void)
113 {
114
115
116 if (menu_items_allocated > 200)
117 {
118 menu_items = Qnil;
119 menu_items_allocated = 0;
120 }
121 eassert (!menu_items_inuse);
122 }
123
124
125
126
127 static void
128 restore_menu_items (Lisp_Object saved)
129 {
130 menu_items = XCAR (saved);
131 menu_items_inuse = ! NILP (menu_items);
132 menu_items_allocated = (VECTORP (menu_items) ? ASIZE (menu_items) : 0);
133 saved = XCDR (saved);
134 menu_items_used = XFIXNUM (XCAR (saved));
135 saved = XCDR (saved);
136 menu_items_n_panes = XFIXNUM (XCAR (saved));
137 saved = XCDR (saved);
138 menu_items_submenu_depth = XFIXNUM (XCAR (saved));
139 }
140
141
142
143
144 void
145 save_menu_items (void)
146 {
147 Lisp_Object saved = list4 (menu_items_inuse ? menu_items : Qnil,
148 make_fixnum (menu_items_used),
149 make_fixnum (menu_items_n_panes),
150 make_fixnum (menu_items_submenu_depth));
151 record_unwind_protect (restore_menu_items, saved);
152 menu_items_inuse = false;
153 menu_items = Qnil;
154 }
155
156
157
158
159 static void
160 ensure_menu_items (int items)
161 {
162 int incr = items - (menu_items_allocated - menu_items_used);
163 if (incr > 0)
164 {
165 menu_items = larger_vector (menu_items, incr, INT_MAX);
166 menu_items_allocated = ASIZE (menu_items);
167 }
168 }
169
170 #if defined HAVE_EXT_MENU_BAR || defined HAVE_ANDROID
171
172
173
174 static void
175 push_submenu_start (void)
176 {
177 ensure_menu_items (1);
178 ASET (menu_items, menu_items_used, Qnil);
179 menu_items_used++;
180 menu_items_submenu_depth++;
181 }
182
183
184
185 static void
186 push_submenu_end (void)
187 {
188 ensure_menu_items (1);
189 ASET (menu_items, menu_items_used, Qlambda);
190 menu_items_used++;
191 menu_items_submenu_depth--;
192 }
193
194 #endif
195
196
197
198 static void
199 push_left_right_boundary (void)
200 {
201 ensure_menu_items (1);
202 ASET (menu_items, menu_items_used, Qquote);
203 menu_items_used++;
204 }
205
206
207
208
209 static void
210 push_menu_pane (Lisp_Object name, Lisp_Object prefix_vec)
211 {
212 ensure_menu_items (MENU_ITEMS_PANE_LENGTH);
213 if (menu_items_submenu_depth == 0)
214 menu_items_n_panes++;
215 ASET (menu_items, menu_items_used, Qt);
216 menu_items_used++;
217 ASET (menu_items, menu_items_used, name);
218 menu_items_used++;
219 ASET (menu_items, menu_items_used, prefix_vec);
220 menu_items_used++;
221 }
222
223
224
225
226
227
228
229
230
231 static void
232 push_menu_item (Lisp_Object name, Lisp_Object enable, Lisp_Object key, Lisp_Object def, Lisp_Object equiv, Lisp_Object type, Lisp_Object selected, Lisp_Object help)
233 {
234 ensure_menu_items (MENU_ITEMS_ITEM_LENGTH);
235
236 ASET (menu_items, menu_items_used + MENU_ITEMS_ITEM_NAME, name);
237 ASET (menu_items, menu_items_used + MENU_ITEMS_ITEM_ENABLE, enable);
238 ASET (menu_items, menu_items_used + MENU_ITEMS_ITEM_VALUE, key);
239 ASET (menu_items, menu_items_used + MENU_ITEMS_ITEM_EQUIV_KEY, equiv);
240 ASET (menu_items, menu_items_used + MENU_ITEMS_ITEM_DEFINITION, def);
241 ASET (menu_items, menu_items_used + MENU_ITEMS_ITEM_TYPE, type);
242 ASET (menu_items, menu_items_used + MENU_ITEMS_ITEM_SELECTED, selected);
243 ASET (menu_items, menu_items_used + MENU_ITEMS_ITEM_HELP, help);
244
245 menu_items_used += MENU_ITEMS_ITEM_LENGTH;
246 }
247
248
249 struct skp
250 {
251 Lisp_Object pending_maps;
252 int maxdepth;
253 int notbuttons;
254 };
255
256 static void single_menu_item (Lisp_Object, Lisp_Object, Lisp_Object,
257 void *);
258
259
260
261
262
263
264
265
266 static void
267 single_keymap_panes (Lisp_Object keymap, Lisp_Object pane_name,
268 Lisp_Object prefix, int maxdepth)
269 {
270 struct skp skp;
271
272 skp.pending_maps = Qnil;
273 skp.maxdepth = maxdepth;
274 skp.notbuttons = 0;
275
276 if (maxdepth <= 0)
277 return;
278
279 push_menu_pane (pane_name, prefix);
280
281 if (!have_boxes ())
282 {
283
284
285
286
287 skp.notbuttons = menu_items_used;
288 }
289
290 map_keymap_canonical (keymap, single_menu_item, Qnil, &skp);
291
292
293 while (CONSP (skp.pending_maps))
294 {
295 Lisp_Object elt, eltcdr, string;
296 elt = XCAR (skp.pending_maps);
297 eltcdr = XCDR (elt);
298 string = XCAR (eltcdr);
299
300
301 single_keymap_panes (Fcar (elt), string, XCDR (eltcdr), maxdepth - 1);
302 skp.pending_maps = XCDR (skp.pending_maps);
303 }
304 }
305
306
307
308
309
310
311
312
313 static void
314 single_menu_item (Lisp_Object key, Lisp_Object item, Lisp_Object dummy, void *skp_v)
315 {
316 Lisp_Object map, item_string, enabled;
317 bool res;
318 struct skp *skp = skp_v;
319
320
321 res = parse_menu_item (item, 0);
322 if (!res)
323 return;
324
325 map = AREF (item_properties, ITEM_PROPERTY_MAP);
326
327 enabled = AREF (item_properties, ITEM_PROPERTY_ENABLE);
328 item_string = AREF (item_properties, ITEM_PROPERTY_NAME);
329
330 if (!NILP (map) && SREF (item_string, 0) == '@')
331 {
332 if (!NILP (enabled))
333
334 skp->pending_maps = Fcons (Fcons (map, Fcons (item_string, key)),
335 skp->pending_maps);
336 return;
337 }
338
339
340
341 if (!have_boxes ())
342 {
343 char const *prefix = 0;
344 Lisp_Object type = AREF (item_properties, ITEM_PROPERTY_TYPE);
345 if (!NILP (type))
346 {
347 Lisp_Object selected
348 = AREF (item_properties, ITEM_PROPERTY_SELECTED);
349
350 if (skp->notbuttons)
351
352 {
353 int idx = skp->notbuttons;
354 int submenu = 0;
355 Lisp_Object tem;
356 while (idx < menu_items_used)
357 {
358 tem
359 = AREF (menu_items, idx + MENU_ITEMS_ITEM_NAME);
360 if (NILP (tem))
361 {
362 idx++;
363 submenu++;
364 }
365 else if (EQ (tem, Qlambda))
366 {
367 idx++;
368 submenu--;
369 }
370 else if (EQ (tem, Qt))
371 idx += 3;
372 else if (EQ (tem, Qquote))
373 idx++;
374 else
375 {
376 if (!submenu && SREF (tem, 0) != '\0'
377 && SREF (tem, 0) != '-')
378 {
379 AUTO_STRING (spaces, " ");
380 ASET (menu_items, idx + MENU_ITEMS_ITEM_NAME,
381 concat2 (spaces, tem));
382 }
383 idx += MENU_ITEMS_ITEM_LENGTH;
384 }
385 }
386 skp->notbuttons = 0;
387 }
388
389
390 if (EQ (type, QCtoggle))
391 prefix = NILP (selected) ? "[ ] " : "[X] ";
392 else if (EQ (type, QCradio))
393 prefix = NILP (selected) ? "( ) " : "(*) ";
394 }
395
396 else if (!skp->notbuttons && SREF (item_string, 0) != '\0'
397 && SREF (item_string, 0) != '-')
398 prefix = " ";
399
400 if (prefix)
401 {
402 AUTO_STRING_WITH_LEN (prefix_obj, prefix, 4);
403 item_string = concat2 (prefix_obj, item_string);
404 }
405 }
406
407 if ((FRAME_TERMCAP_P (XFRAME (Vmenu_updating_frame))
408 || FRAME_MSDOS_P (XFRAME (Vmenu_updating_frame)))
409 && !NILP (map))
410
411 {
412 AUTO_STRING (space_gt, " >");
413 item_string = concat2 (item_string, space_gt);
414 }
415
416 push_menu_item (item_string, enabled, key,
417 AREF (item_properties, ITEM_PROPERTY_DEF),
418 AREF (item_properties, ITEM_PROPERTY_KEYEQ),
419 AREF (item_properties, ITEM_PROPERTY_TYPE),
420 AREF (item_properties, ITEM_PROPERTY_SELECTED),
421 AREF (item_properties, ITEM_PROPERTY_HELP));
422
423 #if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) \
424 || defined (HAVE_NTGUI) || defined (HAVE_HAIKU) || defined (HAVE_PGTK) \
425 || defined (HAVE_ANDROID)
426
427 if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame))
428 && ! (NILP (map) || NILP (enabled)))
429 {
430 push_submenu_start ();
431 single_keymap_panes (map, Qnil, key, skp->maxdepth - 1);
432 push_submenu_end ();
433 }
434 #endif
435 }
436
437
438
439
440 static void
441 keymap_panes (Lisp_Object *keymaps, ptrdiff_t nmaps)
442 {
443 ptrdiff_t mapno;
444
445 init_menu_items ();
446
447
448
449
450 for (mapno = 0; mapno < nmaps; mapno++)
451 single_keymap_panes (keymaps[mapno],
452 Fkeymap_prompt (keymaps[mapno]), Qnil, 10);
453
454 finish_menu_items ();
455 }
456
457
458 static Lisp_Object
459 encode_menu_string (Lisp_Object str)
460 {
461
462
463 if (FRAME_TERMCAP_P (XFRAME (Vmenu_updating_frame)))
464 return str;
465 return ENCODE_MENU_STRING (str);
466 }
467
468
469 static void
470 list_of_items (Lisp_Object pane)
471 {
472 Lisp_Object tail, item, item1;
473
474 for (tail = pane; CONSP (tail); tail = XCDR (tail))
475 {
476 item = XCAR (tail);
477 if (STRINGP (item))
478 push_menu_item (encode_menu_string (item), Qnil, Qnil, Qt,
479 Qnil, Qnil, Qnil, Qnil);
480 else if (CONSP (item))
481 {
482 item1 = XCAR (item);
483 CHECK_STRING (item1);
484 push_menu_item (encode_menu_string (item1), Qt, XCDR (item),
485 Qt, Qnil, Qnil, Qnil, Qnil);
486 }
487 else
488 push_left_right_boundary ();
489
490 }
491 }
492
493
494
495
496 void
497 list_of_panes (Lisp_Object menu)
498 {
499 Lisp_Object tail;
500
501 init_menu_items ();
502
503 for (tail = menu; CONSP (tail); tail = XCDR (tail))
504 {
505 Lisp_Object elt, pane_name, pane_data;
506 elt = XCAR (tail);
507 pane_name = Fcar (elt);
508 CHECK_STRING (pane_name);
509 push_menu_pane (encode_menu_string (pane_name), Qnil);
510 pane_data = Fcdr (elt);
511 CHECK_CONS (pane_data);
512 list_of_items (pane_data);
513 }
514
515 finish_menu_items ();
516 }
517
518
519
520
521 bool
522 parse_single_submenu (Lisp_Object item_key, Lisp_Object item_name,
523 Lisp_Object maps)
524 {
525 Lisp_Object *mapvec;
526 bool top_level_items = 0;
527 USE_SAFE_ALLOCA;
528
529 ptrdiff_t len = list_length (maps);
530
531
532 SAFE_ALLOCA_LISP (mapvec, len);
533 for (ptrdiff_t i = 0; i < len; i++)
534 {
535 mapvec[i] = Fcar (maps);
536 maps = Fcdr (maps);
537 }
538
539
540
541 for (ptrdiff_t i = 0; i < len; i++)
542 {
543 if (!KEYMAPP (mapvec[i]))
544 {
545
546
547 top_level_items = 1;
548 push_menu_pane (Qnil, Qnil);
549 push_menu_item (item_name, Qt, item_key, mapvec[i],
550 Qnil, Qnil, Qnil, Qnil);
551 }
552 else
553 {
554 Lisp_Object prompt;
555 prompt = Fkeymap_prompt (mapvec[i]);
556 single_keymap_panes (mapvec[i],
557 !NILP (prompt) ? prompt : item_name,
558 item_key, 10);
559 }
560 }
561
562 SAFE_FREE ();
563 return top_level_items;
564 }
565
566
567 #if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) || defined (HAVE_NTGUI)
568
569
570
571 widget_value *
572 make_widget_value (const char *name, char *value,
573 bool enabled, Lisp_Object help)
574 {
575 widget_value *wv;
576
577 block_input ();
578 wv = xzalloc (sizeof (widget_value));
579 unblock_input ();
580
581 wv->name = (char *) name;
582 wv->value = value;
583 wv->enabled = enabled;
584 wv->help = help;
585 return wv;
586 }
587
588
589
590
591
592
593 void
594 free_menubar_widget_value_tree (widget_value *wv)
595 {
596 if (! wv) return;
597
598 wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
599
600 if (wv->contents && (wv->contents != (widget_value *) 1))
601 {
602 free_menubar_widget_value_tree (wv->contents);
603 wv->contents = (widget_value *) 0xDEADBEEF;
604 }
605 if (wv->next)
606 {
607 free_menubar_widget_value_tree (wv->next);
608 wv->next = (widget_value *) 0xDEADBEEF;
609 }
610 block_input ();
611 xfree (wv);
612 unblock_input ();
613 }
614
615
616
617
618
619 widget_value *
620 digest_single_submenu (int start, int end, bool top_level_items)
621 {
622 widget_value *wv, *prev_wv, *save_wv, *first_wv;
623 int i;
624 int submenu_depth = 0;
625 widget_value **submenu_stack;
626 bool panes_seen = 0;
627 struct frame *f = XFRAME (Vmenu_updating_frame);
628 USE_SAFE_ALLOCA;
629
630 SAFE_NALLOCA (submenu_stack, 1, menu_items_used);
631 wv = make_widget_value ("menu", NULL, true, Qnil);
632 wv->button_type = BUTTON_TYPE_NONE;
633 first_wv = wv;
634 save_wv = 0;
635 prev_wv = 0;
636
637
638
639
640
641 i = start;
642 while (i < end)
643 {
644 if (NILP (AREF (menu_items, i)))
645 {
646 submenu_stack[submenu_depth++] = save_wv;
647 save_wv = prev_wv;
648 prev_wv = 0;
649 i++;
650 }
651 else if (EQ (AREF (menu_items, i), Qlambda))
652 {
653 prev_wv = save_wv;
654 save_wv = submenu_stack[--submenu_depth];
655 i++;
656 }
657 else if (EQ (AREF (menu_items, i), Qt)
658 && submenu_depth != 0)
659 i += MENU_ITEMS_PANE_LENGTH;
660
661
662 else if (EQ (AREF (menu_items, i), Qquote))
663 i += 1;
664 else if (EQ (AREF (menu_items, i), Qt))
665 {
666
667 Lisp_Object pane_name;
668 const char *pane_string;
669
670 panes_seen = 1;
671
672 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
673
674
675
676 if (!FRAME_TERMCAP_P (f))
677 {
678 #ifdef HAVE_NTGUI
679 if (STRINGP (pane_name))
680 {
681 if (unicode_append_menu)
682
683 pane_name = ENCODE_UTF_8 (pane_name);
684 else if (STRING_MULTIBYTE (pane_name))
685 pane_name = ENCODE_SYSTEM (pane_name);
686
687 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
688 }
689 #elif defined (USE_LUCID) && (defined USE_CAIRO || defined HAVE_XFT)
690 if (STRINGP (pane_name))
691 {
692 pane_name = ENCODE_UTF_8 (pane_name);
693 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
694 }
695 #elif !defined (HAVE_MULTILINGUAL_MENU)
696 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
697 {
698 pane_name = ENCODE_MENU_STRING (pane_name);
699 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
700 }
701 #endif
702 }
703
704 pane_string = (NILP (pane_name)
705 ? "" : SSDATA (pane_name));
706
707
708 if (menu_items_n_panes == 1)
709 pane_string = "";
710
711
712
713
714 if (strcmp (pane_string, ""))
715 {
716
717 wv = make_widget_value (NULL, (char *) 1, true, Qnil);
718 if (save_wv)
719 save_wv->next = wv;
720 else
721 first_wv->contents = wv;
722 wv->lname = pane_name;
723 wv->button_type = BUTTON_TYPE_NONE;
724 save_wv = wv;
725 }
726 else
727 save_wv = first_wv;
728
729 prev_wv = 0;
730 i += MENU_ITEMS_PANE_LENGTH;
731 }
732 else
733 {
734
735 Lisp_Object item_name, enable, descrip, def, type, selected;
736 Lisp_Object help;
737
738
739 if (! panes_seen)
740 emacs_abort ();
741
742 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
743 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
744 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
745 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
746 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
747 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
748 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
749
750
751
752 if (!FRAME_TERMCAP_P (f))
753 {
754 #ifdef HAVE_NTGUI
755 if (STRINGP (item_name))
756 {
757 if (unicode_append_menu)
758 item_name = ENCODE_UTF_8 (item_name);
759 else if (STRING_MULTIBYTE (item_name))
760 item_name = ENCODE_SYSTEM (item_name);
761
762 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
763 }
764
765 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
766 {
767 descrip = ENCODE_SYSTEM (descrip);
768 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
769 }
770 #elif USE_LUCID
771 if (STRINGP (item_name))
772 {
773 item_name = ENCODE_UTF_8 (item_name);
774 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
775 }
776
777 if (STRINGP (descrip))
778 {
779 descrip = ENCODE_UTF_8 (descrip);
780 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
781 }
782 #elif !defined (HAVE_MULTILINGUAL_MENU)
783 if (STRING_MULTIBYTE (item_name))
784 {
785 item_name = ENCODE_MENU_STRING (item_name);
786 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
787 }
788
789 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
790 {
791 descrip = ENCODE_MENU_STRING (descrip);
792 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
793 }
794 #endif
795 }
796
797 wv = make_widget_value (NULL, NULL, !NILP (enable),
798 STRINGP (help) ? help : Qnil);
799 if (prev_wv)
800 prev_wv->next = wv;
801 else
802 save_wv->contents = wv;
803
804 wv->lname = item_name;
805 if (!NILP (descrip))
806 wv->lkey = descrip;
807
808
809 wv->call_data = (!NILP (def) ? (void *) (intptr_t) i : 0);
810
811 if (NILP (type))
812 wv->button_type = BUTTON_TYPE_NONE;
813 else if (EQ (type, QCradio))
814 wv->button_type = BUTTON_TYPE_RADIO;
815 else if (EQ (type, QCtoggle))
816 wv->button_type = BUTTON_TYPE_TOGGLE;
817 else
818 emacs_abort ();
819
820 wv->selected = !NILP (selected);
821
822 prev_wv = wv;
823
824 i += MENU_ITEMS_ITEM_LENGTH;
825 }
826 }
827
828
829
830 if (top_level_items && first_wv->contents && first_wv->contents->next == 0)
831 {
832 wv = first_wv;
833 first_wv = first_wv->contents;
834 xfree (wv);
835 }
836
837 SAFE_FREE ();
838 return first_wv;
839 }
840
841
842
843
844
845
846 void
847 update_submenu_strings (widget_value *first_wv)
848 {
849 widget_value *wv;
850
851 for (wv = first_wv; wv; wv = wv->next)
852 {
853 if (STRINGP (wv->lname))
854 {
855 wv->name = SSDATA (wv->lname);
856
857
858
859 if (wv->value == (char *)1)
860 {
861 if (wv->name[0] == '@')
862 wv->name++;
863 wv->value = 0;
864 }
865 }
866
867 if (STRINGP (wv->lkey))
868 wv->key = SSDATA (wv->lkey);
869
870 if (wv->contents)
871 update_submenu_strings (wv->contents);
872 }
873 }
874
875 #endif
876 #if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) \
877 || defined (HAVE_NTGUI) || defined (HAVE_HAIKU)
878
879
880
881
882
883
884 void
885 find_and_call_menu_selection (struct frame *f, int menu_bar_items_used,
886 Lisp_Object vector, void *client_data)
887 {
888 Lisp_Object prefix, entry;
889 Lisp_Object *subprefix_stack;
890 int submenu_depth = 0;
891 int i;
892 USE_SAFE_ALLOCA;
893
894 entry = Qnil;
895 SAFE_NALLOCA (subprefix_stack, 1, menu_bar_items_used);
896 prefix = Qnil;
897 i = 0;
898
899 while (i < menu_bar_items_used)
900 {
901 if (NILP (AREF (vector, i)))
902 {
903 subprefix_stack[submenu_depth++] = prefix;
904 prefix = entry;
905 i++;
906 }
907 else if (EQ (AREF (vector, i), Qlambda))
908 {
909 prefix = subprefix_stack[--submenu_depth];
910 i++;
911 }
912 else if (EQ (AREF (vector, i), Qt))
913 {
914 prefix = AREF (vector, i + MENU_ITEMS_PANE_PREFIX);
915 i += MENU_ITEMS_PANE_LENGTH;
916 }
917 else
918 {
919 entry = AREF (vector, i + MENU_ITEMS_ITEM_VALUE);
920
921
922 if ((intptr_t) client_data == i)
923 {
924 int j;
925 struct input_event buf;
926 Lisp_Object frame;
927 EVENT_INIT (buf);
928
929 XSETFRAME (frame, f);
930 buf.kind = MENU_BAR_EVENT;
931 buf.frame_or_window = frame;
932 buf.arg = frame;
933 kbd_buffer_store_event (&buf);
934
935 for (j = 0; j < submenu_depth; j++)
936 if (!NILP (subprefix_stack[j]))
937 {
938 buf.kind = MENU_BAR_EVENT;
939 buf.frame_or_window = frame;
940 buf.arg = subprefix_stack[j];
941 kbd_buffer_store_event (&buf);
942 }
943
944 if (!NILP (prefix))
945 {
946 buf.kind = MENU_BAR_EVENT;
947 buf.frame_or_window = frame;
948 buf.arg = prefix;
949 kbd_buffer_store_event (&buf);
950 }
951
952 buf.kind = MENU_BAR_EVENT;
953 buf.frame_or_window = frame;
954 buf.arg = entry;
955 kbd_buffer_store_event (&buf);
956
957 break;
958 }
959 i += MENU_ITEMS_ITEM_LENGTH;
960 }
961 }
962
963 SAFE_FREE ();
964 }
965
966 #endif
967
968 #ifdef HAVE_NS
969
970
971 Lisp_Object
972 find_and_return_menu_selection (struct frame *f, bool keymaps, void *client_data)
973 {
974 Lisp_Object prefix, entry;
975 int i;
976 Lisp_Object *subprefix_stack;
977 int submenu_depth = 0;
978 USE_SAFE_ALLOCA;
979
980 prefix = entry = Qnil;
981 i = 0;
982 SAFE_ALLOCA_LISP (subprefix_stack, menu_items_used);
983
984 while (i < menu_items_used)
985 {
986 if (NILP (AREF (menu_items, i)))
987 {
988 subprefix_stack[submenu_depth++] = prefix;
989 prefix = entry;
990 i++;
991 }
992 else if (EQ (AREF (menu_items, i), Qlambda))
993 {
994 prefix = subprefix_stack[--submenu_depth];
995 i++;
996 }
997 else if (EQ (AREF (menu_items, i), Qt))
998 {
999 prefix
1000 = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
1001 i += MENU_ITEMS_PANE_LENGTH;
1002 }
1003
1004
1005 else if (EQ (AREF (menu_items, i), Qquote))
1006 i += 1;
1007 else
1008 {
1009 entry
1010 = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
1011 if (aref_addr (menu_items, i) == client_data)
1012 {
1013 if (keymaps)
1014 {
1015 int j;
1016
1017 entry = list1 (entry);
1018 if (!NILP (prefix))
1019 entry = Fcons (prefix, entry);
1020 for (j = submenu_depth - 1; j >= 0; j--)
1021 if (!NILP (subprefix_stack[j]))
1022 entry = Fcons (subprefix_stack[j], entry);
1023 }
1024 SAFE_FREE ();
1025 return entry;
1026 }
1027 i += MENU_ITEMS_ITEM_LENGTH;
1028 }
1029 }
1030 SAFE_FREE ();
1031 return Qnil;
1032 }
1033 #endif
1034
1035 ptrdiff_t
1036 menu_item_width (const unsigned char *str)
1037 {
1038 ptrdiff_t len;
1039 const unsigned char *p;
1040
1041 for (len = 0, p = str; *p; )
1042 {
1043 int ch_len, ch = string_char_and_length (p, &ch_len);
1044 len += CHARACTER_WIDTH (ch);
1045 p += ch_len;
1046 }
1047 return len;
1048 }
1049
1050 DEFUN ("menu-bar-menu-at-x-y", Fmenu_bar_menu_at_x_y, Smenu_bar_menu_at_x_y,
1051 2, 3, 0,
1052 doc:
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069 )
1070 (Lisp_Object x, Lisp_Object y, Lisp_Object frame)
1071 {
1072 int row, col;
1073 struct frame *f = decode_any_frame (frame);
1074
1075 if (!FRAME_LIVE_P (f))
1076 return Qnil;
1077
1078 pixel_to_glyph_coords (f, XFIXNUM (x), XFIXNUM (y), &col, &row, NULL, 1);
1079 if (0 <= row && row < FRAME_MENU_BAR_LINES (f))
1080 {
1081 Lisp_Object items, item;
1082 int i;
1083
1084
1085 item = Qnil;
1086 items = FRAME_MENU_BAR_ITEMS (f);
1087
1088
1089
1090 for (i = 0; i < ASIZE (items); i += 4)
1091 {
1092 Lisp_Object pos, str;
1093
1094 str = AREF (items, i + 1);
1095 pos = AREF (items, i + 3);
1096 if (NILP (str))
1097 return item;
1098 if (XFIXNUM (pos) <= col
1099
1100
1101 && col <= XFIXNUM (pos) + menu_item_width (SDATA (str)))
1102 {
1103 item = AREF (items, i);
1104 return item;
1105 }
1106 }
1107 }
1108 return Qnil;
1109 }
1110
1111 Lisp_Object
1112 x_popup_menu_1 (Lisp_Object position, Lisp_Object menu)
1113 {
1114 Lisp_Object keymap, tem, tem2 = Qnil;
1115 int xpos = 0, ypos = 0;
1116 Lisp_Object title;
1117 const char *error_name = NULL;
1118 Lisp_Object selection = Qnil;
1119 struct frame *f;
1120 Lisp_Object x, y, window;
1121 int menuflags = 0;
1122 specpdl_ref specpdl_count = SPECPDL_INDEX ();
1123
1124 if (NILP (position))
1125
1126
1127 return Qnil;
1128
1129 {
1130 bool get_current_pos_p = 0;
1131
1132
1133 if (EQ (position, Qt)
1134 || (CONSP (position)
1135 && (EQ (XCAR (position), Qmenu_bar)
1136 || EQ (XCAR (position), Qtab_bar)
1137 || (CONSP (XCDR (position))
1138 && EQ (XCAR (XCDR (position)), Qtab_bar))
1139 || EQ (XCAR (position), Qtool_bar))))
1140 {
1141 get_current_pos_p = 1;
1142 }
1143 else
1144 {
1145 tem = Fcar (position);
1146 if (CONSP (tem))
1147 {
1148 window = Fcar (Fcdr (position));
1149 x = XCAR (tem);
1150 y = Fcar (XCDR (tem));
1151 }
1152 else
1153 {
1154 menuflags |= MENU_FOR_CLICK;
1155 tem = EVENT_START (position);
1156 window = Fcar (tem);
1157 tem2 = Fcar (Fcdr (tem));
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169 if (!EQ (POSN_POSN (last_nonmenu_event),
1170 POSN_POSN (position))
1171 && CONSP (tem2) && EQ (XCAR (tem2), Qmenu_bar))
1172 menuflags |= MENU_KBD_NAVIGATION;
1173 tem = Fcar (Fcdr (Fcdr (tem)));
1174 x = Fcar (tem);
1175 y = Fcdr (tem);
1176 }
1177
1178
1179
1180
1181
1182
1183 if (NILP (x) && NILP (y))
1184 get_current_pos_p = 1;
1185 }
1186
1187 if (get_current_pos_p)
1188 {
1189
1190 struct frame *new_f = SELECTED_FRAME ();
1191
1192 XSETFASTINT (x, 0);
1193 XSETFASTINT (y, 0);
1194 #ifdef HAVE_X_WINDOWS
1195 if (FRAME_X_P (new_f))
1196 {
1197
1198
1199
1200 if (new_f != 0)
1201 {
1202 int cur_x, cur_y;
1203
1204 x_relative_mouse_position (new_f, &cur_x, &cur_y);
1205
1206 x = make_fixnum (cur_x);
1207 y = make_fixnum (cur_y);
1208 }
1209 }
1210 else
1211 #endif
1212 {
1213 Lisp_Object bar_window;
1214 enum scroll_bar_part part;
1215 Time time;
1216 void (*mouse_position_hook) (struct frame **, int,
1217 Lisp_Object *,
1218 enum scroll_bar_part *,
1219 Lisp_Object *,
1220 Lisp_Object *,
1221 Time *) =
1222 FRAME_TERMINAL (new_f)->mouse_position_hook;
1223
1224 if (mouse_position_hook)
1225 (*mouse_position_hook) (&new_f, 1, &bar_window,
1226 &part, &x, &y, &time);
1227 }
1228
1229 if (new_f != 0)
1230 XSETFRAME (window, new_f);
1231 else
1232 {
1233 window = selected_window;
1234 XSETFASTINT (x, 0);
1235 XSETFASTINT (y, 0);
1236 }
1237 }
1238
1239
1240
1241 if (FRAMEP (window))
1242 {
1243 f = XFRAME (window);
1244 xpos = 0;
1245 ypos = 0;
1246 }
1247 else if (WINDOWP (window))
1248 {
1249 struct window *win = XWINDOW (window);
1250 CHECK_LIVE_WINDOW (window);
1251 f = XFRAME (WINDOW_FRAME (win));
1252
1253 if (FIXNUMP (tem2))
1254 {
1255
1256
1257
1258 xpos = window_box_left (win, TEXT_AREA);
1259 ypos = (WINDOW_TOP_EDGE_Y (win)
1260 + WINDOW_TAB_LINE_HEIGHT (win)
1261 + WINDOW_HEADER_LINE_HEIGHT (win));
1262 }
1263 else
1264 {
1265 xpos = WINDOW_LEFT_EDGE_X (win);
1266 ypos = WINDOW_TOP_EDGE_Y (win);
1267 }
1268 }
1269 else
1270
1271
1272 wrong_type_argument (Qwindowp, window);
1273
1274 xpos += check_integer_range (x,
1275 (xpos < INT_MIN - MOST_NEGATIVE_FIXNUM
1276 ? (EMACS_INT) INT_MIN - xpos
1277 : MOST_NEGATIVE_FIXNUM),
1278 INT_MAX - xpos);
1279 ypos += check_integer_range (y,
1280 (ypos < INT_MIN - MOST_NEGATIVE_FIXNUM
1281 ? (EMACS_INT) INT_MIN - ypos
1282 : MOST_NEGATIVE_FIXNUM),
1283 INT_MAX - ypos);
1284
1285 XSETFRAME (Vmenu_updating_frame, f);
1286 }
1287
1288
1289 record_unwind_protect_void (unuse_menu_items);
1290
1291 title = Qnil;
1292
1293
1294
1295 keymap = get_keymap (menu, 0, 0);
1296 if (CONSP (keymap))
1297 {
1298
1299 Lisp_Object prompt;
1300
1301
1302 keymap_panes (&menu, 1);
1303
1304
1305
1306 prompt = Fkeymap_prompt (keymap);
1307
1308 #if defined (USE_GTK) || defined (HAVE_NS)
1309 if (STRINGP (prompt)
1310 && SCHARS (prompt) > 0
1311 && !NILP (Fget_text_property (make_fixnum (0), Qhide, prompt)))
1312 title = Qnil;
1313 else
1314 #endif
1315 if (!NILP (prompt))
1316 title = prompt;
1317
1318
1319 if (!NILP (prompt) && menu_items_n_panes >= 0)
1320 ASET (menu_items, MENU_ITEMS_PANE_NAME, prompt);
1321
1322 menuflags |= MENU_KEYMAPS;
1323 }
1324 else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
1325 {
1326
1327 ptrdiff_t nmaps = list_length (menu);
1328 Lisp_Object *maps;
1329 ptrdiff_t i;
1330 USE_SAFE_ALLOCA;
1331
1332 SAFE_ALLOCA_LISP (maps, nmaps);
1333 title = Qnil;
1334
1335
1336
1337 for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
1338 {
1339 Lisp_Object prompt;
1340
1341 maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
1342
1343 prompt = Fkeymap_prompt (keymap);
1344 if (NILP (title) && !NILP (prompt))
1345 title = prompt;
1346 }
1347
1348
1349 keymap_panes (maps, nmaps);
1350
1351
1352 if (!NILP (title) && menu_items_n_panes >= 0)
1353 ASET (menu_items, MENU_ITEMS_PANE_NAME, title);
1354
1355 menuflags |= MENU_KEYMAPS;
1356
1357 SAFE_FREE ();
1358 }
1359 else
1360 {
1361
1362 title = Fcar (menu);
1363 CHECK_STRING (title);
1364
1365 list_of_panes (Fcdr (menu));
1366
1367 menuflags &= ~MENU_KEYMAPS;
1368 }
1369
1370 unbind_to (specpdl_count, Qnil);
1371
1372 #ifdef HAVE_WINDOW_SYSTEM
1373
1374 if (!FRAME_TERMCAP_P (f))
1375 Fx_hide_tip ();
1376 #endif
1377
1378 #ifdef HAVE_NTGUI
1379
1380
1381
1382
1383
1384 if (current_popup_menu && FRAME_W32_P (f))
1385 {
1386 discard_menu_items ();
1387 FRAME_DISPLAY_INFO (f)->grabbed = 0;
1388 return Qnil;
1389 }
1390 #endif
1391
1392 record_unwind_protect_void (discard_menu_items);
1393
1394 run_hook (Qx_pre_popup_menu_hook);
1395
1396 #ifdef HAVE_WINDOW_SYSTEM
1397
1398
1399
1400
1401
1402
1403
1404 cancel_hourglass ();
1405 #endif
1406
1407
1408
1409
1410 if (!FRAME_INITIAL_P (f))
1411 selection = FRAME_TERMINAL (f)->menu_show_hook (f, xpos, ypos, menuflags,
1412 title, &error_name);
1413
1414 unbind_to (specpdl_count, Qnil);
1415
1416 #ifdef HAVE_NTGUI
1417
1418
1419
1420
1421 if (FRAME_W32_P (f))
1422 FRAME_DISPLAY_INFO (f)->grabbed = 0;
1423 #endif
1424
1425 if (error_name) error ("%s", error_name);
1426 return selection;
1427 }
1428
1429 DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
1430 doc:
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472 )
1473 (Lisp_Object position, Lisp_Object menu)
1474 {
1475 init_raw_keybuf_count ();
1476 return x_popup_menu_1 (position, menu);
1477 }
1478
1479
1480
1481
1482 static Lisp_Object
1483 emulate_dialog_with_menu (struct frame *f, Lisp_Object contents)
1484 {
1485 Lisp_Object x, y, frame, newpos, prompt = Fcar (contents);
1486 int x_coord, y_coord;
1487
1488 if (FRAME_WINDOW_P (f))
1489 {
1490 x_coord = FRAME_PIXEL_WIDTH (f);
1491 y_coord = FRAME_PIXEL_HEIGHT (f);
1492 }
1493 else
1494 {
1495 x_coord = FRAME_COLS (f);
1496
1497
1498 if (STRINGP (prompt))
1499 x_coord -= SCHARS (prompt);
1500 y_coord = FRAME_TOTAL_LINES (f);
1501 }
1502
1503 XSETFRAME (frame, f);
1504 XSETINT (x, x_coord / 2);
1505 XSETINT (y, y_coord / 2);
1506 newpos = list2 (list2 (x, y), frame);
1507
1508 return Fx_popup_menu (newpos, list2 (prompt, contents));
1509 }
1510
1511 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
1512 doc:
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533 )
1534 (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
1535 {
1536 struct frame *f = NULL;
1537 Lisp_Object window;
1538
1539
1540 if (EQ (position, Qt)
1541 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1542 || EQ (XCAR (position), Qtab_bar)
1543 || EQ (XCAR (position), Qtool_bar))))
1544 window = selected_window;
1545 else if (CONSP (position))
1546 {
1547 Lisp_Object tem = XCAR (position);
1548 if (CONSP (tem))
1549 window = Fcar (XCDR (position));
1550 else
1551 {
1552 tem = Fcar (XCDR (position));
1553 window = Fcar (tem);
1554 }
1555 }
1556 else if (WINDOWP (position) || FRAMEP (position))
1557 window = position;
1558 else
1559 window = Qnil;
1560
1561
1562
1563 if (FRAMEP (window))
1564 f = XFRAME (window);
1565 else if (WINDOWP (window))
1566 {
1567 CHECK_LIVE_WINDOW (window);
1568 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
1569 }
1570 else
1571
1572
1573 CHECK_WINDOW (window);
1574
1575
1576
1577 eassume (f && FRAME_LIVE_P (f));
1578 XSETFRAME (Vmenu_updating_frame, f);
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589 Fredisplay (Qt);
1590
1591
1592 if (FRAME_TERMINAL (f)->popup_dialog_hook)
1593 {
1594 Lisp_Object selection
1595 = FRAME_TERMINAL (f)->popup_dialog_hook (f, header, contents);
1596 #ifdef HAVE_NTGUI
1597
1598
1599
1600 if (!EQ (selection, Qunsupported__w32_dialog))
1601 #endif
1602 return selection;
1603 }
1604
1605 return emulate_dialog_with_menu (f, contents);
1606 }
1607
1608 void
1609 syms_of_menu (void)
1610 {
1611 menu_items = Qnil;
1612 staticpro (&menu_items);
1613
1614 DEFSYM (Qhide, "hide");
1615 DEFSYM (Qx_pre_popup_menu_hook, "x-pre-popup-menu-hook");
1616
1617 DEFVAR_LISP ("x-pre-popup-menu-hook", Vx_pre_popup_menu_hook,
1618 doc:
1619
1620
1621 );
1622 Vx_pre_popup_menu_hook = Qnil;
1623
1624 defsubr (&Sx_popup_menu);
1625 defsubr (&Sx_popup_dialog);
1626 defsubr (&Smenu_bar_menu_at_x_y);
1627 }