Mercurial > hg > xemacs-beta
comparison src/menubar-msw.c @ 251:677f6a0ee643 r20-5b24
Import from CVS: tag r20-5b24
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:19:59 +0200 |
parents | 83b3d10dcba9 |
children | 157b30c96d03 |
comparison
equal
deleted
inserted
replaced
250:f385a461c9aa | 251:677f6a0ee643 |
---|---|
99 static Lisp_Object current_menudesc; | 99 static Lisp_Object current_menudesc; |
100 | 100 |
101 /* Current menubar or popup hashtable. gcpro'ed */ | 101 /* Current menubar or popup hashtable. gcpro'ed */ |
102 static Lisp_Object current_hashtable; | 102 static Lisp_Object current_hashtable; |
103 | 103 |
104 /* Bound by menubar.el */ | |
105 static Lisp_Object Qfind_menu_item; | |
106 | |
107 /* This is used to allocate unique ids to menu items. | 104 /* This is used to allocate unique ids to menu items. |
108 Items ids are in MENU_ITEM_ID_MIN to MENU_ITEM_ID_MAX. | 105 Items ids are in MENU_ITEM_ID_MIN to MENU_ITEM_ID_MAX. |
109 Allocation checks that the item is not already in | 106 Allocation checks that the item is not already in |
110 the TOP_LEVEL_MENU */ | 107 the TOP_LEVEL_MENU */ |
111 /* #### defines go to gui-msw.h */ | 108 |
109 /* #### defines go to gui-msw.h, as the range is shared with toolbars | |
110 (If only toolbars will be implemented as common controls) */ | |
112 #define MENU_ITEM_ID_MIN 0x8000 | 111 #define MENU_ITEM_ID_MIN 0x8000 |
113 #define MENU_ITEM_ID_MAX 0xFFFF | 112 #define MENU_ITEM_ID_MAX 0xFFFF |
114 #define MENU_ITEM_ID_BITS(x) ((x) & 0x7FFF | 0x8000) | 113 #define MENU_ITEM_ID_BITS(x) ((x) & 0x7FFF | 0x8000) |
115 static HMENU top_level_menu; | 114 static HMENU top_level_menu; |
116 | 115 |
117 /* ============= THIS STUFF MIGHT GO SOMEWHERE ELSE ================= */ | |
118 | |
119 /* All these functions are windows sys independent, and are candidates | |
120 to go to lisp code instead */ | |
121 | |
122 /* | |
123 * DESCRIPTOR is a list in the form ({:keyword value}+ rest...). | |
124 * This function extracts all the key-value pairs into the newly | |
125 * created plist, and returns pointer to REST. Original list is not | |
126 * modified (heaven save!) | |
127 */ | |
128 Lisp_Object | |
129 gui_parse_menu_keywords (Lisp_Object descriptor, Lisp_Object *plist) | |
130 { | |
131 Lisp_Object pair, key, val; | |
132 *plist = Qnil; | |
133 LIST_LOOP (pair, descriptor) | |
134 { | |
135 if (!CONSP(pair)) | |
136 signal_simple_error ("Mailformed gui entity descriptor", descriptor); | |
137 key = XCAR(pair); | |
138 if (!KEYWORDP (key)) | |
139 return pair; | |
140 pair = XCDR (pair); | |
141 if (!CONSP(pair)) | |
142 signal_simple_error ("Mailformed gui entity descriptor", descriptor); | |
143 val = XCAR (pair); | |
144 internal_plist_put (plist, key, val); | |
145 } | |
146 return pair; | |
147 } | |
148 | |
149 /* | |
150 * DESC is a vector describing a menu item. The function returns menu | |
151 * item name in NAME, callback form in CALLBACK, and all key-values | |
152 * pairs in PLIST. For old-style vectors, the plist is faked. | |
153 */ | |
154 void | |
155 gui_parse_button_descriptor (Lisp_Object desc, Lisp_Object *name, | |
156 Lisp_Object *callback, Lisp_Object *plist) | |
157 { | |
158 int length = XVECTOR_LENGTH (desc); | |
159 Lisp_Object *contents = XVECTOR_DATA (desc); | |
160 int plist_p; | |
161 | |
162 *name = Qnil; | |
163 *callback = Qnil; | |
164 *plist = Qnil; | |
165 | |
166 if (length < 3) | |
167 signal_simple_error ("Button descriptors must be at least 3 long", desc); | |
168 | |
169 /* length 3: [ "name" callback active-p ] | |
170 length 4: [ "name" callback active-p suffix ] | |
171 or [ "name" callback keyword value ] | |
172 length 5+: [ "name" callback [ keyword value ]+ ] | |
173 */ | |
174 plist_p = (length >= 5 || KEYWORDP (contents [2])); | |
175 | |
176 *name = contents [0]; | |
177 *callback = contents [1]; | |
178 | |
179 if (!plist_p) | |
180 /* the old way */ | |
181 { | |
182 internal_plist_put (plist, Q_active, contents [2]); | |
183 if (length == 4) | |
184 internal_plist_put (plist, Q_suffix, contents [3]); | |
185 } | |
186 else | |
187 /* the new way */ | |
188 { | |
189 int i; | |
190 if (length & 1) | |
191 signal_simple_error ( | |
192 "Button descriptor has an odd number of keywords and values", | |
193 desc); | |
194 | |
195 for (i = 2; i < length;) | |
196 { | |
197 Lisp_Object key = contents [i++]; | |
198 Lisp_Object val = contents [i++]; | |
199 if (!KEYWORDP (key)) | |
200 signal_simple_error_2 ("Not a keyword", key, desc); | |
201 internal_plist_put (plist, key, val); | |
202 } | |
203 } | |
204 } | |
205 | |
206 /* | |
207 * Given PLIST of key-value pairs for a menu item or button, consult | |
208 * :included and :config properties (the latter against | |
209 * CONFLIST). Return value is non-zero when item should *not* appear. | |
210 */ | |
211 int | |
212 gui_plist_says_item_excluded (Lisp_Object plist, Lisp_Object conflist) | |
213 { | |
214 Lisp_Object tem; | |
215 /* This function can call lisp */ | |
216 | |
217 /* Evaluate :included first */ | |
218 tem = internal_plist_get (plist, Q_included); | |
219 if (!UNBOUNDP (tem)) | |
220 { | |
221 tem = Feval (tem); | |
222 if (NILP (tem)) | |
223 return 1; | |
224 } | |
225 | |
226 /* Do :config if conflist is given */ | |
227 if (!NILP (conflist)) | |
228 { | |
229 tem = internal_plist_get (plist, Q_config); | |
230 if (!UNBOUNDP (tem)) | |
231 { | |
232 tem = Fmemq (tem, conflist); | |
233 if (NILP (tem)) | |
234 return 1; | |
235 } | |
236 } | |
237 | |
238 return 0; | |
239 } | |
240 | |
241 /* | |
242 * Given PLIST of key-value pairs for a menu item or button, consult | |
243 * :active property. Return non-zero if the item is *inactive* | |
244 */ | |
245 int | |
246 gui_plist_says_item_inactive (Lisp_Object plist) | |
247 { | |
248 Lisp_Object tem; | |
249 /* This function can call lisp */ | |
250 | |
251 tem = internal_plist_get (plist, Q_active); | |
252 if (!UNBOUNDP (tem)) | |
253 { | |
254 tem = Feval (tem); | |
255 if (NILP (tem)) | |
256 return 1; | |
257 } | |
258 | |
259 return 0; | |
260 } | |
261 | |
262 /* | |
263 * Given PLIST of key-value pairs for a menu item or button, evaluate | |
264 * the form which is the value of :filter property. Filter function | |
265 * given DESC as argument. If there's no :filter property, DESC is | |
266 * returned, otherwise the value returned by the filter function is | |
267 * returned. | |
268 */ | |
269 Lisp_Object | |
270 gui_plist_apply_filter (Lisp_Object plist, Lisp_Object desc) | |
271 { | |
272 Lisp_Object tem; | |
273 /* This function can call lisp */ | |
274 | |
275 tem = internal_plist_get (plist, Q_filter); | |
276 if (UNBOUNDP (tem)) | |
277 return desc; | |
278 else | |
279 return call1 (tem, desc); | |
280 } | |
281 | |
282 /* | |
283 * This is tricky because there's no menu item styles in Windows, only | |
284 * states: Each item may be given no checkmark, radio or check | |
285 * mark. This function returns required mark style as determined by | |
286 * PLIST. Return value is the value of :style property if the item is | |
287 * :seleted, or nil otherwise | |
288 */ | |
289 Lisp_Object | |
290 gui_plist_get_current_style (Lisp_Object plist) | |
291 { | |
292 Lisp_Object style, selected; | |
293 style = internal_plist_get (plist, Q_style); | |
294 if (UNBOUNDP (style) || NILP(style)) | |
295 return Qnil; | |
296 | |
297 selected = internal_plist_get (plist, Q_selected); | |
298 if (UNBOUNDP (selected) || NILP(Feval(selected))) | |
299 return Qnil; | |
300 | |
301 return style; | |
302 } | |
303 | |
304 Lisp_Object | |
305 current_frame_menubar (CONST struct frame* f) | |
306 { | |
307 struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f)); | |
308 return symbol_value_in_buffer (Qcurrent_menubar, w->buffer); | |
309 } | |
310 | |
311 /* ============ END IF STUFF THAT MIGHT GO SOMEWHERE ELSE =============== */ | |
312 | |
313 /* Change these together */ | |
314 #define MAX_MENUITEM_LENGTH 128 | 116 #define MAX_MENUITEM_LENGTH 128 |
315 #define DISPLAYABLE_MAX_MENUITEM_LENGTH "128" | 117 |
316 | |
317 static void | |
318 signal_item_too_long (Lisp_Object name) | |
319 { | |
320 signal_simple_error ("Menu item is longer than " | |
321 DISPLAYABLE_MAX_MENUITEM_LENGTH | |
322 " characters", name); | |
323 } | |
324 | |
325 /* #### If this function returned (FLUSHLEFT . FLUSHRIGHT) it also | |
326 could be moved above that line - it becomes window system | |
327 independant */ | |
328 /* | 118 /* |
329 * This returns Windows-style menu item string: | 119 * This returns Windows-style menu item string: |
330 * "Left Flush\tRight Flush" | 120 * "Left Flush\tRight Flush" |
331 */ | 121 */ |
332 static CONST char* | 122 static char* |
333 plist_get_menu_item_name (Lisp_Object name, Lisp_Object callback, Lisp_Object plist) | 123 displayable_menu_item (struct gui_item* pgui_item) |
334 { | 124 { |
335 /* We construct the name in a static buffer. That's fine, beause | 125 /* We construct the name in a static buffer. That's fine, beause |
336 menu items longer than 128 chars are probably programming errors, | 126 menu items longer than 128 chars are probably programming errors, |
337 and better be caught than displayed! */ | 127 and better be caught than displayed! */ |
338 | 128 |
339 static char buf[MAX_MENUITEM_LENGTH]; | 129 static char buf[MAX_MENUITEM_LENGTH+2]; |
340 char* p = buf; | 130 unsigned int ll, lr; |
341 int buf_left = MAX_MENUITEM_LENGTH - 1; | 131 |
342 Lisp_Object tem; | 132 /* Left flush part of the string */ |
343 | 133 ll = gui_item_display_flush_left (pgui_item, buf, MAX_MENUITEM_LENGTH); |
344 /* Get name first */ | 134 |
345 buf_left -= XSTRING_LENGTH (name); | 135 /* Right flush part */ |
346 if (buf_left < 0) | 136 assert (MAX_MENUITEM_LENGTH > ll + 1); |
347 signal_item_too_long (name); | 137 lr = gui_item_display_flush_right (pgui_item, buf + ll + 1, |
348 strcpy (p, XSTRING_DATA (name)); | 138 MAX_MENUITEM_LENGTH - ll - 1); |
349 p += XSTRING_LENGTH (name); | 139 if (lr) |
350 | 140 buf [ll] = '\t'; |
351 /* Have suffix? */ | 141 |
352 tem = internal_plist_get (plist, Q_suffix); | |
353 if (!UNBOUNDP (tem)) | |
354 { | |
355 if (!STRINGP (tem)) | |
356 signal_simple_error (":suffix must be a string", tem); | |
357 buf_left -= XSTRING_LENGTH (tem) + 1; | |
358 if (buf_left < 0) | |
359 signal_item_too_long (name); | |
360 *p++ = ' '; | |
361 strcpy (p, XSTRING_DATA (tem)); | |
362 p += XSTRING_LENGTH (tem); | |
363 } | |
364 | |
365 /* Have keys? */ | |
366 if (menubar_show_keybindings) | |
367 { | |
368 static char buf2 [1024]; | |
369 buf2[0] = 0; | |
370 | |
371 tem = internal_plist_get (plist, Q_keys); | |
372 if (!UNBOUNDP (tem)) | |
373 { | |
374 if (!STRINGP (tem)) | |
375 signal_simple_error (":keys must be a string", tem); | |
376 if (XSTRING_LENGTH (tem) > sizeof (buf2) - 1) | |
377 signal_item_too_long (name); | |
378 strcpy (buf2, XSTRING_DATA (tem)); | |
379 } | |
380 else if (SYMBOLP (callback)) | |
381 { | |
382 /* #### Warning, dependency here on current_buffer and point */ | |
383 /* #### I've borrowed this warning along with this code from | |
384 menubar-x.c. What does that mean? -- kkm */ | |
385 where_is_to_char (callback, buf2); | |
386 } | |
387 | |
388 if (buf2 [0]) | |
389 { | |
390 int n = strlen (buf2) + 1; | |
391 buf_left -= n; | |
392 if (buf_left < 0) | |
393 signal_item_too_long (name); | |
394 *p++ = '\t'; | |
395 strcpy (p, buf2); | |
396 p += n-1; | |
397 } | |
398 } | |
399 | |
400 *p = 0; | |
401 return buf; | 142 return buf; |
402 } | 143 } |
403 | 144 |
404 /* | 145 /* |
405 * hmenu_to_lisp_object() returns an opaque ptr given menu handle. | 146 * hmenu_to_lisp_object() returns an opaque ptr given menu handle. |
414 * Allocation tries a hash based on item's path and name first. This | 155 * Allocation tries a hash based on item's path and name first. This |
415 * almost guarantees that the same item will override its old value in | 156 * almost guarantees that the same item will override its old value in |
416 * the hashtable rather than abandon it. | 157 * the hashtable rather than abandon it. |
417 */ | 158 */ |
418 static Lisp_Object | 159 static Lisp_Object |
419 allocate_menu_item_id (Lisp_Object path, Lisp_Object name) | 160 allocate_menu_item_id (Lisp_Object path, Lisp_Object name, Lisp_Object suffix) |
420 { | 161 { |
421 UINT id = MENU_ITEM_ID_BITS (HASH2 (internal_hash (path, 0), | 162 UINT id = MENU_ITEM_ID_BITS (HASH3 (internal_hash (path, 0), |
422 internal_hash (name, 0))); | 163 internal_hash (name, 0), |
164 internal_hash (suffix, 0))); | |
423 do { | 165 do { |
424 id = MENU_ITEM_ID_BITS (id + 1); | 166 id = MENU_ITEM_ID_BITS (id + 1); |
425 } while (GetMenuState (top_level_menu, id, MF_BYCOMMAND) != 0xFFFFFFFF); | 167 } while (GetMenuState (top_level_menu, id, MF_BYCOMMAND) != 0xFFFFFFFF); |
426 return make_int (id); | 168 return make_int (id); |
427 } | 169 } |
428 | 170 |
429 static HMENU | 171 static HMENU |
430 create_empty_popup_menu (void) | 172 create_empty_popup_menu (void) |
431 { | 173 { |
432 HMENU submenu = CreatePopupMenu (); | 174 return CreatePopupMenu (); |
433 /* #### It seems that really we do not need "(empty)" at this stage */ | |
434 #if 0 | |
435 AppendMenu (submenu, MF_STRING | MF_GRAYED, EMPTY_ITEM_ID, EMPTY_ITEM_NAME); | |
436 #endif | |
437 return submenu; | |
438 } | 175 } |
439 | 176 |
440 static void | 177 static void |
441 empty_menu (HMENU menu, int add_empty_p) | 178 empty_menu (HMENU menu, int add_empty_p) |
442 { | 179 { |
471 return internal_hash (XCAR(item), 0); | 208 return internal_hash (XCAR(item), 0); |
472 } | 209 } |
473 else if (VECTORP (item)) | 210 else if (VECTORP (item)) |
474 { | 211 { |
475 /* An ordinary item - hash its name and callback form. */ | 212 /* An ordinary item - hash its name and callback form. */ |
476 Lisp_Object plist, name, callback; | 213 return HASH2 (internal_hash (XVECTOR_DATA(item)[0], 0), |
477 gui_parse_button_descriptor (item, &name, &callback, &plist); | 214 internal_hash (XVECTOR_DATA(item)[1], 0)); |
478 return HASH2 (internal_hash (name, 0), | |
479 internal_hash (callback, 0)); | |
480 } | 215 } |
481 | 216 |
482 /* An error - will be caught later */ | 217 /* An error - will be caught later */ |
483 return 0; | 218 return 0; |
484 } | 219 } |
486 static void | 221 static void |
487 populate_menu_add_item (HMENU menu, Lisp_Object path, | 222 populate_menu_add_item (HMENU menu, Lisp_Object path, |
488 Lisp_Object hash_tab, Lisp_Object item, int flush_right) | 223 Lisp_Object hash_tab, Lisp_Object item, int flush_right) |
489 { | 224 { |
490 MENUITEMINFO item_info; | 225 MENUITEMINFO item_info; |
491 struct gcpro gcpro1, gcpro2; | |
492 | 226 |
493 item_info.cbSize = sizeof (item_info); | 227 item_info.cbSize = sizeof (item_info); |
494 item_info.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID; | 228 item_info.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID; |
495 item_info.fState = 0; | 229 item_info.fState = 0; |
496 item_info.wID = 0; | 230 item_info.wID = 0; |
509 } | 243 } |
510 } | 244 } |
511 else if (CONSP (item)) | 245 else if (CONSP (item)) |
512 { | 246 { |
513 /* Submenu */ | 247 /* Submenu */ |
514 Lisp_Object subname = XCAR (item); | |
515 Lisp_Object plist; | |
516 HMENU submenu; | 248 HMENU submenu; |
517 | 249 struct gui_item gui_item; |
518 if (!STRINGP (subname)) | 250 struct gcpro gcpro1; |
251 | |
252 gui_item_init (&gui_item); | |
253 GCPRO1 (gui_item); | |
254 gcpro1.nvars = GUI_ITEM_GCPRO_COUNT; | |
255 | |
256 menu_parse_submenu_keywords (item, &gui_item); | |
257 | |
258 if (!STRINGP (gui_item.name)) | |
519 signal_simple_error ("Menu name (first element) must be a string", item); | 259 signal_simple_error ("Menu name (first element) must be a string", item); |
520 | 260 |
521 item = gui_parse_menu_keywords (XCDR (item), &plist); | 261 if (!gui_item_included_p (&gui_item, Vmenubar_configuration)) |
522 GCPRO1 (plist); | |
523 | |
524 if (gui_plist_says_item_excluded (plist, Vmenubar_configuration)) | |
525 return; | 262 return; |
526 | 263 |
527 if (gui_plist_says_item_inactive (plist)) | 264 if (!gui_item_active_p (&gui_item)) |
528 item_info.fState = MFS_GRAYED; | 265 item_info.fState = MFS_GRAYED; |
529 /* Temptation is to put 'else' right here. Although, the | 266 /* Temptation is to put 'else' right here. Although, the |
530 displayed item won't have an arrow indicating that it is a | 267 displayed item won't have an arrow indicating that it is a |
531 popup. So we go ahead a little bit more and create a popup */ | 268 popup. So we go ahead a little bit more and create a popup */ |
532 submenu = create_empty_popup_menu(); | 269 submenu = create_empty_popup_menu(); |
533 | 270 |
534 item_info.fMask |= MIIM_SUBMENU; | 271 item_info.fMask |= MIIM_SUBMENU; |
535 item_info.dwTypeData = plist_get_menu_item_name (subname, Qnil, plist); | 272 item_info.dwTypeData = displayable_menu_item (&gui_item); |
536 item_info.hSubMenu = submenu; | 273 item_info.hSubMenu = submenu; |
537 | |
538 UNGCPRO; /* plist */ | |
539 | 274 |
540 if (!(item_info.fState & MFS_GRAYED)) | 275 if (!(item_info.fState & MFS_GRAYED)) |
541 { | 276 { |
542 /* Now add the full submenu path as a value to the hash table, | 277 /* Now add the full submenu path as a value to the hash table, |
543 keyed by menu handle */ | 278 keyed by menu handle */ |
544 if (NILP(path)) | 279 if (NILP(path)) |
545 path = list1 (subname); | 280 /* list1 cannot GC */ |
546 else { | 281 path = list1 (gui_item.name); |
547 Lisp_Object arg[2]; | 282 else |
548 arg[0] = path; | 283 { |
549 arg[1] = list1 (subname); | 284 Lisp_Object arg[2] = { path, list1 (gui_item.name) }; |
550 GCPRO1 (arg[1]); | 285 /* Fappend gcpro'es its arg */ |
551 path = Fappend (2, arg); | 286 path = Fappend (2, arg); |
552 UNGCPRO; /* arg[1] */ | 287 } |
553 } | 288 |
554 | 289 /* Fputhash GCPRO'es PATH */ |
555 GCPRO1 (path); | |
556 Fputhash (hmenu_to_lisp_object (submenu), path, hash_tab); | 290 Fputhash (hmenu_to_lisp_object (submenu), path, hash_tab); |
557 UNGCPRO; /* path */ | |
558 } | 291 } |
292 UNGCPRO; /* gui_item */ | |
559 } | 293 } |
560 else if (VECTORP (item)) | 294 else if (VECTORP (item)) |
561 { | 295 { |
562 /* An ordinary item */ | 296 /* An ordinary item */ |
563 Lisp_Object plist, name, callback, style, id; | 297 Lisp_Object style, id; |
564 | 298 struct gui_item gui_item; |
565 gui_parse_button_descriptor (item, &name, &callback, &plist); | 299 struct gcpro gcpro1; |
566 GCPRO2 (plist, callback); | 300 |
567 | 301 gui_item_init (&gui_item); |
568 if (gui_plist_says_item_excluded (plist, Vmenubar_configuration)) | 302 GCPRO1 (gui_item); |
303 gcpro1.nvars = GUI_ITEM_GCPRO_COUNT; | |
304 | |
305 gui_parse_item_keywords (item, &gui_item); | |
306 | |
307 if (!gui_item_included_p (&gui_item, Vmenubar_configuration)) | |
569 return; | 308 return; |
570 | 309 |
571 if (gui_plist_says_item_inactive (plist)) | 310 if (!gui_item_active_p (&gui_item)) |
572 item_info.fState |= MFS_GRAYED; | 311 item_info.fState = MFS_GRAYED; |
573 | 312 |
574 style = gui_plist_get_current_style (plist); | 313 style = (NILP (gui_item.selected) || NILP (Feval (gui_item.selected)) |
314 ? Qnil : gui_item.style); | |
315 | |
575 if (EQ (style, Qradio)) | 316 if (EQ (style, Qradio)) |
576 { | 317 { |
577 item_info.fType |= MFT_RADIOCHECK; | 318 item_info.fType |= MFT_RADIOCHECK; |
578 item_info.fState |= MFS_CHECKED; | 319 item_info.fState |= MFS_CHECKED; |
579 } | 320 } |
580 else if (EQ (style, Qtoggle)) | 321 else if (EQ (style, Qtoggle)) |
581 { | 322 { |
582 item_info.fState |= MFS_CHECKED; | 323 item_info.fState |= MFS_CHECKED; |
583 } | 324 } |
584 | 325 |
585 id = allocate_menu_item_id (path, name); | 326 id = allocate_menu_item_id (path, gui_item.name, |
586 Fputhash (id, callback, hash_tab); | 327 gui_item.suffix); |
587 | 328 Fputhash (id, gui_item.callback, hash_tab); |
588 UNGCPRO; /* plist, callback */ | |
589 | 329 |
590 item_info.wID = (UINT) XINT(id); | 330 item_info.wID = (UINT) XINT(id); |
591 item_info.fType |= MFT_STRING; | 331 item_info.fType |= MFT_STRING; |
592 item_info.dwTypeData = plist_get_menu_item_name (name, callback, plist); | 332 item_info.dwTypeData = displayable_menu_item (&gui_item); |
333 | |
334 UNGCPRO; /* gui_item */ | |
593 } | 335 } |
594 else | 336 else |
595 { | 337 { |
596 signal_simple_error ("Ill-constructed menu descriptor", item); | 338 signal_simple_error ("Mailformed menu item descriptor", item); |
597 } | 339 } |
598 | 340 |
599 if (flush_right) | 341 if (flush_right) |
600 item_info.fType |= MFT_RIGHTJUSTIFY; | 342 item_info.fType |= MFT_RIGHTJUSTIFY; |
601 | 343 |
612 * When called to checksum, DESCRIPTOR has the same meaning, POPULATE_P | 354 * When called to checksum, DESCRIPTOR has the same meaning, POPULATE_P |
613 * is zero, PATH must be Qnil, and the rest of parameters is ignored. | 355 * is zero, PATH must be Qnil, and the rest of parameters is ignored. |
614 * Return value is the menu checksum. | 356 * Return value is the menu checksum. |
615 */ | 357 */ |
616 static unsigned long | 358 static unsigned long |
617 populate_or_checksum_helper (HMENU menu, Lisp_Object path, Lisp_Object descriptor, | 359 populate_or_checksum_helper (HMENU menu, Lisp_Object path, Lisp_Object desc, |
618 Lisp_Object hash_tab, int bar_p, int populate_p) | 360 Lisp_Object hash_tab, int bar_p, int populate_p) |
619 { | 361 { |
620 Lisp_Object menu_name, plist, item_desc; | 362 Lisp_Object item_desc; |
621 int deep_p, flush_right; | 363 int deep_p, flush_right; |
622 struct gcpro gcpro1; | 364 struct gcpro gcpro1; |
623 unsigned long checksum = 0; | 365 unsigned long checksum = 0; |
366 struct gui_item gui_item; | |
367 | |
368 gui_item_init (&gui_item); | |
369 GCPRO1 (gui_item); | |
370 gcpro1.nvars = GUI_ITEM_GCPRO_COUNT; | |
624 | 371 |
625 /* Will initially contain only "(empty)" */ | 372 /* Will initially contain only "(empty)" */ |
626 if (populate_p) | 373 if (populate_p) |
627 empty_menu (menu, 1); | 374 empty_menu (menu, 1); |
628 | 375 |
629 /* PATH set to nil indicates top-level popup or menubar */ | 376 /* PATH set to nil indicates top-level popup or menubar */ |
630 deep_p = !NILP (path); | 377 deep_p = !NILP (path); |
631 | 378 |
632 if (!deep_p) | |
633 top_level_menu = menu; | |
634 | |
635 if (!CONSP(descriptor)) | |
636 signal_simple_error ("Menu descriptor must be a list", descriptor); | |
637 | |
638 if (STRINGP (XCAR (descriptor))) | |
639 { | |
640 menu_name = XCAR (descriptor); | |
641 descriptor = XCDR (descriptor); | |
642 } | |
643 else | |
644 { | |
645 menu_name = Qnil; | |
646 if (deep_p) /* Not a popup or bar */ | |
647 signal_simple_error ("Menu must have a name", descriptor); | |
648 } | |
649 | |
650 /* Fetch keywords prepending the item list */ | 379 /* Fetch keywords prepending the item list */ |
651 descriptor = gui_parse_menu_keywords (descriptor, &plist); | 380 desc = menu_parse_submenu_keywords (desc, &gui_item); |
652 GCPRO1 (plist); | 381 |
653 descriptor = gui_plist_apply_filter (plist, descriptor); | 382 /* Check that menu name is specified when expected */ |
654 UNGCPRO; /* plist */ | 383 if (NILP (gui_item.name) && deep_p) |
655 | 384 signal_simple_error ("Menu must have a name", desc); |
656 /* Loop thru the descriptor's CDR and add items for each entry */ | 385 |
386 /* Apply filter if specified */ | |
387 if (!NILP (gui_item.filter)) | |
388 desc = call1 (gui_item.filter, desc); | |
389 | |
390 /* Loop thru the desc's CDR and add items for each entry */ | |
657 flush_right = 0; | 391 flush_right = 0; |
658 EXTERNAL_LIST_LOOP (item_desc, descriptor) | 392 EXTERNAL_LIST_LOOP (item_desc, desc) |
659 { | 393 { |
660 if (NILP (XCAR (item_desc))) | 394 if (NILP (XCAR (item_desc))) |
661 { | 395 { |
662 if (bar_p) | 396 if (bar_p) |
663 flush_right = 1; | 397 flush_right = 1; |
679 RemoveMenu (menu, EMPTY_ITEM_ID, MF_BYCOMMAND); | 413 RemoveMenu (menu, EMPTY_ITEM_ID, MF_BYCOMMAND); |
680 | 414 |
681 /* Add the header to the popup, if told so. The same as in X - an | 415 /* Add the header to the popup, if told so. The same as in X - an |
682 insensitive item, and a separator (Seems to me, there were | 416 insensitive item, and a separator (Seems to me, there were |
683 two separators in X... In Windows this looks ugly, anywats. */ | 417 two separators in X... In Windows this looks ugly, anywats. */ |
684 if (!bar_p && !deep_p && popup_menu_titles && !NILP(menu_name)) | 418 if (!bar_p && !deep_p && popup_menu_titles && !NILP(gui_item.name)) |
685 { | 419 { |
420 CHECK_STRING (gui_item.name); | |
686 InsertMenu (menu, 0, MF_BYPOSITION | MF_STRING | MF_DISABLED, | 421 InsertMenu (menu, 0, MF_BYPOSITION | MF_STRING | MF_DISABLED, |
687 0, XSTRING_DATA(menu_name)); | 422 0, XSTRING_DATA(gui_item.name)); |
688 InsertMenu (menu, 1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); | 423 InsertMenu (menu, 1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); |
689 SetMenuDefaultItem (menu, 0, MF_BYPOSITION); | 424 SetMenuDefaultItem (menu, 0, MF_BYPOSITION); |
690 } | 425 } |
691 } | 426 } |
427 UNGCPRO; /* gui_item */ | |
692 return checksum; | 428 return checksum; |
693 } | 429 } |
694 | 430 |
695 static void | 431 static void |
696 populate_menu (HMENU menu, Lisp_Object path, Lisp_Object descriptor, | 432 populate_menu (HMENU menu, Lisp_Object path, Lisp_Object desc, |
697 Lisp_Object hash_tab, int bar_p) | 433 Lisp_Object hash_tab, int bar_p) |
698 { | 434 { |
699 populate_or_checksum_helper (menu, path, descriptor, hash_tab, bar_p, 1); | 435 populate_or_checksum_helper (menu, path, desc, hash_tab, bar_p, 1); |
700 } | 436 } |
701 | 437 |
702 static unsigned long | 438 static unsigned long |
703 checksum_menu (Lisp_Object descriptor) | 439 checksum_menu (Lisp_Object desc) |
704 { | 440 { |
705 return populate_or_checksum_helper (NULL, Qnil, descriptor, Qunbound, 0, 0); | 441 return populate_or_checksum_helper (NULL, Qnil, desc, Qunbound, 0, 0); |
706 } | |
707 | |
708 static Lisp_Object | |
709 find_menu (Lisp_Object desc, Lisp_Object path) | |
710 { | |
711 /* #### find-menu-item is not what's required here. | |
712 Need to write this in C, or improve lisp */ | |
713 if (!NILP (path)) | |
714 { | |
715 desc = call2 (Qfind_menu_item, desc, path); | |
716 /* desc is (supposed to be) (ITEM . PARENT). Supposed | |
717 to signal but sometimes manages to return nil */ | |
718 if (!NILP(desc)) | |
719 { | |
720 CHECK_CONS (desc); | |
721 desc = XCAR (desc); | |
722 } | |
723 } | |
724 return desc; | |
725 } | 442 } |
726 | 443 |
727 static void | 444 static void |
728 update_frame_menubar_maybe (struct frame* f) | 445 update_frame_menubar_maybe (struct frame* f) |
729 { | 446 { |
730 HMENU menubar = GetMenu (FRAME_MSWINDOWS_HANDLE (f)); | 447 HMENU menubar = GetMenu (FRAME_MSWINDOWS_HANDLE (f)); |
731 struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f)); | 448 struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f)); |
732 Lisp_Object desc = (!NILP (w->menubar_visible_p) | 449 Lisp_Object desc = (!NILP (w->menubar_visible_p) |
733 ? symbol_value_in_buffer (Qcurrent_menubar, w->buffer) | 450 ? symbol_value_in_buffer (Qcurrent_menubar, w->buffer) |
734 : Qnil); | 451 : Qnil); |
452 | |
453 top_level_menu = menubar; | |
735 | 454 |
736 if (NILP (desc) && menubar != NULL) | 455 if (NILP (desc) && menubar != NULL) |
737 { | 456 { |
738 /* Menubar has gone */ | 457 /* Menubar has gone */ |
739 FRAME_MSWINDOWS_MENU_HASHTABLE(f) = Qnil; | 458 FRAME_MSWINDOWS_MENU_HASHTABLE(f) = Qnil; |
840 error ("internal menu error"); | 559 error ("internal menu error"); |
841 #endif | 560 #endif |
842 | 561 |
843 /* Now find a desc chunk for it. If none, then probably menu open | 562 /* Now find a desc chunk for it. If none, then probably menu open |
844 hook has played too much games around stuff */ | 563 hook has played too much games around stuff */ |
845 desc = current_menudesc; | 564 desc = Fmenu_find_real_submenu (current_menudesc, path); |
846 if (!NILP (path)) | 565 if (NILP (desc)) |
847 { | 566 signal_simple_error ("This menu does not exist any more", path); |
848 desc = find_menu (desc, path); | |
849 if (NILP (desc)) | |
850 signal_simple_error ("This menu does not exist any more", path); | |
851 } | |
852 | 567 |
853 /* Now, stuff it */ | 568 /* Now, stuff it */ |
854 /* DESC may be generated by filter, so we have to gcpro it */ | 569 /* DESC may be generated by filter, so we have to gcpro it */ |
855 GCPRO1 (desc); | 570 GCPRO1 (desc); |
856 populate_menu (menu, path, desc, current_hashtable, 0); | 571 populate_menu (menu, path, desc, current_hashtable, 0); |
860 | 575 |
861 static Lisp_Object | 576 static Lisp_Object |
862 unsafe_handle_wm_initmenu_1 (struct frame* f) | 577 unsafe_handle_wm_initmenu_1 (struct frame* f) |
863 { | 578 { |
864 /* This function can call lisp */ | 579 /* This function can call lisp */ |
580 | |
581 /* NOTE: This is called for the bar only, WM_INITMENU | |
582 for popups is filtered out */ | |
583 | |
865 /* #### - this menubar update mechanism is expensively anti-social and | 584 /* #### - this menubar update mechanism is expensively anti-social and |
866 the activate-menubar-hook is now mostly obsolete. */ | 585 the activate-menubar-hook is now mostly obsolete. */ |
867 | 586 |
868 /* We simply ignore return value. In any case, we construct the bar | 587 /* We simply ignore return value. In any case, we construct the bar |
869 on the fly */ | 588 on the fly */ |
875 current_hashtable = FRAME_MSWINDOWS_MENU_HASHTABLE(f); | 594 current_hashtable = FRAME_MSWINDOWS_MENU_HASHTABLE(f); |
876 assert (HASHTABLEP (current_hashtable)); | 595 assert (HASHTABLEP (current_hashtable)); |
877 | 596 |
878 return Qt; | 597 return Qt; |
879 } | 598 } |
880 | |
881 #ifdef KKM_DOES_NOT_LIKE_UNDOCS_SOMETIMES | |
882 | |
883 /* #### This may become wrong in future Windows */ | |
884 | |
885 static Lisp_Object | |
886 unsafe_handle_wm_exitmenuloop_1 (struct frame* f) | |
887 { | |
888 if (!NILP (current_tracking_popup)) | |
889 prune_menubar (f); | |
890 return Qt; | |
891 } | |
892 | |
893 #endif | |
894 | 599 |
895 /* | 600 /* |
896 * Return value is Qt if we have dispatched the command, | 601 * Return value is Qt if we have dispatched the command, |
897 * or Qnil if id has not been mapped to a callback. | 602 * or Qnil if id has not been mapped to a callback. |
898 * Window procedure may try other targets to route the | 603 * Window procedure may try other targets to route the |
922 if (SYMBOLP (command)) | 627 if (SYMBOLP (command)) |
923 funcsym = Qcall_interactively; | 628 funcsym = Qcall_interactively; |
924 else if (CONSP (command)) | 629 else if (CONSP (command)) |
925 funcsym = Qeval; | 630 funcsym = Qeval; |
926 else | 631 else |
927 signal_simple_error ("Illegal callback", command); | 632 signal_simple_error ("Callback must be either evallable form or a symbol", |
633 command); | |
928 | 634 |
929 XSETFRAME (frame, f); | 635 XSETFRAME (frame, f); |
930 enqueue_misc_user_event (frame, funcsym, command); | 636 enqueue_misc_user_event (frame, funcsym, command); |
931 | 637 |
932 /* Needs good bump also, for WM_COMMAND may have been dispatched from | 638 /* Needs good bump also, for WM_COMMAND may have been dispatched from |
957 unsafe_handle_wm_initmenu (Lisp_Object u_n_u_s_e_d) | 663 unsafe_handle_wm_initmenu (Lisp_Object u_n_u_s_e_d) |
958 { | 664 { |
959 return unsafe_handle_wm_initmenu_1 (wm_initmenu_frame); | 665 return unsafe_handle_wm_initmenu_1 (wm_initmenu_frame); |
960 } | 666 } |
961 | 667 |
962 #ifdef KKM_DOES_NOT_LIKE_UNDOCS_SOMETIMES | |
963 static Lisp_Object | |
964 unsafe_handle_wm_exitmenuloop (Lisp_Object u_n_u_s_e_d) | |
965 { | |
966 return unsafe_handle_wm_exitmenuloop_1 (wm_initmenu_frame); | |
967 } | |
968 #endif | |
969 | |
970 Lisp_Object | 668 Lisp_Object |
971 mswindows_handle_wm_initmenupopup (HMENU hmenu, struct frame* frm) | 669 mswindows_handle_wm_initmenupopup (HMENU hmenu, struct frame* frm) |
972 { | 670 { |
973 /* We cannot pass hmenu as a lisp object. Use static var */ | 671 /* We cannot pass hmenu as a lisp object. Use static var */ |
974 wm_initmenu_menu = hmenu; | 672 wm_initmenu_menu = hmenu; |
986 return mswindows_protect_modal_loop (unsafe_handle_wm_initmenu, Qnil); | 684 return mswindows_protect_modal_loop (unsafe_handle_wm_initmenu, Qnil); |
987 } | 685 } |
988 return Qt; | 686 return Qt; |
989 } | 687 } |
990 | 688 |
689 /* #### This function goes away. Removing it now may | |
690 interfere with pending patch 980128-jhar */ | |
991 Lisp_Object | 691 Lisp_Object |
992 mswindows_handle_wm_exitmenuloop (struct frame* f) | 692 mswindows_handle_wm_exitmenuloop (struct frame* f) |
993 { | 693 { |
994 #ifdef KKM_DOES_NOT_LIKE_UNDOCS_SOMETIMES | |
995 wm_initmenu_frame = f; | |
996 return mswindows_protect_modal_loop (unsafe_handle_wm_exitmenuloop, Qnil); | |
997 #else | |
998 return Qt; | 694 return Qt; |
999 #endif | |
1000 } | 695 } |
1001 | 696 |
1002 | 697 |
1003 /*------------------------------------------------------------------------*/ | 698 /*------------------------------------------------------------------------*/ |
1004 /* Methods */ | 699 /* Methods */ |
1005 /*------------------------------------------------------------------------*/ | 700 /*------------------------------------------------------------------------*/ |
1006 | 701 |
1007 static void | 702 static void |
1008 mswindows_update_frame_menubars (struct frame* f) | 703 mswindows_update_frame_menubars (struct frame* f) |
1009 { | 704 { |
1010 update_frame_menubar_maybe (f); | 705 /* #### KLUDGE. menubar.c calls us when the following |
706 condition is true: | |
707 (f->menubar_changed || f->windows_changed) | |
708 Is that much really necessary? | |
709 */ | |
710 if (f->menubar_changed) | |
711 update_frame_menubar_maybe (f); | |
1011 } | 712 } |
1012 | 713 |
1013 static void | 714 static void |
1014 mswindows_free_frame_menubars (struct frame* f) | 715 mswindows_free_frame_menubars (struct frame* f) |
1015 { | 716 { |
1058 | 759 |
1059 current_menudesc = menu_desc; | 760 current_menudesc = menu_desc; |
1060 current_hashtable = Fmake_hashtable (make_int(10), Qequal); | 761 current_hashtable = Fmake_hashtable (make_int(10), Qequal); |
1061 menu = create_empty_popup_menu(); | 762 menu = create_empty_popup_menu(); |
1062 Fputhash (hmenu_to_lisp_object (menu), Qnil, current_hashtable); | 763 Fputhash (hmenu_to_lisp_object (menu), Qnil, current_hashtable); |
764 top_level_menu = menu; | |
1063 | 765 |
1064 ok = TrackPopupMenu (menu, | 766 ok = TrackPopupMenu (menu, |
1065 TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, | 767 TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, |
1066 pt.x, pt.y, 0, | 768 pt.x, pt.y, 0, |
1067 FRAME_MSWINDOWS_HANDLE (f), NULL); | 769 FRAME_MSWINDOWS_HANDLE (f), NULL); |
1084 /* Initialization */ | 786 /* Initialization */ |
1085 /*------------------------------------------------------------------------*/ | 787 /*------------------------------------------------------------------------*/ |
1086 void | 788 void |
1087 syms_of_menubar_mswindows (void) | 789 syms_of_menubar_mswindows (void) |
1088 { | 790 { |
1089 defsymbol (&Qfind_menu_item, "find-menu-item"); | |
1090 } | 791 } |
1091 | 792 |
1092 void | 793 void |
1093 console_type_create_menubar_mswindows (void) | 794 console_type_create_menubar_mswindows (void) |
1094 { | 795 { |
1104 current_hashtable = Qnil; | 805 current_hashtable = Qnil; |
1105 | 806 |
1106 staticpro (¤t_menudesc); | 807 staticpro (¤t_menudesc); |
1107 staticpro (¤t_hashtable); | 808 staticpro (¤t_hashtable); |
1108 | 809 |
1109 Fprovide (intern ("mswindows-menubars")); | |
1110 } |