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