xmenu.c 44.6 KB
Newer Older
Jim Blandy's avatar
Jim Blandy committed
1
/* X Communication module for terminals which understand the X protocol.
2
   Copyright (C) 1986, 1988, 1993, 1994 Free Software Foundation, Inc.
Jim Blandy's avatar
Jim Blandy committed
3 4 5 6 7

This file is part of GNU Emacs.

GNU Emacs is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
Jim Blandy's avatar
Jim Blandy committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
any later version.

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* X pop-up deck-of-cards menu facility for gnuemacs.
 *
 * Written by Jon Arnold and Roman Budzianowski
 * Mods and rewrite by Robert Krawitz
 *
 */

27 28 29
/* Modified by Fred Pierresteguy on December 93
   to make the popup menus and menubar use the Xt.  */

Jim Blandy's avatar
Jim Blandy committed
30 31 32 33
#include <stdio.h>

/* On 4.3 this loses if it comes after xterm.h.  */
#include <signal.h>
34
#include <config.h>
Jim Blandy's avatar
Jim Blandy committed
35
#include "lisp.h"
36
#include "termhooks.h"
Jim Blandy's avatar
Jim Blandy committed
37
#include "frame.h"
Jim Blandy's avatar
Jim Blandy committed
38
#include "window.h"
Jim Blandy's avatar
Jim Blandy committed
39
#include "keyboard.h"
40
#include "blockinput.h"
Jim Blandy's avatar
Jim Blandy committed
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

/* This may include sys/types.h, and that somehow loses
   if this is not done before the other system files.  */
#include "xterm.h"

/* Load sys/types.h if not already loaded.
   In some systems loading it twice is suicidal.  */
#ifndef makedev
#include <sys/types.h>
#endif

#include "dispextern.h"

#ifdef HAVE_X11
#include "../oldXMenu/XMenu.h"
#else
#include <X/XMenu.h>
#endif

60 61 62 63 64 65 66 67 68 69
#ifdef USE_X_TOOLKIT
#include <X11/Xlib.h>
#include <X11/IntrinsicP.h>
#include <X11/CoreP.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Paned.h>
#include "../lwlib/lwlib.h"
#include "../lwlib/xlwmenuP.h"
#endif /* USE_X_TOOLKIT */

Jim Blandy's avatar
Jim Blandy committed
70 71 72 73 74 75 76 77
#define min(x,y) (((x) < (y)) ? (x) : (y))
#define max(x,y) (((x) > (y)) ? (x) : (y))

#define NUL 0

#ifndef TRUE
#define TRUE 1
#define FALSE 0
78
#endif /* TRUE */
Jim Blandy's avatar
Jim Blandy committed
79 80 81 82 83 84 85

#ifdef HAVE_X11
extern Display *x_current_display;
#else
#define	ButtonReleaseMask ButtonReleased
#endif /* not HAVE_X11 */

86
extern Lisp_Object Qmenu_enable;
87
extern Lisp_Object Qmenu_bar;
Jim Blandy's avatar
Jim Blandy committed
88 89
Lisp_Object xmenu_show ();
extern int x_error_handler ();
90 91 92 93 94 95 96 97
#ifdef USE_X_TOOLKIT
static widget_value *set_menu_items ();
static int string_width ();
static void free_menu_items ();
#endif

/* we need a unique id for each popup menu and dialog box */
unsigned int popup_id_tick;
Jim Blandy's avatar
Jim Blandy committed
98 99 100 101 102 103 104 105 106 107 108

/*************************************************************/

#if 0
/* Ignoring the args is easiest.  */
xmenu_quit ()
{
  error ("Unknown XMenu error");
}
#endif

109

Jim Blandy's avatar
Jim Blandy committed
110 111
DEFUN ("x-popup-menu",Fx_popup_menu, Sx_popup_menu, 1, 2, 0,
  "Pop up a deck-of-cards menu and return user's selection.\n\
112 113
POSITION is a position specification.  This is either a mouse button event\n\
or a list ((XOFFSET YOFFSET) WINDOW)\n\
Jim Blandy's avatar
Jim Blandy committed
114
where XOFFSET and YOFFSET are positions in characters from the top left\n\
115
corner of WINDOW's frame.  (WINDOW may be a frame object instead of a window.)\n\
Jim Blandy's avatar
Jim Blandy committed
116 117 118
This controls the position of the center of the first line\n\
in the first pane of the menu, not the top left of the menu as a whole.\n\
\n\
119 120 121 122 123 124
MENU is a specifier for a menu.  For the simplest case, MENU is a keymap.\n\
The menu items come from key bindings that have a menu string as well as\n\
a definition; actually, the \"definition\" in such a key binding looks like\n\
\(STRING . REAL-DEFINITION).  To give the menu a title, put a string into\n\
the keymap as a top-level element.\n\n\
You can also use a list of keymaps as MENU.\n\
125 126 127
  Then each keymap makes a separate pane.\n\
When MENU is a keymap or a list of keymaps, the return value\n\
is a list of events.\n\n\
128
Alternatively, you can specify a menu of multiple panes\n\
129 130 131 132
  with a list of the form (TITLE PANE1 PANE2...),\n\
where each pane is a list of form (TITLE ITEM1 ITEM2...).\n\
Each ITEM is normally a cons cell (STRING . VALUE);\n\
but a string can appear as an item--that makes a nonselectable line\n\
133 134
in the menu.\n\
With this form of menu, the return value is VALUE from the chosen item.")
135 136
  (position, menu)
     Lisp_Object position, menu;
Jim Blandy's avatar
Jim Blandy committed
137
{
138
  int number_of_panes, panes;
139
  Lisp_Object XMenu_return, keymap, tem;
Jim Blandy's avatar
Jim Blandy committed
140 141 142
  int XMenu_xpos, XMenu_ypos;
  char **menus;
  char ***names;
143
  int **enables;
Jim Blandy's avatar
Jim Blandy committed
144
  Lisp_Object **obj_list;
145
  Lisp_Object *prefixes;
Jim Blandy's avatar
Jim Blandy committed
146 147 148 149
  int *items;
  char *title;
  char *error_name;
  Lisp_Object ltitle, selection;
150
  int i, j, menubarp = 0;
Jim Blandy's avatar
Jim Blandy committed
151
  FRAME_PTR f;
Jim Blandy's avatar
Jim Blandy committed
152
  Lisp_Object x, y, window;
153 154 155
#ifdef USE_X_TOOLKIT
  widget_value *val, *vw = 0;
#endif /* USE_X_TOOLKIT */
Jim Blandy's avatar
Jim Blandy committed
156

Karl Heuer's avatar
Karl Heuer committed
157
  check_x ();
158 159 160 161 162 163 164 165 166 167
  /* Decode the first argument: find the window and the coordinates.  */
  tem = Fcar (position);
  if (XTYPE (tem) == Lisp_Cons)
    {
      window = Fcar (Fcdr (position));
      x = Fcar (tem);
      y = Fcar (Fcdr (tem));
    }
  else
    {
168 169 170
      tem = Fcar (Fcdr (position));  /* EVENT_START (position) */
      window = Fcar (tem);	     /* POSN_WINDOW (tem) */
      tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
171 172 173
      x = Fcar (tem);
      y = Fcdr (tem);
    }
Jim Blandy's avatar
Jim Blandy committed
174 175
  CHECK_NUMBER (x, 0);
  CHECK_NUMBER (y, 0);
176

177 178 179
  if (XTYPE (window) == Lisp_Frame)
    {
      f = XFRAME (window);
180
      
181 182 183 184 185 186 187
      XMenu_xpos = 0;
      XMenu_ypos = 0;
    }
  else if (XTYPE (window) == Lisp_Window)
    {
      CHECK_LIVE_WINDOW (window, 0);
      f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
188 189 190 191 192
      
      XMenu_xpos = FONT_WIDTH (f->display.x->font) 
	* XWINDOW (window)->left;
      XMenu_ypos = FONT_HEIGHT (f->display.x->font) 
	* XWINDOW (window)->top;
193
    }
194 195 196 197
  else
    /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
       but I don't want to make one now.  */
    CHECK_WINDOW (window, 0);
198

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
#ifdef USE_X_TOOLKIT
  tem = Fcar (Fcdr (Fcar (Fcdr (position))));
  if (XTYPE (Fcar (position)) != Lisp_Cons
      && CONSP (tem)
      && EQ (Fcar (tem), Qmenu_bar))
    {
      /* We are in the menubar */
      XlwMenuWidget mw;
      int w1 = 0, w2;
    
      mw = (XlwMenuWidget)f->display.x->menubar_widget;
      menubarp = 1;
      for (vw = mw->menu.old_stack [0]->contents; vw; vw = vw->next)
	{
	  w2 = w1;
	  w1 += string_width (mw, vw->name) 
	    + 2 * (mw->menu.horizontal_spacing + 
		   mw->menu.shadow_thickness);
	  if (XINT (x) < w1)
	    {
	      XMenu_xpos = w2 + 4;
	      XMenu_ypos = 0;
	      break;
	    }
	}
    }
  else
    {
      XMenu_xpos += FONT_WIDTH (f->display.x->font) * XINT (x);
      XMenu_ypos += FONT_HEIGHT (f->display.x->font) * XINT (y);
    }

  BLOCK_INPUT;
  XMenu_xpos += (f->display.x->widget->core.x
		 + f->display.x->widget->core.border_width);
  XMenu_ypos += (f->display.x->widget->core.y 
		 + f->display.x->widget->core.border_width
		 + f->display.x->menubar_widget->core.height);
  UNBLOCK_INPUT;

  val = set_menu_items (menu, &prefixes, &panes, &names, 
			&enables, &menus, &items, &number_of_panes, &obj_list, 
			&title, &error_name);
  selection = xmenu_show (f, val, XMenu_xpos, XMenu_ypos,
			  menubarp, vw);

  free_menu_items (names, enables, menus, items, number_of_panes, obj_list, 
		   title, error_name);

  if (selection != NUL)
    {				/* selected something */
      XMenu_return = selection;
    }
  else
    {				/* nothing selected */
      XMenu_return = Qnil;
    }

  return XMenu_return;

#else /* not USE_X_TOOLKIT */
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
#ifdef HAVE_X11
  {
    Window child;
    int win_x = 0, win_y = 0;

    /* Find the position of the outside upper-left corner of
       the inner window, with respect to the outer window.  */
    if (f->display.x->parent_desc != ROOT_WINDOW)
      {
	BLOCK_INPUT;
	XTranslateCoordinates (x_current_display,

			       /* From-window, to-window.  */
			       f->display.x->window_desc,
			       f->display.x->parent_desc,

			       /* From-position, to-position.  */
			       0, 0, &win_x, &win_y,

			       /* Child of window.  */
			       &child);
	UNBLOCK_INPUT;
	XMenu_xpos += win_x;
	XMenu_ypos += win_y;
      }
  }
286
#endif /* HAVE_X11 */
287

288 289
  XMenu_xpos += FONT_WIDTH (f->display.x->font) * XINT (x);
  XMenu_ypos += FONT_HEIGHT (f->display.x->font) * XINT (y);
Jim Blandy's avatar
Jim Blandy committed
290

Jim Blandy's avatar
Jim Blandy committed
291 292
  XMenu_xpos += f->display.x->left_pos;
  XMenu_ypos += f->display.x->top_pos;
Jim Blandy's avatar
Jim Blandy committed
293

294

295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
  keymap = Fkeymapp (menu);
  tem = Qnil;
  if (XTYPE (menu) == Lisp_Cons)
    tem = Fkeymapp (Fcar (menu));
  if (!NILP (keymap))
    {
      /* We were given a keymap.  Extract menu info from the keymap.  */
      Lisp_Object prompt;
      keymap = get_keymap (menu);

      /* Search for a string appearing directly as an element of the keymap.
	 That string is the title of the menu.  */
      prompt = map_prompt (keymap);
      if (!NILP (prompt))
	title = (char *) XSTRING (prompt)->data;

      /* Extract the detailed info to make one pane.  */
312
      number_of_panes = keymap_panes (&obj_list, &menus, &names, &enables,
313
				      &items, &prefixes, &menu, 1);
314 315 316 317 318 319
      /* The menu title seems to be ignored,
	 so put it in the pane title.  */
      if (menus[0] == 0)
	menus[0] = title;
    }
  else if (!NILP (tem))
Jim Blandy's avatar
Jim Blandy committed
320
    {
321 322 323 324 325 326 327 328 329 330 331
      /* We were given a list of keymaps.  */
      Lisp_Object prompt;
      int nmaps = XFASTINT (Flength (menu));
      Lisp_Object *maps
	= (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
      int i;
      title = 0;

      /* The first keymap that has a prompt string
	 supplies the menu title.  */
      for (tem = menu, i = 0; XTYPE (tem) == Lisp_Cons; tem = Fcdr (tem))
Jim Blandy's avatar
Jim Blandy committed
332
	{
333 334 335 336 337
	  maps[i++] = keymap = get_keymap (Fcar (tem));

	  prompt = map_prompt (keymap);
	  if (title == 0 && !NILP (prompt))
	    title = (char *) XSTRING (prompt)->data;
Jim Blandy's avatar
Jim Blandy committed
338
	}
339 340

      /* Extract the detailed info to make one pane.  */
341
      number_of_panes = keymap_panes (&obj_list, &menus, &names, &enables,
342
				      &items, &prefixes, maps, nmaps);
343 344 345 346 347 348 349 350 351 352 353
      /* The menu title seems to be ignored,
	 so put it in the pane title.  */
      if (menus[0] == 0)
	menus[0] = title;
    }
  else
    {
      /* We were given an old-fashioned menu.  */
      ltitle = Fcar (menu);
      CHECK_STRING (ltitle, 1);
      title = (char *) XSTRING (ltitle)->data;
354
      prefixes = 0;
355 356
      number_of_panes = list_of_panes (&obj_list, &menus, &names, &enables,
				       &items, Fcdr (menu));
357 358 359 360 361 362 363 364 365
    }
#ifdef XDEBUG
  fprintf (stderr, "Panes = %d\n", number_of_panes);
  for (i = 0; i < number_of_panes; i++)
    {
      fprintf (stderr, "Pane %d has lines %d title %s\n",
	       i, items[i], menus[i]);
      for (j = 0; j < items[i]; j++)
	fprintf (stderr, "    Item %d %s\n", j, names[i][j]);
Jim Blandy's avatar
Jim Blandy committed
366 367
    }
#endif
368

Jim Blandy's avatar
Jim Blandy committed
369
  BLOCK_INPUT;
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
  {
    Window root;
    int root_x, root_y;
    int dummy_int;
    unsigned int dummy_uint;
    Window dummy_window;

    /* Figure out which root window F is on.  */
    XGetGeometry (x_current_display, FRAME_X_WINDOW (f), &root,
		  &dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
		  &dummy_uint, &dummy_uint);

    /* Translate the menu co-ordinates within f to menu co-ordinates
       on that root window.  */
    if (! XTranslateCoordinates (x_current_display,
				 FRAME_X_WINDOW (f), root,
				 XMenu_xpos, XMenu_ypos, &root_x, &root_y,
				 &dummy_window))
      /* But XGetGeometry said root was the root window of f's screen!  */ 
      abort ();
390
    selection = xmenu_show (root, XMenu_xpos, XMenu_ypos, names, enables,
391 392
			    menus, prefixes, items, number_of_panes, obj_list,
			    title, &error_name);
393
  }
Jim Blandy's avatar
Jim Blandy committed
394
  UNBLOCK_INPUT;
395
  /* fprintf (stderr, "selection = %x\n", selection);  */
Jim Blandy's avatar
Jim Blandy committed
396 397 398 399 400 401 402 403 404
  if (selection != NUL)
    {				/* selected something */
      XMenu_return = selection;
    }
  else
    {				/* nothing selected */
      XMenu_return = Qnil;
    }
  /* now free up the strings */
405
  for (i = 0; i < number_of_panes; i++)
Jim Blandy's avatar
Jim Blandy committed
406
    {
407 408 409
      xfree (names[i]);
      xfree (enables[i]);
      xfree (obj_list[i]);
Jim Blandy's avatar
Jim Blandy committed
410
    }
411 412 413 414 415
  xfree (menus);
  xfree (obj_list);
  xfree (names);
  xfree (enables);
  xfree (items);
416
  /* free (title); */
Jim Blandy's avatar
Jim Blandy committed
417 418
  if (error_name) error (error_name);
  return XMenu_return;
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
#endif /* not USE_X_TOOLKIT */
}

#ifdef USE_X_TOOLKIT

static void
dispatch_dummy_expose (w, x, y)
     Widget w;
     int x;
     int y;
{
  XExposeEvent dummy;
	
  dummy.type = Expose;
  dummy.window = XtWindow (w);
  dummy.count = 0;
  dummy.serial = 0;
  dummy.send_event = 0;
  dummy.display = XtDisplay (w);
  dummy.x = x;
  dummy.y = y;

  XtDispatchEvent (&dummy);
}

static int
string_width (mw, s)
     XlwMenuWidget mw;
     char* s;
{
  XCharStruct xcs;
  int drop;
  
  XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
  return xcs.width;
Jim Blandy's avatar
Jim Blandy committed
454 455
}

456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
static int
event_is_in_menu_item (mw, event, name, string_w)
     XlwMenuWidget mw;
     struct input_event *event;
     char *name;
     int *string_w;
{
  *string_w += string_width (mw, name) 
    + 2 * (mw->menu.horizontal_spacing + mw->menu.shadow_thickness);
  return (XINT (event->x) < *string_w);
}


Lisp_Object
map_event_to_object (event, f)
     struct input_event *event;
     FRAME_PTR f;
{
  int i,j, string_w;
  window_state*	ws;
  XlwMenuWidget mw = (XlwMenuWidget) f->display.x->menubar_widget;
  widget_value *val;


  string_w = 0;
  /* Find the window */
  for (val = mw->menu.old_stack [0]->contents; val; val = val->next)
    {
      ws = &mw->menu.windows [0];
      if (ws && event_is_in_menu_item (mw, event, val->name, &string_w))
	{
	  Lisp_Object items;
	  items = FRAME_MENU_BAR_ITEMS (f);
	  for (; CONSP (items); items = XCONS (items)->cdr)
	    if (!strcmp (val->name,
			XSTRING (Fcar (Fcdr (Fcar (items))))->data))
	      return items;
	}
    }
  return Qnil;
}

static widget_value *
set_menu_items (menu, prefixes, panes, names, enables, menus,
		items, number_of_panes, obj_list, title, error_name)
     Lisp_Object menu;
     Lisp_Object **prefixes;
     int *panes;
     char ***names[];
     int ***enables;
     char ***menus;
     int **items;
     int *number_of_panes;
     Lisp_Object ***obj_list;
     char **title;
     char **error_name;
{
  Lisp_Object keymap, tem;
  Lisp_Object ltitle, selection;
  int i, j;
  widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
  int last, selidx, lpane, status;
  int lines, sofar;

  keymap = Fkeymapp (menu);
  tem = Qnil;

  if (XTYPE (menu) == Lisp_Cons)
    tem = Fkeymapp (Fcar (menu));
  if (!NILP (keymap))
    {
      /* We were given a keymap.  Extract menu info from the keymap.  */
      Lisp_Object prompt;
      keymap = get_keymap (menu);

      /* Search for a string appearing directly as an element of the keymap.
	 That string is the title of the menu.  */
      prompt = map_prompt (keymap);
      if (!NILP (prompt))
	*title = (char *) XSTRING (prompt)->data;

      /* Extract the detailed info to make one pane.  */
      *number_of_panes = keymap_panes (obj_list, menus, names, enables,
				      items, prefixes, menu, 1);
      /* The menu title seems to be ignored,
	 so put it in the pane title.  */
      if ((*menus)[0] == 0)
	(*menus)[0] = *title;
    }
  else if (!NILP (tem))
    {
      /* We were given a list of keymaps.  */
      Lisp_Object prompt;
      int nmaps = XFASTINT (Flength (menu));
      Lisp_Object *maps
	= (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
      int i;
      *title = 0;

      /* The first keymap that has a prompt string
	 supplies the menu title.  */
      for (tem = menu, i = 0; XTYPE (tem) == Lisp_Cons; tem = Fcdr (tem))
	{
	  maps[i++] = keymap = get_keymap (Fcar (tem));

	  prompt = map_prompt (keymap);
	  if (*title == 0 && !NILP (prompt))
	    *title = (char *) XSTRING (prompt)->data;
	}

      /* Extract the detailed info to make one pane.  */
      *number_of_panes = keymap_panes (obj_list, menus, names, enables,
				      items, prefixes, maps, nmaps);
      /* The menu title seems to be ignored,
	 so put it in the pane title.  */
      if ((*menus)[0] == 0)
	(*menus)[0] = *title;
    }
  else
    {
      /* We were given an old-fashioned menu.  */
      ltitle = Fcar (menu);
      CHECK_STRING (ltitle, 1);
      *title = (char *) XSTRING (ltitle)->data;
      *prefixes = 0;
      *number_of_panes = list_of_panes (obj_list, menus, names, enables,
				       items, Fcdr (menu));
    }

  *error_name = 0;
  if (*number_of_panes == 0)
    return 0;

  *error_name = (char *) 0;		/* Initialize error pointer to null */

  wv = malloc_widget_value ();
  wv->name = "menu";
  wv->value = 0;
  wv->enabled = 1;
  first_wv = wv;
 
  for (*panes = 0, lines = 0; *panes < *number_of_panes;
       lines += (*items)[*panes], (*panes)++)
    ;
  /* datap = (struct indices *) xmalloc (lines * sizeof (struct indices)); */
  /* datap = (char *) xmalloc (lines * sizeof (char));
    datap_save = datap;*/
  
  for (*panes = 0, sofar = 0; *panes < *number_of_panes;
       sofar += (*items)[*panes], (*panes)++)
    {
      if (strcmp((*menus)[*panes], ""))
	{
	  wv = malloc_widget_value ();
	  if (save_wv)
	    save_wv->next = wv;
	  else
	    first_wv->contents = wv;
	  wv->name = (*menus)[*panes];
	  wv->value = 0;
	  wv->enabled = 1;
	}
      prev_wv = 0;
      save_wv = wv;

      for (selidx = 0; selidx < (*items)[*panes]; selidx++)
	{
	  wv = malloc_widget_value ();
	  if (prev_wv) 
	    prev_wv->next = wv;
	  else 
	    save_wv->contents = wv;
	  wv->name = (*names)[*panes][selidx];
	  wv->value = 0;
	  selection = (*obj_list)[*panes][selidx];
	  if (*prefixes != 0)
	    {
	      selection = Fcons (selection, Qnil);
	      if (!NILP ((*prefixes)[*panes]))
		selection = Fcons ((*prefixes)[*panes], selection);
	    }
	  wv->call_data = LISP_TO_VOID(selection);
	  wv->enabled = (*enables)[*panes][selidx];
	  prev_wv = wv;
	}
    }

  return (first_wv);
}

static void
free_menu_items (names, enables, menus, items, number_of_panes, 
		 obj_list, title, error_name)
     char **names[];
     int *enables[];
     char **menus;
     int *items;
     int number_of_panes;
     Lisp_Object **obj_list;
     char *title;
     char *error_name;
{
  int i;
  /* now free up the strings */
  for (i = 0; i < number_of_panes; i++)
    {
      xfree (names[i]);
      xfree (enables[i]);
      xfree (obj_list[i]);
    }
  xfree (menus);
  xfree (obj_list);
  xfree (names);
  xfree (enables);
  xfree (items);
  /* free (title); */
  if (error_name) error (error_name);

}

static Lisp_Object menu_item_selection;

static void
popup_selection_callback (widget, id, client_data)
     Widget widget;
     LWLIB_ID id;
     XtPointer client_data;
{
  VOID_TO_LISP (menu_item_selection, client_data);
}

static void
popup_down_callback (widget, id, client_data)
     Widget widget;
     LWLIB_ID id;
     XtPointer client_data;
{
  BLOCK_INPUT;
  lw_destroy_all_widgets (id);
  UNBLOCK_INPUT;
}

/* This recursively calls free_widget_value() on the tree of widgets.
   It must free all data that was malloc'ed for these widget_values.
   Currently, emacs only allocates new storage for the `key' slot.
   All other slots are pointers into the data of Lisp_Strings, and
   must be left alone.
 */
void
free_menubar_widget_value_tree (wv)
     widget_value *wv;
{
  if (! wv) return;
  if (wv->key) xfree (wv->key);

  wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;

  if (wv->contents && (wv->contents != (widget_value*)1))
    {
      free_menubar_widget_value_tree (wv->contents);
      wv->contents = (widget_value *) 0xDEADBEEF;
    }
  if (wv->next)
    {
      free_menubar_widget_value_tree (wv->next);
      wv->next = (widget_value *) 0xDEADBEEF;
    }
  BLOCK_INPUT;
  free_widget_value (wv);
  UNBLOCK_INPUT;
}

static void
update_one_frame_psheets (f)
     FRAME_PTR f;
{
  struct x_display *x = f->display.x;
  
  int menubar_changed;
  
  menubar_changed = (x->menubar_widget
		     && !XtIsManaged (x->menubar_widget));

  if (! (menubar_changed))
    return;

  BLOCK_INPUT;
  XawPanedSetRefigureMode (x->column_widget, 0);
  
  /* the order in which children are managed is the top to
     bottom order in which they are displayed in the paned window.
     First, remove the text-area widget.
   */
  XtUnmanageChild (x->edit_widget);

  /* remove the menubar that is there now, and put up the menubar that
     should be there.
   */
  if (menubar_changed)
    {
      XtManageChild (x->menubar_widget);
      XtMapWidget (x->menubar_widget);
      XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, 0);
    }


  /* Re-manage the text-area widget */
  XtManageChild (x->edit_widget);

  /* and now thrash the sizes */
  XawPanedSetRefigureMode (x->column_widget, 1);
  UNBLOCK_INPUT;
}

void
set_frame_menubar (f)
     FRAME_PTR f;
{
  Widget menubar_widget = f->display.x->menubar_widget;
  int id = (int) f;
  Lisp_Object tail;
  widget_value *wv, *save_wv, *first_wv, *prev_wv = 0;

  BLOCK_INPUT;

  wv = malloc_widget_value ();
  wv->name = "menubar";
  wv->value = 0;
  wv->enabled = 1;
  save_wv = first_wv = wv;


  for (tail = FRAME_MENU_BAR_ITEMS (f); CONSP (tail); tail = XCONS (tail)->cdr)
    {
      Lisp_Object string;

      string = Fcar (Fcdr (Fcar (tail)));

      wv = malloc_widget_value ();
      if (prev_wv) 
	prev_wv->next = wv;
      else 
	save_wv->contents = wv;
      wv->name = XSTRING (string)->data;
      wv->value = 0;
      wv->enabled = 1;
      prev_wv = wv;
    }

  if (menubar_widget)
    lw_modify_all_widgets (id, first_wv, False);
  else
    {
      menubar_widget = lw_create_widget ("menubar", "menubar", 
					 id, first_wv, 
					 f->display.x->column_widget, 
					 0, 0,
					 0, 0);
      f->display.x->menubar_widget = menubar_widget;
      XtVaSetValues (menubar_widget,
		     XtNshowGrip, 0,
		     XtNresizeToPreferred, 1,
		     XtNallowResize, 1,
		     0);
    }
  
  free_menubar_widget_value_tree (first_wv);

  update_one_frame_psheets (f);

  UNBLOCK_INPUT;
}
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845

void
free_frame_menubar (f)
     FRAME_PTR f;
{
  Widget menubar_widget;
  int id;

  menubar_widget = f->display.x->menubar_widget;
  id = (int) f;
  
  if (menubar_widget)
    {
      BLOCK_INPUT;
      lw_destroy_all_widgets (id);
      UNBLOCK_INPUT;
    }
}
846 847
#endif /* USE_X_TOOLKIT */

Jim Blandy's avatar
Jim Blandy committed
848 849 850 851 852
struct indices {
  int pane;
  int line;
};

853 854 855 856 857
extern void process_expose_from_menu ();

#ifdef USE_X_TOOLKIT
extern XtAppContext Xt_app_con;

Jim Blandy's avatar
Jim Blandy committed
858
Lisp_Object
859 860 861 862 863 864 865 866
xmenu_show (f, val, x, y, menubarp, vw)
     FRAME_PTR f;
     widget_value *val;
     int x;
     int y;
     int menubarp;
     widget_value *vw;
{
867
  int menu_id, item_length;
868 869
  Lisp_Object selection;
  Widget menu;
870
  XlwMenuWidget menuw = (XlwMenuWidget) f->display.x->menubar_widget;
871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886

  /*
   * Define and allocate a foreign event queue to hold events
   * that don't belong to XMenu.  These events are later restored
   * to the X event queue.
   */
  typedef struct _xmeventque 
    {
      XEvent event;
      struct _xmeventque *next;
    } XMEventQue;
  
  XMEventQue *feq = NULL;    		/* Foreign event queue. */
  XMEventQue *feq_tmp;		/* Foreign event queue temporary. */
  
  BLOCK_INPUT;
887 888
  if (val == 0) return Qnil;

889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
  menu_id = ++popup_id_tick;
  menu = lw_create_widget ("popup", val->name, menu_id, val, 
			   f->display.x->widget, 1, 0,
			   popup_selection_callback, popup_down_callback);
  free_menubar_widget_value_tree (val);

  /* reset the selection */
  menu_item_selection = Qnil;

  {
    XButtonPressedEvent dummy;
    XlwMenuWidget mw;
    
    mw = ((XlwMenuWidget)
	  ((CompositeWidget)menu)->composite.children[0]);

    dummy.type = ButtonPress;
    dummy.serial = 0;
    dummy.send_event = 0;
    dummy.display = XtDisplay (menu);
    dummy.window = XtWindow (XtParent (menu));
    dummy.time = CurrentTime;
    dummy.button = 0;
    dummy.x_root = x;
    dummy.y_root = y;

    if (menubarp)
      {
	vw->call_data = (XtPointer) 1;
	dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
      }


    /* We activate directly the lucid implementation */
    pop_up_menu (mw, &dummy);
  }

926 927 928 929 930 931 932 933
  if (menubarp)
    {
      item_length = (x + string_width (menuw, vw->name) 
		     + (2 * (menuw->menu.horizontal_spacing 
			     + menuw->menu.shadow_thickness))
		     - 4);
    }

934 935 936 937 938 939 940 941 942 943 944 945 946 947
  /* Enters XEvent loop */
  while (1)
    {

      XEvent event;
      XtAppNextEvent (Xt_app_con, &event);
      if (event.type == ButtonRelease)
	{
	  XtDispatchEvent (&event);
	  break;
	}
      else
	if (event.type == Expose)
	  process_expose_from_menu (event);
948 949
      else 
	if (event.type == MotionNotify 
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964
	    && menubarp
	    && ((event.xmotion.y_root 
		 >= (f->display.x->widget->core.y 
		     + f->display.x->widget->core.border_width))
		&& (event.xmotion.y_root
		    < (f->display.x->widget->core.y
		       + f->display.x->widget->core.border_width
		       + f->display.x->menubar_widget->core.height)))
	    && ((event.xmotion.x_root
		 >= (f->display.x->widget->core.x
		     + f->display.x->widget->core.border_width))
		&& (event.xmotion.x_root
		    < (f->display.x->widget->core.x
		       + f->display.x->widget->core.border_width
		       + f->display.x->widget->core.width)))
965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
	    && (event.xmotion.x_root >= item_length
		|| event.xmotion.x_root < (x - 4)))
	  {
	    BLOCK_INPUT;
	    XtUngrabPointer ((Widget)
			     ((XlwMenuWidget)
			      ((CompositeWidget)menu)->composite.children[0]),
			     event.xbutton.time);
	    lw_destroy_all_widgets (menu_id); 
	    UNBLOCK_INPUT;

	    event.type = ButtonPress;
	        event.xbutton.time = CurrentTime;
	        event.xbutton.button = Button1;
	    event.xbutton.window = XtWindow (f->display.x->menubar_widget);
	    event.xbutton.x = (event.xbutton.x_root 
			       - (f->display.x->widget->core.x
				  + f->display.x->widget->core.border_width));
	    XPutBackEvent (XDISPLAY &event);
	    break;
	  }

987 988 989 990 991 992 993 994 995 996 997 998 999 1000
      XtDispatchEvent (&event);
      feq_tmp = (XMEventQue *) malloc (sizeof (XMEventQue));

      if (feq_tmp == NULL) 
	return(Qnil);

      feq_tmp->event = event;
      feq_tmp->next = feq;
      feq = feq_tmp;
    }
      
  if (menubarp)
    {
      vw->call_data = (XtPointer) 0;
1001
      dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
    }

  /* Return any foreign events that were queued to the X event queue.  */
  while (feq != NULL) 
    {
      feq_tmp = feq;
      XPutBackEvent (XDISPLAY &feq_tmp->event);
      feq = feq_tmp->next;
      free ((char *)feq_tmp);
    }

  UNBLOCK_INPUT;

  return menu_item_selection;
}

#else /* not USE_X_TOOLKIT */
1019
xmenu_show (parent, startx, starty, line_list, enable_list, pane_list,
1020
	    prefixes, line_cnt, pane_cnt, item_list, title, error)
Jim Blandy's avatar
Jim Blandy committed
1021 1022 1023
     Window parent;		
     int startx, starty;	/* upper left corner position BROKEN */
     char **line_list[];   	/* list of strings for items */
Paul Reilly's avatar
Paul Reilly committed
1024
     int *enable_list[];   	/* enable flags of lines */
Jim Blandy's avatar
Jim Blandy committed
1025
     char *pane_list[];		/* list of pane titles */
1026
     Lisp_Object *prefixes;	/* Prefix key for each pane */
Jim Blandy's avatar
Jim Blandy committed
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
     char *title;
     int pane_cnt;		/* total number of panes */
     Lisp_Object *item_list[];	/* All items */
     int line_cnt[];		/* Lines in each pane */
     char **error;		/* Error returned */
{
  XMenu *GXMenu;
  int last, panes, selidx, lpane, status;
  int lines, sofar;
  Lisp_Object entry;
  /* struct indices *datap, *datap_save; */
  char *datap;
  int ulx, uly, width, height;
  int dispwidth, dispheight;
1041

1042
  *error = 0;
1043 1044 1045
  if (pane_cnt == 0)
    return 0;

1046
  BLOCK_INPUT;
Jim Blandy's avatar
Jim Blandy committed
1047
  *error = (char *) 0;		/* Initialize error pointer to null */
1048

Jim Blandy's avatar
Jim Blandy committed
1049 1050 1051 1052
  GXMenu = XMenuCreate (XDISPLAY parent, "emacs");
  if (GXMenu == NUL)
    {
      *error = "Can't create menu";
1053
      UNBLOCK_INPUT;
Jim Blandy's avatar
Jim Blandy committed
1054 1055
      return (0);
    }
1056
 
1057 1058
  for (panes = 0, lines = 0; panes < pane_cnt;
       lines += line_cnt[panes], panes++)
Jim Blandy's avatar
Jim Blandy committed
1059 1060
    ;
  /* datap = (struct indices *) xmalloc (lines * sizeof (struct indices)); */
1061
  /* datap = (char *) xmalloc (lines * sizeof (char));
Jim Blandy's avatar
Jim Blandy committed
1062 1063
    datap_save = datap;*/
  
1064 1065
  for (panes = 0, sofar = 0; panes < pane_cnt;
       sofar += line_cnt[panes], panes++)
Jim Blandy's avatar
Jim Blandy committed
1066 1067 1068 1069 1070 1071 1072
    {
      /* create all the necessary panes */
      lpane = XMenuAddPane (XDISPLAY GXMenu, pane_list[panes], TRUE);
      if (lpane == XM_FAILURE)
	{
	  XMenuDestroy (XDISPLAY GXMenu);
	  *error = "Can't create pane";
1073
	  UNBLOCK_INPUT;
Jim Blandy's avatar
Jim Blandy committed
1074 1075
	  return (0);
	}
1076

1077
      for (selidx = 0; selidx < line_cnt[panes]; selidx++)
Jim Blandy's avatar
Jim Blandy committed
1078 1079 1080 1081 1082
	{
	  /* add the selection stuff to the menus */
	  /* datap[selidx+sofar].pane = panes;
	     datap[selidx+sofar].line = selidx; */
	  if (XMenuAddSelection (XDISPLAY GXMenu, lpane, 0,
1083 1084
				 line_list[panes][selidx],
				 enable_list[panes][selidx])
Jim Blandy's avatar
Jim Blandy committed
1085 1086 1087 1088 1089 1090
	      == XM_FAILURE)
	    {
	      XMenuDestroy (XDISPLAY GXMenu);
	      /* free (datap); */
	      *error = "Can't add selection to menu";
	      /* error ("Can't add selection to menu"); */
1091
	      UNBLOCK_INPUT;
Jim Blandy's avatar
Jim Blandy committed
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130
	      return (0);
	    }
	}
    }
  /* all set and ready to fly */
  XMenuRecompute (XDISPLAY GXMenu);
  dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display));
  dispheight = DisplayHeight (x_current_display, XDefaultScreen (x_current_display));
  startx = min (startx, dispwidth);
  starty = min (starty, dispheight);
  startx = max (startx, 1);
  starty = max (starty, 1);
  XMenuLocate (XDISPLAY GXMenu, 0, 0, startx, starty,
	       &ulx, &uly, &width, &height);
  if (ulx+width > dispwidth)
    {
      startx -= (ulx + width) - dispwidth;
      ulx = dispwidth - width;
    }
  if (uly+height > dispheight)
    {
      starty -= (uly + height) - dispheight;
      uly = dispheight - height;
    }
  if (ulx < 0) startx -= ulx;
  if (uly < 0) starty -= uly;
    
  XMenuSetFreeze (GXMenu, TRUE);
  panes = selidx = 0;
  
  status = XMenuActivate (XDISPLAY GXMenu, &panes, &selidx,
			  startx, starty, ButtonReleaseMask, &datap);
  switch (status)
    {
    case XM_SUCCESS:
#ifdef XDEBUG
      fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
#endif
      entry = item_list[panes][selidx];
1131 1132 1133 1134 1135 1136
      if (prefixes != 0)
	{
	  entry = Fcons (entry, Qnil);
	  if (!NILP (prefixes[panes]))
	    entry = Fcons (prefixes[panes], entry);
	}
Jim Blandy's avatar
Jim Blandy committed
1137 1138
      break;
    case XM_FAILURE:
1139
      /* free (datap_save); */
Jim Blandy's avatar
Jim Blandy committed
1140 1141 1142 1143 1144 1145 1146 1147 1148
      XMenuDestroy (XDISPLAY GXMenu);
      *error = "Can't activate menu";
      /* error ("Can't activate menu"); */
    case XM_IA_SELECT:
    case XM_NO_SELECT:
      entry = Qnil;
      break;
    }
  XMenuDestroy (XDISPLAY GXMenu);
1149
  UNBLOCK_INPUT;
1150
  /* free (datap_save);*/
Jim Blandy's avatar
Jim Blandy committed
1151 1152
  return (entry);
}
1153
#endif /* not USE_X_TOOLKIT */
Jim Blandy's avatar
Jim Blandy committed
1154 1155 1156

syms_of_xmenu ()
{
1157
  popup_id_tick = (1<<16);	
Jim Blandy's avatar
Jim Blandy committed
1158 1159
  defsubr (&Sx_popup_menu);
}
1160

1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
/* Figure out the current keyboard equivalent of a menu item ITEM1.
   Store the equivalent key sequence in *SAVEDKEY_PTR
   and the textual description (to use in the menu display) in *DESCRIP_PTR.
   Also cache them in the item itself.
   Return the real definition to execute.  */

static Lisp_Object
menu_item_equiv_key (item1, savedkey_ptr, descrip_ptr)
     Lisp_Object item1;
     Lisp_Object *savedkey_ptr, *descrip_ptr;
{
  /* This is what is left after the menu item name.  */
  Lisp_Object overdef;
  /* This is the real definition--the function to run.  */
  Lisp_Object def;
  /* These are the saved equivalent keyboard key sequence
     and its key-description.  */
  Lisp_Object savedkey, descrip;
  Lisp_Object def1;
  int changed = 0;

  overdef = def = Fcdr (item1);

  /* Get out the saved equivalent-keyboard-key info.  */
  savedkey = descrip = Qnil;
1186
  if (CONSP (overdef) && VECTORP (XCONS (overdef)->car))
1187 1188 1189
    {
      savedkey = XCONS (overdef)->car;
      def = XCONS (def)->cdr;
1190
      if (CONSP (def) && STRINGP (XCONS (def)->car))
1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220
	{
	  descrip = XCONS (def)->car;
	  def = XCONS (def)->cdr;
	}
    }

  /* Is it still valid?  */
  def1 = Qnil;
  if (!NILP (savedkey))
    def1 = Fkey_binding (savedkey, Qnil);
  /* If not, update it.  */
  if (! EQ (def1, def))
    {
      changed = 1;
      descrip = Qnil;
      savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil);
      if (VECTORP (savedkey)
	  && EQ (XVECTOR (savedkey)->contents[0], Qmenu_bar))
	savedkey = Qnil;
      if (!NILP (savedkey))
	{
	  descrip = Fkey_description (savedkey);
	  descrip = concat2 (make_string ("  (", 3), descrip);
	  descrip = concat2 (descrip, make_string (")", 1));
	}
    }

  /* Store back the recorded keyboard key sequence
     if we changed it.  */
  if (!NILP (savedkey)
1221
      && CONSP (overdef) && VECTORP (XCONS (overdef)->car))
1222 1223 1224 1225 1226
    {
      if (changed)
	{
	  XCONS (overdef)->car = savedkey;
	  def1 = XCONS (overdef)->cdr;
1227
	  if (CONSP (def1) && STRINGP (XCONS (def1)->car))
1228 1229 1230 1231 1232 1233 1234
	    XCONS (def1)->car = descrip;
	}
    }
  /* If we had none but need one now, add it.  */
  else if (!NILP (savedkey))
    XCONS (item1)->cdr
      = overdef = Fcons (savedkey, Fcons (descrip, def));
1235 1236
  /* If we had one but no longer should have one, delete it.  */
  else if (CONSP (overdef) && VECTORP (XCONS (overdef)->car))
1237 1238
    {
      XCONS (item1)->cdr = overdef = XCONS (overdef)->cdr;
1239
      if (CONSP (overdef) && STRINGP (XCONS (overdef)->car))
1240 1241 1242 1243 1244 1245 1246 1247
	XCONS (item1)->cdr = overdef = XCONS (overdef)->cdr;
    }

  *savedkey_ptr = savedkey;
  *descrip_ptr = descrip;
  return def;
}

1248
/* Construct the vectors that describe a menu
1249
   and store them in *VECTOR, *PANES, *NAMES, *ENABLES and *ITEMS.
1250 1251 1252 1253 1254 1255
   Each of those four values is a vector indexed by pane number.
   Return the number of panes.

   KEYMAPS is a vector of keymaps.  NMAPS gives the length of KEYMAPS.  */

int
1256
keymap_panes (vector, panes, names, enables, items, prefixes, keymaps, nmaps)
1257 1258 1259
     Lisp_Object ***vector;	/* RETURN all menu objects */
     char ***panes;		/* RETURN pane names */
     char ****names;		/* RETURN all line names */
1260
     int ***enables;		/* RETURN enable-flags of lines */
1261
     int **items;		/* RETURN number of items per pane */
1262
     Lisp_Object **prefixes;	/* RETURN vector of prefix keys, per pane */
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279
     Lisp_Object *keymaps;
     int nmaps;
{
  /* Number of panes we have made.  */
  int p = 0;
  /* Number of panes we have space for.  */
  int npanes_allocated = nmaps;
  int mapno;

  if (npanes_allocated < 4)
    npanes_allocated = 4;

  /* Make space for an estimated number of panes.  */
  *vector = (Lisp_Object **) xmalloc (npanes_allocated * sizeof (Lisp_Object *));
  *panes = (char **) xmalloc (npanes_allocated * sizeof (char *));
  *items = (int *) xmalloc (npanes_allocated * sizeof (int));
  *names = (char ***) xmalloc (npanes_allocated * sizeof (char **));
1280
  *enables = (int **) xmalloc (npanes_allocated * sizeof (int *));
1281
  *prefixes = (Lisp_Object *) xmalloc (npanes_allocated * sizeof (Lisp_Object));
1282 1283 1284 1285 1286

  /* Loop over the given keymaps, making a pane for each map.
     But don't make a pane that is empty--ignore that map instead.
     P is the number of panes we have made so far.  */
  for (mapno = 0; mapno < nmaps; mapno++)
1287
    single_keymap_panes (keymaps[mapno], panes, vector, names, enables, items,
1288
			 prefixes, &p, &npanes_allocated, "");
1289 1290 1291 1292 1293

  /* Return the number of panes.  */
  return p;
}

1294 1295 1296 1297 1298 1299 1300 1301 1302 1303
/* This is used as the handler when calling internal_condition_case_1.  */

static Lisp_Object
single_keymap_panes_1 (arg)
     Lisp_Object arg;
{
  return Qnil;
}

/* This is a recursive subroutine of keymap_panes.
1304 1305 1306 1307
   It handles one keymap, KEYMAP.
   The other arguments are passed along
   or point to local variables of the previous function.  */

1308
single_keymap_panes (keymap, panes, vector, names, enables, items, prefixes,
1309 1310 1311 1312 1313
		     p_ptr, npanes_allocated_ptr, pane_name)
     Lisp_Object keymap;
     Lisp_Object ***vector;	/* RETURN all menu objects */
     char ***panes;		/* RETURN pane names */
     char ****names;		/* RETURN all line names */
1314
     int ***enables;		/* RETURN enable flags of lines */
1315
     int **items;		/* RETURN number of items per pane */
1316
     Lisp_Object **prefixes;	/* RETURN vector of prefix keys, per pane */
1317 1318 1319 1320 1321 1322
     int *p_ptr;
     int *npanes_allocated_ptr;
     char *pane_name;
{
  int i;
  Lisp_Object pending_maps;
1323 1324
  Lisp_Object tail, item, item1, item_string, table;
  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341

  pending_maps = Qnil;

  /* Make sure we have room for another pane.  */
  if (*p_ptr == *npanes_allocated_ptr)
    {
      *npanes_allocated_ptr *= 2;

      *vector
	= (Lisp_Object **) xrealloc (*vector,
				     *npanes_allocated_ptr * sizeof (Lisp_Object *));
      *panes
	= (char **) xrealloc (*panes,
			      *npanes_allocated_ptr * sizeof (char *));
      *items
	= (int *) xrealloc (*items,
			    *npanes_allocated_ptr * sizeof (int));
1342 1343 1344 1345
      *prefixes
	= (Lisp_Object *) xrealloc (*prefixes,
				    (*npanes_allocated_ptr
				     * sizeof (Lisp_Object)));
1346 1347 1348
      *names
	= (char ***) xrealloc (*names,
			       *npanes_allocated_ptr * sizeof (char **));
1349 1350 1351
      *enables
	= (int **) xrealloc (*enables,
			     *npanes_allocated_ptr * sizeof (int *));
1352 1353 1354 1355 1356
    }

  /* When a menu comes from keymaps, don't give names to the panes.  */
  (*panes)[*p_ptr] = pane_name;

1357 1358 1359 1360
  /* Normally put nil as pane's prefix key.
     Caller will override this if appropriate.  */
  (*prefixes)[*p_ptr] = Qnil;

1361 1362 1363
  /* Get the length of the list level of the keymap.  */
  i = XFASTINT (Flength (keymap));

1364 1365 1366 1367
  /* Add in lengths of any arrays.  */
  for (tail = keymap; XTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr)
    if (XTYPE (XCONS (tail)->car) == Lisp_Vector)
      i += XVECTOR (XCONS (tail)->car)->size;
1368 1369 1370 1371 1372

  /* Create vectors for the names and values of the items in the pane.
     I is an upper bound for the number of items.  */
  (*vector)[*p_ptr] = (Lisp_Object *) xmalloc (i * sizeof (Lisp_Object));
  (*names)[*p_ptr] = (char **) xmalloc (i * sizeof (char *));
1373
  (*enables)[*p_ptr] = (int *) xmalloc (i * sizeof (int));
1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386

  /* I is now the index of the next unused slots.  */
  i = 0;
  for (tail = keymap; XTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr)
    {
      /* Look at each key binding, and if it has a menu string,
	 make a menu item from it.  */
      item = XCONS (tail)->car;
      if (XTYPE (item) == Lisp_Cons)
	{
	  item1 = XCONS (item)->cdr;
	  if (XTYPE (item1) == Lisp_Cons)
	    {
1387 1388
	      item_string = XCONS (item1)->car;
	      if (XTYPE (item_string) == Lisp_String)
1389
		{
1390 1391 1392 1393 1394 1395 1396
		  /* This is the real definition--the function to run.  */
		  Lisp_Object def;
		  /* These are the saved equivalent keyboard key sequence
		     and its key-description.  */
		  Lisp_Object savedkey, descrip;
		  Lisp_Object tem, enabled;

1397 1398 1399 1400 1401 1402
		  /* If a help string follows the item string,
		     skip it.  */
		  if (CONSP (XCONS (item1)->cdr)
		      && STRINGP (XCONS (XCONS (item1)->cdr)->car))
		    item1 = XCONS (item1)->cdr;

1403
		  def = menu_item_equiv_key (item1, &savedkey, &descrip);
1404 1405 1406 1407 1408 1409 1410

		  enabled = Qt;
		  if (XTYPE (def) == Lisp_Symbol)
		    {
		      /* No property, or nil, means enable.
			 Otherwise, enable if value is not nil.  */
		      tem = Fget (def, Qmenu_enable);
1411 1412 1413 1414 1415
		      /* GCPRO because we will call eval.