/* X Communication module for terminals which understand the X protocol.
- Copyright (C) 1986, 1988, 1993, 1994, 1996, 1999, 2000, 2001, 2003, 2004,
- 2005 Free Software Foundation, Inc.
+ Copyright (C) 1986, 1988, 1993, 1994, 1996, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005, 2006 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, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
/* X pop-up deck-of-cards menu facility for GNU Emacs.
*
#include <config.h>
+#if 0 /* Why was this included? And without syssignal.h? */
/* On 4.3 this loses if it comes after xterm.h. */
#include <signal.h>
+#endif
#include <stdio.h>
extern void set_frame_menubar P_ ((FRAME_PTR, int, int));
extern XtAppContext Xt_app_con;
-static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, char **));
+static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, Lisp_Object,
+ char **));
static void popup_get_selection P_ ((XEvent *, struct x_display_info *,
LWLIB_ID, int));
#include "gtkutil.h"
#define HAVE_BOXES 1
extern void set_frame_menubar P_ ((FRAME_PTR, int, int));
-static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, char **));
+static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, Lisp_Object,
+ char **));
#endif
/* This is how to deal with multibyte text if HAVE_MULTILINGUAL_MENU
static void
init_menu_items ()
{
+ if (!NILP (menu_items_inuse))
+ error ("Trying to use a menu from within a menu-entry");
+
if (NILP (menu_items))
{
menu_items_allocated = 60;
menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
}
- if (!NILP (menu_items_inuse))
- error ("Trying to use a menu from within a menu-entry");
menu_items_inuse = Qt;
menu_items_used = 0;
menu_items_n_panes = 0;
xassert (NILP (menu_items_inuse));
}
+/* This undoes save_menu_items, and it is called by the specpdl unwind
+ mechanism. */
+
+static Lisp_Object
+restore_menu_items (saved)
+ Lisp_Object saved;
+{
+ menu_items = XCAR (saved);
+ menu_items_inuse = (! NILP (menu_items) ? Qt : Qnil);
+ menu_items_allocated = (VECTORP (menu_items) ? ASIZE (menu_items) : 0);
+ saved = XCDR (saved);
+ menu_items_used = XINT (XCAR (saved));
+ saved = XCDR (saved);
+ menu_items_n_panes = XINT (XCAR (saved));
+ saved = XCDR (saved);
+ menu_items_submenu_depth = XINT (XCAR (saved));
+ return Qnil;
+}
+
+/* Push the whole state of menu_items processing onto the specpdl.
+ It will be restored when the specpdl is unwound. */
+
+static void
+save_menu_items ()
+{
+ Lisp_Object saved = list4 (!NILP (menu_items_inuse) ? menu_items : Qnil,
+ make_number (menu_items_used),
+ make_number (menu_items_n_panes),
+ make_number (menu_items_submenu_depth));
+ record_unwind_protect (restore_menu_items, saved);
+ menu_items_inuse = Qnil;
+ menu_items = Qnil;
+}
+\f
/* Make the menu_items vector twice as large. */
static void
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));
With this form of menu, the return value is VALUE from the chosen item.
If POSITION is nil, don't display the menu at all, just precalculate the
-cached information about equivalent key sequences. */)
+cached information about equivalent key sequences.
+
+If the user gets rid of the menu without making a valid choice, for
+instance by clicking the mouse away from a valid choice or by typing
+keyboard input, then this normally results in a quit and
+`x-popup-menu' does not return. But if POSITION is a mouse button
+event (indicating that the user invoked the menu with the mouse) then
+no quit occurs and `x-popup-menu' returns nil. */)
(position, menu)
Lisp_Object position, menu;
{
xpos += XINT (x);
ypos += XINT (y);
+
+ XSETFRAME (Vmenu_updating_frame, f);
}
- Vmenu_updating_frame = Qnil;
+ else
+ Vmenu_updating_frame = Qnil;
#endif /* HAVE_MENUS */
record_unwind_protect (unuse_menu_items, Qnil);
#ifdef HAVE_MENUS
-DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 2, 0,
+DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
doc: /* Pop up a dialog box and return user's selection.
POSITION specifies which frame to use.
This is normally a mouse button event or a window or frame.
The dialog box appears in the middle of the specified frame.
CONTENTS specifies the alternatives to display in the dialog box.
-It is a list of the form (TITLE ITEM1 ITEM2...).
+It is a list of the form (DIALOG ITEM1 ITEM2...).
Each ITEM is a cons cell (STRING . VALUE).
The return value is VALUE from the chosen item.
An ITEM may also be just a string--that makes a nonselectable item.
An ITEM may also be nil--that means to put all preceding items
on the left of the dialog box and all following items on the right.
-\(By default, approximately half appear on each side.) */)
- (position, contents)
- Lisp_Object position, contents;
+\(By default, approximately half appear on each side.)
+
+If HEADER is non-nil, the frame title for the box is "Information",
+otherwise it is "Question".
+
+If the user gets rid of the dialog box without making a valid choice,
+for instance using the window manager, then this produces a quit and
+`x-popup-dialog' does not return. */)
+ (position, contents, header)
+ Lisp_Object position, contents, header;
{
FRAME_PTR f = NULL;
Lisp_Object window;
/* Display them in a dialog box. */
BLOCK_INPUT;
- selection = xdialog_show (f, 0, title, &error_name);
+ selection = xdialog_show (f, 0, title, header, &error_name);
UNBLOCK_INPUT;
unbind_to (specpdl_count, Qnil);
}
}
+DEFUN ("menu-bar-start", Fmenu_bar_start, Smenu_bar_start, 0, 1, "i",
+ doc: /* Start key navigation of the menu bar in FRAME.
+This initially opens the first menu bar item and you can then navigate with the
+arrow keys, select a menu entry with the return key or cancel with the
+escape key. If FRAME has no menu bar this function does nothing.
+
+If FRAME is nil or not given, use the selected frame. */)
+ (frame)
+ Lisp_Object frame;
+{
+ XEvent ev;
+ FRAME_PTR f = check_x_frame (frame);
+ Widget menubar;
+ BLOCK_INPUT;
+
+ if (FRAME_EXTERNAL_MENU_BAR (f))
+ set_frame_menubar (f, 0, 1);
+
+ menubar = FRAME_X_OUTPUT (f)->menubar_widget;
+ if (menubar)
+ {
+ Window child;
+ int error_p = 0;
+
+ x_catch_errors (FRAME_X_DISPLAY (f));
+ memset (&ev, 0, sizeof ev);
+ ev.xbutton.display = FRAME_X_DISPLAY (f);
+ ev.xbutton.window = XtWindow (menubar);
+ ev.xbutton.root = FRAME_X_DISPLAY_INFO (f)->root_window;
+ ev.xbutton.time = XtLastTimestampProcessed (FRAME_X_DISPLAY (f));
+ ev.xbutton.button = Button1;
+ ev.xbutton.x = ev.xbutton.y = FRAME_MENUBAR_HEIGHT (f) / 2;
+ ev.xbutton.same_screen = True;
+
+#ifdef USE_MOTIF
+ {
+ Arg al[2];
+ WidgetList list;
+ Cardinal nr;
+ XtSetArg (al[0], XtNchildren, &list);
+ XtSetArg (al[1], XtNnumChildren, &nr);
+ XtGetValues (menubar, al, 2);
+ ev.xbutton.window = XtWindow (list[0]);
+ }
+#endif
+
+ XTranslateCoordinates (FRAME_X_DISPLAY (f),
+ /* From-window, to-window. */
+ ev.xbutton.window, ev.xbutton.root,
+
+ /* From-position, to-position. */
+ ev.xbutton.x, ev.xbutton.y,
+ &ev.xbutton.x_root, &ev.xbutton.y_root,
+
+ /* Child of win. */
+ &child);
+ error_p = x_had_errors_p (FRAME_X_DISPLAY (f));
+ x_uncatch_errors ();
+
+ if (! error_p)
+ {
+ ev.type = ButtonPress;
+ ev.xbutton.state = 0;
+
+ XtDispatchEvent (&ev);
+ ev.xbutton.type = ButtonRelease;
+ ev.xbutton.state = Button1Mask;
+ XtDispatchEvent (&ev);
+ }
+ }
+
+ UNBLOCK_INPUT;
+
+ return Qnil;
+}
#endif /* USE_X_TOOLKIT */
+
#ifdef USE_GTK
+DEFUN ("menu-bar-start", Fmenu_bar_start, Smenu_bar_start, 0, 1, "i",
+ doc: /* Start key navigation of the menu bar in FRAME.
+This initially opens the first menu bar item and you can then navigate with the
+arrow keys, select a menu entry with the return key or cancel with the
+escape key. If FRAME has no menu bar this function does nothing.
+
+If FRAME is nil or not given, use the selected frame. */)
+ (frame)
+ Lisp_Object frame;
+{
+ GtkWidget *menubar;
+ BLOCK_INPUT;
+ FRAME_PTR f = check_x_frame (frame);
+
+ if (FRAME_EXTERNAL_MENU_BAR (f))
+ set_frame_menubar (f, 0, 1);
+
+ menubar = FRAME_X_OUTPUT (f)->menubar_widget;
+ if (menubar)
+ {
+ /* Activate the first menu. */
+ GList *children = gtk_container_get_children (GTK_CONTAINER (menubar));
+
+ gtk_menu_shell_select_item (GTK_MENU_SHELL (menubar),
+ GTK_WIDGET (children->data));
+
+ popup_activated_flag = 1;
+ g_list_free (children);
+ }
+ UNBLOCK_INPUT;
+
+ return Qnil;
+}
+
/* Loop util popup_activated_flag is set to zero in a callback.
Used for popup menus and dialogs. */
if (! cb_data || ! cb_data->cl_data || ! cb_data->cl_data->f)
return;
+ /* For a group of radio buttons, GTK calls the selection callback first
+ for the item that was active before the selection and then for the one that
+ is active after the selection. For C-h k this means we get the help on
+ the deselected item and then the selected item is executed. Prevent that
+ by ignoring the non-active item. */
+ if (GTK_IS_RADIO_MENU_ITEM (widget)
+ && ! gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)))
+ return;
+
/* When a menu is popped down, X generates a focus event (i.e. focus
goes back to the frame below the menu). Since GTK buffers events,
we force it out here before the menu selection event. Otherwise
int i;
int submenu_depth = 0;
widget_value **submenu_stack;
+ int panes_seen = 0;
submenu_stack
= (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
Lisp_Object pane_name, prefix;
char *pane_string;
+ panes_seen++;
+
pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
wv->enabled = 1;
wv->button_type = BUTTON_TYPE_NONE;
wv->help = Qnil;
+ save_wv = wv;
}
- save_wv = wv;
+ else
+ save_wv = first_wv;
+
prev_wv = 0;
i += MENU_ITEMS_PANE_LENGTH;
}
Lisp_Object item_name, enable, descrip, def, type, selected;
Lisp_Object help;
+ /* All items should be contained in panes. */
+ if (panes_seen == 0)
+ abort ();
+
item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
{
if (STRINGP (wv->lname))
{
- wv->name = SDATA (wv->lname);
+ wv->name = (char *) SDATA (wv->lname);
/* Ignore the @ that means "separate pane".
This is a kludge, but this isn't worth more time. */
}
if (STRINGP (wv->lkey))
- wv->key = SDATA (wv->lkey);
+ wv->key = (char *) SDATA (wv->lkey);
if (wv->contents)
update_submenu_strings (wv->contents);
because it is not reentrant. */
specbind (Qdebug_on_next_call, Qnil);
- record_unwind_protect (Fset_match_data, Fmatch_data (Qnil, Qnil));
- record_unwind_protect (unuse_menu_items, Qnil);
+ record_unwind_save_match_data ();
if (NILP (Voverriding_local_map_menu_flag))
{
specbind (Qoverriding_terminal_local_map, Qnil);
/* Fill in menu_items with the current menu bar contents.
This can evaluate Lisp code. */
+ save_menu_items ();
+
menu_items = f->menu_bar_vector;
menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
}
set_buffer_internal_1 (prev);
- unbind_to (specpdl_count, Qnil);
/* If there has been no change in the Lisp-level contents
of the menu bar, skip redisplaying it. Just exit. */
+ /* Compare the new menu items with the ones computed last time. */
for (i = 0; i < previous_menu_items_used; i++)
if (menu_items_used == i
|| (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i])))
break;
if (i == menu_items_used && i == previous_menu_items_used && i != 0)
{
+ /* The menu items have not changed. Don't bother updating
+ the menus in any form, since it would be a no-op. */
free_menubar_widget_value_tree (first_wv);
discard_menu_items ();
-
+ unbind_to (specpdl_count, Qnil);
return;
}
+ /* The menu items are different, so store them in the frame. */
+ f->menu_bar_vector = menu_items;
+ f->menu_bar_items_used = menu_items_used;
+
+ /* This calls restore_menu_items to restore menu_items, etc.,
+ as they were outside. */
+ unbind_to (specpdl_count, Qnil);
+
/* 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;
wv = wv->next;
}
- f->menu_bar_vector = menu_items;
- f->menu_bar_items_used = menu_items_used;
- discard_menu_items ();
}
else
{
"button6", "button7", "button8", "button9", "button10" };
static Lisp_Object
-xdialog_show (f, keymaps, title, error)
+xdialog_show (f, keymaps, title, header, error_name)
FRAME_PTR f;
int keymaps;
- Lisp_Object title;
- char **error;
+ Lisp_Object title, header;
+ char **error_name;
{
int i, nb_buttons=0;
char dialog_name[6];
/* 1 means we've seen the boundary between left-hand elts and right-hand. */
int boundary_seen = 0;
- *error = NULL;
+ *error_name = NULL;
if (menu_items_n_panes > 1)
{
- *error = "Multiple panes in dialog box";
+ *error_name = "Multiple panes in dialog box";
return Qnil;
}
if (NILP (item_name))
{
free_menubar_widget_value_tree (first_wv);
- *error = "Submenu in dialog items";
+ *error_name = "Submenu in dialog items";
return Qnil;
}
if (EQ (item_name, Qquote))
if (nb_buttons >= 9)
{
free_menubar_widget_value_tree (first_wv);
- *error = "Too many dialog items";
+ *error_name = "Too many dialog items";
return Qnil;
}
wv = xmalloc_widget_value ();
wv->name = dialog_name;
wv->help = Qnil;
+
+ /* Frame title: 'Q' = Question, 'I' = Information.
+ Can also have 'E' = Error if, one day, we want
+ a popup for errors. */
+ if (NILP(header))
+ dialog_name[0] = 'Q';
+ else
+ dialog_name[0] = 'I';
+
/* 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';
+ and how many buttons are on the right. */
dialog_name[1] = '0' + nb_buttons;
dialog_name[2] = 'B';
dialog_name[3] = 'R';
return Qnil;
}
+ /* Don't GC while we prepare and show the menu,
+ because we give the oldxmenu library pointers to the
+ contents of strings. */
+ inhibit_garbage_collection ();
+
#ifdef HAVE_X_WINDOWS
/* Adjust coordinates to relative to the outer (window manager) window. */
x += FRAME_OUTER_TO_INNER_DIFF_X (f);
#endif
defsubr (&Sx_popup_menu);
+
+#if defined (USE_GTK) || defined (USE_X_TOOLKIT)
+ defsubr (&Smenu_bar_start);
+#endif
+
#ifdef HAVE_MENUS
defsubr (&Sx_popup_dialog);
#endif