Commit | Line | Data |
---|---|---|
dcfdbac7 | 1 | /* X Communication module for terminals which understand the X protocol. |
774910eb | 2 | Copyright (C) 1986, 1988, 1992 Free Software Foundation, Inc. |
dcfdbac7 JB |
3 | |
4 | This file is part of GNU Emacs. | |
5 | ||
6 | GNU Emacs is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
774910eb | 8 | the Free Software Foundation; either version 2, or (at your option) |
dcfdbac7 JB |
9 | any later version. |
10 | ||
11 | GNU Emacs is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with GNU Emacs; see the file COPYING. If not, write to | |
18 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
19 | ||
20 | /* X pop-up deck-of-cards menu facility for gnuemacs. | |
21 | * | |
22 | * Written by Jon Arnold and Roman Budzianowski | |
23 | * Mods and rewrite by Robert Krawitz | |
24 | * | |
25 | */ | |
26 | ||
dcfdbac7 JB |
27 | #ifdef XDEBUG |
28 | #include <stdio.h> | |
29 | #endif | |
30 | ||
31 | /* On 4.3 this loses if it comes after xterm.h. */ | |
32 | #include <signal.h> | |
33 | #include "config.h" | |
34 | #include "lisp.h" | |
7708e9bd | 35 | #include "frame.h" |
dcfdbac7 | 36 | #include "window.h" |
031b0e31 | 37 | #include "keyboard.h" |
dcfdbac7 JB |
38 | |
39 | /* This may include sys/types.h, and that somehow loses | |
40 | if this is not done before the other system files. */ | |
41 | #include "xterm.h" | |
42 | ||
43 | /* Load sys/types.h if not already loaded. | |
44 | In some systems loading it twice is suicidal. */ | |
45 | #ifndef makedev | |
46 | #include <sys/types.h> | |
47 | #endif | |
48 | ||
49 | #include "dispextern.h" | |
50 | ||
51 | #ifdef HAVE_X11 | |
52 | #include "../oldXMenu/XMenu.h" | |
53 | #else | |
54 | #include <X/XMenu.h> | |
55 | #endif | |
56 | ||
57 | #define min(x,y) (((x) < (y)) ? (x) : (y)) | |
58 | #define max(x,y) (((x) > (y)) ? (x) : (y)) | |
59 | ||
60 | #define NUL 0 | |
61 | ||
62 | #ifndef TRUE | |
63 | #define TRUE 1 | |
64 | #define FALSE 0 | |
65 | #endif TRUE | |
66 | ||
67 | #ifdef HAVE_X11 | |
68 | extern Display *x_current_display; | |
69 | #else | |
70 | #define ButtonReleaseMask ButtonReleased | |
71 | #endif /* not HAVE_X11 */ | |
72 | ||
73 | Lisp_Object xmenu_show (); | |
74 | extern int x_error_handler (); | |
75 | ||
76 | /*************************************************************/ | |
77 | ||
78 | #if 0 | |
79 | /* Ignoring the args is easiest. */ | |
80 | xmenu_quit () | |
81 | { | |
82 | error ("Unknown XMenu error"); | |
83 | } | |
84 | #endif | |
85 | ||
86 | DEFUN ("x-popup-menu",Fx_popup_menu, Sx_popup_menu, 1, 2, 0, | |
87 | "Pop up a deck-of-cards menu and return user's selection.\n\ | |
088831f6 RS |
88 | POSITION is a position specification. This is either a mouse button event\n\ |
89 | or a list ((XOFFSET YOFFSET) WINDOW)\n\ | |
dcfdbac7 | 90 | where XOFFSET and YOFFSET are positions in characters from the top left\n\ |
7708e9bd | 91 | corner of WINDOW's frame. A mouse-event list will serve for this.\n\ |
dcfdbac7 JB |
92 | This controls the position of the center of the first line\n\ |
93 | in the first pane of the menu, not the top left of the menu as a whole.\n\ | |
94 | \n\ | |
088831f6 RS |
95 | MENU is a specifier for a menu. For the simplest case, MENU is a keymap.\n\ |
96 | The menu items come from key bindings that have a menu string as well as\n\ | |
97 | a definition; actually, the \"definition\" in such a key binding looks like\n\ | |
98 | \(STRING . REAL-DEFINITION). To give the menu a title, put a string into\n\ | |
99 | the keymap as a top-level element.\n\n\ | |
100 | You can also use a list of keymaps as MENU.\n\ | |
101 | Then each keymap makes a separate pane.\n\n\ | |
102 | Alternatively, you can specify a menu of multiple panes\n\ | |
103 | with a list of the form\n\ | |
104 | \(TITLE PANE1 PANE2...), where each pane is a list of form\n\ | |
dcfdbac7 JB |
105 | \(TITLE (LINE ITEM)...). Each line should be a string, and item should\n\ |
106 | be the return value for that line (i.e. if it is selected.") | |
088831f6 RS |
107 | (position, menu) |
108 | Lisp_Object position, menu; | |
dcfdbac7 JB |
109 | { |
110 | int number_of_panes; | |
088831f6 | 111 | Lisp_Object XMenu_return, keymap, tem; |
dcfdbac7 JB |
112 | int XMenu_xpos, XMenu_ypos; |
113 | char **menus; | |
114 | char ***names; | |
115 | Lisp_Object **obj_list; | |
116 | int *items; | |
117 | char *title; | |
118 | char *error_name; | |
119 | Lisp_Object ltitle, selection; | |
120 | int i, j; | |
7708e9bd | 121 | FRAME_PTR f; |
dcfdbac7 JB |
122 | Lisp_Object x, y, window; |
123 | ||
088831f6 RS |
124 | /* Decode the first argument: find the window and the coordinates. */ |
125 | tem = Fcar (position); | |
126 | if (XTYPE (tem) == Lisp_Cons) | |
127 | { | |
128 | window = Fcar (Fcdr (position)); | |
129 | x = Fcar (tem); | |
130 | y = Fcar (Fcdr (tem)); | |
131 | } | |
132 | else | |
133 | { | |
031b0e31 JB |
134 | tem = EVENT_START (position); |
135 | window = POSN_WINDOW (tem); | |
136 | tem = POSN_WINDOW_POSN (tem); | |
088831f6 RS |
137 | x = Fcar (tem); |
138 | y = Fcdr (tem); | |
139 | } | |
774910eb | 140 | CHECK_LIVE_WINDOW (window, 0); |
dcfdbac7 JB |
141 | CHECK_NUMBER (x, 0); |
142 | CHECK_NUMBER (y, 0); | |
088831f6 | 143 | |
7708e9bd | 144 | f = XFRAME (WINDOW_FRAME (XWINDOW (window))); |
dcfdbac7 | 145 | |
088831f6 RS |
146 | XMenu_xpos |
147 | = FONT_WIDTH (f->display.x->font) * (XINT (x) + XWINDOW (window)->left); | |
148 | XMenu_ypos | |
149 | = FONT_HEIGHT (f->display.x->font) * (XINT (y) + XWINDOW (window)->top); | |
7708e9bd JB |
150 | XMenu_xpos += f->display.x->left_pos; |
151 | XMenu_ypos += f->display.x->top_pos; | |
dcfdbac7 | 152 | |
088831f6 RS |
153 | keymap = Fkeymapp (menu); |
154 | tem = Qnil; | |
155 | if (XTYPE (menu) == Lisp_Cons) | |
156 | tem = Fkeymapp (Fcar (menu)); | |
157 | if (!NILP (keymap)) | |
158 | { | |
159 | /* We were given a keymap. Extract menu info from the keymap. */ | |
160 | Lisp_Object prompt; | |
161 | keymap = get_keymap (menu); | |
162 | ||
163 | /* Search for a string appearing directly as an element of the keymap. | |
164 | That string is the title of the menu. */ | |
165 | prompt = map_prompt (keymap); | |
166 | if (!NILP (prompt)) | |
167 | title = (char *) XSTRING (prompt)->data; | |
168 | ||
169 | /* Extract the detailed info to make one pane. */ | |
170 | number_of_panes = keymap_panes (&obj_list, &menus, &names, &items, | |
171 | &menu, 1); | |
172 | /* The menu title seems to be ignored, | |
173 | so put it in the pane title. */ | |
174 | if (menus[0] == 0) | |
175 | menus[0] = title; | |
176 | } | |
177 | else if (!NILP (tem)) | |
dcfdbac7 | 178 | { |
088831f6 RS |
179 | /* We were given a list of keymaps. */ |
180 | Lisp_Object prompt; | |
181 | int nmaps = XFASTINT (Flength (menu)); | |
182 | Lisp_Object *maps | |
183 | = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object)); | |
184 | int i; | |
185 | title = 0; | |
186 | ||
187 | /* The first keymap that has a prompt string | |
188 | supplies the menu title. */ | |
189 | for (tem = menu, i = 0; XTYPE (tem) == Lisp_Cons; tem = Fcdr (tem)) | |
dcfdbac7 | 190 | { |
088831f6 RS |
191 | maps[i++] = keymap = get_keymap (Fcar (tem)); |
192 | ||
193 | prompt = map_prompt (keymap); | |
194 | if (title == 0 && !NILP (prompt)) | |
195 | title = (char *) XSTRING (prompt)->data; | |
dcfdbac7 | 196 | } |
088831f6 RS |
197 | |
198 | /* Extract the detailed info to make one pane. */ | |
199 | number_of_panes = keymap_panes (&obj_list, &menus, &names, &items, | |
200 | maps, nmaps); | |
201 | /* The menu title seems to be ignored, | |
202 | so put it in the pane title. */ | |
203 | if (menus[0] == 0) | |
204 | menus[0] = title; | |
205 | } | |
206 | else | |
207 | { | |
208 | /* We were given an old-fashioned menu. */ | |
209 | ltitle = Fcar (menu); | |
210 | CHECK_STRING (ltitle, 1); | |
211 | title = (char *) XSTRING (ltitle)->data; | |
212 | number_of_panes = list_of_panes (&obj_list, &menus, &names, &items, | |
213 | Fcdr (menu)); | |
214 | } | |
215 | #ifdef XDEBUG | |
216 | fprintf (stderr, "Panes = %d\n", number_of_panes); | |
217 | for (i = 0; i < number_of_panes; i++) | |
218 | { | |
219 | fprintf (stderr, "Pane %d has lines %d title %s\n", | |
220 | i, items[i], menus[i]); | |
221 | for (j = 0; j < items[i]; j++) | |
222 | fprintf (stderr, " Item %d %s\n", j, names[i][j]); | |
dcfdbac7 JB |
223 | } |
224 | #endif | |
225 | BLOCK_INPUT; | |
226 | selection = xmenu_show (ROOT_WINDOW, XMenu_xpos, XMenu_ypos, names, menus, | |
088831f6 RS |
227 | items, number_of_panes, obj_list, title, |
228 | &error_name); | |
dcfdbac7 | 229 | UNBLOCK_INPUT; |
088831f6 | 230 | /* fprintf (stderr, "selection = %x\n", selection); */ |
dcfdbac7 JB |
231 | if (selection != NUL) |
232 | { /* selected something */ | |
233 | XMenu_return = selection; | |
234 | } | |
235 | else | |
236 | { /* nothing selected */ | |
237 | XMenu_return = Qnil; | |
238 | } | |
239 | /* now free up the strings */ | |
088831f6 | 240 | for (i = 0; i < number_of_panes; i++) |
dcfdbac7 JB |
241 | { |
242 | free (names[i]); | |
243 | free (obj_list[i]); | |
244 | } | |
245 | free (menus); | |
246 | free (obj_list); | |
247 | free (names); | |
248 | free (items); | |
088831f6 | 249 | /* free (title); */ |
dcfdbac7 JB |
250 | if (error_name) error (error_name); |
251 | return XMenu_return; | |
252 | } | |
253 | ||
254 | struct indices { | |
255 | int pane; | |
256 | int line; | |
257 | }; | |
258 | ||
259 | Lisp_Object | |
260 | xmenu_show (parent, startx, starty, line_list, pane_list, line_cnt, | |
261 | pane_cnt, item_list, title, error) | |
262 | Window parent; | |
263 | int startx, starty; /* upper left corner position BROKEN */ | |
264 | char **line_list[]; /* list of strings for items */ | |
265 | char *pane_list[]; /* list of pane titles */ | |
266 | char *title; | |
267 | int pane_cnt; /* total number of panes */ | |
268 | Lisp_Object *item_list[]; /* All items */ | |
269 | int line_cnt[]; /* Lines in each pane */ | |
270 | char **error; /* Error returned */ | |
271 | { | |
272 | XMenu *GXMenu; | |
273 | int last, panes, selidx, lpane, status; | |
274 | int lines, sofar; | |
275 | Lisp_Object entry; | |
276 | /* struct indices *datap, *datap_save; */ | |
277 | char *datap; | |
278 | int ulx, uly, width, height; | |
279 | int dispwidth, dispheight; | |
088831f6 RS |
280 | |
281 | if (pane_cnt == 0) | |
282 | return 0; | |
283 | ||
dcfdbac7 JB |
284 | *error = (char *) 0; /* Initialize error pointer to null */ |
285 | GXMenu = XMenuCreate (XDISPLAY parent, "emacs"); | |
286 | if (GXMenu == NUL) | |
287 | { | |
288 | *error = "Can't create menu"; | |
289 | return (0); | |
290 | } | |
291 | ||
088831f6 RS |
292 | for (panes = 0, lines = 0; panes < pane_cnt; |
293 | lines += line_cnt[panes], panes++) | |
dcfdbac7 JB |
294 | ; |
295 | /* datap = (struct indices *) xmalloc (lines * sizeof (struct indices)); */ | |
088831f6 | 296 | /* datap = (char *) xmalloc (lines * sizeof (char)); |
dcfdbac7 JB |
297 | datap_save = datap;*/ |
298 | ||
088831f6 RS |
299 | for (panes = 0, sofar = 0; panes < pane_cnt; |
300 | sofar += line_cnt[panes], panes++) | |
dcfdbac7 JB |
301 | { |
302 | /* create all the necessary panes */ | |
303 | lpane = XMenuAddPane (XDISPLAY GXMenu, pane_list[panes], TRUE); | |
304 | if (lpane == XM_FAILURE) | |
305 | { | |
306 | XMenuDestroy (XDISPLAY GXMenu); | |
307 | *error = "Can't create pane"; | |
308 | return (0); | |
309 | } | |
088831f6 | 310 | for (selidx = 0; selidx < line_cnt[panes]; selidx++) |
dcfdbac7 JB |
311 | { |
312 | /* add the selection stuff to the menus */ | |
313 | /* datap[selidx+sofar].pane = panes; | |
314 | datap[selidx+sofar].line = selidx; */ | |
315 | if (XMenuAddSelection (XDISPLAY GXMenu, lpane, 0, | |
316 | line_list[panes][selidx], TRUE) | |
317 | == XM_FAILURE) | |
318 | { | |
319 | XMenuDestroy (XDISPLAY GXMenu); | |
320 | /* free (datap); */ | |
321 | *error = "Can't add selection to menu"; | |
322 | /* error ("Can't add selection to menu"); */ | |
323 | return (0); | |
324 | } | |
325 | } | |
326 | } | |
327 | /* all set and ready to fly */ | |
328 | XMenuRecompute (XDISPLAY GXMenu); | |
329 | dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display)); | |
330 | dispheight = DisplayHeight (x_current_display, XDefaultScreen (x_current_display)); | |
331 | startx = min (startx, dispwidth); | |
332 | starty = min (starty, dispheight); | |
333 | startx = max (startx, 1); | |
334 | starty = max (starty, 1); | |
335 | XMenuLocate (XDISPLAY GXMenu, 0, 0, startx, starty, | |
336 | &ulx, &uly, &width, &height); | |
337 | if (ulx+width > dispwidth) | |
338 | { | |
339 | startx -= (ulx + width) - dispwidth; | |
340 | ulx = dispwidth - width; | |
341 | } | |
342 | if (uly+height > dispheight) | |
343 | { | |
344 | starty -= (uly + height) - dispheight; | |
345 | uly = dispheight - height; | |
346 | } | |
347 | if (ulx < 0) startx -= ulx; | |
348 | if (uly < 0) starty -= uly; | |
349 | ||
350 | XMenuSetFreeze (GXMenu, TRUE); | |
351 | panes = selidx = 0; | |
352 | ||
353 | status = XMenuActivate (XDISPLAY GXMenu, &panes, &selidx, | |
354 | startx, starty, ButtonReleaseMask, &datap); | |
355 | switch (status) | |
356 | { | |
357 | case XM_SUCCESS: | |
358 | #ifdef XDEBUG | |
359 | fprintf (stderr, "pane= %d line = %d\n", panes, selidx); | |
360 | #endif | |
361 | entry = item_list[panes][selidx]; | |
362 | break; | |
363 | case XM_FAILURE: | |
088831f6 | 364 | /* free (datap_save); */ |
dcfdbac7 JB |
365 | XMenuDestroy (XDISPLAY GXMenu); |
366 | *error = "Can't activate menu"; | |
367 | /* error ("Can't activate menu"); */ | |
368 | case XM_IA_SELECT: | |
369 | case XM_NO_SELECT: | |
370 | entry = Qnil; | |
371 | break; | |
372 | } | |
373 | XMenuDestroy (XDISPLAY GXMenu); | |
088831f6 | 374 | /* free (datap_save);*/ |
dcfdbac7 JB |
375 | return (entry); |
376 | } | |
377 | ||
378 | syms_of_xmenu () | |
379 | { | |
380 | defsubr (&Sx_popup_menu); | |
381 | } | |
088831f6 RS |
382 | \f |
383 | /* Construct the vectors that describe a menu | |
384 | and store them in *VECTOR, *PANES, *NAMES and *ITEMS. | |
385 | Each of those four values is a vector indexed by pane number. | |
386 | Return the number of panes. | |
387 | ||
388 | KEYMAPS is a vector of keymaps. NMAPS gives the length of KEYMAPS. */ | |
389 | ||
390 | int | |
391 | keymap_panes (vector, panes, names, items, keymaps, nmaps) | |
392 | Lisp_Object ***vector; /* RETURN all menu objects */ | |
393 | char ***panes; /* RETURN pane names */ | |
394 | char ****names; /* RETURN all line names */ | |
395 | int **items; /* RETURN number of items per pane */ | |
396 | Lisp_Object *keymaps; | |
397 | int nmaps; | |
398 | { | |
399 | /* Number of panes we have made. */ | |
400 | int p = 0; | |
401 | /* Number of panes we have space for. */ | |
402 | int npanes_allocated = nmaps; | |
403 | int mapno; | |
404 | ||
405 | if (npanes_allocated < 4) | |
406 | npanes_allocated = 4; | |
407 | ||
408 | /* Make space for an estimated number of panes. */ | |
409 | *vector = (Lisp_Object **) xmalloc (npanes_allocated * sizeof (Lisp_Object *)); | |
410 | *panes = (char **) xmalloc (npanes_allocated * sizeof (char *)); | |
411 | *items = (int *) xmalloc (npanes_allocated * sizeof (int)); | |
412 | *names = (char ***) xmalloc (npanes_allocated * sizeof (char **)); | |
413 | ||
414 | /* Loop over the given keymaps, making a pane for each map. | |
415 | But don't make a pane that is empty--ignore that map instead. | |
416 | P is the number of panes we have made so far. */ | |
417 | for (mapno = 0; mapno < nmaps; mapno++) | |
418 | single_keymap_panes (keymaps[mapno], panes, vector, names, items, | |
419 | &p, &npanes_allocated, ""); | |
420 | ||
421 | /* Return the number of panes. */ | |
422 | return p; | |
423 | } | |
424 | ||
425 | /* This is a recursive subroutine of the previous function. | |
426 | It handles one keymap, KEYMAP. | |
427 | The other arguments are passed along | |
428 | or point to local variables of the previous function. */ | |
429 | ||
430 | single_keymap_panes (keymap, panes, vector, names, items, | |
431 | p_ptr, npanes_allocated_ptr, pane_name) | |
432 | Lisp_Object keymap; | |
433 | Lisp_Object ***vector; /* RETURN all menu objects */ | |
434 | char ***panes; /* RETURN pane names */ | |
435 | char ****names; /* RETURN all line names */ | |
436 | int **items; /* RETURN number of items per pane */ | |
437 | int *p_ptr; | |
438 | int *npanes_allocated_ptr; | |
439 | char *pane_name; | |
440 | { | |
441 | int i; | |
442 | Lisp_Object pending_maps; | |
443 | Lisp_Object tail, item, item1, item2, table; | |
444 | ||
445 | pending_maps = Qnil; | |
446 | ||
447 | /* Make sure we have room for another pane. */ | |
448 | if (*p_ptr == *npanes_allocated_ptr) | |
449 | { | |
450 | *npanes_allocated_ptr *= 2; | |
451 | ||
452 | *vector | |
453 | = (Lisp_Object **) xrealloc (*vector, | |
454 | *npanes_allocated_ptr * sizeof (Lisp_Object *)); | |
455 | *panes | |
456 | = (char **) xrealloc (*panes, | |
457 | *npanes_allocated_ptr * sizeof (char *)); | |
458 | *items | |
459 | = (int *) xrealloc (*items, | |
460 | *npanes_allocated_ptr * sizeof (int)); | |
461 | *names | |
462 | = (char ***) xrealloc (*names, | |
463 | *npanes_allocated_ptr * sizeof (char **)); | |
464 | } | |
465 | ||
466 | /* When a menu comes from keymaps, don't give names to the panes. */ | |
467 | (*panes)[*p_ptr] = pane_name; | |
468 | ||
469 | /* Get the length of the list level of the keymap. */ | |
470 | i = XFASTINT (Flength (keymap)); | |
471 | ||
ab6ee1a0 RS |
472 | /* Add in lengths of any arrays. */ |
473 | for (tail = keymap; XTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr) | |
474 | if (XTYPE (XCONS (tail)->car) == Lisp_Vector) | |
475 | i += XVECTOR (XCONS (tail)->car)->size; | |
088831f6 RS |
476 | |
477 | /* Create vectors for the names and values of the items in the pane. | |
478 | I is an upper bound for the number of items. */ | |
479 | (*vector)[*p_ptr] = (Lisp_Object *) xmalloc (i * sizeof (Lisp_Object)); | |
480 | (*names)[*p_ptr] = (char **) xmalloc (i * sizeof (char *)); | |
481 | ||
482 | /* I is now the index of the next unused slots. */ | |
483 | i = 0; | |
484 | for (tail = keymap; XTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr) | |
485 | { | |
486 | /* Look at each key binding, and if it has a menu string, | |
487 | make a menu item from it. */ | |
488 | item = XCONS (tail)->car; | |
489 | if (XTYPE (item) == Lisp_Cons) | |
490 | { | |
491 | item1 = XCONS (item)->cdr; | |
492 | if (XTYPE (item1) == Lisp_Cons) | |
493 | { | |
494 | item2 = XCONS (item1)->car; | |
495 | if (XTYPE (item2) == Lisp_String) | |
496 | { | |
497 | Lisp_Object tem; | |
498 | tem = Fkeymapp (Fcdr (item1)); | |
499 | if (XSTRING (item2)->data[0] == '@' && !NILP (tem)) | |
500 | pending_maps = Fcons (Fcons (Fcdr (item1), item2), | |
501 | pending_maps); | |
502 | else | |
503 | { | |
504 | (*names)[*p_ptr][i] = (char *) XSTRING (item2)->data; | |
505 | /* The menu item "value" is the key bound here. */ | |
506 | (*vector)[*p_ptr][i] = XCONS (item)->car; | |
507 | i++; | |
508 | } | |
509 | } | |
510 | } | |
511 | } | |
ab6ee1a0 RS |
512 | else if (XTYPE (item) == Lisp_Vector) |
513 | { | |
514 | /* Loop over the char values represented in the vector. */ | |
515 | int len = XVECTOR (item)->size; | |
516 | int c; | |
517 | for (c = 0; c < len; c++) | |
518 | { | |
519 | Lisp_Object character; | |
520 | XFASTINT (character) = c; | |
521 | item1 = XVECTOR (item)->contents[c]; | |
522 | if (XTYPE (item1) == Lisp_Cons) | |
523 | { | |
524 | item2 = XCONS (item1)->car; | |
525 | if (XTYPE (item2) == Lisp_String) | |
526 | { | |
527 | Lisp_Object tem; | |
528 | tem = Fkeymapp (Fcdr (item1)); | |
529 | if (XSTRING (item2)->data[0] == '@' && !NILP (tem)) | |
530 | pending_maps = Fcons (Fcons (Fcdr (item1), item2), | |
531 | pending_maps); | |
532 | else | |
533 | { | |
534 | (*names)[*p_ptr][i] = (char *) XSTRING (item2)->data; | |
535 | /* The menu item "value" is the key bound here. */ | |
536 | (*vector)[*p_ptr][i] = character; | |
537 | i++; | |
538 | } | |
539 | } | |
540 | } | |
541 | } | |
542 | } | |
088831f6 RS |
543 | } |
544 | /* Record the number of items in the pane. */ | |
545 | (*items)[*p_ptr] = i; | |
546 | ||
547 | /* If we just made an empty pane, get rid of it. */ | |
548 | if (i == 0) | |
549 | { | |
550 | free ((*vector)[*p_ptr]); | |
551 | free ((*names)[*p_ptr]); | |
552 | } | |
553 | /* Otherwise, advance past it. */ | |
554 | else | |
555 | (*p_ptr)++; | |
556 | ||
557 | /* Process now any submenus which want to be panes at this level. */ | |
558 | while (!NILP (pending_maps)) | |
559 | { | |
560 | Lisp_Object elt; | |
561 | elt = Fcar (pending_maps); | |
562 | single_keymap_panes (Fcar (elt), panes, vector, names, items, | |
563 | p_ptr, npanes_allocated_ptr, | |
564 | /* Add 1 to discard the @. */ | |
565 | (char *) XSTRING (XCONS (elt)->cdr)->data + 1); | |
566 | pending_maps = Fcdr (pending_maps); | |
567 | } | |
568 | } | |
569 | \f | |
570 | /* Construct the vectors that describe a menu | |
571 | and store them in *VECTOR, *PANES, *NAMES and *ITEMS. | |
572 | Each of those four values is a vector indexed by pane number. | |
573 | Return the number of panes. | |
574 | ||
575 | MENU is the argument that was given to Fx_popup_menu. */ | |
dcfdbac7 | 576 | |
088831f6 | 577 | int |
dcfdbac7 JB |
578 | list_of_panes (vector, panes, names, items, menu) |
579 | Lisp_Object ***vector; /* RETURN all menu objects */ | |
580 | char ***panes; /* RETURN pane names */ | |
581 | char ****names; /* RETURN all line names */ | |
582 | int **items; /* RETURN number of items per pane */ | |
583 | Lisp_Object menu; | |
584 | { | |
585 | Lisp_Object tail, item, item1; | |
586 | int i; | |
587 | ||
588 | if (XTYPE (menu) != Lisp_Cons) menu = wrong_type_argument (Qlistp, menu); | |
589 | ||
088831f6 | 590 | i = XFASTINT (Flength (menu)); |
dcfdbac7 JB |
591 | |
592 | *vector = (Lisp_Object **) xmalloc (i * sizeof (Lisp_Object *)); | |
593 | *panes = (char **) xmalloc (i * sizeof (char *)); | |
594 | *items = (int *) xmalloc (i * sizeof (int)); | |
595 | *names = (char ***) xmalloc (i * sizeof (char **)); | |
596 | ||
088831f6 | 597 | for (i = 0, tail = menu; !NILP (tail); tail = Fcdr (tail), i++) |
dcfdbac7 | 598 | { |
088831f6 RS |
599 | item = Fcdr (Fcar (tail)); |
600 | if (XTYPE (item) != Lisp_Cons) (void) wrong_type_argument (Qlistp, item); | |
dcfdbac7 | 601 | #ifdef XDEBUG |
088831f6 | 602 | fprintf (stderr, "list_of_panes check tail, i=%d\n", i); |
dcfdbac7 | 603 | #endif |
088831f6 RS |
604 | item1 = Fcar (Fcar (tail)); |
605 | CHECK_STRING (item1, 1); | |
dcfdbac7 | 606 | #ifdef XDEBUG |
088831f6 RS |
607 | fprintf (stderr, "list_of_panes check pane, i=%d%s\n", i, |
608 | XSTRING (item1)->data); | |
dcfdbac7 | 609 | #endif |
088831f6 RS |
610 | (*panes)[i] = (char *) XSTRING (item1)->data; |
611 | (*items)[i] = list_of_items ((*vector)+i, (*names)+i, item); | |
612 | /* (*panes)[i] = (char *) xmalloc ((XSTRING (item1)->size)+1); | |
613 | bcopy (XSTRING (item1)->data, (*panes)[i], XSTRING (item1)->size + 1) | |
614 | ; */ | |
dcfdbac7 JB |
615 | } |
616 | return i; | |
617 | } | |
088831f6 RS |
618 | \f |
619 | /* Construct the lists of values and names for a single pane, from the | |
620 | alist PANE. Put them in *VECTOR and *NAMES. | |
621 | Return the number of items. */ | |
dcfdbac7 | 622 | |
088831f6 | 623 | int |
dcfdbac7 JB |
624 | list_of_items (vector, names, pane) /* get list from emacs and put to vector */ |
625 | Lisp_Object **vector; /* RETURN menu "objects" */ | |
626 | char ***names; /* RETURN line names */ | |
627 | Lisp_Object pane; | |
628 | { | |
629 | Lisp_Object tail, item, item1; | |
630 | int i; | |
631 | ||
632 | if (XTYPE (pane) != Lisp_Cons) pane = wrong_type_argument (Qlistp, pane); | |
633 | ||
088831f6 | 634 | i = XFASTINT (Flength (pane, 1)); |
dcfdbac7 JB |
635 | |
636 | *vector = (Lisp_Object *) xmalloc (i * sizeof (Lisp_Object)); | |
637 | *names = (char **) xmalloc (i * sizeof (char *)); | |
638 | ||
088831f6 | 639 | for (i = 0, tail = pane; !NILP (tail); tail = Fcdr (tail), i++) |
dcfdbac7 | 640 | { |
088831f6 RS |
641 | item = Fcar (tail); |
642 | if (XTYPE (item) != Lisp_Cons) (void) wrong_type_argument (Qlistp, item); | |
dcfdbac7 | 643 | #ifdef XDEBUG |
088831f6 | 644 | fprintf (stderr, "list_of_items check tail, i=%d\n", i); |
dcfdbac7 | 645 | #endif |
088831f6 RS |
646 | (*vector)[i] = Fcdr (item); |
647 | item1 = Fcar (item); | |
648 | CHECK_STRING (item1, 1); | |
dcfdbac7 | 649 | #ifdef XDEBUG |
088831f6 RS |
650 | fprintf (stderr, "list_of_items check item, i=%d%s\n", i, |
651 | XSTRING (item1)->data); | |
dcfdbac7 | 652 | #endif |
088831f6 | 653 | (*names)[i] = (char *) XSTRING (item1)->data; |
dcfdbac7 JB |
654 | } |
655 | return i; | |
656 | } |