/* X Communication module for terminals which understand the X protocol.
- Copyright (C) 1986, 1988, 1993, 1994 Free Software Foundation, Inc.
+ Copyright (C) 1986, 1988, 1993, 1994, 1996 Free Software Foundation, Inc.
This file is part of GNU Emacs.
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. */
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
/* X pop-up deck-of-cards menu facility for gnuemacs.
*
#define FALSE 0
#endif /* no TRUE */
+Lisp_Object Vmenu_updating_frame;
+
Lisp_Object Qdebug_on_next_call;
+Lisp_Object Qmenu_alias;
+
extern Lisp_Object Qmenu_enable;
extern Lisp_Object Qmenu_bar;
extern Lisp_Object Qmouse_click, Qevent_kind;
static int popup_activated_flag;
static int next_menubar_widget_id;
+
+/* This is set nonzero after the user activates the menu bar, and set
+ to zero again after the menu bars are redisplayed by prepare_menu_bar.
+ While it is nonzero, all calls to set_frame_menubar go deep.
+
+ I don't understand why this is needed, but it does seem to be
+ needed on Motif, according to Marcus Daniels <marcus@sysc.pdx.edu>. */
+
+int pending_menu_activation;
\f
#ifdef USE_X_TOOLKIT
/* If the command is an alias for another
(such as easymenu.el and lmenu.el set it up),
see if the original command name has equivalent keys. */
- if (SYMBOLP (def) && SYMBOLP (XSYMBOL (def)->function))
+ if (SYMBOLP (def) && SYMBOLP (XSYMBOL (def)->function)
+ && ! NILP (Fget (def, Qmenu_alias)))
savedkey = Fwhere_is_internal (XSYMBOL (def)->function,
Qnil, Qt, Qnil);
else
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++)
- single_keymap_panes (keymaps[mapno], Qnil, Qnil, notreal);
+ single_keymap_panes (keymaps[mapno], Qnil, Qnil, notreal, 10);
finish_menu_items ();
}
The other arguments are passed along
or point to local variables of the previous function.
If NOTREAL is nonzero,
- don't bother really computing whether an item is enabled. */
+ don't bother really computing whether an item is enabled.
+
+ If we encounter submenus deeper than MAXDEPTH levels, ignore them. */
static void
-single_keymap_panes (keymap, pane_name, prefix, notreal)
+single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
Lisp_Object keymap;
Lisp_Object pane_name;
Lisp_Object prefix;
int notreal;
+ int maxdepth;
{
Lisp_Object pending_maps;
Lisp_Object tail, item, item1, item_string, table;
struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
+ if (maxdepth <= 0)
+ return;
+
pending_maps = Qnil;
push_menu_pane (pane_name, prefix);
{
push_submenu_start ();
single_keymap_panes (submap, Qnil,
- XCONS (item)->car, notreal);
+ XCONS (item)->car, notreal,
+ maxdepth - 1);
push_submenu_end ();
}
#endif
{
push_submenu_start ();
single_keymap_panes (submap, Qnil,
- character, notreal);
+ character, notreal,
+ maxdepth - 1);
push_submenu_end ();
}
#endif
/* We no longer discard the @ from the beginning of the string here.
Instead, we do this in xmenu_show. */
single_keymap_panes (Fcar (elt), string,
- XCONS (eltcdr)->cdr, notreal);
+ XCONS (eltcdr)->cdr, notreal, maxdepth - 1);
pending_maps = Fcdr (pending_maps);
}
}
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\
+If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.\n\
+Otherwise, REAL-DEFINITION should be a valid key binding definition.\n\
+\n\
You can also use a list of keymaps as MENU.\n\
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\
+\n\
Alternatively, you can specify a menu of multiple panes\n\
with a list of the form (TITLE PANE1 PANE2...),\n\
where each pane is a list of form (TITLE ITEM1 ITEM2...).\n\
int for_click = 0;
struct gcpro gcpro1;
+#ifdef HAVE_MENUS
if (! NILP (position))
{
check_x ();
CHECK_LIVE_WINDOW (window, 0);
f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
- xpos = (FONT_WIDTH (f->output_data.x->font) * XWINDOW (window)->left);
- ypos = (f->output_data.x->line_height * XWINDOW (window)->top);
+ xpos = (FONT_WIDTH (f->output_data.x->font)
+ * XFASTINT (XWINDOW (window)->left));
+ ypos = (f->output_data.x->line_height
+ * XFASTINT (XWINDOW (window)->top));
}
else
/* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
xpos += XINT (x);
ypos += XINT (y);
+
+ XSETFRAME (Vmenu_updating_frame, f);
}
+ Vmenu_updating_frame = Qnil;
+#endif /* HAVE_MENUS */
title = Qnil;
GCPRO1 (title);
/* 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 (title) && !NILP (prompt))
+ title = prompt;
/* Make that be the pane title of the first pane. */
if (!NILP (prompt) && menu_items_n_panes >= 0)
return Qnil;
}
+#ifdef HAVE_MENUS
/* Display them in a menu. */
BLOCK_INPUT;
discard_menu_items ();
UNGCPRO;
+#endif /* HAVE_MENUS */
if (error_name) error (error_name);
return selection;
}
+#ifdef HAVE_MENUS
+
DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 2, 0,
"Pop up a dialog box and return user's selection.\n\
POSITION specifies which frame to use.\n\
}
else if (WINDOWP (position) || FRAMEP (position))
window = position;
+ else
+ window = Qnil;
/* Decode where to put the menu. */
/* Handle expose events for editor frames right away. */
if (event.type == Expose)
process_expose_from_menu (event);
- /* Make sure we don't consider buttons grabbed after menu goes. */
+ /* Make sure we don't consider buttons grabbed after menu goes.
+ And make sure to deactivate for any ButtonRelease,
+ even if XtDispatchEvent doesn't do that. */
else if (event.type == ButtonRelease
&& dpyinfo->display == event.xbutton.display)
- dpyinfo->grabbed &= ~(1 << event.xbutton.button);
+ {
+ dpyinfo->grabbed &= ~(1 << event.xbutton.button);
+ popup_activated_flag = 0;
+#ifdef USE_MOTIF /* Pretending that the event came from a
+ Btn1Down seems the only way to convince Motif to
+ activate its callbacks; setting the XmNmenuPost
+ isn't working. --marcus@sysc.pdx.edu. */
+ event.xbutton.button = 1;
+#endif
+ }
/* If the user presses a key, deactivate the menu.
The user is likely to do that if we get wedged. */
else if (event.type == KeyPress
}
/* Queue all events not for this popup,
- except for Expose, which we've already handled.
+ except for Expose, which we've already handled, and ButtonRelease.
Note that the X window is associated with the frame if this
is a menu bar popup, but not if it's a dialog box. So we use
x_non_menubar_window_to_frame, not x_any_window_to_frame. */
if (event.type != Expose
+ && !(event.type == ButtonRelease
+ && dpyinfo->display == event.xbutton.display)
&& (event.xany.display != dpyinfo->display
|| x_non_menubar_window_to_frame (dpyinfo, event.xany.window)))
{
menu_bar_activate_event out of the Emacs event queue.
To activate the menu bar, we use the X button-press event
- that was saved in saved_button_event.
+ that was saved in saved_menu_event.
That makes the toolkit do its thing.
But first we recompute the menu bar contents (the whole tree).
x_activate_menubar (f)
FRAME_PTR f;
{
- if (f->output_data.x->saved_button_event->type != ButtonPress)
+ if (!f->output_data.x->saved_menu_event->type)
return;
set_frame_menubar (f, 0, 1);
-
BLOCK_INPUT;
- XtDispatchEvent ((XEvent *) f->output_data.x->saved_button_event);
+ XtDispatchEvent ((XEvent *) f->output_data.x->saved_menu_event);
UNBLOCK_INPUT;
-
+#ifdef USE_MOTIF
+ if (f->output_data.x->saved_menu_event->type == ButtonRelease)
+ pending_menu_activation = 1;
+#endif
+
/* Ignore this if we get it a second time. */
- f->output_data.x->saved_button_event->type = 0;
+ f->output_data.x->saved_menu_event->type = 0;
}
/* Detect if a dialog or menu has been posted. */
popup_activated_flag = 0;
}
+/* Allocate a widget_value, blocking input. */
+
+widget_value *
+xmalloc_widget_value ()
+{
+ widget_value *value;
+
+ BLOCK_INPUT;
+ value = malloc_widget_value ();
+ UNBLOCK_INPUT;
+
+ return value;
+}
/* This recursively calls free_widget_value on the tree of widgets.
It must free all data that was malloc'ed for these widget_values.
But don't make a pane that is empty--ignore that map instead. */
for (i = 0; i < len; i++)
{
- if (SYMBOLP (mapvec[i]))
+ if (SYMBOLP (mapvec[i])
+ || (CONSP (mapvec[i])
+ && NILP (Fkeymapp (mapvec[i]))))
{
+ /* Here we have a command at top level in the menu bar
+ as opposed to a submenu. */
top_level_items = 1;
push_menu_pane (Qnil, Qnil);
push_menu_item (item_name, Qt, item_key, mapvec[i], Qnil);
}
else
- single_keymap_panes (mapvec[i], item_name, item_key, 0);
+ single_keymap_panes (mapvec[i], item_name, item_key, 0, 10);
}
/* Create a tree of widget_value objects
submenu_stack
= (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
- wv = malloc_widget_value ();
+ wv = xmalloc_widget_value ();
wv->name = "menu";
wv->value = 0;
wv->enabled = 1;
with its items as a submenu beneath it. */
if (strcmp (pane_string, ""))
{
- wv = malloc_widget_value ();
+ wv = xmalloc_widget_value ();
if (save_wv)
save_wv->next = wv;
else
= XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
- wv = malloc_widget_value ();
+ wv = xmalloc_widget_value ();
if (prev_wv)
prev_wv->next = wv;
else
int i;
LWLIB_ID id;
+ XSETFRAME (Vmenu_updating_frame, f);
+
if (f->output_data.x->id == 0)
f->output_data.x->id = next_menubar_widget_id++;
id = f->output_data.x->id;
if (! menubar_widget)
deep_p = 1;
+ else if (pending_menu_activation && !deep_p)
+ deep_p = 1;
+ /* Make the first call for any given frame always go deep. */
+ else if (!f->output_data.x->saved_menu_event && !deep_p)
+ {
+ deep_p = 1;
+ f->output_data.x->saved_menu_event = (XEvent*)xmalloc (sizeof (XEvent));
+ f->output_data.x->saved_menu_event->type = 0;
+ }
- wv = malloc_widget_value ();
+ wv = xmalloc_widget_value ();
wv->name = "menubar";
wv->value = 0;
wv->enabled = 1;
= (Lisp_Object *) alloca (previous_menu_items_used
* sizeof (Lisp_Object));
+ /* If we are making a new widget, its contents are empty,
+ do always reinitialize them. */
+ if (! menubar_widget)
+ previous_menu_items_used = 0;
+
buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
specbind (Qinhibit_quit, Qt);
/* Don't let the debugger step into this code
because it is not reentrant. */
specbind (Qdebug_on_next_call, Qnil);
- record_unwind_protect (Fstore_match_data, Fmatch_data ());
+ record_unwind_protect (Fstore_match_data, Fmatch_data (Qnil, Qnil));
if (NILP (Voverriding_local_map_menu_flag))
{
specbind (Qoverriding_terminal_local_map, Qnil);
really recompute the menubar from the value. */
if (! NILP (Vlucid_menu_bar_dirty_flag))
call0 (Qrecompute_lucid_menubar);
- call1 (Vrun_hooks, Qmenu_bar_update_hook);
+ safe_run_hooks (Qmenu_bar_update_hook);
FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
items = FRAME_MENU_BAR_ITEMS (f);
menu_items = f->menu_bar_vector;
menu_items_allocated = XVECTOR (menu_items)->size;
init_menu_items ();
- for (i = 0; i < XVECTOR (items)->size; i += 3)
+ for (i = 0; i < XVECTOR (items)->size; i += 4)
{
Lisp_Object key, string, maps;
for (i = 0; i < previous_menu_items_used; i++)
if (menu_items_used == i
- || (previous_items[i] != XVECTOR (menu_items)->contents[i]))
+ || (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i])))
break;
if (i == menu_items_used && i == previous_menu_items_used && i != 0)
{
/* Now GC cannot happen during the lifetime of the widget_value,
so it's safe to store data from a Lisp_String. */
wv = first_wv->contents;
- for (i = 0; i < XVECTOR (items)->size; i += 3)
+ for (i = 0; i < XVECTOR (items)->size; i += 4)
{
Lisp_Object string;
string = XVECTOR (items)->contents[i + 1];
just the top level menu bar strings. */
items = FRAME_MENU_BAR_ITEMS (f);
- for (i = 0; i < XVECTOR (items)->size; i += 3)
+ for (i = 0; i < XVECTOR (items)->size; i += 4)
{
Lisp_Object string;
if (NILP (string))
break;
- wv = malloc_widget_value ();
+ wv = xmalloc_widget_value ();
wv->name = (char *) XSTRING (string)->data;
wv->value = 0;
wv->enabled = 1;
+ /* This prevents lwlib from assuming this
+ menu item is really supposed to be empty. */
+ /* The EMACS_INT cast avoids a warning.
+ This value just has to be different from small integers. */
+ wv->call_data = (void *) (EMACS_INT) (-1);
if (prev_wv)
prev_wv->next = wv;
+ f->output_data.x->menubar_widget->core.border_width)
: 0);
+#if 0 /* Experimentally, we now get the right results
+ for -geometry -0-0 without this. 24 Aug 96, rms. */
#ifdef USE_LUCID
if (FRAME_EXTERNAL_MENU_BAR (f))
{
menubar_size += ibw;
}
#endif /* USE_LUCID */
+#endif /* 0 */
f->output_data.x->menubar_height = menubar_size;
}
int id;
menubar_widget = f->output_data.x->menubar_widget;
+
+ f->output_data.x->menubar_height = 0;
if (menubar_widget)
{
/* F is the frame the menu is for.
X and Y are the frame-relative specified position,
relative to the inside upper left corner of the frame F.
- FOR_CLICK if this menu was invoked for a mouse click.
+ FOR_CLICK is nonzero if this menu was invoked for a mouse click.
KEYMAPS is 1 if this menu was specified with keymaps;
in that case, we return a list containing the chosen item's value
and perhaps also the pane's prefix.
/* Create a tree of widget_value objects
representing the panes and their items. */
- wv = malloc_widget_value ();
+ wv = xmalloc_widget_value ();
wv->name = "menu";
wv->value = 0;
wv->enabled = 1;
with its items as a submenu beneath it. */
if (!keymaps && strcmp (pane_string, ""))
{
- wv = malloc_widget_value ();
+ wv = xmalloc_widget_value ();
if (save_wv)
save_wv->next = wv;
else
= XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
- wv = malloc_widget_value ();
+ wv = xmalloc_widget_value ();
if (prev_wv)
prev_wv->next = wv;
else
/* Deal with the title, if it is non-nil. */
if (!NILP (title))
{
- widget_value *wv_title = malloc_widget_value ();
- widget_value *wv_sep1 = malloc_widget_value ();
- widget_value *wv_sep2 = malloc_widget_value ();
+ widget_value *wv_title = xmalloc_widget_value ();
+ widget_value *wv_sep1 = xmalloc_widget_value ();
+ widget_value *wv_sep2 = xmalloc_widget_value ();
wv_sep2->name = "--";
wv_sep2->next = first_wv->contents;
dummy.send_event = 0;
dummy.display = FRAME_X_DISPLAY (f);
dummy.time = CurrentTime;
- dummy.button = 0;
dummy.root = FRAME_X_DISPLAY_INFO (f)->root_window;
dummy.window = dummy.root;
dummy.subwindow = dummy.root;
dummy.y_root = y;
dummy.x = x;
dummy.y = y;
+ dummy.state = (FRAME_X_DISPLAY_INFO (f)->grabbed >> 1) * Button1Mask;
+ dummy.button = 0;
+ for (i = 0; i < 5; i++)
+ if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
+ dummy.button = i;
/* Don't allow any geometry request from the user. */
XtSetArg (av[ac], XtNgeometry, 0); ac++;
prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
pane_string = (NILP (pane_name)
? "" : (char *) XSTRING (pane_name)->data);
- prev_wv = malloc_widget_value ();
+ prev_wv = xmalloc_widget_value ();
prev_wv->value = pane_string;
if (keymaps && !NILP (prefix))
prev_wv->name++;
i++;
continue;
}
- if (nb_buttons >= 10)
+ if (nb_buttons >= 9)
{
free_menubar_widget_value_tree (first_wv);
*error = "Too many dialog items";
return Qnil;
}
- wv = malloc_widget_value ();
+ wv = xmalloc_widget_value ();
prev_wv->next = wv;
wv->name = (char *) button_names[nb_buttons];
if (!NILP (descrip))
if (! boundary_seen)
left_count = nb_buttons - nb_buttons / 2;
- wv = malloc_widget_value ();
+ wv = xmalloc_widget_value ();
wv->name = dialog_name;
/* Dialog boxes use a really stupid name encoding
}
#endif /* not USE_X_TOOLKIT */
+
+#endif /* HAVE_MENUS */
\f
syms_of_xmenu ()
{
staticpro (&menu_items);
menu_items = Qnil;
+ Qmenu_alias = intern ("menu-alias");
+ staticpro (&Qmenu_alias);
+
Qdebug_on_next_call = intern ("debug-on-next-call");
staticpro (&Qdebug_on_next_call);
+ DEFVAR_LISP ("menu-updating-frame", &Vmenu_updating_frame,
+ "Frame for which we are updating a menu.\n\
+The enable predicate for a menu command should check this variable.");
+ Vmenu_updating_frame = Qnil;
+
#ifdef USE_X_TOOLKIT
widget_id_tick = (1<<16);
next_menubar_widget_id = 1;
#endif
defsubr (&Sx_popup_menu);
+#ifdef HAVE_MENUS
defsubr (&Sx_popup_dialog);
+#endif
}