XEmacs 21.2-b1
[chise/xemacs-chise.git.1] / src / menubar-msw.c
1 /* Implements an elisp-programmable menubar -- Win32
2    Copyright (C) 1993, 1994 Free Software Foundation, Inc.
3    Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
4    Copyright (C) 1997 Kirill M. Katsnelson <kkm@kis.ru>
5
6 This file is part of XEmacs.
7
8 XEmacs is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 2, or (at your option) any
11 later version.
12
13 XEmacs is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with XEmacs; see the file COPYING.  If not, write to
20 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA.  */
22
23 /* Synched up with: Not in FSF. */
24
25 /* Autorship:
26    Initially written by kkm 12/24/97,
27    peeking into and copying stuff from menubar-x.c
28    */
29
30 /* Algotirhm for handling menus is as follows. When window's menubar
31  * is created, current-menubar is not traversed in depth. Rather, only
32  * top level items, both items and pulldowns, are added to the
33  * menubar. Each pulldown is initially empty. When a pulldown is
34  * selected and about to open, corresponding element of
35  * current-menubar is found, and the newly open pulldown is
36  * populated. This is made again in the same non-recursive manner.
37  *
38  * This algorithm uses hash tables to find out element of the menu
39  * descriptor list given menu handle. The key is an opaque ptr data
40  * type, keeping menu handle, and the value is a list of strings
41  * representing the path from the root of the menu to the item
42  * descriptor. Each frame has an associated hashtable.
43  *
44  * Leaf items are assigned a unique id based on item's hash. When an
45  * item is selected, Windows sends back the id. Unfortunately, only
46  * low 16 bit of the ID are sent, and there's no way to get the 32-bit
47  * value. Yes, Win32 is just a different set of bugs than X! Aside
48  * from this blame, another hasing mechanism is required to map menu
49  * ids to commands (which are actually Lisp_Object's). This mapping is
50  * performed in the same hashtable, as the lifetime of both maps is
51  * exactly the same. This is unabmigous, as menu handles are
52  * represented by lisp opaques, while command ids are by lisp
53  * integers. The additional advantage for this is that command forms
54  * are automatically GC-protected, which is important because these
55  * may be transient forms generated by :filter functions.
56  *
57  * The hashtable is not allowed to grow too much; it is pruned
58  * whenever this is safe to do. This is done by re-creating the menu
59  * bar, and clearing and refilling the hash table from scratch.
60  *
61  * Popup menus are handled identially to pulldowns. A static hash
62  * table is used for popup menus, and lookup is made not in
63  * current-menubar but in a lisp form supplied to the `popup'
64  * function.
65  *
66  * Another Windows weirdness is that there's no way to tell that a
67  * popup has been dismissed without making selection. We need to know
68  * that to cleanup the popup menu hashtable, but this is not honestly
69  * doable using *documented* sequence of messages. Sticking to
70  * particular knowledge is bad because this may break in Windows NT
71  * 5.0, or Windows 98, or other future version. Instead, I allow the
72  * hashtables to hang around, and not clear them, unless WM_COMMAND is
73  * received. This is worthy some memory but more safe. Hacks welcome,
74  * anyways!
75  *
76  */
77
78 #include <config.h>
79 #include "lisp.h"
80 #include <limits.h>
81
82 #include "buffer.h"
83 #include "commands.h"
84 #include "console-msw.h"
85 #include "elhash.h"
86 #include "events.h"
87 #include "frame.h"
88 #include "gui.h"
89 #include "lisp.h"
90 #include "menubar.h"
91 #include "menubar-msw.h"
92 #include "opaque.h"
93 #include "window.h"
94
95 /* #### */
96 #define REPLACE_ME_WITH_GLOBAL_VARIABLE_WHICH_CONTROLS_RIHGT_FLUSH 0
97
98 #define EMPTY_ITEM_ID ((UINT)LISP_TO_VOID (Qunbound))
99 #define EMPTY_ITEM_NAME "(empty)"
100
101 /* Current menu (bar or popup) descriptor. gcpro'ed */
102 static Lisp_Object current_menudesc;
103
104 /* Current menubar or popup hashtable. gcpro'ed */
105 static Lisp_Object current_hashtable;
106
107 /* This is used to allocate unique ids to menu items.
108    Items ids are in MENU_ITEM_ID_MIN to MENU_ITEM_ID_MAX.
109    Allocation checks that the item is not already in
110    the TOP_LEVEL_MENU */
111
112 /* #### defines go to gui-msw.h, as the range is shared with toolbars
113    (If only toolbars will be implemented as common controls) */
114 #define MENU_ITEM_ID_MIN 0x8000
115 #define MENU_ITEM_ID_MAX 0xFFFF
116 #define MENU_ITEM_ID_BITS(x) (((x) & 0x7FFF) | 0x8000)
117 static HMENU top_level_menu;
118
119 #define MAX_MENUITEM_LENGTH 128
120
121 /*
122  * This returns Windows-style menu item string:
123  * "Left Flush\tRight Flush"
124  */
125 static char*
126 displayable_menu_item (struct gui_item* pgui_item)
127 {
128   /* We construct the name in a static buffer. That's fine, beause
129      menu items longer than 128 chars are probably programming errors,
130      and better be caught than displayed! */
131   
132   static char buf[MAX_MENUITEM_LENGTH+2];
133   unsigned int ll, lr;
134
135   /* Left flush part of the string */
136   ll = gui_item_display_flush_left (pgui_item, buf, MAX_MENUITEM_LENGTH);
137
138   /* Right flush part */
139   assert (MAX_MENUITEM_LENGTH > ll + 1);
140   lr = gui_item_display_flush_right (pgui_item, buf + ll + 1,
141                                      MAX_MENUITEM_LENGTH - ll - 1);
142   if (lr)
143     buf [ll] = '\t';
144
145   return buf;
146 }
147
148 /*
149  * hmenu_to_lisp_object() returns an opaque ptr given menu handle.
150  */
151 static Lisp_Object
152 hmenu_to_lisp_object (HMENU hmenu)
153 {
154   return make_opaque_ptr (hmenu);
155 }
156
157 /*
158  * Allocation tries a hash based on item's path and name first. This
159  * almost guarantees that the same item will override its old value in
160  * the hashtable rather than abandon it.
161  */
162 static Lisp_Object
163 allocate_menu_item_id (Lisp_Object path, Lisp_Object name, Lisp_Object suffix)
164 {
165   UINT id = MENU_ITEM_ID_BITS (HASH3 (internal_hash (path, 0),
166                                       internal_hash (name, 0),
167                                       internal_hash (suffix, 0)));
168   do {
169       id = MENU_ITEM_ID_BITS (id + 1);
170   } while (GetMenuState (top_level_menu, id, MF_BYCOMMAND) != 0xFFFFFFFF);
171   return make_int (id);
172 }
173
174 static HMENU
175 create_empty_popup_menu (void)
176 {
177   return CreatePopupMenu ();
178 }
179
180 static void
181 empty_menu (HMENU menu, int add_empty_p)
182 {
183   while (DeleteMenu (menu, 0, MF_BYPOSITION));
184   if (add_empty_p)
185     AppendMenu (menu, MF_STRING | MF_GRAYED, EMPTY_ITEM_ID, EMPTY_ITEM_NAME);
186 }
187
188 /*
189  * The idea of checksumming is that we must hash minimal object
190  * which is neccessarily changes when the item changes. For separator
191  * this is a constant, for grey strings and submenus these are hashes
192  * of names, since sumbenus are unpopulated until opened so always
193  * equal otherwise. For items, this is a full hash value of a callback,
194  * because a callback may me a form which can be changed only somewhere
195  * in depth.
196  */
197 static unsigned long
198 checksum_menu_item (Lisp_Object item)
199 {
200   if (STRINGP (item))
201     {
202       /* Separator or unselectable text - hash as a string + 13 */
203       if (separator_string_p (XSTRING_DATA (item)))
204         return 13;
205       else
206         return internal_hash (item, 0) + 13;
207     }
208   else if (CONSP (item))
209     {
210       /* Submenu - hash by its string name + 0 */
211       return internal_hash (XCAR(item), 0);
212     }
213   else if (VECTORP (item))
214     {
215       /* An ordinary item - hash its name and callback form. */
216       return HASH2 (internal_hash (XVECTOR_DATA(item)[0], 0),
217                     internal_hash (XVECTOR_DATA(item)[1], 0));
218     }
219  
220   /* An error - will be caught later */
221   return 0;
222 }
223
224 static void
225 populate_menu_add_item (HMENU menu, Lisp_Object path,
226                         Lisp_Object hash_tab, Lisp_Object item, int flush_right)
227 {
228   MENUITEMINFO item_info;
229
230   item_info.cbSize = sizeof (item_info);
231   item_info.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
232   item_info.fState = 0;
233   item_info.wID = 0;
234   item_info.fType = 0;
235
236   if (STRINGP (item))
237     {
238       /* Separator or unselectable text */
239       if (separator_string_p (XSTRING_DATA (item)))
240         item_info.fType = MFT_SEPARATOR;
241       else
242         {
243           item_info.fType = MFT_STRING;
244           item_info.fState = MFS_DISABLED;
245           item_info.dwTypeData = XSTRING_DATA (item);
246         }
247     }
248   else if (CONSP (item))
249     {
250       /* Submenu */
251       HMENU submenu;
252       struct gui_item gui_item;
253       struct gcpro gcpro1;
254
255       gui_item_init (&gui_item);
256       GCPRO_GUI_ITEM (&gui_item);
257
258       menu_parse_submenu_keywords (item, &gui_item);
259
260       if (!STRINGP (gui_item.name))
261         signal_simple_error ("Menu name (first element) must be a string", item);
262
263       if (!gui_item_included_p (&gui_item, Vmenubar_configuration))
264         return;
265
266       if (!gui_item_active_p (&gui_item))
267         item_info.fState = MFS_GRAYED;
268       /* Temptation is to put 'else' right here. Although, the
269          displayed item won't have an arrow indicating that it is a
270          popup.  So we go ahead a little bit more and create a popup */
271       submenu = create_empty_popup_menu();
272
273       item_info.fMask |= MIIM_SUBMENU;
274       item_info.dwTypeData = displayable_menu_item (&gui_item);
275       item_info.hSubMenu = submenu;
276
277       if (!(item_info.fState & MFS_GRAYED))
278         {
279           /* Now add the full submenu path as a value to the hash table,
280              keyed by menu handle */
281           if (NILP(path))
282             /* list1 cannot GC */
283             path = list1 (gui_item.name);
284           else
285             {
286               Lisp_Object arg[2];
287               arg[0] = path;
288               arg[1] = list1 (gui_item.name);
289               /* Fappend gcpro'es its arg */
290               path = Fappend (2, arg);
291             }
292
293           /* Fputhash GCPRO'es PATH */
294           Fputhash (hmenu_to_lisp_object (submenu), path, hash_tab);
295         }
296       UNGCPRO; /* gui_item */
297     } 
298   else if (VECTORP (item))
299     {
300       /* An ordinary item */
301       Lisp_Object style, id;
302       struct gui_item gui_item;
303       struct gcpro gcpro1;
304
305       gui_item_init (&gui_item);
306       GCPRO_GUI_ITEM (&gui_item);
307
308       gui_parse_item_keywords (item, &gui_item);
309
310       if (!gui_item_included_p (&gui_item, Vmenubar_configuration))
311         return;
312
313       if (!gui_item_active_p (&gui_item))
314         item_info.fState = MFS_GRAYED;
315
316       style = (NILP (gui_item.selected) || NILP (Feval (gui_item.selected))
317                ? Qnil : gui_item.style);
318
319       if (EQ (style, Qradio))
320         {
321           item_info.fType |= MFT_RADIOCHECK;
322           item_info.fState |= MFS_CHECKED;
323         }
324       else if (EQ (style, Qtoggle))
325         {
326           item_info.fState |= MFS_CHECKED;
327         }
328
329       id = allocate_menu_item_id (path, gui_item.name,
330                                   gui_item.suffix);
331       Fputhash (id, gui_item.callback, hash_tab);
332
333       item_info.wID = (UINT) XINT(id);
334       item_info.fType |= MFT_STRING;
335       item_info.dwTypeData = displayable_menu_item (&gui_item);
336
337       UNGCPRO; /* gui_item */
338     }
339   else
340     {
341       signal_simple_error ("Mailformed menu item descriptor", item);
342     }
343
344   if (flush_right)
345     item_info.fType |= MFT_RIGHTJUSTIFY;
346
347   InsertMenuItem (menu, UINT_MAX, TRUE, &item_info);
348 }  
349
350 /*
351  * This function is called from populate_menu and checksum_menu.
352  * When called to populate, MENU is a menu handle, PATH is a
353  * list of strings representing menu path from root to this submenu,
354  * DESCRIPTOR is a menu descriptor, HASH_TAB is a hashtable associated
355  * with root menu, BAR_P indicates whether this called for a menubar or
356  * a popup, and POPULATE_P is non-zero. Return value must be ignored.
357  * When called to checksum, DESCRIPTOR has the same meaning, POPULATE_P
358  * is zero, PATH must be Qnil, and the rest of parameters is ignored.
359  * Return value is the menu checksum.
360  */
361 static unsigned long
362 populate_or_checksum_helper (HMENU menu, Lisp_Object path, Lisp_Object desc,
363                              Lisp_Object hash_tab, int bar_p, int populate_p)
364 {
365   Lisp_Object item_desc;
366   int deep_p, flush_right;
367   struct gcpro gcpro1;
368   unsigned long checksum;
369   struct gui_item gui_item;
370
371   gui_item_init (&gui_item);
372   GCPRO_GUI_ITEM (&gui_item);
373
374   /* We are sometimes called with the menubar unchanged, and with changed
375      right flush. We have to update the menubar in ths case,
376      so account for the compliance setting in the hash value */
377   checksum = REPLACE_ME_WITH_GLOBAL_VARIABLE_WHICH_CONTROLS_RIHGT_FLUSH;
378
379   /* Will initially contain only "(empty)" */
380   if (populate_p)
381     empty_menu (menu, 1);
382
383   /* PATH set to nil indicates top-level popup or menubar */
384   deep_p = !NILP (path);
385
386   /* Fetch keywords prepending the item list */
387   desc = menu_parse_submenu_keywords (desc, &gui_item);
388
389   /* Check that menu name is specified when expected */
390   if (NILP (gui_item.name) && deep_p)
391     signal_simple_error ("Menu must have a name", desc);
392
393   /* Apply filter if specified */
394   if (!NILP (gui_item.filter))
395     desc = call1 (gui_item.filter, desc);
396
397   /* Loop thru the desc's CDR and add items for each entry */
398   flush_right = 0;
399   EXTERNAL_LIST_LOOP (item_desc, desc)
400     {
401       if (NILP (XCAR (item_desc)))
402         {
403           /* Do not flush right menubar items when MS style compiant */
404           if (bar_p && !REPLACE_ME_WITH_GLOBAL_VARIABLE_WHICH_CONTROLS_RIHGT_FLUSH)
405             flush_right = 1;
406           if (!populate_p)
407             checksum = HASH2 (checksum, LISP_HASH (Qnil));
408         }
409       else if (populate_p)
410         populate_menu_add_item (menu, path, hash_tab,
411                                 XCAR (item_desc), flush_right);
412       else
413         checksum = HASH2 (checksum,
414                           checksum_menu_item (XCAR (item_desc)));
415     }
416   
417   if (populate_p)
418     {
419       /* Remove the "(empty)" item, if there are other ones */
420       if (GetMenuItemCount (menu) > 1)
421         RemoveMenu (menu, EMPTY_ITEM_ID, MF_BYCOMMAND);
422
423       /* Add the header to the popup, if told so. The same as in X - an
424          insensitive item, and a separator (Seems to me, there were
425          two separators in X... In Windows this looks ugly, anywats. */
426       if (!bar_p && !deep_p && popup_menu_titles && !NILP(gui_item.name))
427         {
428           CHECK_STRING (gui_item.name);
429           InsertMenu (menu, 0, MF_BYPOSITION | MF_STRING | MF_DISABLED,
430                       0, XSTRING_DATA(gui_item.name));
431           InsertMenu (menu, 1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
432           SetMenuDefaultItem (menu, 0, MF_BYPOSITION);
433         }
434     }
435   UNGCPRO; /* gui_item */
436   return checksum;
437 }
438
439 static void
440 populate_menu (HMENU menu, Lisp_Object path, Lisp_Object desc,
441                              Lisp_Object hash_tab, int bar_p)
442 {
443   populate_or_checksum_helper (menu, path, desc, hash_tab, bar_p, 1);
444 }
445
446 static unsigned long
447 checksum_menu (Lisp_Object desc)
448 {
449   return populate_or_checksum_helper (NULL, Qnil, desc, Qunbound, 0, 0);
450 }
451
452 static void
453 update_frame_menubar_maybe (struct frame* f)
454 {
455   HMENU menubar = GetMenu (FRAME_MSWINDOWS_HANDLE (f));
456   struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f));
457   Lisp_Object desc = (!NILP (w->menubar_visible_p)
458                       ? symbol_value_in_buffer (Qcurrent_menubar, w->buffer)
459                       : Qnil);
460
461   top_level_menu = menubar;
462
463   if (NILP (desc) && menubar != NULL)
464     {
465       /* Menubar has gone */
466       FRAME_MSWINDOWS_MENU_HASHTABLE(f) = Qnil;
467       SetMenu (FRAME_MSWINDOWS_HANDLE (f), NULL);
468       DestroyMenu (menubar);
469       DrawMenuBar (FRAME_MSWINDOWS_HANDLE (f));
470       return;
471     }
472
473   if (!NILP (desc) && menubar == NULL)
474     {
475       /* Menubar has appeared */
476       menubar = CreateMenu ();
477       goto populate;
478     }
479
480   if (NILP (desc))
481     {
482       /* We did not have the bar and are not going to */
483       return;
484     }
485
486   /* Now we bail out if the menubar has not changed */
487   if (FRAME_MSWINDOWS_MENU_CHECKSUM(f) == checksum_menu (desc))
488     return;
489
490 populate:
491   /* Come with empty hash table */
492   if (NILP (FRAME_MSWINDOWS_MENU_HASHTABLE(f)))
493     FRAME_MSWINDOWS_MENU_HASHTABLE(f) = Fmake_hashtable (make_int (50), Qequal);
494   else
495     Fclrhash (FRAME_MSWINDOWS_MENU_HASHTABLE(f));
496
497   Fputhash (hmenu_to_lisp_object (menubar), Qnil,
498             FRAME_MSWINDOWS_MENU_HASHTABLE(f));
499   populate_menu (menubar, Qnil, desc,
500                  FRAME_MSWINDOWS_MENU_HASHTABLE(f), 1);
501   SetMenu (FRAME_MSWINDOWS_HANDLE (f), menubar);
502   DrawMenuBar (FRAME_MSWINDOWS_HANDLE (f));
503
504   FRAME_MSWINDOWS_MENU_CHECKSUM(f) = checksum_menu (desc);
505 }
506
507 static void
508 prune_menubar (struct frame *f)
509 {
510   HMENU menubar = GetMenu (FRAME_MSWINDOWS_HANDLE (f));
511   Lisp_Object desc = current_frame_menubar (f);
512   if (menubar == NULL)
513     return;
514
515   /* #### If a filter function has set desc to Qnil, this abort()
516      triggers. To resolve, we must prevent filters explicitely from
517      mangling with the active menu. In apply_filter probably?
518      Is copy-tree on the whole menu too expensive? */
519   if (NILP(desc))
520     /* abort(); */
521     return;
522
523   /* We do the trick by removing all items and re-populating top level */
524   empty_menu (menubar, 0);
525
526   assert (HASHTABLEP (FRAME_MSWINDOWS_MENU_HASHTABLE(f)));
527   Fclrhash (FRAME_MSWINDOWS_MENU_HASHTABLE(f));
528
529   Fputhash (hmenu_to_lisp_object (menubar), Qnil,
530             FRAME_MSWINDOWS_MENU_HASHTABLE(f));
531   populate_menu (menubar, Qnil, desc, 
532                  FRAME_MSWINDOWS_MENU_HASHTABLE(f), 1);
533 }
534
535 /*
536  * This is called when cleanup is possible. It is better not to
537  * clean things up at all than do it too earaly!
538  */
539 static void
540 menu_cleanup (struct frame *f)
541 {
542   /* This function can GC */
543   current_menudesc = Qnil;
544   current_hashtable = Qnil;
545   prune_menubar (f);
546 }
547   
548 \f
549 /*------------------------------------------------------------------------*/
550 /* Message handlers                                                       */
551 /*------------------------------------------------------------------------*/
552 static Lisp_Object
553 unsafe_handle_wm_initmenupopup_1 (HMENU menu, struct frame* f)
554 {
555   /* This function can call lisp, beat dogs and stick chewing gum to
556      everything! */
557
558   Lisp_Object path, desc;
559   struct gcpro gcpro1;
560
561   /* Find which guy is going to explode */
562   path = Fgethash (hmenu_to_lisp_object (menu), current_hashtable, Qunbound);
563   assert (!UNBOUNDP (path));
564 #ifdef DEBUG_XEMACS
565   /* Allow to continue in a debugger after assert - not so fatal */
566   if (UNBOUNDP (path))
567     error ("internal menu error");
568 #endif
569
570   /* Now find a desc chunk for it. If none, then probably menu open
571      hook has played too much games around stuff */
572   desc = Fmenu_find_real_submenu (current_menudesc, path);
573   if (NILP (desc))
574     signal_simple_error ("This menu does not exist any more", path);
575
576   /* Now, stuff it */
577   /* DESC may be generated by filter, so we have to gcpro it */
578   GCPRO1 (desc);
579   populate_menu (menu, path, desc, current_hashtable, 0);
580   UNGCPRO;
581   return Qt;
582 }
583
584 static Lisp_Object
585 unsafe_handle_wm_initmenu_1 (struct frame* f)
586 {
587   /* This function can call lisp */
588
589   /* NOTE: This is called for the bar only, WM_INITMENU
590      for popups is filtered out */
591
592   /* #### - this menubar update mechanism is expensively anti-social and
593      the activate-menubar-hook is now mostly obsolete. */
594
595   /* We simply ignore return value. In any case, we construct the bar
596      on the fly */
597   run_hook (Qactivate_menubar_hook);
598
599   update_frame_menubar_maybe (f);
600
601   current_menudesc = current_frame_menubar (f);
602   current_hashtable = FRAME_MSWINDOWS_MENU_HASHTABLE(f);
603   assert (HASHTABLEP (current_hashtable));
604
605   return Qt;
606 }
607
608 /*
609  * Return value is Qt if we have dispatched the command,
610  * or Qnil if id has not been mapped to a callback.
611  * Window procedure may try other targets to route the
612  * command if we return nil
613  */
614 Lisp_Object
615 mswindows_handle_wm_command (struct frame* f, WORD id)
616 {
617   /* Try to map the command id through the proper hash table */
618   Lisp_Object data, fn, arg, frame;
619   struct gcpro gcpro1;
620
621   data = Fgethash (make_int (id), current_hashtable, Qunbound);
622   if (UNBOUNDP (data))
623     {
624       menu_cleanup (f);
625       return Qnil;
626     }
627
628   /* Need to gcpro because the hashtable may get destroyed by
629      menu_cleanup(), and will not gcpro the data any more */
630   GCPRO1 (data);
631   menu_cleanup (f);
632
633   /* Ok, this is our one. Enqueue it. */
634   get_gui_callback (data, &fn, &arg);
635   XSETFRAME (frame, f);
636   /* this used to call mswindows_enqueue_misc_user_event but that
637      breaks customize because the misc_event gets eval'ed in some
638      cicumstances. Don't change it back unless you can fix the
639      customize problem also.*/
640   enqueue_misc_user_event (frame, fn, arg);
641   mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
642
643   UNGCPRO; /* data */
644   return Qt;
645 }
646
647 \f
648 /*------------------------------------------------------------------------*/
649 /* Message handling proxies                                               */
650 /*------------------------------------------------------------------------*/
651
652 static HMENU wm_initmenu_menu;
653 static struct frame* wm_initmenu_frame;
654
655 static Lisp_Object
656 unsafe_handle_wm_initmenupopup (Lisp_Object u_n_u_s_e_d)
657 {
658   return unsafe_handle_wm_initmenupopup_1 (wm_initmenu_menu, wm_initmenu_frame);
659 }
660
661 static Lisp_Object
662 unsafe_handle_wm_initmenu (Lisp_Object u_n_u_s_e_d)
663 {
664   return unsafe_handle_wm_initmenu_1 (wm_initmenu_frame);
665 }
666
667 Lisp_Object
668 mswindows_handle_wm_initmenupopup (HMENU hmenu, struct frame* frm)
669 {
670   /* We cannot pass hmenu as a lisp object. Use static var */
671   wm_initmenu_menu = hmenu;
672   wm_initmenu_frame = frm;
673   return mswindows_protect_modal_loop (unsafe_handle_wm_initmenupopup, Qnil);
674 }
675
676 Lisp_Object
677 mswindows_handle_wm_initmenu (HMENU hmenu, struct frame* f)
678 {
679   /* Handle only frame menubar, ignore if from popup or system menu */
680   if (GetMenu (FRAME_MSWINDOWS_HANDLE(f)) == hmenu)
681     {
682       wm_initmenu_frame = f;
683       return mswindows_protect_modal_loop (unsafe_handle_wm_initmenu, Qnil);
684     }
685   return Qt;
686 }
687
688 \f
689 /*------------------------------------------------------------------------*/
690 /* Methods                                                                */
691 /*------------------------------------------------------------------------*/
692
693 static void
694 mswindows_update_frame_menubars (struct frame* f)
695 {
696   update_frame_menubar_maybe (f);
697 }
698
699 static void
700 mswindows_free_frame_menubars (struct frame* f)
701 {
702   FRAME_MSWINDOWS_MENU_HASHTABLE(f) = Qnil;
703 }
704
705 static void
706 mswindows_popup_menu (Lisp_Object menu_desc, Lisp_Object event)
707 {
708   struct frame *f = selected_frame ();
709   struct Lisp_Event *eev = NULL;
710   HMENU menu;
711   POINT pt;
712   int ok;
713
714   if (!NILP (event))
715     {
716       CHECK_LIVE_EVENT (event);
717       eev = XEVENT (event);
718       if (eev->event_type != button_press_event
719           && eev->event_type != button_release_event)
720         wrong_type_argument (Qmouse_event_p, event);
721     }
722   else if (!NILP (Vthis_command_keys))
723     {
724       /* if an event wasn't passed, use the last event of the event sequence
725          currently being executed, if that event is a mouse event */
726       eev = XEVENT (Vthis_command_keys); /* last event first */
727       if (eev->event_type != button_press_event
728           && eev->event_type != button_release_event)
729         eev = NULL;
730     }
731
732   /* Default is to put the menu at the point (10, 10) in frame */
733   if (eev)
734     {
735       pt.x = eev->event.button.x;
736       pt.y = eev->event.button.y;
737       ClientToScreen (FRAME_MSWINDOWS_HANDLE (f), &pt);
738     }
739   else
740     pt.x = pt.y = 10;
741
742   if (SYMBOLP (menu_desc))
743     menu_desc = Fsymbol_value (menu_desc);
744   CHECK_CONS (menu_desc);
745   CHECK_STRING (XCAR (menu_desc));
746
747   current_menudesc = menu_desc;
748   current_hashtable = Fmake_hashtable (make_int(10), Qequal);
749   menu = create_empty_popup_menu();
750   Fputhash (hmenu_to_lisp_object (menu), Qnil, current_hashtable);
751   top_level_menu = menu;
752   
753   /* see comments in menubar-x.c */
754   if (zmacs_regions)
755     zmacs_region_stays = 1;
756   
757   ok = TrackPopupMenu (menu,
758                        TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
759                        pt.x, pt.y, 0,
760                        FRAME_MSWINDOWS_HANDLE (f), NULL);
761
762   DestroyMenu (menu);
763
764   /* Signal a signal if caught by Track...() modal loop */
765   mswindows_unmodalize_signal_maybe ();
766
767   /* This is probably the only real reason for failure */
768   if (!ok) {
769     menu_cleanup (f);
770     signal_simple_error ("Cannot track popup menu while in menu",
771                          menu_desc);
772   }
773 }
774
775 \f
776 /*------------------------------------------------------------------------*/
777 /* Initialization                                                         */
778 /*------------------------------------------------------------------------*/
779 void
780 syms_of_menubar_mswindows (void)
781 {
782 }
783
784 void
785 console_type_create_menubar_mswindows (void)
786 {
787   CONSOLE_HAS_METHOD (mswindows, update_frame_menubars);
788   CONSOLE_HAS_METHOD (mswindows, free_frame_menubars);
789   CONSOLE_HAS_METHOD (mswindows, popup_menu);
790 }
791
792 void
793 vars_of_menubar_mswindows (void)
794 {
795   current_menudesc = Qnil;
796   current_hashtable = Qnil;
797
798   staticpro (&current_menudesc);
799   staticpro (&current_hashtable);
800 }