/* Rewritten for clarity and GC protection by rms in Feb 94. */
-#include <stdio.h>
-
/* On 4.3 this loses if it comes after xterm.h. */
#include <signal.h>
#include <config.h>
+
+#include <stdio.h>
#include "lisp.h"
#include "termhooks.h"
#include "frame.h"
#include "window.h"
#include "keyboard.h"
#include "blockinput.h"
+#include "puresize.h"
/* This may include sys/types.h, and that somehow loses
if this is not done before the other system files. */
#include <X11/IntrinsicP.h>
#include <X11/CoreP.h>
#include <X11/StringDefs.h>
+#include <X11/Shell.h>
#include <X11/Xaw/Paned.h>
#include "../lwlib/lwlib.h"
#include "../lwlib/xlwmenuP.h"
extern void process_expose_from_menu ();
extern XtAppContext Xt_app_con;
-static int string_width ();
+static Lisp_Object xdialog_show ();
#endif
static Lisp_Object xmenu_show ();
the item string, the enable flag, the item's value,
and the equivalent keyboard key's description string.
+ In some cases, multiple levels of menus may be described.
+ A single vector slot containing nil indicates the start of a submenu.
+ A single vector slot containing lambda indicates the end of a submenu.
+ The submenu follows a menu item which is the way to reach the submenu.
+
+ A single vector slot containing quote indicates that the
+ following items should appear on the right of a dialog box.
+
Using a Lisp vector to hold this information while we decode it
takes care of protecting all the data from GC. */
/* This is the index in menu_items of the first empty slot. */
static int menu_items_used;
-/* The number of panes currently recorded in menu_items. */
+/* The number of panes currently recorded in menu_items,
+ excluding those within submenus. */
static int menu_items_n_panes;
+/* Current depth within submenus. */
+static int menu_items_submenu_depth;
+
/* Initialize the menu_items structure if we haven't already done so.
Also mark it as currently empty. */
menu_items_used = 0;
menu_items_n_panes = 0;
+ menu_items_submenu_depth = 0;
}
/* Call at the end of generating the data in menu_items.
}
}
+/* Make the menu_items vector twice as large. */
+
+static void
+grow_menu_items ()
+{
+ Lisp_Object old;
+ int old_size = menu_items_allocated;
+ old = menu_items;
+
+ menu_items_allocated *= 2;
+ menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
+ bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents,
+ old_size * sizeof (Lisp_Object));
+}
+
+/* Begin a submenu. */
+
+static void
+push_submenu_start ()
+{
+ if (menu_items_used + 1 > menu_items_allocated)
+ grow_menu_items ();
+
+ XVECTOR (menu_items)->contents[menu_items_used++] = Qnil;
+ menu_items_submenu_depth++;
+}
+
+/* End a submenu. */
+
+static void
+push_submenu_end ()
+{
+ if (menu_items_used + 1 > menu_items_allocated)
+ grow_menu_items ();
+
+ XVECTOR (menu_items)->contents[menu_items_used++] = Qlambda;
+ menu_items_submenu_depth--;
+}
+
+/* Indicate boundary between left and right. */
+
+static void
+push_left_right_boundary ()
+{
+ if (menu_items_used + 1 > menu_items_allocated)
+ grow_menu_items ();
+
+ XVECTOR (menu_items)->contents[menu_items_used++] = Qquote;
+}
+
/* Start a new menu pane in menu_items..
NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */
Lisp_Object name, prefix_vec;
{
if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated)
- {
- Lisp_Object old;
- int old_size = menu_items_allocated;
- old = menu_items;
+ grow_menu_items ();
- menu_items_allocated *= 2;
- menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
- bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents,
- old_size * sizeof (Lisp_Object));
- }
-
- menu_items_n_panes++;
+ if (menu_items_submenu_depth == 0)
+ menu_items_n_panes++;
XVECTOR (menu_items)->contents[menu_items_used++] = Qt;
XVECTOR (menu_items)->contents[menu_items_used++] = name;
XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec;
Lisp_Object name, enable, key, equiv;
{
if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
- {
- Lisp_Object old;
- int old_size = menu_items_allocated;
- old = menu_items;
-
- menu_items_allocated *= 2;
- menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
- bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents,
- old_size * sizeof (Lisp_Object));
- }
+ grow_menu_items ();
XVECTOR (menu_items)->contents[menu_items_used++] = name;
XVECTOR (menu_items)->contents[menu_items_used++] = enable;
Lisp_Object savedkey, descrip;
Lisp_Object def1;
int changed = 0;
+ struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
/* If a help string follows the item string, skip it. */
if (CONSP (XCONS (item1)->cdr)
descrip = XCONS (cachelist)->cdr;
}
+ GCPRO4 (def, def1, savedkey, descrip);
+
/* Is it still valid? */
def1 = Qnil;
if (!NILP (savedkey))
def1 = Fkey_binding (savedkey, Qnil);
/* If not, update it. */
if (! EQ (def1, def)
+ /* If the command is an alias for another
+ (such as easymenu.el and lmenu.el set it up),
+ check if the original command matches the cached command. */
+ && !(SYMBOLP (def) && SYMBOLP (XSYMBOL (def)->function)
+ && EQ (def1, XSYMBOL (def)->function))
/* If something had no key binding before, don't recheck it--
doing that takes too much time and makes menus too slow. */
&& !(!NILP (cachelist) && NILP (savedkey)))
changed = 1;
descrip = Qnil;
savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil);
+ /* 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))
+ savedkey = Fwhere_is_internal (XSYMBOL (def)->function,
+ Qnil, Qt, Qnil);
+
if (VECTORP (savedkey)
&& EQ (XVECTOR (savedkey)->contents[0], Qmenu_bar))
savedkey = Qnil;
/* Cache the data we just got in a sublist of the menu binding. */
if (NILP (cachelist))
- XCONS (item1)->cdr = Fcons (Fcons (savedkey, descrip), def);
+ {
+ CHECK_IMPURE (item1);
+ XCONS (item1)->cdr = Fcons (Fcons (savedkey, descrip), def);
+ }
else if (changed)
{
XCONS (cachelist)->car = savedkey;
XCONS (cachelist)->cdr = descrip;
}
+ UNGCPRO;
*descrip_ptr = descrip;
return def;
}
}
/* Return non-nil if the command DEF is enabled when used as a menu item.
- This is based on looking for a menu-enable property. */
+ This is based on looking for a menu-enable property.
+ If NOTREAL is set, don't bother really computing this. */
static Lisp_Object
-menu_item_enabled_p (def)
+menu_item_enabled_p (def, notreal)
Lisp_Object def;
+ int notreal;
{
Lisp_Object enabled, tem;
enabled = Qt;
+ if (notreal)
+ return enabled;
if (XTYPE (def) == Lisp_Symbol)
{
/* No property, or nil, means enable.
}
\f
/* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
- and generate menu panes for them in menu_items. */
+ and generate menu panes for them in menu_items.
+ If NOTREAL is nonzero,
+ don't bother really computing whether an item is enabled. */
static void
-keymap_panes (keymaps, nmaps)
+keymap_panes (keymaps, nmaps, notreal)
Lisp_Object *keymaps;
int nmaps;
+ int notreal;
{
int mapno;
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);
+ single_keymap_panes (keymaps[mapno], Qnil, Qnil, notreal);
finish_menu_items ();
}
/* This is a recursive subroutine of keymap_panes.
It handles one keymap, KEYMAP.
The other arguments are passed along
- or point to local variables of the previous function. */
+ or point to local variables of the previous function.
+ If NOTREAL is nonzero,
+ don't bother really computing whether an item is enabled. */
static void
-single_keymap_panes (keymap, pane_name, prefix)
+single_keymap_panes (keymap, pane_name, prefix, notreal)
Lisp_Object keymap;
Lisp_Object pane_name;
Lisp_Object prefix;
+ int notreal;
{
Lisp_Object pending_maps;
Lisp_Object tail, item, item1, item_string, table;
Lisp_Object descrip;
Lisp_Object tem, enabled;
- def = menu_item_equiv_key (item_string, item1, &descrip);
-
- /* GCPRO because we will call eval.
+ /* GCPRO because ...enabled_p will call eval
+ and ..._equiv_key may autoload something.
Protecting KEYMAP preserves everything we use;
aside from that, must protect whatever might be
a string. Since there's no GCPRO5, we refetch
item_string instead of protecting it. */
+ descrip = def = Qnil;
GCPRO4 (keymap, pending_maps, def, descrip);
- enabled = menu_item_enabled_p (def);
+
+ def = menu_item_equiv_key (item_string, item1, &descrip);
+ enabled = menu_item_enabled_p (def, notreal);
+
UNGCPRO;
item_string = XCONS (item1)->car;
pending_maps = Fcons (Fcons (def, Fcons (item_string, XCONS (item)->car)),
pending_maps);
else
- push_menu_item (item_string, enabled, XCONS (item)->car,
- descrip);
+ {
+ Lisp_Object submap;
+ GCPRO4 (keymap, pending_maps, descrip, item_string);
+ submap = get_keymap_1 (def, 0, 1);
+ UNGCPRO;
+#ifndef USE_X_TOOLKIT
+ /* Indicate visually that this is a submenu. */
+ if (!NILP (submap))
+ item_string = concat2 (item_string,
+ build_string (" >"));
+#endif
+ push_menu_item (item_string, enabled, XCONS (item)->car,
+ descrip);
+#ifdef USE_X_TOOLKIT
+ /* Display a submenu using the toolkit. */
+ if (! NILP (submap))
+ {
+ push_submenu_start ();
+ single_keymap_panes (submap, Qnil,
+ XCONS (item)->car, notreal);
+ push_submenu_end ();
+ }
+#endif
+ }
}
}
}
Lisp_Object descrip;
Lisp_Object tem, enabled;
- def = menu_item_equiv_key (item_string, item1, &descrip);
-
- /* GCPRO because we will call eval.
+ /* GCPRO because ...enabled_p will call eval
+ and ..._equiv_key may autoload something.
Protecting KEYMAP preserves everything we use;
aside from that, must protect whatever might be
a string. Since there's no GCPRO5, we refetch
item_string instead of protecting it. */
GCPRO4 (keymap, pending_maps, def, descrip);
- enabled = menu_item_enabled_p (def);
+ descrip = def = Qnil;
+
+ def = menu_item_equiv_key (item_string, item1, &descrip);
+ enabled = menu_item_enabled_p (def, notreal);
+
UNGCPRO;
item_string = XCONS (item1)->car;
pending_maps = Fcons (Fcons (def, Fcons (item_string, character)),
pending_maps);
else
- push_menu_item (item_string, enabled,
- character, descrip);
+ {
+ Lisp_Object submap;
+ GCPRO4 (keymap, pending_maps, descrip, item_string);
+ submap = get_keymap_1 (def, 0, 1);
+ UNGCPRO;
+#ifndef USE_X_TOOLKIT
+ if (!NILP (submap))
+ item_string = concat2 (item_string,
+ build_string (" >"));
+#endif
+ push_menu_item (item_string, enabled, character,
+ descrip);
+#ifdef USE_X_TOOLKIT
+ if (! NILP (submap))
+ {
+ push_submenu_start ();
+ single_keymap_panes (submap, Qnil,
+ character, notreal);
+ push_submenu_end ();
+ }
+#endif
+ }
}
}
}
/* Process now any submenus which want to be panes at this level. */
while (!NILP (pending_maps))
{
- Lisp_Object elt, eltcdr;
+ Lisp_Object elt, eltcdr, string;
elt = Fcar (pending_maps);
eltcdr = XCONS (elt)->cdr;
- single_keymap_panes (Fcar (elt),
- /* Fails to discard the @. */
- XCONS (eltcdr)->car, XCONS (eltcdr)->cdr);
+ string = XCONS (eltcdr)->car;
+ /* 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);
pending_maps = Fcdr (pending_maps);
}
}
{
item = Fcar (tail);
if (STRINGP (item))
- push_menu_item (item, Qnil, Qnil);
+ push_menu_item (item, Qnil, Qnil, Qnil);
+ else if (NILP (item))
+ push_left_right_boundary ();
else
{
CHECK_CONS (item, 0);
}
}
\f
-DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 1, 2, 0,
+DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
"Pop up a deck-of-cards menu and return user's selection.\n\
POSITION is a position specification. This is either a mouse button event\n\
or a list ((XOFFSET YOFFSET) WINDOW)\n\
-where XOFFSET and YOFFSET are positions in characters from the top left\n\
+where XOFFSET and YOFFSET are positions in pixels from the top left\n\
corner of WINDOW's frame. (WINDOW may be a frame object instead of a window.)\n\
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\
int menubarp = 0;
struct gcpro gcpro1;
- check_x ();
-
if (! NILP (position))
{
+ check_x ();
+
/* Decode the first argument: find the window and the coordinates. */
if (EQ (position, Qt))
{
/* Use the mouse's current position. */
- FRAME_PTR new_f;
+ FRAME_PTR new_f = 0;
Lisp_Object bar_window;
int part;
unsigned long time;
- (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
- XSET (window, Lisp_Frame, new_f);
+ if (mouse_position_hook)
+ (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
+ if (new_f != 0)
+ XSET (window, Lisp_Frame, new_f);
+ else
+ {
+ window = selected_window;
+ XFASTINT (x) = 0;
+ XFASTINT (y) = 0;
+ }
}
else
{
/* Determine whether this menu is handling a menu bar click. */
tem = Fcar (Fcdr (Fcar (Fcdr (position))));
- if (XTYPE (Fcar (position)) != Lisp_Cons
- && CONSP (tem)
- && EQ (Fcar (tem), Qmenu_bar))
+ if (CONSP (tem) && EQ (Fcar (tem), Qmenu_bar))
menubarp = 1;
}
}
f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
xpos = (FONT_WIDTH (f->display.x->font) * XWINDOW (window)->left);
- ypos = (FONT_HEIGHT (f->display.x->font) * XWINDOW (window)->top);
+ ypos = (f->display.x->line_height * XWINDOW (window)->top);
}
else
/* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
keymap = get_keymap (menu);
/* Extract the detailed info to make one pane. */
- keymap_panes (&menu, 1);
+ keymap_panes (&menu, 1, NILP (position));
/* Search for a string appearing directly as an element of the keymap.
That string is the title of the menu. */
}
/* Extract the detailed info to make one pane. */
- keymap_panes (maps, nmaps);
+ keymap_panes (maps, nmaps, NILP (position));
/* Make the title be the pane title of the first pane. */
if (!NILP (title) && menu_items_n_panes >= 0)
if (error_name) error (error_name);
return selection;
}
+
+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\
+This is normally a mouse button event or a window or frame.\n\
+If POSITION is t, it means to use the frame the mouse is on.\n\
+The dialog box appears in the middle of the specified frame.\n\
+\n\
+CONTENTS specifies the alternatives to display in the dialog box.\n\
+It is a list of the form (TITLE ITEM1 ITEM2...).\n\
+Each ITEM is a cons cell (STRING . VALUE).\n\
+The return value is VALUE from the chosen item.\n\n\
+An ITEM may also be just a string--that makes a nonselectable item.\n\
+An ITEM may also be nil--that means to put all preceding items\n\
+on the left of the dialog box and all following items on the right.\n\
+\(By default, approximately half appear on each side.)")
+ (position, contents)
+ Lisp_Object position, contents;
+{
+ FRAME_PTR f;
+ Lisp_Object window;
+
+ check_x ();
+
+ /* Decode the first argument: find the window or frame to use. */
+ if (EQ (position, Qt))
+ {
+#if 0 /* Using the frame the mouse is on may not be right. */
+ /* Use the mouse's current position. */
+ FRAME_PTR new_f = 0;
+ Lisp_Object bar_window;
+ int part;
+ unsigned long time;
+ Lisp_Object x, y;
+
+ (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
+
+ if (new_f != 0)
+ XSET (window, Lisp_Frame, new_f);
+ else
+ window = selected_window;
+#endif
+ /* Decode the first argument: find the window and the coordinates. */
+ if (EQ (position, Qt))
+ window = selected_window;
+ }
+ else if (CONSP (position))
+ {
+ Lisp_Object tem;
+ tem = Fcar (position);
+ if (XTYPE (tem) == Lisp_Cons)
+ window = Fcar (Fcdr (position));
+ else
+ {
+ tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
+ window = Fcar (tem); /* POSN_WINDOW (tem) */
+ }
+ }
+ else if (WINDOWP (position) || FRAMEP (position))
+ window = position;
+
+ /* Decode where to put the menu. */
+
+ if (XTYPE (window) == Lisp_Frame)
+ f = XFRAME (window);
+ else if (XTYPE (window) == Lisp_Window)
+ {
+ CHECK_LIVE_WINDOW (window, 0);
+ f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
+ }
+ else
+ /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
+ but I don't want to make one now. */
+ CHECK_WINDOW (window, 0);
+
+#ifndef USE_X_TOOLKIT
+ /* Display a menu with these alternatives
+ in the middle of frame F. */
+ {
+ Lisp_Object x, y, frame, newpos;
+ XSET (frame, Lisp_Frame, f);
+ XSET (x, Lisp_Int, x_pixel_width (f) / 2);
+ XSET (y, Lisp_Int, x_pixel_height (f) / 2);
+ newpos = Fcons (Fcons (x, Fcons (y, Qnil)), Fcons (frame, Qnil));
+
+ return Fx_popup_menu (newpos,
+ Fcons (Fcar (contents), Fcons (contents, Qnil)));
+ }
+#else
+ {
+ Lisp_Object title;
+ char *error_name;
+ Lisp_Object selection;
+
+ /* Decode the dialog items from what was specified. */
+ title = Fcar (contents);
+ CHECK_STRING (title, 1);
+
+ list_of_panes (Fcons (contents, Qnil));
+
+ /* Display them in a dialog box. */
+ BLOCK_INPUT;
+ selection = xdialog_show (f, 0, 0, title, &error_name);
+ UNBLOCK_INPUT;
+
+ discard_menu_items ();
+
+ if (error_name) error (error_name);
+ return selection;
+ }
+#endif
+}
\f
#ifdef USE_X_TOOLKIT
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;
+ XtDispatchEvent ((XEvent *) &dummy);
}
static int
return Qnil;
}
+#ifdef __STDC__
+static Lisp_Object *volatile menu_item_selection;
+#else
static Lisp_Object *menu_item_selection;
+#endif
static void
popup_selection_callback (widget, id, client_data)
UNBLOCK_INPUT;
}
+static void
+dialog_selection_callback (widget, id, client_data)
+ Widget widget;
+ LWLIB_ID id;
+ XtPointer client_data;
+{
+ if ((int)client_data != -1)
+ menu_item_selection = (Lisp_Object *) 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.
In Emacs, many slots are pointers into the data of Lisp_Strings, and
UNBLOCK_INPUT;
}
+extern void EmacsFrameSetCharSize ();
+
static void
-update_one_frame_psheets (f)
+update_frame_menubar (f)
FRAME_PTR f;
{
struct x_display *x = f->display.x;
-
+ int columns, rows;
int menubar_changed;
menubar_changed = (x->menubar_widget
return;
BLOCK_INPUT;
+ /* Save the size of the frame because the pane widget doesn't accept to
+ resize itself. So force it. */
+ columns = f->width;
+ rows = f->height;
+
+
XawPanedSetRefigureMode (x->column_widget, 0);
/* the order in which children are managed is the top to
/* and now thrash the sizes */
XawPanedSetRefigureMode (x->column_widget, 1);
+
+ /* Force the pane widget to resize itself with the right values. */
+ EmacsFrameSetCharSize (x->edit_widget, columns, rows);
+
UNBLOCK_INPUT;
}
void
-set_frame_menubar (f)
+set_frame_menubar (f, first_time)
FRAME_PTR f;
+ int first_time;
{
Widget menubar_widget = f->display.x->menubar_widget;
int id = (int) f;
wv->enabled = 1;
save_wv = first_wv = wv;
- items = FRAME_MENU_BAR_ITEMS (f);
+ if (NILP (items = FRAME_MENU_BAR_ITEMS (f)))
+ items = FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
for (i = 0; i < XVECTOR (items)->size; i += 3)
{
prev_wv->next = wv;
else
save_wv->contents = wv;
- wv->name = XSTRING (string)->data;
+ wv->name = (char *) XSTRING (string)->data;
wv->value = 0;
wv->enabled = 1;
prev_wv = wv;
free_menubar_widget_value_tree (first_wv);
- update_one_frame_psheets (f);
+ /* Don't update the menubar the first time it is created via x_window. */
+ if (!first_time)
+ update_frame_menubar (f);
UNBLOCK_INPUT;
}
UNBLOCK_INPUT;
}
}
+/* Called from Fx_create_frame to create the inital menubar of a frame
+ before it is mapped, so that the window is mapped with the menubar already
+ there instead of us tacking it on later and thrashing the window after it
+ is visible. */
+void
+initialize_frame_menubar (f)
+ FRAME_PTR f;
+{
+ set_frame_menubar (f, 1);
+}
\f
-/* Nonzero if position X, Y relative to inside of frame F
- is in some other menu bar item. */
+/* Horizontal bounds of the current menu bar item. */
static int this_menu_bar_item_beg;
static int this_menu_bar_item_end;
+/* Horizontal position of the end of the last menu bar item. */
+
+static int last_menu_bar_item_end;
+
+/* Nonzero if position X, Y is in the menu bar and in some menu bar item
+ but not in the current menu bar item. */
+
static int
other_menu_bar_item_p (f, x, y)
FRAME_PTR f;
int x, y;
{
return (y >= 0
+ && f->display.x->menubar_widget != 0
&& y < f->display.x->menubar_widget->core.height
&& x >= 0
- && x < f->display.x->menubar_widget->core.width
+ && x < last_menu_bar_item_end
&& (x >= this_menu_bar_item_end
|| x < this_menu_bar_item_beg));
}
#ifdef USE_X_TOOLKIT
+extern unsigned last_event_timestamp;
+extern Lisp_Object Vdouble_click_time;
+
+extern unsigned int x_mouse_grabbed;
+extern Lisp_Object Vmouse_depressed;
+
static Lisp_Object
xmenu_show (f, x, y, menubarp, keymaps, title, error)
FRAME_PTR f;
int menu_id;
Widget menu;
XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
+ Arg av [2];
+ int ac = 0;
/* This is the menu bar item (if any) that led to this menu. */
widget_value *menubar_item = 0;
widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
+ widget_value **submenu_stack
+ = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
+ Lisp_Object *subprefix_stack
+ = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
+ int submenu_depth = 0;
/* Define a queue to save up for later unreading
all X events that don't pertain to the menu. */
struct event_queue *queue = NULL;
struct event_queue *queue_tmp;
+ Position root_x, root_y;
+
+ int first_pane;
+ int next_release_must_exit = 0;
+
*error = NULL;
+ if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
+ {
+ *error = "Empty menu";
+ return Qnil;
+ }
this_menu_bar_item_beg = -1;
this_menu_bar_item_end = -1;
+ last_menu_bar_item_end = -1;
/* Figure out which menu bar item, if any, this menu is for. */
if (menubarp)
{
int xbeg;
int xend = 0;
+ widget_value *mb_item = 0;
- for (menubar_item = menubar->menu.old_stack[0]->contents;
- menubar_item;
- menubar_item = menubar_item->next)
+ for (mb_item = menubar->menu.old_stack[0]->contents;
+ mb_item;
+ mb_item = mb_item->next)
{
xbeg = xend;
- xend += (string_width (menubar, menubar_item->name)
+ xend += (string_width (menubar, mb_item->name)
+ 2 * (menubar->menu.horizontal_spacing
+ menubar->menu.shadow_thickness));
- if (x < xend)
+ if (x >= xbeg && x < xend)
{
x = xbeg + 4;
y = 0;
+ menubar_item = mb_item;
/* Arrange to show a different menu if we move in the menu bar
to a different item. */
this_menu_bar_item_beg = xbeg;
this_menu_bar_item_end = xend;
- break;
}
}
+ last_menu_bar_item_end = xend;
}
if (menubar_item == 0)
menubarp = 0;
/* Offset the coordinates to root-relative. */
- x += (f->display.x->widget->core.x
- + f->display.x->widget->core.border_width);
- y += (f->display.x->widget->core.y
- + f->display.x->widget->core.border_width
- + f->display.x->menubar_widget->core.height);
+ if (f->display.x->menubar_widget != 0)
+ y += f->display.x->menubar_widget->core.height;
+ XtTranslateCoords (f->display.x->widget,
+ x, y, &root_x, &root_y);
+ x = root_x;
+ y = root_y;
/* Create a tree of widget_value objects
representing the panes and their items. */
wv->value = 0;
wv->enabled = 1;
first_wv = wv;
+ first_pane = 1;
/* Loop over all panes and items, filling in the tree. */
i = 0;
while (i < menu_items_used)
{
- if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+ if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
+ {
+ submenu_stack[submenu_depth++] = save_wv;
+ save_wv = prev_wv;
+ prev_wv = 0;
+ first_pane = 1;
+ i++;
+ }
+ else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
+ {
+ prev_wv = save_wv;
+ save_wv = submenu_stack[--submenu_depth];
+ first_pane = 0;
+ i++;
+ }
+ else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
+ && submenu_depth != 0)
+ i += MENU_ITEMS_PANE_LENGTH;
+ /* Ignore a nil in the item list.
+ It's meaningful only for dialog boxes. */
+ else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+ i += 1;
+ else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
{
/* Create a new pane. */
Lisp_Object pane_name, prefix;
prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
pane_string = (NILP (pane_name)
? "" : (char *) XSTRING (pane_name)->data);
- /* If there is just one pane, put all its items directly
+ /* If there is just one top-level pane, put all its items directly
under the top-level menu. */
if (menu_items_n_panes == 1)
pane_string = "";
/* If the pane has a meaningful name,
make the pane a top-level menu item
with its items as a submenu beneath it. */
- if (strcmp (pane_string, ""))
+ if (!keymaps && strcmp (pane_string, ""))
{
wv = malloc_widget_value ();
if (save_wv)
wv->name++;
wv->value = 0;
wv->enabled = 1;
+ save_wv = wv;
+ prev_wv = 0;
}
- save_wv = wv;
- prev_wv = 0;
+ else if (first_pane)
+ {
+ save_wv = wv;
+ prev_wv = 0;
+ }
+ first_pane = 0;
i += MENU_ITEMS_PANE_LENGTH;
}
else
prev_wv->next = wv;
else
save_wv->contents = wv;
- wv->name = XSTRING (item_name)->data;
+ wv->name = (char *) XSTRING (item_name)->data;
if (!NILP (descrip))
- wv->key = XSTRING (descrip)->data;
+ wv->key = (char *) XSTRING (descrip)->data;
wv->value = 0;
wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
wv->enabled = !NILP (enable);
menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
f->display.x->widget, 1, 0,
popup_selection_callback, popup_down_callback);
+
+ /* Don't allow any geometry request from the user. */
+ XtSetArg (av[ac], XtNgeometry, 0); ac++;
+ XtSetValues (menu, av, ac);
+
/* Free the widget_value objects we used to specify the contents. */
free_menubar_widget_value_tree (first_wv);
pop_up_menu (mw, &dummy);
}
+ /* No need to check a second time since this is done in the XEvent loop.
+ This slows done the execution. */
+#ifdef XMENU_FOO
/* Check again whether the mouse has moved to another menu bar item. */
if (check_mouse_other_menu_bar (f))
{
/* The mouse moved into a different menu bar item.
We should bring up that item's menu instead.
First pop down this menu. */
+#if 0 /* xlwmenu.c now does this. */
XtUngrabPointer ((Widget)
((XlwMenuWidget)
((CompositeWidget)menu)->composite.children[0]),
CurrentTime);
+#endif
lw_destroy_all_widgets (menu_id);
goto pop_down;
}
+#endif
/* Process events that apply to the menu. */
while (1)
{
XEvent event;
+ int queue_and_exit = 0;
+ int in_this_menu = 0, in_menu_bar = 0;
+ Widget widget;
XtAppNextEvent (Xt_app_con, &event);
+
+ /* Check whether the event happened in the menu
+ or any child of it. */
+ widget = XtWindowToWidget (XDISPLAY event.xany.window);
+
+ while (widget)
+ {
+ if (widget == menu)
+ {
+ in_this_menu = 1;
+ break;
+ }
+ if (widget == f->display.x->menubar_widget)
+ {
+ in_menu_bar = 1;
+ break;
+ }
+ widget = XtParent (widget);
+ }
+
if (event.type == ButtonRelease)
{
- XtDispatchEvent (&event);
- break;
+ /* Do the work of construct_mouse_click since it can't
+ be called. Initially, the popup menu has been called
+ from a ButtonPress in the edit_widget. Then the mouse
+ has been set to grabbed. Reset it now. */
+ x_mouse_grabbed &= ~(1 << event.xbutton.button);
+ if (!x_mouse_grabbed)
+ Vmouse_depressed = Qnil;
+
+ /* If we release the button soon without selecting anything,
+ stay in the loop--that is, leave the menu posted.
+ Otherwise, exit this loop and thus pop down the menu. */
+ if (! in_this_menu
+ && (next_release_must_exit
+ || !(((XButtonEvent *) (&event))->time - last_event_timestamp
+ < XINT (Vdouble_click_time))))
+ break;
+ }
+ /* A button press outside the menu => pop it down. */
+ else if (event.type == ButtonPress && !in_this_menu)
+ break;
+ else if (event.type == ButtonPress)
+ next_release_must_exit = 1;
+ else if (event.type == KeyPress)
+ {
+ /* Exit the loop, but first queue this event for reuse. */
+ queue_and_exit = 1;
}
else if (event.type == Expose)
process_expose_from_menu (event);
- else if (event.type == MotionNotify)
+ /* If the mouse moves to a different menu bar item, switch to
+ that item's menu. But only if the button is still held down. */
+ else if (event.type == MotionNotify
+ && x_mouse_grabbed)
{
int event_x = (event.xmotion.x_root
- (f->display.x->widget->core.x
/* The mouse moved into a different menu bar item.
We should bring up that item's menu instead.
First pop down this menu. */
+#if 0 /* xlwmenu.c now does this. */
XtUngrabPointer ((Widget)
((XlwMenuWidget)
((CompositeWidget)menu)->composite.children[0]),
event.xbutton.time);
+#endif
lw_destroy_all_widgets (menu_id);
/* Put back an event that will bring up the other item's menu. */
break;
}
}
+ else if (event.type == UnmapNotify)
+ {
+ /* If the menu disappears, there is no need to stay in the
+ loop. */
+ if (event.xunmap.window == menu->core.window)
+ break;
+ }
XtDispatchEvent (&event);
- if (XtWindowToWidget(XDISPLAY event.xany.window) != menu)
+
+ if (queue_and_exit || (!in_this_menu && !in_menu_bar))
{
queue_tmp
= (struct event_queue *) malloc (sizeof (struct event_queue));
queue = queue_tmp;
}
}
+ if (queue_and_exit)
+ break;
}
pop_down:
dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
}
+ /* fp turned off the following statement and wrote a comment
+ that it is unnecessary--that the menu has already disappeared.
+ I observer that is not so. -- rms. */
/* Make sure the menu disappears. */
lw_destroy_all_widgets (menu_id);
XPutBackEvent (XDISPLAY &queue_tmp->event);
queue = queue_tmp->next;
free ((char *)queue_tmp);
+ /* Cause these events to get read as soon as we UNBLOCK_INPUT. */
+ interrupt_input_pending = 1;
}
/* Find the selected item, and its pane, to return
{
Lisp_Object entry;
- if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+ if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
+ {
+ subprefix_stack[submenu_depth++] = prefix;
+ prefix = entry;
+ i++;
+ }
+ else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
+ {
+ prefix = subprefix_stack[--submenu_depth];
+ i++;
+ }
+ else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
{
prefix
= XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
{
if (keymaps != 0)
{
+ int j;
+
entry = Fcons (entry, Qnil);
if (!NILP (prefix))
entry = Fcons (prefix, entry);
+ for (j = submenu_depth - 1; j >= 0; j--)
+ if (!NILP (subprefix_stack[j]))
+ entry = Fcons (subprefix_stack[j], entry);
}
return entry;
}
return Qnil;
}
+static char * button_names [] = {
+ "button1", "button2", "button3", "button4", "button5",
+ "button6", "button7", "button8", "button9", "button10" };
+
+static Lisp_Object
+xdialog_show (f, menubarp, keymaps, title, error)
+ FRAME_PTR f;
+ int menubarp;
+ int keymaps;
+ Lisp_Object title;
+ char **error;
+{
+ int i, nb_buttons=0;
+ int dialog_id;
+ Widget menu;
+ XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
+ char dialog_name[6];
+
+ /* This is the menu bar item (if any) that led to this menu. */
+ widget_value *menubar_item = 0;
+
+ widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
+
+ /* Define a queue to save up for later unreading
+ all X events that don't pertain to the menu. */
+ struct event_queue
+ {
+ XEvent event;
+ struct event_queue *next;
+ };
+
+ struct event_queue *queue = NULL;
+ struct event_queue *queue_tmp;
+
+ /* Number of elements seen so far, before boundary. */
+ int left_count = 0;
+ /* 1 means we've seen the boundary between left-hand elts and right-hand. */
+ int boundary_seen = 0;
+
+ *error = NULL;
+
+ if (menu_items_n_panes > 1)
+ {
+ *error = "Multiple panes in dialog box";
+ return Qnil;
+ }
+
+ /* Create a tree of widget_value objects
+ representing the text label and buttons. */
+ {
+ Lisp_Object pane_name, prefix;
+ char *pane_string;
+ pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
+ 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->value = pane_string;
+ if (keymaps && !NILP (prefix))
+ prev_wv->name++;
+ prev_wv->enabled = 1;
+ prev_wv->name = "message";
+ first_wv = prev_wv;
+
+ /* Loop over all panes and items, filling in the tree. */
+ i = MENU_ITEMS_PANE_LENGTH;
+ while (i < menu_items_used)
+ {
+
+ /* Create a new item within current pane. */
+ Lisp_Object item_name, enable, descrip;
+ item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
+ enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
+ descrip
+ = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
+
+ if (NILP (item_name))
+ {
+ free_menubar_widget_value_tree (first_wv);
+ *error = "Submenu in dialog items";
+ return Qnil;
+ }
+ if (EQ (item_name, Qquote))
+ {
+ /* This is the boundary between left-side elts
+ and right-side elts. Stop incrementing right_count. */
+ boundary_seen = 1;
+ i++;
+ continue;
+ }
+ if (nb_buttons >= 10)
+ {
+ free_menubar_widget_value_tree (first_wv);
+ *error = "Too many dialog items";
+ return Qnil;
+ }
+
+ wv = malloc_widget_value ();
+ prev_wv->next = wv;
+ wv->name = (char *) button_names[nb_buttons];
+ if (!NILP (descrip))
+ wv->key = (char *) XSTRING (descrip)->data;
+ wv->value = (char *) XSTRING (item_name)->data;
+ wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
+ wv->enabled = !NILP (enable);
+ prev_wv = wv;
+
+ if (! boundary_seen)
+ left_count++;
+
+ nb_buttons++;
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+
+ /* If the boundary was not specified,
+ by default put half on the left and half on the right. */
+ if (! boundary_seen)
+ left_count = nb_buttons - nb_buttons / 2;
+
+ wv = malloc_widget_value ();
+ wv->name = dialog_name;
+
+ /* Dialog boxes use a really stupid name encoding
+ which specifies how many buttons to use
+ and how many buttons are on the right.
+ The Q means something also. */
+ dialog_name[0] = 'Q';
+ dialog_name[1] = '0' + nb_buttons;
+ dialog_name[2] = 'B';
+ dialog_name[3] = 'R';
+ /* Number of buttons to put on the right. */
+ dialog_name[4] = '0' + nb_buttons - left_count;
+ dialog_name[5] = 0;
+ wv->contents = first_wv;
+ first_wv = wv;
+ }
+
+ /* Actually create the dialog. */
+ dialog_id = ++popup_id_tick;
+ menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
+ f->display.x->widget, 1, 0,
+ dialog_selection_callback, 0);
+#if 0 /* This causes crashes, and seems to be redundant -- rms. */
+ lw_modify_all_widgets (dialog_id, first_wv, True);
+#endif
+ lw_modify_all_widgets (dialog_id, first_wv->contents, True);
+ /* Free the widget_value objects we used to specify the contents. */
+ free_menubar_widget_value_tree (first_wv);
+
+ /* No selection has been chosen yet. */
+ menu_item_selection = 0;
+
+ /* Display the menu. */
+ lw_pop_up_all_widgets (dialog_id);
+
+ /* Process events that apply to the menu. */
+ 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);
+ XtDispatchEvent (&event);
+ if (XtWindowToWidget(XDISPLAY event.xany.window) != menu)
+ {
+ queue_tmp = (struct event_queue *) malloc (sizeof (struct event_queue));
+
+ if (queue_tmp != NULL)
+ {
+ queue_tmp->event = event;
+ queue_tmp->next = queue;
+ queue = queue_tmp;
+ }
+ }
+ }
+ pop_down:
+
+ /* State that no mouse buttons are now held.
+ That is not necessarily true, but the fiction leads to reasonable
+ results, and it is a pain to ask which are actually held now
+ or track this in the loop above. */
+ x_mouse_grabbed = 0;
+
+ /* Unread any events that we got but did not handle. */
+ while (queue != NULL)
+ {
+ queue_tmp = queue;
+ XPutBackEvent (XDISPLAY &queue_tmp->event);
+ queue = queue_tmp->next;
+ free ((char *)queue_tmp);
+ /* Cause these events to get read as soon as we UNBLOCK_INPUT. */
+ interrupt_input_pending = 1;
+ }
+
+ /* Find the selected item, and its pane, to return
+ the proper value. */
+ if (menu_item_selection != 0)
+ {
+ Lisp_Object prefix;
+
+ prefix = Qnil;
+ i = 0;
+ while (i < menu_items_used)
+ {
+ Lisp_Object entry;
+
+ if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+ {
+ prefix
+ = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
+ i += MENU_ITEMS_PANE_LENGTH;
+ }
+ else
+ {
+ entry
+ = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
+ if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
+ {
+ if (keymaps != 0)
+ {
+ entry = Fcons (entry, Qnil);
+ if (!NILP (prefix))
+ entry = Fcons (prefix, entry);
+ }
+ return entry;
+ }
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+ }
+
+ return Qnil;
+}
#else /* not USE_X_TOOLKIT */
static Lisp_Object
char *datap;
int ulx, uly, width, height;
int dispwidth, dispheight;
- int i;
+ int i, j;
+ int maxwidth;
int dummy_int;
unsigned int dummy_uint;
if (menu_items_n_panes == 0)
return Qnil;
+ if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
+ {
+ *error = "Empty menu";
+ return Qnil;
+ }
+
/* 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,
return Qnil;
}
i += MENU_ITEMS_PANE_LENGTH;
+
+ /* Find the width of the widest item in this pane. */
+ maxwidth = 0;
+ j = i;
+ while (j < menu_items_used)
+ {
+ Lisp_Object item;
+ item = XVECTOR (menu_items)->contents[j];
+ if (EQ (item, Qt))
+ break;
+ if (NILP (item))
+ {
+ j++;
+ continue;
+ }
+ width = XSTRING (item)->size;
+ if (width > maxwidth)
+ maxwidth = width;
+
+ j += MENU_ITEMS_ITEM_LENGTH;
+ }
}
+ /* Ignore a nil in the item list.
+ It's meaningful only for dialog boxes. */
+ else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+ i += 1;
else
{
/* Create a new item within current pane. */
Lisp_Object item_name, enable, descrip;
+ unsigned char *item_data;
item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
descrip
= XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
if (!NILP (descrip))
- item_name = concat2 (item_name, descrip);
+ {
+ int gap = maxwidth - XSTRING (item_name)->size;
+#ifdef C_ALLOCA
+ Lisp_Object spacer;
+ spacer = Fmake_string (make_number (gap), make_number (' '));
+ item_name = concat2 (item_name, spacer);
+ item_name = concat2 (item_name, descrip);
+ item_data = XSTRING (item_name)->data;
+#else
+ /* if alloca is fast, use that to make the space,
+ to reduce gc needs. */
+ item_data
+ = (unsigned char *) alloca (maxwidth
+ + XSTRING (descrip)->size + 1);
+ bcopy (XSTRING (item_name)->data, item_data,
+ XSTRING (item_name)->size);
+ for (j = XSTRING (item_name)->size; j < maxwidth; j++)
+ item_data[j] = ' ';
+ bcopy (XSTRING (descrip)->data, item_data + j,
+ XSTRING (descrip)->size);
+ item_data[j + XSTRING (descrip)->size] = 0;
+#endif
+ }
+ else
+ item_data = XSTRING (item_name)->data;
- if (XMenuAddSelection (XDISPLAY menu, lpane, 0,
- XSTRING (item_name)->data,
+ if (XMenuAddSelection (XDISPLAY menu, lpane, 0, item_data,
!NILP (enable))
== XM_FAILURE)
{
i += MENU_ITEMS_ITEM_LENGTH;
}
}
-
+
/* All set and ready to fly. */
XMenuRecompute (XDISPLAY menu);
dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display));
}
if (ulx < 0) x -= ulx;
if (uly < 0) y -= uly;
-
+
+ XMenuSetAEQ (menu, TRUE);
XMenuSetFreeze (menu, TRUE);
pane = selidx = 0;
break;
case XM_FAILURE:
- XMenuDestroy (XDISPLAY menu);
*error = "Can't activate menu";
case XM_IA_SELECT:
case XM_NO_SELECT:
break;
}
XMenuDestroy (XDISPLAY menu);
+
+ /* State that no mouse buttons are now held.
+ (The oldXMenu code doesn't track this info for us.)
+ That is not necessarily true, but the fiction leads to reasonable
+ results, and it is a pain to ask which are actually held now. */
+ x_mouse_grabbed = 0;
+
return entry;
}
#endif /* not USE_X_TOOLKIT */
popup_id_tick = (1<<16);
defsubr (&Sx_popup_menu);
+ defsubr (&Sx_popup_dialog);
}