#include "buffer.h"
#include "charset.h"
#include "coding.h"
+#include "sysselect.h"
#ifdef MSDOS
#include "msdos.h"
extern Lisp_Object Qmenu_bar_update_hook;
#ifdef USE_X_TOOLKIT
-extern void set_frame_menubar ();
+extern void set_frame_menubar P_ ((FRAME_PTR, int, int));
extern XtAppContext Xt_app_con;
-static Lisp_Object xdialog_show ();
-static void popup_get_selection ();
+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));
/* Define HAVE_BOXES if menus can handle radio and toggle buttons. */
#ifdef USE_GTK
#include "gtkutil.h"
#define HAVE_BOXES 1
-extern void set_frame_menubar ();
-static Lisp_Object xdialog_show ();
+extern void set_frame_menubar P_ ((FRAME_PTR, int, int));
+static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, char **));
#endif
/* This is how to deal with multibyte text if HAVE_MULTILINGUAL_MENU
static void list_of_panes P_ ((Lisp_Object));
static void list_of_items P_ ((Lisp_Object));
-extern EMACS_TIME timer_check P_ ((int));
\f
/* This holds a Lisp vector that holds the results of decoding
the keymaps or alist-of-alists that specify a menu.
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
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. */
}
#endif
}
+
+
+#ifndef MSDOS
+
+/* Return non-zero if a dialog or popup menu is already popped up. */
+
+int
+x_menu_in_use ()
+{
+ return ! NILP (menu_items_inuse);
+}
+
+/* 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;
+}
+
+/* Wait for an X event to arrive or for a timer to expire. */
+
+void
+x_menu_wait_for_event (void *data)
+{
+ extern EMACS_TIME timer_check P_ ((int));
+
+ /* Another way to do this is to register a timer callback, that can be
+ done in GTK and Xt. But we have to do it like this when using only X
+ anyway, and with callbacks we would have three variants for timer handling
+ instead of the small ifdefs below. */
+
+ while (
+#ifdef USE_X_TOOLKIT
+ ! XtAppPending (Xt_app_con)
+#elif defined USE_GTK
+ ! gtk_events_pending ()
+#else
+ ! XPending ((Display*) data)
+#endif
+ )
+ {
+ EMACS_TIME next_time = timer_check (1);
+ long secs = EMACS_SECS (next_time);
+ long usecs = EMACS_USECS (next_time);
+ SELECT_TYPE read_fds;
+ struct x_display_info *dpyinfo;
+ int n = 0;
+
+ FD_ZERO (&read_fds);
+ for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
+ {
+ int fd = ConnectionNumber (dpyinfo->display);
+ FD_SET (fd, &read_fds);
+ if (fd > n) n = fd;
+ }
+
+ if (secs < 0 || (secs == 0 && usecs == 0))
+ {
+ /* Sometimes timer_check returns -1 (no timers) even if there are
+ timers. So do a timeout anyway. */
+ EMACS_SET_SECS (next_time, 1);
+ EMACS_SET_USECS (next_time, 0);
+ }
+
+ select (n + 1, &read_fds, (SELECT_TYPE *)0, (SELECT_TYPE *)0, &next_time);
+ }
+}
+#endif /* ! MSDOS */
+
\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 DO_TIMERS is nonzero, run timers.
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)
XEvent *initial_event;
while (popup_activated_flag)
{
- /* If we have no events to run, consider timers. */
- if (do_timers && !XtAppPending (Xt_app_con))
- timer_check (1);
-
- if (initial_event)
+ if (initial_event)
{
event = *initial_event;
initial_event = 0;
}
else
- XtAppNextEvent (Xt_app_con, &event);
+ {
+ if (do_timers) x_menu_wait_for_event (0);
+ XtAppNextEvent (Xt_app_con, &event);
+ }
/* Make sure we don't consider buttons grabbed after menu goes.
And make sure to deactivate for any ButtonRelease,
#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 ()
+popup_widget_loop (do_timers, widget)
+ int do_timers;
+ GtkWidget *widget;
{
++popup_activated_flag;
/* Process events in the Gtk event loop until done. */
while (popup_activated_flag)
{
+ if (do_timers) x_menu_wait_for_event (0);
gtk_main_iteration ();
}
}
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,
gtk_widget_show_all (menu);
gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, &popup_x_y, i, 0);
+ fprintf (stderr, "Unwind: %p\n", menu);
+ 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 ();
+ 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, 0);
- /* 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 */
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 ();
+ 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)))));
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)
int maxwidth;
int dummy_int;
unsigned int dummy_uint;
+ int specpdl_count = SPECPDL_INDEX ();
*error = 0;
if (menu_items_n_panes == 0)
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)
{
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;
}