Mercurial > hg > xemacs-beta
comparison src/menubar-msw.c @ 428:3ecd8885ac67 r21-2-22
Import from CVS: tag r21-2-22
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:28:15 +0200 |
parents | |
children | 8de8e3f6228a |
comparison
equal
deleted
inserted
replaced
427:0a0253eac470 | 428:3ecd8885ac67 |
---|---|
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 /* Author: | |
26 Initially written by kkm 12/24/97, | |
27 peeking into and copying stuff from menubar-x.c | |
28 */ | |
29 | |
30 /* Algorithm 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 hash table. | |
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 hashing mechanism is required to map menu | |
49 * ids to commands (which are actually Lisp_Object's). This mapping is | |
50 * performed in the same hash table, as the lifetime of both maps is | |
51 * exactly the same. This is unambigous, 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 hash table 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 identically 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 hash table, 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 * hash tables 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 hash table. gcpro'ed */ | |
105 static Lisp_Object current_hash_table; | |
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 /* | |
120 * This returns Windows-style menu item string: | |
121 * "Left Flush\tRight Flush" | |
122 */ | |
123 static char* | |
124 displayable_menu_item (Lisp_Object gui_item, int bar_p) | |
125 { | |
126 /* We construct the name in a static buffer. That's fine, because | |
127 menu items longer than 128 chars are probably programming errors, | |
128 and better be caught than displayed! */ | |
129 | |
130 static char buf[MAX_MENUITEM_LENGTH+2]; | |
131 char *ptr; | |
132 unsigned int ll, lr; | |
133 | |
134 /* Left flush part of the string */ | |
135 ll = gui_item_display_flush_left (gui_item, buf, MAX_MENUITEM_LENGTH); | |
136 | |
137 /* Escape '&' as '&&' */ | |
138 ptr = buf; | |
139 while ((ptr=memchr (ptr, '&', ll-(ptr-buf))) != NULL) | |
140 { | |
141 if (ll+2 >= MAX_MENUITEM_LENGTH) | |
142 signal_simple_error ("Menu item produces too long displayable string", | |
143 XGUI_ITEM (gui_item)->name); | |
144 memmove (ptr+1, ptr, (ll-(ptr-buf))+1); | |
145 ll++; | |
146 ptr+=2; | |
147 } | |
148 | |
149 /* Replace XEmacs accelerator '%_' with Windows accelerator '&' */ | |
150 ptr = buf; | |
151 while ((ptr=memchr (ptr, '%', ll-(ptr-buf))) != NULL) | |
152 { | |
153 if (*(ptr+1) == '_') | |
154 { | |
155 *ptr = '&'; | |
156 memmove (ptr+1, ptr+2, ll-(ptr-buf+2)); | |
157 ll--; | |
158 } | |
159 ptr++; | |
160 } | |
161 | |
162 /* Right flush part, unless we're at the top-level where it's not allowed */ | |
163 if (!bar_p) | |
164 { | |
165 assert (MAX_MENUITEM_LENGTH > ll + 1); | |
166 lr = gui_item_display_flush_right (gui_item, buf + ll + 1, | |
167 MAX_MENUITEM_LENGTH - ll - 1); | |
168 if (lr) | |
169 buf [ll] = '\t'; | |
170 } | |
171 | |
172 return buf; | |
173 } | |
174 | |
175 /* | |
176 * hmenu_to_lisp_object() returns an opaque ptr given menu handle. | |
177 */ | |
178 static Lisp_Object | |
179 hmenu_to_lisp_object (HMENU hmenu) | |
180 { | |
181 return make_opaque_ptr (hmenu); | |
182 } | |
183 | |
184 /* | |
185 * Allocation tries a hash based on item's path and name first. This | |
186 * almost guarantees that the same item will override its old value in | |
187 * the hash table rather than abandon it. | |
188 */ | |
189 static Lisp_Object | |
190 allocate_menu_item_id (Lisp_Object path, Lisp_Object name, Lisp_Object suffix) | |
191 { | |
192 UINT id = MENU_ITEM_ID_BITS (HASH3 (internal_hash (path, 0), | |
193 internal_hash (name, 0), | |
194 internal_hash (suffix, 0))); | |
195 do { | |
196 id = MENU_ITEM_ID_BITS (id + 1); | |
197 } while (GetMenuState (top_level_menu, id, MF_BYCOMMAND) != 0xFFFFFFFF); | |
198 return make_int (id); | |
199 } | |
200 | |
201 static HMENU | |
202 create_empty_popup_menu (void) | |
203 { | |
204 return CreatePopupMenu (); | |
205 } | |
206 | |
207 static void | |
208 empty_menu (HMENU menu, int add_empty_p) | |
209 { | |
210 while (DeleteMenu (menu, 0, MF_BYPOSITION)); | |
211 if (add_empty_p) | |
212 AppendMenu (menu, MF_STRING | MF_GRAYED, EMPTY_ITEM_ID, EMPTY_ITEM_NAME); | |
213 } | |
214 | |
215 /* | |
216 * The idea of checksumming is that we must hash minimal object | |
217 * which is necessarily changes when the item changes. For separator | |
218 * this is a constant, for grey strings and submenus these are hashes | |
219 * of names, since submenus are unpopulated until opened so always | |
220 * equal otherwise. For items, this is a full hash value of a callback, | |
221 * because a callback may me a form which can be changed only somewhere | |
222 * in depth. | |
223 */ | |
224 static unsigned long | |
225 checksum_menu_item (Lisp_Object item) | |
226 { | |
227 if (STRINGP (item)) | |
228 { | |
229 /* Separator or unselectable text - hash as a string + 13 */ | |
230 if (separator_string_p (XSTRING_DATA (item))) | |
231 return 13; | |
232 else | |
233 return internal_hash (item, 0) + 13; | |
234 } | |
235 else if (CONSP (item)) | |
236 { | |
237 /* Submenu - hash by its string name + 0 */ | |
238 return internal_hash (XCAR(item), 0); | |
239 } | |
240 else if (VECTORP (item)) | |
241 { | |
242 /* An ordinary item - hash its name and callback form. */ | |
243 return HASH2 (internal_hash (XVECTOR_DATA(item)[0], 0), | |
244 internal_hash (XVECTOR_DATA(item)[1], 0)); | |
245 } | |
246 | |
247 /* An error - will be caught later */ | |
248 return 0; | |
249 } | |
250 | |
251 static void | |
252 populate_menu_add_item (HMENU menu, Lisp_Object path, | |
253 Lisp_Object hash_tab, Lisp_Object item, | |
254 int flush_right, int bar_p) | |
255 { | |
256 MENUITEMINFO item_info; | |
257 | |
258 item_info.cbSize = sizeof (item_info); | |
259 item_info.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID; | |
260 item_info.fState = 0; | |
261 item_info.wID = 0; | |
262 item_info.fType = 0; | |
263 | |
264 if (STRINGP (item)) | |
265 { | |
266 /* Separator or unselectable text */ | |
267 if (separator_string_p (XSTRING_DATA (item))) | |
268 item_info.fType = MFT_SEPARATOR; | |
269 else | |
270 { | |
271 item_info.fType = MFT_STRING; | |
272 item_info.fState = MFS_DISABLED; | |
273 item_info.dwTypeData = XSTRING_DATA (item); | |
274 } | |
275 } | |
276 else if (CONSP (item)) | |
277 { | |
278 /* Submenu */ | |
279 HMENU submenu; | |
280 Lisp_Object gui_item = allocate_gui_item (); | |
281 struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item); | |
282 struct gcpro gcpro1; | |
283 | |
284 GCPRO1 (gui_item); | |
285 | |
286 menu_parse_submenu_keywords (item, gui_item); | |
287 | |
288 if (!STRINGP (pgui_item->name)) | |
289 signal_simple_error ("Menu name (first element) must be a string", item); | |
290 | |
291 if (!gui_item_included_p (gui_item, Vmenubar_configuration)) | |
292 return; | |
293 | |
294 if (!gui_item_active_p (gui_item)) | |
295 item_info.fState = MFS_GRAYED; | |
296 /* Temptation is to put 'else' right here. Although, the | |
297 displayed item won't have an arrow indicating that it is a | |
298 popup. So we go ahead a little bit more and create a popup */ | |
299 submenu = create_empty_popup_menu(); | |
300 | |
301 item_info.fMask |= MIIM_SUBMENU; | |
302 item_info.dwTypeData = displayable_menu_item (gui_item, bar_p); | |
303 item_info.hSubMenu = submenu; | |
304 | |
305 if (!(item_info.fState & MFS_GRAYED)) | |
306 { | |
307 /* Now add the full submenu path as a value to the hash table, | |
308 keyed by menu handle */ | |
309 if (NILP(path)) | |
310 /* list1 cannot GC */ | |
311 path = list1 (pgui_item->name); | |
312 else | |
313 { | |
314 Lisp_Object arg[2]; | |
315 arg[0] = path; | |
316 arg[1] = list1 (pgui_item->name); | |
317 /* Fappend gcpro'es its arg */ | |
318 path = Fappend (2, arg); | |
319 } | |
320 | |
321 /* Fputhash GCPRO'es PATH */ | |
322 Fputhash (hmenu_to_lisp_object (submenu), path, hash_tab); | |
323 } | |
324 UNGCPRO; /* gui_item */ | |
325 } | |
326 else if (VECTORP (item)) | |
327 { | |
328 /* An ordinary item */ | |
329 Lisp_Object style, id; | |
330 Lisp_Object gui_item = gui_parse_item_keywords (item); | |
331 struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item); | |
332 struct gcpro gcpro1; | |
333 | |
334 GCPRO1 (gui_item); | |
335 | |
336 if (!gui_item_included_p (gui_item, Vmenubar_configuration)) | |
337 return; | |
338 | |
339 if (!gui_item_active_p (gui_item)) | |
340 item_info.fState = MFS_GRAYED; | |
341 | |
342 style = (NILP (pgui_item->selected) || NILP (Feval (pgui_item->selected)) | |
343 ? Qnil : pgui_item->style); | |
344 | |
345 if (EQ (style, Qradio)) | |
346 { | |
347 item_info.fType |= MFT_RADIOCHECK; | |
348 item_info.fState |= MFS_CHECKED; | |
349 } | |
350 else if (EQ (style, Qtoggle)) | |
351 { | |
352 item_info.fState |= MFS_CHECKED; | |
353 } | |
354 | |
355 id = allocate_menu_item_id (path, pgui_item->name, | |
356 pgui_item->suffix); | |
357 Fputhash (id, pgui_item->callback, hash_tab); | |
358 | |
359 item_info.wID = (UINT) XINT(id); | |
360 item_info.fType |= MFT_STRING; | |
361 item_info.dwTypeData = displayable_menu_item (gui_item, bar_p); | |
362 | |
363 UNGCPRO; /* gui_item */ | |
364 } | |
365 else | |
366 { | |
367 signal_simple_error ("Malformed menu item descriptor", item); | |
368 } | |
369 | |
370 if (flush_right) | |
371 item_info.fType |= MFT_RIGHTJUSTIFY; | |
372 | |
373 InsertMenuItem (menu, UINT_MAX, TRUE, &item_info); | |
374 } | |
375 | |
376 /* | |
377 * This function is called from populate_menu and checksum_menu. | |
378 * When called to populate, MENU is a menu handle, PATH is a | |
379 * list of strings representing menu path from root to this submenu, | |
380 * DESCRIPTOR is a menu descriptor, HASH_TAB is a hash table associated | |
381 * with root menu, BAR_P indicates whether this called for a menubar or | |
382 * a popup, and POPULATE_P is non-zero. Return value must be ignored. | |
383 * When called to checksum, DESCRIPTOR has the same meaning, POPULATE_P | |
384 * is zero, PATH must be Qnil, and the rest of parameters is ignored. | |
385 * Return value is the menu checksum. | |
386 */ | |
387 static unsigned long | |
388 populate_or_checksum_helper (HMENU menu, Lisp_Object path, Lisp_Object desc, | |
389 Lisp_Object hash_tab, int bar_p, int populate_p) | |
390 { | |
391 Lisp_Object item_desc; | |
392 int deep_p, flush_right; | |
393 struct gcpro gcpro1; | |
394 unsigned long checksum; | |
395 Lisp_Object gui_item = allocate_gui_item (); | |
396 struct Lisp_Gui_Item* pgui_item = XGUI_ITEM (gui_item); | |
397 GCPRO1 (gui_item); | |
398 | |
399 /* We are sometimes called with the menubar unchanged, and with changed | |
400 right flush. We have to update the menubar in this case, | |
401 so account for the compliance setting in the hash value */ | |
402 checksum = REPLACE_ME_WITH_GLOBAL_VARIABLE_WHICH_CONTROLS_RIHGT_FLUSH; | |
403 | |
404 /* Will initially contain only "(empty)" */ | |
405 if (populate_p) | |
406 empty_menu (menu, 1); | |
407 | |
408 /* PATH set to nil indicates top-level popup or menubar */ | |
409 deep_p = !NILP (path); | |
410 | |
411 /* Fetch keywords prepending the item list */ | |
412 desc = menu_parse_submenu_keywords (desc, gui_item); | |
413 | |
414 /* Check that menu name is specified when expected */ | |
415 if (NILP (pgui_item->name) && deep_p) | |
416 signal_simple_error ("Menu must have a name", desc); | |
417 | |
418 /* Apply filter if specified */ | |
419 if (!NILP (pgui_item->filter)) | |
420 desc = call1 (pgui_item->filter, desc); | |
421 | |
422 /* Loop thru the desc's CDR and add items for each entry */ | |
423 flush_right = 0; | |
424 EXTERNAL_LIST_LOOP (item_desc, desc) | |
425 { | |
426 if (NILP (XCAR (item_desc))) | |
427 { | |
428 /* Do not flush right menubar items when MS style compliant */ | |
429 if (bar_p && !REPLACE_ME_WITH_GLOBAL_VARIABLE_WHICH_CONTROLS_RIHGT_FLUSH) | |
430 flush_right = 1; | |
431 if (!populate_p) | |
432 checksum = HASH2 (checksum, LISP_HASH (Qnil)); | |
433 } | |
434 else if (populate_p) | |
435 populate_menu_add_item (menu, path, hash_tab, | |
436 XCAR (item_desc), flush_right, bar_p); | |
437 else | |
438 checksum = HASH2 (checksum, | |
439 checksum_menu_item (XCAR (item_desc))); | |
440 } | |
441 | |
442 if (populate_p) | |
443 { | |
444 /* Remove the "(empty)" item, if there are other ones */ | |
445 if (GetMenuItemCount (menu) > 1) | |
446 RemoveMenu (menu, EMPTY_ITEM_ID, MF_BYCOMMAND); | |
447 | |
448 /* Add the header to the popup, if told so. The same as in X - an | |
449 insensitive item, and a separator (Seems to me, there were | |
450 two separators in X... In Windows this looks ugly, anyways. */ | |
451 if (!bar_p && !deep_p && popup_menu_titles && !NILP(pgui_item->name)) | |
452 { | |
453 CHECK_STRING (pgui_item->name); | |
454 InsertMenu (menu, 0, MF_BYPOSITION | MF_STRING | MF_DISABLED, | |
455 0, XSTRING_DATA(pgui_item->name)); | |
456 InsertMenu (menu, 1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); | |
457 SetMenuDefaultItem (menu, 0, MF_BYPOSITION); | |
458 } | |
459 } | |
460 UNGCPRO; /* gui_item */ | |
461 return checksum; | |
462 } | |
463 | |
464 static void | |
465 populate_menu (HMENU menu, Lisp_Object path, Lisp_Object desc, | |
466 Lisp_Object hash_tab, int bar_p) | |
467 { | |
468 populate_or_checksum_helper (menu, path, desc, hash_tab, bar_p, 1); | |
469 } | |
470 | |
471 static unsigned long | |
472 checksum_menu (Lisp_Object desc) | |
473 { | |
474 return populate_or_checksum_helper (NULL, Qnil, desc, Qunbound, 0, 0); | |
475 } | |
476 | |
477 static void | |
478 update_frame_menubar_maybe (struct frame* f) | |
479 { | |
480 HMENU menubar = GetMenu (FRAME_MSWINDOWS_HANDLE (f)); | |
481 struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f)); | |
482 Lisp_Object desc = (!NILP (w->menubar_visible_p) | |
483 ? symbol_value_in_buffer (Qcurrent_menubar, w->buffer) | |
484 : Qnil); | |
485 | |
486 top_level_menu = menubar; | |
487 | |
488 if (NILP (desc) && menubar != NULL) | |
489 { | |
490 /* Menubar has gone */ | |
491 FRAME_MSWINDOWS_MENU_HASH_TABLE(f) = Qnil; | |
492 SetMenu (FRAME_MSWINDOWS_HANDLE (f), NULL); | |
493 DestroyMenu (menubar); | |
494 DrawMenuBar (FRAME_MSWINDOWS_HANDLE (f)); | |
495 return; | |
496 } | |
497 | |
498 if (!NILP (desc) && menubar == NULL) | |
499 { | |
500 /* Menubar has appeared */ | |
501 menubar = CreateMenu (); | |
502 goto populate; | |
503 } | |
504 | |
505 if (NILP (desc)) | |
506 { | |
507 /* We did not have the bar and are not going to */ | |
508 return; | |
509 } | |
510 | |
511 /* Now we bail out if the menubar has not changed */ | |
512 if (FRAME_MSWINDOWS_MENU_CHECKSUM(f) == checksum_menu (desc)) | |
513 return; | |
514 | |
515 populate: | |
516 /* Come with empty hash table */ | |
517 if (NILP (FRAME_MSWINDOWS_MENU_HASH_TABLE(f))) | |
518 FRAME_MSWINDOWS_MENU_HASH_TABLE(f) = | |
519 make_lisp_hash_table (50, HASH_TABLE_NON_WEAK, HASH_TABLE_EQUAL); | |
520 else | |
521 Fclrhash (FRAME_MSWINDOWS_MENU_HASH_TABLE(f)); | |
522 | |
523 Fputhash (hmenu_to_lisp_object (menubar), Qnil, | |
524 FRAME_MSWINDOWS_MENU_HASH_TABLE(f)); | |
525 populate_menu (menubar, Qnil, desc, | |
526 FRAME_MSWINDOWS_MENU_HASH_TABLE(f), 1); | |
527 SetMenu (FRAME_MSWINDOWS_HANDLE (f), menubar); | |
528 DrawMenuBar (FRAME_MSWINDOWS_HANDLE (f)); | |
529 | |
530 FRAME_MSWINDOWS_MENU_CHECKSUM(f) = checksum_menu (desc); | |
531 } | |
532 | |
533 static void | |
534 prune_menubar (struct frame *f) | |
535 { | |
536 HMENU menubar = GetMenu (FRAME_MSWINDOWS_HANDLE (f)); | |
537 Lisp_Object desc = current_frame_menubar (f); | |
538 if (menubar == NULL) | |
539 return; | |
540 | |
541 /* #### If a filter function has set desc to Qnil, this abort() | |
542 triggers. To resolve, we must prevent filters explicitly from | |
543 mangling with the active menu. In apply_filter probably? | |
544 Is copy-tree on the whole menu too expensive? */ | |
545 if (NILP(desc)) | |
546 /* abort(); */ | |
547 return; | |
548 | |
549 /* We do the trick by removing all items and re-populating top level */ | |
550 empty_menu (menubar, 0); | |
551 | |
552 assert (HASH_TABLEP (FRAME_MSWINDOWS_MENU_HASH_TABLE(f))); | |
553 Fclrhash (FRAME_MSWINDOWS_MENU_HASH_TABLE(f)); | |
554 | |
555 Fputhash (hmenu_to_lisp_object (menubar), Qnil, | |
556 FRAME_MSWINDOWS_MENU_HASH_TABLE(f)); | |
557 populate_menu (menubar, Qnil, desc, | |
558 FRAME_MSWINDOWS_MENU_HASH_TABLE(f), 1); | |
559 } | |
560 | |
561 /* | |
562 * This is called when cleanup is possible. It is better not to | |
563 * clean things up at all than do it too early! | |
564 */ | |
565 static void | |
566 menu_cleanup (struct frame *f) | |
567 { | |
568 /* This function can GC */ | |
569 current_menudesc = Qnil; | |
570 current_hash_table = Qnil; | |
571 prune_menubar (f); | |
572 } | |
573 | |
574 | |
575 /*------------------------------------------------------------------------*/ | |
576 /* Message handlers */ | |
577 /*------------------------------------------------------------------------*/ | |
578 static Lisp_Object | |
579 unsafe_handle_wm_initmenupopup_1 (HMENU menu, struct frame* f) | |
580 { | |
581 /* This function can call lisp, beat dogs and stick chewing gum to | |
582 everything! */ | |
583 | |
584 Lisp_Object path, desc; | |
585 struct gcpro gcpro1; | |
586 | |
587 /* Find which guy is going to explode */ | |
588 path = Fgethash (hmenu_to_lisp_object (menu), current_hash_table, Qunbound); | |
589 assert (!UNBOUNDP (path)); | |
590 #ifdef DEBUG_XEMACS | |
591 /* Allow to continue in a debugger after assert - not so fatal */ | |
592 if (UNBOUNDP (path)) | |
593 error ("internal menu error"); | |
594 #endif | |
595 | |
596 /* Now find a desc chunk for it. If none, then probably menu open | |
597 hook has played too much games around stuff */ | |
598 desc = Fmenu_find_real_submenu (current_menudesc, path); | |
599 if (NILP (desc)) | |
600 signal_simple_error ("This menu does not exist any more", path); | |
601 | |
602 /* Now, stuff it */ | |
603 /* DESC may be generated by filter, so we have to gcpro it */ | |
604 GCPRO1 (desc); | |
605 populate_menu (menu, path, desc, current_hash_table, 0); | |
606 UNGCPRO; | |
607 return Qt; | |
608 } | |
609 | |
610 static Lisp_Object | |
611 unsafe_handle_wm_initmenu_1 (struct frame* f) | |
612 { | |
613 /* This function can call lisp */ | |
614 | |
615 /* NOTE: This is called for the bar only, WM_INITMENU | |
616 for popups is filtered out */ | |
617 | |
618 /* #### - this menubar update mechanism is expensively anti-social and | |
619 the activate-menubar-hook is now mostly obsolete. */ | |
620 | |
621 /* We simply ignore return value. In any case, we construct the bar | |
622 on the fly */ | |
623 run_hook (Qactivate_menubar_hook); | |
624 | |
625 update_frame_menubar_maybe (f); | |
626 | |
627 current_menudesc = current_frame_menubar (f); | |
628 current_hash_table = FRAME_MSWINDOWS_MENU_HASH_TABLE(f); | |
629 assert (HASH_TABLEP (current_hash_table)); | |
630 | |
631 return Qt; | |
632 } | |
633 | |
634 /* | |
635 * Return value is Qt if we have dispatched the command, | |
636 * or Qnil if id has not been mapped to a callback. | |
637 * Window procedure may try other targets to route the | |
638 * command if we return nil | |
639 */ | |
640 Lisp_Object | |
641 mswindows_handle_wm_command (struct frame* f, WORD id) | |
642 { | |
643 /* Try to map the command id through the proper hash table */ | |
644 Lisp_Object data, fn, arg, frame; | |
645 struct gcpro gcpro1; | |
646 | |
647 if (NILP (current_hash_table)) | |
648 return Qnil; | |
649 | |
650 data = Fgethash (make_int (id), current_hash_table, Qunbound); | |
651 | |
652 if (UNBOUNDP (data)) | |
653 { | |
654 menu_cleanup (f); | |
655 return Qnil; | |
656 } | |
657 | |
658 /* Need to gcpro because the hash table may get destroyed by | |
659 menu_cleanup(), and will not gcpro the data any more */ | |
660 GCPRO1 (data); | |
661 menu_cleanup (f); | |
662 | |
663 /* Ok, this is our one. Enqueue it. */ | |
664 get_gui_callback (data, &fn, &arg); | |
665 XSETFRAME (frame, f); | |
666 /* this used to call mswindows_enqueue_misc_user_event but that | |
667 breaks customize because the misc_event gets eval'ed in some | |
668 cicumstances. Don't change it back unless you can fix the | |
669 customize problem also.*/ | |
670 enqueue_misc_user_event (frame, fn, arg); | |
671 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); | |
672 | |
673 UNGCPRO; /* data */ | |
674 return Qt; | |
675 } | |
676 | |
677 | |
678 /*------------------------------------------------------------------------*/ | |
679 /* Message handling proxies */ | |
680 /*------------------------------------------------------------------------*/ | |
681 | |
682 static HMENU wm_initmenu_menu; | |
683 static struct frame* wm_initmenu_frame; | |
684 | |
685 static Lisp_Object | |
686 unsafe_handle_wm_initmenupopup (Lisp_Object u_n_u_s_e_d) | |
687 { | |
688 return unsafe_handle_wm_initmenupopup_1 (wm_initmenu_menu, wm_initmenu_frame); | |
689 } | |
690 | |
691 static Lisp_Object | |
692 unsafe_handle_wm_initmenu (Lisp_Object u_n_u_s_e_d) | |
693 { | |
694 return unsafe_handle_wm_initmenu_1 (wm_initmenu_frame); | |
695 } | |
696 | |
697 Lisp_Object | |
698 mswindows_handle_wm_initmenupopup (HMENU hmenu, struct frame* frm) | |
699 { | |
700 /* We cannot pass hmenu as a lisp object. Use static var */ | |
701 wm_initmenu_menu = hmenu; | |
702 wm_initmenu_frame = frm; | |
703 return mswindows_protect_modal_loop (unsafe_handle_wm_initmenupopup, Qnil); | |
704 } | |
705 | |
706 Lisp_Object | |
707 mswindows_handle_wm_initmenu (HMENU hmenu, struct frame* f) | |
708 { | |
709 /* Handle only frame menubar, ignore if from popup or system menu */ | |
710 if (GetMenu (FRAME_MSWINDOWS_HANDLE(f)) == hmenu) | |
711 { | |
712 wm_initmenu_frame = f; | |
713 return mswindows_protect_modal_loop (unsafe_handle_wm_initmenu, Qnil); | |
714 } | |
715 return Qt; | |
716 } | |
717 | |
718 | |
719 /*------------------------------------------------------------------------*/ | |
720 /* Methods */ | |
721 /*------------------------------------------------------------------------*/ | |
722 | |
723 static void | |
724 mswindows_update_frame_menubars (struct frame* f) | |
725 { | |
726 update_frame_menubar_maybe (f); | |
727 } | |
728 | |
729 static void | |
730 mswindows_free_frame_menubars (struct frame* f) | |
731 { | |
732 FRAME_MSWINDOWS_MENU_HASH_TABLE(f) = Qnil; | |
733 } | |
734 | |
735 static void | |
736 mswindows_popup_menu (Lisp_Object menu_desc, Lisp_Object event) | |
737 { | |
738 struct frame *f = selected_frame (); | |
739 struct Lisp_Event *eev = NULL; | |
740 HMENU menu; | |
741 POINT pt; | |
742 int ok; | |
743 | |
744 if (!NILP (event)) | |
745 { | |
746 CHECK_LIVE_EVENT (event); | |
747 eev = XEVENT (event); | |
748 if (eev->event_type != button_press_event | |
749 && eev->event_type != button_release_event) | |
750 wrong_type_argument (Qmouse_event_p, event); | |
751 } | |
752 else if (!NILP (Vthis_command_keys)) | |
753 { | |
754 /* if an event wasn't passed, use the last event of the event sequence | |
755 currently being executed, if that event is a mouse event */ | |
756 eev = XEVENT (Vthis_command_keys); /* last event first */ | |
757 if (eev->event_type != button_press_event | |
758 && eev->event_type != button_release_event) | |
759 eev = NULL; | |
760 } | |
761 | |
762 /* Default is to put the menu at the point (10, 10) in frame */ | |
763 if (eev) | |
764 { | |
765 pt.x = eev->event.button.x; | |
766 pt.y = eev->event.button.y; | |
767 ClientToScreen (FRAME_MSWINDOWS_HANDLE (f), &pt); | |
768 } | |
769 else | |
770 pt.x = pt.y = 10; | |
771 | |
772 if (SYMBOLP (menu_desc)) | |
773 menu_desc = Fsymbol_value (menu_desc); | |
774 CHECK_CONS (menu_desc); | |
775 CHECK_STRING (XCAR (menu_desc)); | |
776 | |
777 current_menudesc = menu_desc; | |
778 current_hash_table = | |
779 make_lisp_hash_table (10, HASH_TABLE_NON_WEAK, HASH_TABLE_EQUAL); | |
780 menu = create_empty_popup_menu(); | |
781 Fputhash (hmenu_to_lisp_object (menu), Qnil, current_hash_table); | |
782 top_level_menu = menu; | |
783 | |
784 /* see comments in menubar-x.c */ | |
785 if (zmacs_regions) | |
786 zmacs_region_stays = 1; | |
787 | |
788 ok = TrackPopupMenu (menu, | |
789 TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, | |
790 pt.x, pt.y, 0, | |
791 FRAME_MSWINDOWS_HANDLE (f), NULL); | |
792 | |
793 DestroyMenu (menu); | |
794 | |
795 /* Signal a signal if caught by Track...() modal loop */ | |
796 mswindows_unmodalize_signal_maybe (); | |
797 | |
798 /* This is probably the only real reason for failure */ | |
799 if (!ok) { | |
800 menu_cleanup (f); | |
801 signal_simple_error ("Cannot track popup menu while in menu", | |
802 menu_desc); | |
803 } | |
804 } | |
805 | |
806 | |
807 /*------------------------------------------------------------------------*/ | |
808 /* Initialization */ | |
809 /*------------------------------------------------------------------------*/ | |
810 void | |
811 syms_of_menubar_mswindows (void) | |
812 { | |
813 } | |
814 | |
815 void | |
816 console_type_create_menubar_mswindows (void) | |
817 { | |
818 CONSOLE_HAS_METHOD (mswindows, update_frame_menubars); | |
819 CONSOLE_HAS_METHOD (mswindows, free_frame_menubars); | |
820 CONSOLE_HAS_METHOD (mswindows, popup_menu); | |
821 } | |
822 | |
823 void | |
824 vars_of_menubar_mswindows (void) | |
825 { | |
826 current_menudesc = Qnil; | |
827 current_hash_table = Qnil; | |
828 | |
829 staticpro (¤t_menudesc); | |
830 staticpro (¤t_hash_table); | |
831 } |