/* X Communication module for terminals which understand the X protocol.
Copyright (C) 1986, 1988, 1993, 1994, 1996, 1999, 2000, 2001, 2002, 2003,
- 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+ 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
This file is part of GNU Emacs.
#endif
#include <stdio.h>
+#include <setjmp.h>
#include "lisp.h"
#include "keyboard.h"
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#ifdef USE_LUCID
+#include "xsettings.h"
+#include "../lwlib/xlwmenu.h"
#ifdef HAVE_XAW3D
#include <X11/Xaw3d/Paned.h>
#else /* !HAVE_XAW3D */
Lisp_Object Qdebug_on_next_call;
-extern Lisp_Object Vmenu_updating_frame;
-
extern Lisp_Object Qmenu_bar;
extern Lisp_Object QCtoggle, QCradio;
#endif
static int update_frame_menubar P_ ((struct frame *));
-static Lisp_Object xmenu_show P_ ((struct frame *, int, int, int, int,
- Lisp_Object, char **));
\f
/* Flag which when set indicates a dialog or menu has been posted by
Xt on behalf of one of the widget sets. */
for the window where the mouse is in. This could be the menu bar,
the scroll bar or the edit window. Fx_popup_menu needs to be
sure it is the edit window. */
-static void
+void
mouse_position_for_popup (f, x, y)
FRAME_PTR f;
int *x;
#endif /* HAVE_X_WINDOWS */
-DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
- doc: /* Pop up a deck-of-cards menu and return user's selection.
-POSITION is a position specification. This is either a mouse button event
-or a list ((XOFFSET YOFFSET) WINDOW)
-where XOFFSET and YOFFSET are positions in pixels from the top left
-corner of WINDOW. (WINDOW may be a window or a frame object.)
-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.
-The menu items come from key bindings that have a menu string as well as
-a definition; actually, the "definition" in such a key binding looks like
-\(STRING . REAL-DEFINITION). To give the menu a title, put a string into
-the keymap as a top-level element.
-
-If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
-Otherwise, REAL-DEFINITION should be a valid key binding definition.
-
-You can also use a list of keymaps as MENU.
- Then each keymap makes a separate pane.
-
-When MENU is a keymap or a list of keymaps, the return value is the
-list of events corresponding to the user's choice. Note that
-`x-popup-menu' does not actually execute the command bound to that
-sequence of events.
-
-Alternatively, you can specify a menu of multiple panes
- with a list of the form (TITLE PANE1 PANE2...),
-where each pane is a list of form (TITLE ITEM1 ITEM2...).
-Each ITEM is normally a cons cell (STRING . VALUE);
-but a string can appear as an item--that makes a nonselectable line
-in the menu.
-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.
-
-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;
-{
- Lisp_Object keymap, tem;
- int xpos = 0, ypos = 0;
- Lisp_Object title;
- char *error_name = NULL;
- Lisp_Object selection = Qnil;
- FRAME_PTR f = NULL;
- Lisp_Object x, y, window;
- int keymaps = 0;
- int for_click = 0;
- int specpdl_count = SPECPDL_INDEX ();
- struct gcpro gcpro1;
-
-#ifdef HAVE_MENUS
- if (! NILP (position))
- {
- int get_current_pos_p = 0;
- check_x ();
-
- /* Decode the first argument: find the window and the coordinates. */
- if (EQ (position, Qt)
- || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
- || EQ (XCAR (position), Qtool_bar))))
- {
- get_current_pos_p = 1;
- }
- else
- {
- tem = Fcar (position);
- if (CONSP (tem))
- {
- window = Fcar (Fcdr (position));
- x = XCAR (tem);
- y = Fcar (XCDR (tem));
- }
- else
- {
- for_click = 1;
- tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
- window = Fcar (tem); /* POSN_WINDOW (tem) */
- tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
- x = Fcar (tem);
- y = Fcdr (tem);
- }
-
- /* If a click happens in an external tool bar or a detached
- tool bar, x and y is NIL. In that case, use the current
- mouse position. This happens for the help button in the
- tool bar. Ideally popup-menu should pass NIL to
- this function, but it doesn't. */
- if (NILP (x) && NILP (y))
- get_current_pos_p = 1;
- }
-
- if (get_current_pos_p)
- {
- /* Use the mouse's current position. */
- FRAME_PTR new_f = SELECTED_FRAME ();
-#ifdef HAVE_X_WINDOWS
- /* Can't use mouse_position_hook for X since it returns
- coordinates relative to the window the mouse is in,
- we need coordinates relative to the edit widget always. */
- if (new_f != 0)
- {
- int cur_x, cur_y;
-
- mouse_position_for_popup (new_f, &cur_x, &cur_y);
- /* cur_x/y may be negative, so use make_number. */
- x = make_number (cur_x);
- y = make_number (cur_y);
- }
-
-#else /* not HAVE_X_WINDOWS */
- Lisp_Object bar_window;
- enum scroll_bar_part part;
- unsigned long time;
-
- if (mouse_position_hook)
- (*mouse_position_hook) (&new_f, 1, &bar_window,
- &part, &x, &y, &time);
-#endif /* not HAVE_X_WINDOWS */
-
- if (new_f != 0)
- XSETFRAME (window, new_f);
- else
- {
- window = selected_window;
- XSETFASTINT (x, 0);
- XSETFASTINT (y, 0);
- }
- }
-
- CHECK_NUMBER (x);
- CHECK_NUMBER (y);
-
- /* Decode where to put the menu. */
-
- if (FRAMEP (window))
- {
- f = XFRAME (window);
- xpos = 0;
- ypos = 0;
- }
- else if (WINDOWP (window))
- {
- CHECK_LIVE_WINDOW (window);
- f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
-
- xpos = WINDOW_LEFT_EDGE_X (XWINDOW (window));
- ypos = WINDOW_TOP_EDGE_Y (XWINDOW (window));
- }
- else
- /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
- but I don't want to make one now. */
- CHECK_WINDOW (window);
-
- xpos += XINT (x);
- ypos += XINT (y);
-
- if (! FRAME_X_P (f))
- error ("Can not put X menu on non-X terminal");
-
- XSETFRAME (Vmenu_updating_frame, f);
- }
- else
- Vmenu_updating_frame = Qnil;
-#endif /* HAVE_MENUS */
-
- record_unwind_protect (unuse_menu_items, Qnil);
- title = Qnil;
- GCPRO1 (title);
-
- /* Decode the menu items from what was specified. */
-
- keymap = get_keymap (menu, 0, 0);
- if (CONSP (keymap))
- {
- /* We were given a keymap. Extract menu info from the keymap. */
- Lisp_Object prompt;
-
- /* Extract the detailed info to make one pane. */
- 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. */
- prompt = Fkeymap_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)
- XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
-
- keymaps = 1;
- }
- else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
- {
- /* We were given a list of keymaps. */
- int nmaps = XFASTINT (Flength (menu));
- Lisp_Object *maps
- = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
- int i;
-
- title = Qnil;
-
- /* The first keymap that has a prompt string
- supplies the menu title. */
- for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
- {
- Lisp_Object prompt;
-
- maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
-
- prompt = Fkeymap_prompt (keymap);
- if (NILP (title) && !NILP (prompt))
- title = prompt;
- }
-
- /* Extract the detailed info to make one pane. */
- 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)
- XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
-
- keymaps = 1;
- }
- else
- {
- /* We were given an old-fashioned menu. */
- title = Fcar (menu);
- CHECK_STRING (title);
-
- list_of_panes (Fcdr (menu));
-
- keymaps = 0;
- }
-
- unbind_to (specpdl_count, Qnil);
-
- if (NILP (position))
- {
- discard_menu_items ();
- UNGCPRO;
- return Qnil;
- }
-
-#ifdef HAVE_MENUS
- /* Display them in a menu. */
- BLOCK_INPUT;
-
- selection = xmenu_show (f, xpos, ypos, for_click,
- keymaps, title, &error_name);
- UNBLOCK_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, 3, 0,
but I don't want to make one now. */
CHECK_WINDOW (window);
- if (! FRAME_X_P (f))
- error ("Can not put X dialog on non-X terminal");
+ if (! FRAME_X_P (f) && ! FRAME_MSDOS_P (f))
+ error ("Can not put X dialog on this terminal");
+
+ /* Force a redisplay before showing the dialog. If a frame is created
+ just before showing the dialog, its contents may not have been fully
+ drawn, as this depends on timing of events from the X server. Redisplay
+ is not done when a dialog is shown. If redisplay could be done in the
+ X event loop (i.e. the X event loop does not run in a signal handler)
+ this would not be needed.
+
+ Do this before creating the widget value that points to Lisp
+ string contents, because Fredisplay may GC and relocate them. */
+ Fredisplay (Qt);
#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
/* Display a menu with these alternatives
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
#endif
)
{
- EMACS_TIME next_time = timer_check (1);
+ EMACS_TIME next_time = timer_check (1), *ntp;
long secs = EMACS_SECS (next_time);
long usecs = EMACS_USECS (next_time);
SELECT_TYPE read_fds;
int fd = ConnectionNumber (dpyinfo->display);
FD_SET (fd, &read_fds);
if (fd > n) n = fd;
+ XFlush (dpyinfo->display);
}
- 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);
- }
+ if (secs < 0 && usecs < 0)
+ ntp = 0;
+ else
+ ntp = &next_time;
- select (n + 1, &read_fds, (SELECT_TYPE *)0, (SELECT_TYPE *)0, &next_time);
+ select (n + 1, &read_fds, (SELECT_TYPE *)0, (SELECT_TYPE *)0, ntp);
}
}
#endif /* ! MSDOS */
return 1;
}
+#ifdef USE_LUCID
+static void
+apply_systemfont_to_dialog (w)
+ Widget w;
+{
+ const char *fn = xsettings_get_system_normal_font ();
+ if (fn)
+ {
+ XrmDatabase db = XtDatabase (XtDisplay (w));
+ if (db)
+ XrmPutStringResource (&db, "*dialog.faceName", fn);
+ }
+}
+
+static void
+apply_systemfont_to_menu (w)
+ Widget w;
+{
+ const char *fn = xsettings_get_system_normal_font ();
+ int defflt;
+
+ if (!fn) return;
+
+ if (XtIsShell (w)) /* popup menu */
+ {
+ Widget *childs = NULL;
+
+ XtVaGetValues (w, XtNchildren, &childs, NULL);
+ if (*childs) w = *childs;
+ }
+
+ /* Only use system font if the default is used for the menu. */
+ XtVaGetValues (w, XtNdefaultFace, &defflt, NULL);
+ if (defflt)
+ XtVaSetValues (w, XtNfaceName, fn, NULL);
+}
+#endif
+
/* Set the contents of the menubar widgets of frame F.
The argument FIRST_TIME is currently ignored;
it is set the first time this is called, from initialize_frame_menubar. */
/* Make menu pop down on C-g. */
XtOverrideTranslations (menubar_widget, override);
+ apply_systemfont_to_menu (menubar_widget);
}
{
+ 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. */
+#if 1 /* Experimentally, we now get the right results
+ for -geometry -0-0 without this. 24 Aug 96, rms.
+ Maybe so, but the menu bar size is missing the pixels so the
+ WM size hints are off by theses pixel. Jan D, oct 2009. */
#ifdef USE_LUCID
if (FRAME_EXTERNAL_MENU_BAR (f))
{
menubar_size += ibw;
}
#endif /* USE_LUCID */
-#endif /* 0 */
+#endif /* 1 */
f->output_data.x->menubar_height = menubar_size;
}
{
struct next_popup_x_y* data = (struct next_popup_x_y*)user_data;
GtkRequisition req;
- int disp_width = FRAME_X_DISPLAY_INFO (data->f)->width;
- int disp_height = FRAME_X_DISPLAY_INFO (data->f)->height;
+ struct x_display_info *dpyinfo = FRAME_X_DISPLAY_INFO (data->f);
+ int disp_width = x_display_pixel_width (dpyinfo);
+ int disp_height = x_display_pixel_height (dpyinfo);
*x = data->x;
*y = data->y;
menu pops down.
menu_item_selection will be set to the selection. */
static void
-create_and_show_popup_menu (f, first_wv, x, y, for_click)
+create_and_show_popup_menu (f, first_wv, x, y, for_click, timestamp)
FRAME_PTR f;
widget_value *first_wv;
int x;
int y;
int for_click;
+ EMACS_UINT timestamp;
{
int i;
GtkWidget *menu;
/* Display the menu. */
gtk_widget_show_all (menu);
- gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, &popup_x_y, i, 0);
+
+ gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, &popup_x_y, i,
+ timestamp > 0 ? timestamp : gtk_get_current_event_time());
record_unwind_protect (pop_down_menu, make_save_value (menu, 0));
- if (GTK_WIDGET_MAPPED (menu))
+ if (gtk_widget_get_mapped (menu))
{
/* Set this to one. popup_widget_loop increases it by one, so it becomes
two. show_help_echo uses this to detect popup menus. */
menu pops down.
menu_item_selection will be set to the selection. */
static void
-create_and_show_popup_menu (f, first_wv, x, y, for_click)
+create_and_show_popup_menu (f, first_wv, x, y, for_click, timestamp)
FRAME_PTR f;
widget_value *first_wv;
int x;
int y;
int for_click;
+ EMACS_UINT timestamp;
{
int i;
Arg av[2];
popup_deactivate_callback,
menu_highlight_callback);
+ apply_systemfont_to_menu (menu);
+
dummy.type = ButtonPress;
dummy.serial = 0;
dummy.send_event = 0;
#endif /* not USE_GTK */
-static Lisp_Object
-xmenu_show (f, x, y, for_click, keymaps, title, error)
- FRAME_PTR f;
- int x;
- int y;
- int for_click;
- int keymaps;
- Lisp_Object title;
- char **error;
+Lisp_Object
+xmenu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps,
+ Lisp_Object title, char **error, EMACS_UINT timestamp)
{
int i;
widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
menu_item_selection = 0;
/* Actually create and show the menu until popped down. */
- create_and_show_popup_menu (f, first_wv, x, y, for_click);
+ create_and_show_popup_menu (f, first_wv, x, y, for_click, timestamp);
/* Free the widget_value objects we used to specify the contents. */
free_menubar_widget_value_tree (first_wv);
abort();
dialog_id = widget_id_tick++;
+#ifdef HAVE_XFT
+ apply_systemfont_to_dialog (f->output_data.x->widget);
+#endif
lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
f->output_data.x->widget, 1, 0,
dialog_selection_callback, 0, 0);
lw_modify_all_widgets (dialog_id, first_wv->contents, True);
-
/* Display the dialog box. */
lw_pop_up_all_widgets (dialog_id);
popup_activated_flag = 1;
/* No selection has been chosen yet. */
menu_item_selection = 0;
- /* Force a redisplay before showing the dialog. If a frame is created
- just before showing the dialog, its contents may not have been fully
- drawn, as this depends on timing of events from the X server. Redisplay
- is not done when a dialog is shown. If redisplay could be done in the
- X event loop (i.e. the X event loop does not run in a signal handler)
- this would not be needed. */
- Fredisplay (Qt);
-
/* Actually create and show the dialog. */
create_and_show_dialog (f, first_wv);
}
-static Lisp_Object
-xmenu_show (f, x, y, for_click, keymaps, title, error)
+Lisp_Object
+xmenu_show (f, x, y, for_click, keymaps, title, error, timestamp)
FRAME_PTR f;
int x, y;
int for_click;
int keymaps;
Lisp_Object title;
char **error;
+ EMACS_UINT timestamp;
{
Window root;
XMenu *menu;
unsigned int dummy_uint;
int specpdl_count = SPECPDL_INDEX ();
- if (! FRAME_X_P (f))
+ if (! FRAME_X_P (f) && ! FRAME_MSDOS_P (f))
abort ();
*error = 0;
y -= (uly + height) - dispheight;
uly = dispheight - height;
}
+#ifndef HAVE_X_WINDOWS
+ if (FRAME_HAS_MINIBUF_P (f) && uly+height > dispheight - 1)
+ {
+ /* Move the menu away of the echo area, to avoid overwriting the
+ menu with help echo messages or vice versa. */
+ if (BUFFERP (echo_area_buffer[0]) && WINDOWP (echo_area_window))
+ {
+ y -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
+ uly -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
+ }
+ else
+ {
+ y--;
+ uly--;
+ }
+ }
+#endif
if (ulx < 0) x -= ulx;
if (uly < 0) y -= uly;
void
syms_of_xmenu ()
{
- Qdebug_on_next_call = intern ("debug-on-next-call");
+ Qdebug_on_next_call = intern_c_string ("debug-on-next-call");
staticpro (&Qdebug_on_next_call);
#ifdef USE_X_TOOLKIT
next_menubar_widget_id = 1;
#endif
- defsubr (&Sx_popup_menu);
defsubr (&Smenu_or_popup_active_p);
#if defined (USE_GTK) || defined (USE_X_TOOLKIT)
defsubr (&Sx_menu_bar_open_internal);
- Ffset (intern ("accelerate-menu"),
- intern (Sx_menu_bar_open_internal.symbol_name));
+ Ffset (intern_c_string ("accelerate-menu"),
+ intern_c_string (Sx_menu_bar_open_internal.symbol_name));
#endif
#ifdef HAVE_MENUS