/* X Communication module for terminals which understand the X protocol.
- Copyright (C) 1986, 1988, 1993, 1994, 1996, 1999, 2000, 2001, 2003, 2004
- Free Software Foundation, Inc.
+ Copyright (C) 1986, 1988, 1993, 1994, 1996, 1999, 2000, 2001, 2003, 2004,
+ 2005 Free Software Foundation, Inc.
This file is part of GNU Emacs.
static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, char **));
static void popup_get_selection P_ ((XEvent *, struct x_display_info *,
- LWLIB_ID, int, int));
+ LWLIB_ID, int));
/* Define HAVE_BOXES if menus can handle radio and toggle buttons. */
/* gtk just uses utf-8. */
# define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
#else
-/* I'm not convinced ENCODE_SYSTEM is defined correctly, or maybe
- something else should be used here. Except under MS-Windows it
- just converts to unibyte, but encoding with `locale-coding-system'
- seems better -- X may actually display the result correctly, and
- it's not necessarily equivalent to the unibyte text. -- fx */
-# define ENCODE_MENU_STRING(str) ENCODE_SYSTEM (str)
+# define ENCODE_MENU_STRING(str) string_make_unibyte (str)
#endif
static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
static Lisp_Object
unuse_menu_items (dummy)
- int dummy;
+ Lisp_Object dummy;
{
return menu_items_inuse = Qnil;
}
return; /* Not a menu item. */
map = XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP];
-
+
if (skp->notreal)
{
/* We don't want to make a menu, just traverse the keymaps to
init_menu_items ();
- for (tail = menu; !NILP (tail); tail = Fcdr (tail))
+ for (tail = menu; CONSP (tail); tail = XCDR (tail))
{
Lisp_Object elt, pane_name, pane_data;
- elt = Fcar (tail);
+ elt = XCAR (tail);
pane_name = Fcar (elt);
CHECK_STRING (pane_name);
push_menu_pane (ENCODE_MENU_STRING (pane_name), Qnil);
{
Lisp_Object tail, item, item1;
- for (tail = pane; !NILP (tail); tail = Fcdr (tail))
+ for (tail = pane; CONSP (tail); tail = XCDR (tail))
{
- item = Fcar (tail);
+ item = XCAR (tail);
if (STRINGP (item))
push_menu_item (ENCODE_MENU_STRING (item), Qnil, Qnil, Qt,
Qnil, Qnil, Qnil, Qnil);
- else if (NILP (item))
- push_left_right_boundary ();
- else
+ else if (CONSP (item))
{
- CHECK_CONS (item);
- item1 = Fcar (item);
+ item1 = XCAR (item);
CHECK_STRING (item1);
- push_menu_item (ENCODE_MENU_STRING (item1), Qt, Fcdr (item),
+ push_menu_item (ENCODE_MENU_STRING (item1), Qt, XCDR (item),
Qt, Qnil, Qnil, Qnil, Qnil);
}
+ else
+ push_left_right_boundary ();
+
}
}
\f
or a list ((XOFFSET YOFFSET) WINDOW)
where XOFFSET and YOFFSET are positions in pixels from the top left
corner of WINDOW's frame. (WINDOW may be a frame object instead of a window.)
-This controls the position of the center of the first line
-in the first pane of the menu, not the top left of the menu as a whole.
+This controls the position of the top left of the menu as a whole.
If POSITION is t, it means to use the current mouse position.
MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
if (CONSP (tem))
{
window = Fcar (Fcdr (position));
- x = Fcar (tem);
- y = Fcar (Fcdr (tem));
+ x = XCAR (tem);
+ y = Fcar (XCDR (tem));
}
else
{
/* The first keymap that has a prompt string
supplies the menu title. */
- for (tem = menu, i = 0; CONSP (tem); tem = Fcdr (tem))
+ for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
{
Lisp_Object prompt;
- maps[i++] = keymap = get_keymap (Fcar (tem), 1, 0);
+ maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
prompt = Fkeymap_prompt (keymap);
if (NILP (title) && !NILP (prompt))
the dialog. Also, the lesstif/motif version crashes if there are
no buttons. */
contents = Fcons (title, Fcons (Fcons (build_string ("Ok"), Qt), Qnil));
-
+
list_of_panes (Fcons (contents, Qnil));
/* Display them in a dialog box. */
#ifndef MSDOS
+/* Set menu_items_inuse so no other popup menu or dialog is created. */
+
+void
+x_menu_set_in_use (in_use)
+ int in_use;
+{
+ menu_items_inuse = in_use ? Qt : Qnil;
+ popup_activated_flag = in_use;
+}
+
/* Wait for an X event to arrive or for a timer to expire. */
-static void
+void
x_menu_wait_for_event (void *data)
{
extern EMACS_TIME timer_check P_ ((int));
while (
#ifdef USE_X_TOOLKIT
- XtAppPending (Xt_app_con)
+ ! XtAppPending (Xt_app_con)
#elif defined USE_GTK
! gtk_events_pending ()
#else
\f
#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
+#ifdef USE_X_TOOLKIT
+
/* Loop in Xt until the menu pulldown or dialog popup has been
popped down (deactivated). This is used for x-popup-menu
and x-popup-dialog; it is not used for the menu bar.
- If DOWN_ON_KEYPRESS is nonzero, pop down if a key is pressed.
-
NOTE: All calls to popup_get_selection should be protected
with BLOCK_INPUT, UNBLOCK_INPUT wrappers. */
-#ifdef USE_X_TOOLKIT
static void
-popup_get_selection (initial_event, dpyinfo, id, do_timers, down_on_keypress)
+popup_get_selection (initial_event, dpyinfo, id, do_timers)
XEvent *initial_event;
struct x_display_info *dpyinfo;
LWLIB_ID id;
int do_timers;
- int down_on_keypress;
{
XEvent event;
while (popup_activated_flag)
{
- if (initial_event)
+ if (initial_event)
{
event = *initial_event;
initial_event = 0;
event.xbutton.state = 0;
#endif
}
- /* If the user presses a key that doesn't go to the menu,
- deactivate the menu.
- The user is likely to do that if we get wedged.
- All toolkits now pop down menus on ESC.
- For dialogs however, the focus may not be on the dialog, so
- in that case, we pop down. */
+ /* Pop down on C-g and Escape. */
else if (event.type == KeyPress
- && down_on_keypress
&& dpyinfo->display == event.xbutton.display)
{
KeySym keysym = XLookupKeysym (&event.xkey, 0);
- if (!IsModifierKey (keysym)
- && x_any_window_to_frame (dpyinfo, event.xany.window) != NULL)
- popup_activated_flag = 0;
+
+ if ((keysym == XK_g && (event.xkey.state & ControlMask) != 0)
+ || keysym == XK_Escape) /* Any escape, ignore modifiers. */
+ popup_activated_flag = 0;
}
x_dispatch_event (&event, event.xany.display);
#ifdef USE_GTK
/* Loop util popup_activated_flag is set to zero in a callback.
Used for popup menus and dialogs. */
+
static void
-popup_widget_loop (do_timers)
+popup_widget_loop (do_timers, widget)
int do_timers;
+ GtkWidget *widget;
{
++popup_activated_flag;
/* Find the menu selection and store it in the keyboard buffer.
F is the frame the menu is on.
MENU_BAR_ITEMS_USED is the length of VECTOR.
- VECTOR is an array of menu events for the whole menu.
- */
-void
+ VECTOR is an array of menu events for the whole menu. */
+
+static void
find_and_call_menu_selection (f, menu_bar_items_used, vector, client_data)
FRAME_PTR f;
- int menu_bar_items_used;
+ EMACS_INT menu_bar_items_used;
Lisp_Object vector;
void *client_data;
{
if (! cb_data || ! cb_data->cl_data || ! cb_data->cl_data->f)
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
+ sit-for will exit at once if the focus event follows the menu selection
+ event. */
+
+ BLOCK_INPUT;
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ UNBLOCK_INPUT;
+
find_and_call_menu_selection (cb_data->cl_data->f,
cb_data->cl_data->menu_bar_items_used,
cb_data->cl_data->menu_bar_vector,
#ifndef HAVE_MULTILINGUAL_MENU
if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
{
- pane_name = ENCODE_SYSTEM (pane_name);
+ pane_name = ENCODE_MENU_STRING (pane_name);
AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
}
#endif
}
else
{
+ char menuOverride[] = "Ctrl<KeyPress>g: MenuGadgetEscape()";
+ XtTranslations override = XtParseTranslationTable (menuOverride);
+
menubar_widget = lw_create_widget ("menubar", "menubar", id, first_wv,
f->output_data.x->column_widget,
0,
popup_deactivate_callback,
menu_highlight_callback);
f->output_data.x->menubar_widget = menubar_widget;
+
+ /* Make menu pop down on C-g. */
+ XtOverrideTranslations (menubar_widget, override);
}
{
GtkRequisition req;
int disp_width = FRAME_X_DISPLAY_INFO (data->f)->width;
int disp_height = FRAME_X_DISPLAY_INFO (data->f)->height;
-
+
*x = data->x;
*y = data->y;
if (cb_data) menu_item_selection = (Lisp_Object *) cb_data->call_data;
}
+static Lisp_Object
+pop_down_menu (arg)
+ Lisp_Object arg;
+{
+ struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
+
+ popup_activated_flag = 0;
+ BLOCK_INPUT;
+ gtk_widget_destroy (GTK_WIDGET (p->pointer));
+ UNBLOCK_INPUT;
+ return Qnil;
+}
+
/* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the
menu pops down.
menu_item_selection will be set to the selection. */
GtkWidget *menu;
GtkMenuPositionFunc pos_func = 0; /* Pop up at pointer. */
struct next_popup_x_y popup_x_y;
+ int specpdl_count = SPECPDL_INDEX ();
xg_crazy_callback_abort = 1;
menu = xg_create_widget ("popup", first_wv->name, f, first_wv,
G_CALLBACK (menu_highlight_callback));
xg_crazy_callback_abort = 0;
- for (i = 0; i < 5; i++)
- if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
- break;
-
if (! for_click)
{
/* Not invoked by a click. pop up at x/y. */
popup_x_y.x = x;
popup_x_y.y = y;
popup_x_y.f = f;
- }
+ i = 0; /* gtk_menu_popup needs this to be 0 for a non-button popup. */
+ }
+ else
+ {
+ for (i = 0; i < 5; i++)
+ if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
+ break;
+ }
+
/* Display the menu. */
gtk_widget_show_all (menu);
gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, &popup_x_y, i, 0);
+ record_unwind_protect (pop_down_menu, make_save_value (menu, 0));
+
/* Set this to one. popup_widget_loop increases it by one, so it becomes
two. show_help_echo uses this to detect popup menus. */
popup_activated_flag = 1;
/* Process events that apply to the menu. */
- popup_widget_loop (0);
+ popup_widget_loop (1, menu);
- gtk_widget_destroy (menu);
+ unbind_to (specpdl_count, Qnil);
/* Must reset this manually because the button release event is not passed
to Emacs event loop. */
menu_item_selection = (Lisp_Object *) client_data;
}
+/* ARG is the LWLIB ID of the dialog box, represented
+ as a Lisp object as (HIGHPART . LOWPART). */
+
+static Lisp_Object
+pop_down_menu (arg)
+ Lisp_Object arg;
+{
+ LWLIB_ID id = (XINT (XCAR (arg)) << 4 * sizeof (LWLIB_ID)
+ | XINT (XCDR (arg)));
+
+ BLOCK_INPUT;
+ lw_destroy_all_widgets (id);
+ UNBLOCK_INPUT;
+ popup_activated_flag = 0;
+
+ return Qnil;
+}
+
/* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the
menu pops down.
menu_item_selection will be set to the selection. */
/* Display the menu. */
lw_popup_menu (menu, (XEvent *) &dummy);
popup_activated_flag = 1;
+
+ {
+ int fact = 4 * sizeof (LWLIB_ID);
+ int specpdl_count = SPECPDL_INDEX ();
+ record_unwind_protect (pop_down_menu,
+ Fcons (make_number (menu_id >> (fact)),
+ make_number (menu_id & ~(-1 << (fact)))));
- /* Process events that apply to the menu. */
- popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id, 0, 0);
+ /* Process events that apply to the menu. */
+ popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id, 1);
- /* fp turned off the following statement and wrote a comment
- that it is unnecessary--that the menu has already disappeared.
- Nowadays the menu disappears ok, all right, but
- we need to delete the widgets or multiple ones will pile up. */
- lw_destroy_all_widgets (menu_id);
+ unbind_to (specpdl_count, Qnil);
+ }
}
#endif /* not USE_GTK */
#ifndef HAVE_MULTILINGUAL_MENU
if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
{
- pane_name = ENCODE_SYSTEM (pane_name);
+ pane_name = ENCODE_MENU_STRING (pane_name);
AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
}
#endif
if (menu)
{
+ int specpdl_count = SPECPDL_INDEX ();
+ record_unwind_protect (pop_down_menu, make_save_value (menu, 0));
+
/* Display the menu. */
gtk_widget_show_all (menu);
/* Process events that apply to the menu. */
- popup_widget_loop (1);
+ popup_widget_loop (1, menu);
- gtk_widget_destroy (menu);
+ unbind_to (specpdl_count, Qnil);
}
}
}
-/* ARG is the LWLIB ID of the dialog box, represented
- as a Lisp object as (HIGHPART . LOWPART). */
-
-Lisp_Object
-xdialog_show_unwind (arg)
- Lisp_Object arg;
-{
- LWLIB_ID id = (XINT (XCAR (arg)) << 4 * sizeof (LWLIB_ID)
- | XINT (XCDR (arg)));
- BLOCK_INPUT;
- lw_destroy_all_widgets (id);
- UNBLOCK_INPUT;
- popup_activated_flag = 0;
- return Qnil;
-}
-
-
/* Pop up the dialog for frame F defined by FIRST_WV and loop until the
dialog pops down.
menu_item_selection will be set to the selection. */
int fact = 4 * sizeof (LWLIB_ID);
/* xdialog_show_unwind is responsible for popping the dialog box down. */
- record_unwind_protect (xdialog_show_unwind,
+ record_unwind_protect (pop_down_menu,
Fcons (make_number (dialog_id >> (fact)),
make_number (dialog_id & ~(-1 << (fact)))));
popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f),
- dialog_id, 1, 1);
+ dialog_id, 1);
unbind_to (count, Qnil);
}
}
}
}
+ else
+ /* Make "Cancel" equivalent to C-g. */
+ Fsignal (Qquit, Qnil);
return Qnil;
}
Qnil, menu_object, make_number (item), 1);
}
+static Lisp_Object
+pop_down_menu (arg)
+ Lisp_Object arg;
+{
+ struct Lisp_Save_Value *p1 = XSAVE_VALUE (Fcar (arg));
+ struct Lisp_Save_Value *p2 = XSAVE_VALUE (Fcdr (arg));
+
+ FRAME_PTR f = p1->pointer;
+ XMenu *menu = p2->pointer;
+
+ BLOCK_INPUT;
+#ifndef MSDOS
+ XUngrabPointer (FRAME_X_DISPLAY (f), CurrentTime);
+ XUngrabKeyboard (FRAME_X_DISPLAY (f), CurrentTime);
+#endif
+ XMenuDestroy (FRAME_X_DISPLAY (f), menu);
+
+#ifdef HAVE_X_WINDOWS
+ /* Assume the mouse has moved out of the X window.
+ If it has actually moved in, we will get an EnterNotify. */
+ x_mouse_leave (FRAME_X_DISPLAY_INFO (f));
+
+ /* 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. */
+ FRAME_X_DISPLAY_INFO (f)->grabbed = 0;
+
+#endif /* HAVE_X_WINDOWS */
+
+ UNBLOCK_INPUT;
+
+ return Qnil;
+}
+
static Lisp_Object
xmenu_show (f, x, y, for_click, keymaps, title, error)
char *datap;
int ulx, uly, width, height;
int dispwidth, dispheight;
- int i, j;
+ int i, j, lines, maxlines;
int maxwidth;
int dummy_int;
unsigned int dummy_uint;
+ int specpdl_count = SPECPDL_INDEX ();
*error = 0;
if (menu_items_n_panes == 0)
#ifdef HAVE_X_WINDOWS
/* Adjust coordinates to relative to the outer (window manager) window. */
- {
- 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->output_data.x->parent_desc != FRAME_X_DISPLAY_INFO (f)->root_window)
- {
- BLOCK_INPUT;
- XTranslateCoordinates (FRAME_X_DISPLAY (f),
-
- /* From-window, to-window. */
- f->output_data.x->window_desc,
- f->output_data.x->parent_desc,
-
- /* From-position, to-position. */
- 0, 0, &win_x, &win_y,
-
- /* Child of window. */
- &child);
- UNBLOCK_INPUT;
- x += win_x;
- y += win_y;
- }
- }
+ x += FRAME_OUTER_TO_INNER_DIFF_X (f);
+ y += FRAME_OUTER_TO_INNER_DIFF_Y (f);
#endif /* HAVE_X_WINDOWS */
/* Adjust coordinates to be root-window-relative. */
y += f->top_pos;
/* Create all the necessary panes and their items. */
- i = 0;
+ maxlines = lines = i = 0;
while (i < menu_items_used)
{
if (EQ (XVECTOR (menu_items)->contents[i], Qt))
Lisp_Object pane_name, prefix;
char *pane_string;
+ maxlines = max (maxlines, lines);
+ lines = 0;
pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
pane_string = (NILP (pane_name)
return Qnil;
}
i += MENU_ITEMS_ITEM_LENGTH;
+ lines++;
}
}
+ maxlines = max (maxlines, lines);
+
/* All set and ready to fly. */
XMenuRecompute (FRAME_X_DISPLAY (f), menu);
dispwidth = DisplayWidth (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
if (ulx < 0) x -= ulx;
if (uly < 0) y -= uly;
+ if (! for_click)
+ {
+ /* If position was not given by a mouse click, adjust so upper left
+ corner of the menu as a whole ends up at given coordinates. This
+ is what x-popup-menu says in its documentation. */
+ x += width/2;
+ y += 1.5*height/(maxlines+2);
+ }
+
XMenuSetAEQ (menu, TRUE);
XMenuSetFreeze (menu, TRUE);
pane = selidx = 0;
#ifndef MSDOS
XMenuActivateSetWaitFunction (x_menu_wait_for_event, FRAME_X_DISPLAY (f));
#endif
+
+ record_unwind_protect (pop_down_menu,
+ Fcons (make_save_value (f, 0),
+ make_save_value (menu, 0)));
/* Help display under X won't work because XMenuActivate contains
a loop that doesn't give Emacs a chance to process it. */
menu_help_frame = f;
status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx,
- x, y, ButtonReleaseMask, &datap,
- menu_help_callback);
-
-
-#ifdef HAVE_X_WINDOWS
- /* Assume the mouse has moved out of the X window.
- If it has actually moved in, we will get an EnterNotify. */
- x_mouse_leave (FRAME_X_DISPLAY_INFO (f));
-#endif
+ x, y, ButtonReleaseMask, &datap,
+ menu_help_callback);
switch (status)
{
case XM_FAILURE:
*error = "Can't activate menu";
case XM_IA_SELECT:
+ entry = Qnil;
+ break;
case XM_NO_SELECT:
+ /* Make "Cancel" equivalent to C-g unless this menu was popped up by
+ a mouse press. */
+ if (! for_click)
+ Fsignal (Qquit, Qnil);
entry = Qnil;
break;
}
- XMenuDestroy (FRAME_X_DISPLAY (f), menu);
-#ifdef HAVE_X_WINDOWS
- /* 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. */
- FRAME_X_DISPLAY_INFO (f)->grabbed = 0;
-#endif
+ unbind_to (specpdl_count, Qnil);
return entry;
}