Give subprocess creation a way to find a valid current directory
[bpt/emacs.git] / src / xmenu.c
... / ...
CommitLineData
1/* X Communication module for terminals which understand the X protocol.
2 Copyright (C) 1986, 1988, 1992 Free Software Foundation, Inc.
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the 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
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"
35#include "frame.h"
36#include "window.h"
37#include "keyboard.h"
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
68extern Display *x_current_display;
69#else
70#define ButtonReleaseMask ButtonReleased
71#endif /* not HAVE_X11 */
72
73Lisp_Object xmenu_show ();
74extern int x_error_handler ();
75
76/*************************************************************/
77
78#if 0
79/* Ignoring the args is easiest. */
80xmenu_quit ()
81{
82 error ("Unknown XMenu error");
83}
84#endif
85
86DEFUN ("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\
88POSITION is a position specification. This is either a mouse button event\n\
89or a list ((XOFFSET YOFFSET) WINDOW)\n\
90where XOFFSET and YOFFSET are positions in characters from the top left\n\
91corner of WINDOW's frame. A mouse-event list will serve for this.\n\
92This controls the position of the center of the first line\n\
93in the first pane of the menu, not the top left of the menu as a whole.\n\
94\n\
95MENU is a specifier for a menu. For the simplest case, MENU is a keymap.\n\
96The menu items come from key bindings that have a menu string as well as\n\
97a 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\
99the keymap as a top-level element.\n\n\
100You can also use a list of keymaps as MENU.\n\
101 Then each keymap makes a separate pane.\n\n\
102Alternatively, 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\
105\(TITLE (LINE ITEM)...). Each line should be a string, and item should\n\
106be the return value for that line (i.e. if it is selected.")
107 (position, menu)
108 Lisp_Object position, menu;
109{
110 int number_of_panes;
111 Lisp_Object XMenu_return, keymap, tem;
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;
121 FRAME_PTR f;
122 Lisp_Object x, y, window;
123
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 {
134 tem = EVENT_START (position);
135 window = POSN_WINDOW (tem);
136 tem = POSN_WINDOW_POSN (tem);
137 x = Fcar (tem);
138 y = Fcdr (tem);
139 }
140 CHECK_LIVE_WINDOW (window, 0);
141 CHECK_NUMBER (x, 0);
142 CHECK_NUMBER (y, 0);
143
144 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
145
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);
150 XMenu_xpos += f->display.x->left_pos;
151 XMenu_ypos += f->display.x->top_pos;
152
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))
178 {
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))
190 {
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;
196 }
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]);
223 }
224#endif
225 BLOCK_INPUT;
226 selection = xmenu_show (ROOT_WINDOW, XMenu_xpos, XMenu_ypos, names, menus,
227 items, number_of_panes, obj_list, title,
228 &error_name);
229 UNBLOCK_INPUT;
230 /* fprintf (stderr, "selection = %x\n", selection); */
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 */
240 for (i = 0; i < number_of_panes; i++)
241 {
242 free (names[i]);
243 free (obj_list[i]);
244 }
245 free (menus);
246 free (obj_list);
247 free (names);
248 free (items);
249 /* free (title); */
250 if (error_name) error (error_name);
251 return XMenu_return;
252}
253
254struct indices {
255 int pane;
256 int line;
257};
258
259Lisp_Object
260xmenu_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;
280
281 if (pane_cnt == 0)
282 return 0;
283
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
292 for (panes = 0, lines = 0; panes < pane_cnt;
293 lines += line_cnt[panes], panes++)
294 ;
295 /* datap = (struct indices *) xmalloc (lines * sizeof (struct indices)); */
296 /* datap = (char *) xmalloc (lines * sizeof (char));
297 datap_save = datap;*/
298
299 for (panes = 0, sofar = 0; panes < pane_cnt;
300 sofar += line_cnt[panes], panes++)
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 }
310 for (selidx = 0; selidx < line_cnt[panes]; selidx++)
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:
364 /* free (datap_save); */
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);
374 /* free (datap_save);*/
375 return (entry);
376}
377
378syms_of_xmenu ()
379{
380 defsubr (&Sx_popup_menu);
381}
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
390int
391keymap_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
430single_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
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;
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 }
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 }
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. */
576
577int
578list_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
590 i = XFASTINT (Flength (menu));
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
597 for (i = 0, tail = menu; !NILP (tail); tail = Fcdr (tail), i++)
598 {
599 item = Fcdr (Fcar (tail));
600 if (XTYPE (item) != Lisp_Cons) (void) wrong_type_argument (Qlistp, item);
601#ifdef XDEBUG
602 fprintf (stderr, "list_of_panes check tail, i=%d\n", i);
603#endif
604 item1 = Fcar (Fcar (tail));
605 CHECK_STRING (item1, 1);
606#ifdef XDEBUG
607 fprintf (stderr, "list_of_panes check pane, i=%d%s\n", i,
608 XSTRING (item1)->data);
609#endif
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 ; */
615 }
616 return i;
617}
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. */
622
623int
624list_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
634 i = XFASTINT (Flength (pane, 1));
635
636 *vector = (Lisp_Object *) xmalloc (i * sizeof (Lisp_Object));
637 *names = (char **) xmalloc (i * sizeof (char *));
638
639 for (i = 0, tail = pane; !NILP (tail); tail = Fcdr (tail), i++)
640 {
641 item = Fcar (tail);
642 if (XTYPE (item) != Lisp_Cons) (void) wrong_type_argument (Qlistp, item);
643#ifdef XDEBUG
644 fprintf (stderr, "list_of_items check tail, i=%d\n", i);
645#endif
646 (*vector)[i] = Fcdr (item);
647 item1 = Fcar (item);
648 CHECK_STRING (item1, 1);
649#ifdef XDEBUG
650 fprintf (stderr, "list_of_items check item, i=%d%s\n", i,
651 XSTRING (item1)->data);
652#endif
653 (*names)[i] = (char *) XSTRING (item1)->data;
654 }
655 return i;
656}