summaryrefslogtreecommitdiff
path: root/src/gtkutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gtkutil.c')
-rw-r--r--src/gtkutil.c346
1 files changed, 247 insertions, 99 deletions
diff --git a/src/gtkutil.c b/src/gtkutil.c
index 1f92040ff69..4d0b50573f7 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -36,9 +36,128 @@ Boston, MA 02111-1307, USA. */
#include "coding.h"
#include <gdk/gdkkeysyms.h>
+
#define FRAME_TOTAL_PIXEL_HEIGHT(f) \
(FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
+
+/***********************************************************************
+ Display handling functions
+ ***********************************************************************/
+
+#ifdef HAVE_GTK_MULTIDISPLAY
+
+/* Return the GdkDisplay that corresponds to the X display DPY. */
+static GdkDisplay *
+xg_get_gdk_display (dpy)
+ Display *dpy;
+{
+ return gdk_x11_lookup_xdisplay (dpy);
+}
+
+/* When the GTK widget W is to be created on a display for F that
+ is not the default display, set the display for W.
+ W can be a GtkMenu or a GtkWindow widget. */
+static void
+xg_set_screen (w, f)
+ GtkWidget *w;
+ FRAME_PTR f;
+{
+ if (FRAME_X_DISPLAY (f) != GDK_DISPLAY ())
+ {
+ GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
+ GdkScreen *gscreen = gdk_display_get_default_screen (gdpy);
+
+ if (GTK_IS_MENU (w))
+ gtk_menu_set_screen (GTK_MENU (w), gscreen);
+ else
+ gtk_window_set_screen (GTK_WINDOW (w), gscreen);
+ }
+}
+
+
+#else /* not HAVE_GTK_MULTIDISPLAY */
+
+/* Make some defines so we can use the GTK 2.2 functions when
+ compiling with GTK 2.0. */
+#define xg_set_screen(w, f)
+#define gdk_xid_table_lookup_for_display(dpy, w) gdk_xid_table_lookup (w)
+#define gdk_pixmap_foreign_new_for_display(dpy, p) gdk_pixmap_foreign_new (p)
+#define gdk_cursor_new_for_display(dpy, c) gdk_cursor_new (c)
+#define gdk_x11_lookup_xdisplay(dpy) 0
+#define GdkDisplay void
+
+#endif /* not HAVE_GTK_MULTIDISPLAY */
+
+/* Open a display named by DISPLAY_NAME. The display is returned in *DPY.
+ *DPY is set to NULL if the display can't be opened.
+
+ Returns non-zero if display could be opened, zero if display could not
+ be opened, and less than zero if the GTK version doesn't support
+ multipe displays. */
+int
+xg_display_open (display_name, dpy)
+ char *display_name;
+ Display **dpy;
+{
+#ifdef HAVE_GTK_MULTIDISPLAY
+ GdkDisplay *gdpy;
+
+ gdpy = gdk_display_open (display_name);
+ *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
+
+ return gdpy != NULL;
+
+#else /* not HAVE_GTK_MULTIDISPLAY */
+
+ return -1;
+#endif /* not HAVE_GTK_MULTIDISPLAY */
+}
+
+
+void
+xg_display_close (Display *dpy)
+{
+#ifdef HAVE_GTK_MULTIDISPLAY
+ GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
+
+ /* GTK 2.2 has a bug that makes gdk_display_close crash (bug
+ http://bugzilla.gnome.org/show_bug.cgi?id=85715). This way
+ we can continue running, but there will be memory leaks. */
+
+#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 4
+
+ /* If this is the default display, we must change it before calling
+ dispose, otherwise it will crash. */
+ if (gdk_display_get_default () == gdpy)
+ {
+ struct x_display_info *dpyinfo;
+ Display *new_dpy = 0;
+ GdkDisplay *gdpy_new;
+
+ /* Find another display. */
+ for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
+ if (dpyinfo->display != dpy)
+ {
+ new_dpy = dpyinfo->display;
+ break;
+ }
+
+ if (! new_dpy) return; /* Emacs will exit anyway. */
+
+ gdpy_new = gdk_x11_lookup_xdisplay (new_dpy);
+ gdk_display_manager_set_default_display (gdk_display_manager_get (),
+ gdpy_new);
+ }
+
+ g_object_run_dispose (G_OBJECT (gdpy));
+
+#else
+ /* I hope this will be fixed in GTK 2.4. It is what bug 85715 says. */
+ gdk_display_close (gdpy);
+#endif
+#endif /* HAVE_GTK_MULTIDISPLAY */
+}
/***********************************************************************
@@ -48,10 +167,6 @@ Boston, MA 02111-1307, USA. */
NULL if no timer is started. */
static struct atimer *xg_timer;
-/* The cursor used for scroll bars and popup menus.
- We only have one cursor for all scroll bars and all popup menus. */
-static GdkCursor *xg_left_ptr_cursor;
-
/* The next two variables and functions are taken from lwlib. */
static widget_value *widget_value_free_list;
@@ -103,24 +218,48 @@ free_widget_value (wv)
}
}
-/* Set *CURSOR on W and all widgets W contain. We must do like this
- for scroll bars and menu because they create widgets internally,
- and it is those widgets that are visible.
- If *CURSOR is NULL, create a GDK_LEFT_PTR cursor and set *CURSOR to
- the created cursor. */
-void
+/* Create and return the cursor to be used for popup menus and
+ scroll bars on display DPY. */
+GdkCursor *
+xg_create_default_cursor (dpy)
+ Display *dpy;
+{
+ GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
+ return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
+}
+
+/* For the image defined in IMG, make and return a GdkPixmap for
+ the pixmap in *GPIX, and a GdkBitmap for the mask in *GMASK.
+ If IMG has no mask, *GMASK is set to NULL.
+ The image is defined on the display where frame F is. */
+static void
+xg_get_gdk_pixmap_and_mask (f, img, gpix, gmask)
+ FRAME_PTR f;
+ struct image *img;
+ GdkPixmap **gpix;
+ GdkBitmap **gmask;
+{
+ GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
+
+ *gpix = gdk_pixmap_foreign_new_for_display (gdpy, img->pixmap);
+ *gmask = img->mask ?
+ (GdkBitmap*) gdk_pixmap_foreign_new_for_display (gdpy, img->mask)
+ : 0;
+}
+
+
+/* Set CURSOR on W and all widgets W contain. We must do like this
+ for scroll bars and menu because they create widgets internally,
+ and it is those widgets that are visible. */
+static void
xg_set_cursor (w, cursor)
GtkWidget *w;
- GdkCursor **cursor;
+ GdkCursor *cursor;
{
GList *children = gdk_window_peek_children (w->window);
- /* Create the cursor unless already created. */
- if (! *cursor)
- *cursor = gdk_cursor_new (GDK_LEFT_PTR);
-
- gdk_window_set_cursor (w->window, *cursor);
+ gdk_window_set_cursor (w->window, cursor);
/* The scroll bar widget has more than one GDK window (had to look at
the source to figure this out), and there is no way to set cursor
@@ -128,7 +267,7 @@ xg_set_cursor (w, cursor)
Ditto for menus. */
for ( ; children; children = g_list_next (children))
- gdk_window_set_cursor (GDK_WINDOW (children->data), *cursor);
+ gdk_window_set_cursor (GDK_WINDOW (children->data), cursor);
}
/* Timer function called when a timeout occurs for xg_timer.
@@ -376,25 +515,28 @@ xg_frame_set_char_size (f, cols, rows)
gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
pixelwidth, pixelheight);
xg_resize_widgets (f, pixelwidth, pixelheight);
-
+ x_wm_set_size_hint (f, 0, 0);
SET_FRAME_GARBAGED (f);
cancel_mouse_face (f);
}
-/* Convert an X Window WSESC to its corresponding GtkWidget.
+/* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
Must be done like this, because GtkWidget:s can have "hidden"
X Window that aren't accessible.
Return 0 if no widget match WDESC. */
GtkWidget *
-xg_win_to_widget (wdesc)
+xg_win_to_widget (dpy, wdesc)
+ Display *dpy;
Window wdesc;
{
gpointer gdkwin;
GtkWidget *gwdesc = 0;
BLOCK_INPUT;
- gdkwin = gdk_xid_table_lookup (wdesc);
+
+ gdkwin = gdk_xid_table_lookup_for_display (gdk_x11_lookup_xdisplay (dpy),
+ wdesc);
if (gdkwin)
{
GdkEvent event;
@@ -429,9 +571,9 @@ xg_pix_to_gcolor (w, pixel, c)
Return TRUE to tell GTK that this expose event has been fully handeled
and that GTK shall do nothing more with it. */
static gboolean
-xg_fixed_handle_expose(GtkWidget *widget,
- GdkEventExpose *event,
- gpointer user_data)
+xg_fixed_handle_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer user_data)
{
GList *iter;
@@ -483,6 +625,8 @@ xg_create_frame_widgets (f)
BLOCK_INPUT;
wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ xg_set_screen (wtop, f);
+
wvbox = gtk_vbox_new (FALSE, 0);
wfixed = gtk_fixed_new (); /* Must have this to place scroll bars */
@@ -512,7 +656,8 @@ xg_create_frame_widgets (f)
gtk_fixed_set_has_window (GTK_FIXED (wfixed), TRUE);
- gtk_widget_set_size_request (wfixed, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
+ gtk_widget_set_size_request (wfixed, FRAME_PIXEL_WIDTH (f),
+ FRAME_PIXEL_HEIGHT (f));
gtk_container_add (GTK_CONTAINER (wtop), wvbox);
gtk_box_pack_end (GTK_BOX (wvbox), wfixed, TRUE, TRUE, 0);
@@ -963,6 +1108,8 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p)
filewin = gtk_file_selection_new (prompt);
filesel = GTK_FILE_SELECTION (filewin);
+ xg_set_screen (filewin, f);
+
gtk_widget_set_name (filewin, "emacs-filedialog");
gtk_window_set_transient_for (GTK_WINDOW (filewin),
@@ -1333,27 +1480,29 @@ xg_separator_p (char *label)
return 0;
}
-GtkWidget *xg_did_tearoff;
+static int xg_detached_menus;
+
+/* Returns non-zero if there are detached menus. */
+int
+xg_have_tear_offs ()
+{
+ return xg_detached_menus > 0;
+}
/* Callback invoked when a detached menu window is removed. Here we
- delete the popup menu.
+ decrease the xg_detached_menus count.
WIDGET is the top level window that is removed (the parent of the menu).
- EVENT is the event that triggers the window removal.
- CLIENT_DATA points to the menu that is detached.
-
- Returns TRUE to tell GTK to stop processing this event. */
-static gboolean
-tearoff_remove (widget, event, client_data)
+ CLIENT_DATA is not used. */
+static void
+tearoff_remove (widget, client_data)
GtkWidget *widget;
- GdkEvent *event;
gpointer client_data;
{
- gtk_widget_destroy (GTK_WIDGET (client_data));
- return TRUE;
+ if (xg_detached_menus > 0) --xg_detached_menus;
}
-/* Callback invoked when a menu is detached. It sets the xg_did_tearoff
- variable.
+/* Callback invoked when a menu is detached. It increases the
+ xg_detached_menus count.
WIDGET is the GtkTearoffMenuItem.
CLIENT_DATA is not used. */
static void
@@ -1362,31 +1511,15 @@ tearoff_activate (widget, client_data)
gpointer client_data;
{
GtkWidget *menu = gtk_widget_get_parent (widget);
- if (! gtk_menu_get_tearoff_state (GTK_MENU (menu)))
- return;
-
- xg_did_tearoff = menu;
+ if (gtk_menu_get_tearoff_state (GTK_MENU (menu)))
+ {
+ ++xg_detached_menus;
+ g_signal_connect (G_OBJECT (gtk_widget_get_toplevel (widget)),
+ "destroy",
+ G_CALLBACK (tearoff_remove), 0);
+ }
}
-/* If a detach of a popup menu is done, this function should be called
- to keep the menu around until the detached window is removed.
- MENU is the top level menu for the popup,
- SUBMENU is the menu that got detached (that is MENU or a
- submenu of MENU), see the xg_did_tearoff variable. */
-void
-xg_keep_popup (menu, submenu)
- GtkWidget *menu;
- GtkWidget *submenu;
-{
- GtkWidget *p;
-
- /* Find the top widget for the detached menu. */
- p = gtk_widget_get_toplevel (submenu);
-
- /* Delay destroying the menu until the detached menu is removed. */
- g_signal_connect (G_OBJECT (p), "unmap_event",
- G_CALLBACK (tearoff_remove), menu);
-}
/* Create a menu item widget, and connect the callbacks.
ITEM decribes the menu item.
@@ -1514,7 +1647,11 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
if (! topmenu)
{
- if (! menu_bar_p) wmenu = gtk_menu_new ();
+ if (! menu_bar_p)
+ {
+ wmenu = gtk_menu_new ();
+ xg_set_screen (wmenu, f);
+ }
else wmenu = gtk_menu_bar_new ();
/* Put cl_data on the top menu for easier access. */
@@ -1585,7 +1722,7 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
highlight_cb,
0,
0,
- 1,
+ add_tearoff_p,
0,
cl_data,
0);
@@ -1626,37 +1763,39 @@ xg_create_widget (type, name, f, val,
GCallback highlight_cb;
{
GtkWidget *w = 0;
+ int menu_bar_p = strcmp (type, "menubar") == 0;
+ int pop_up_p = strcmp (type, "popup") == 0;
+
if (strcmp (type, "dialog") == 0)
{
w = create_dialog (val, select_cb, deactivate_cb);
+ xg_set_screen (w, f);
gtk_window_set_transient_for (GTK_WINDOW (w),
GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
-
- if (w)
- gtk_widget_set_name (w, "emacs-dialog");
+ gtk_widget_set_name (w, "emacs-dialog");
}
- else if (strcmp (type, "menubar") == 0 || strcmp (type, "popup") == 0)
+ else if (menu_bar_p || pop_up_p)
{
w = create_menus (val->contents,
f,
select_cb,
deactivate_cb,
highlight_cb,
- strcmp (type, "popup") == 0,
- strcmp (type, "menubar") == 0,
- 1,
+ pop_up_p,
+ menu_bar_p,
+ menu_bar_p,
0,
0,
name);
/* Set the cursor to an arrow for popup menus when they are mapped.
This is done by default for menu bar menus. */
- if (strcmp (type, "popup") == 0)
+ if (pop_up_p)
{
/* Must realize so the GdkWindow inside the widget is created. */
gtk_widget_realize (w);
- xg_set_cursor (w, &xg_left_ptr_cursor);
+ xg_set_cursor (w, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
}
}
else
@@ -1834,9 +1973,16 @@ xg_update_menubar (menubar, f, list, iter, pos, val,
is up to date when leaving the minibuffer. */
GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem)));
char *utf8_label = get_utf8_string (val->name);
+ GtkWidget *submenu = gtk_menu_item_get_submenu (witem);
gtk_label_set_text (wlabel, utf8_label);
+ /* If this item has a submenu that has been detached, change
+ the title in the WM decorations also. */
+ if (submenu && gtk_menu_get_tearoff_state (GTK_MENU (submenu)))
+ /* Set the title of the detached window. */
+ gtk_menu_set_title (GTK_MENU (submenu), utf8_label);
+
iter = g_list_next (iter);
val = val->next;
++pos;
@@ -2222,18 +2368,15 @@ xg_modify_menubar_widgets (menubar, f, val, deep_p,
cl_data = (xg_menu_cb_data*) g_object_get_data (G_OBJECT (menubar),
XG_FRAME_DATA);
- if (! deep_p)
- {
- widget_value *cur = val->contents;
- xg_update_menubar (menubar, f, &list, list, 0, cur,
- select_cb, highlight_cb, cl_data);
- }
- else
+ xg_update_menubar (menubar, f, &list, list, 0, val->contents,
+ select_cb, highlight_cb, cl_data);
+
+ if (deep_p);
{
widget_value *cur;
/* Update all sub menus.
- We must keep the submenu names (GTK menu item widgets) since the
+ We must keep the submenus (GTK menu item widgets) since the
X Window in the XEvent that activates the menu are those widgets. */
/* Update cl_data, menu_item things in F may have changed. */
@@ -2268,8 +2411,10 @@ xg_modify_menubar_widgets (menubar, f, val, deep_p,
a new menu bar item, it has no sub menu yet. So we set the
newly created sub menu under witem. */
if (newsub != sub)
- gtk_menu_item_set_submenu (witem, newsub);
-
+ {
+ xg_set_screen (newsub, f);
+ gtk_menu_item_set_submenu (witem, newsub);
+ }
}
}
@@ -2425,16 +2570,17 @@ xg_get_widget_from_map (idx)
return 0;
}
-/* Return the scrollbar id for X Window WID.
+/* Return the scrollbar id for X Window WID on display DPY.
Return -1 if WID not in id_to_widget. */
int
-xg_get_scroll_id_for_window (wid)
+xg_get_scroll_id_for_window (dpy, wid)
+ Display *dpy;
Window wid;
{
int idx;
GtkWidget *w;
- w = xg_win_to_widget (wid);
+ w = xg_win_to_widget (dpy, wid);
if (w)
{
@@ -2541,7 +2687,7 @@ xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
wscroll, -1, -1);
/* Set the cursor to an arrow. */
- xg_set_cursor (wscroll, &xg_left_ptr_cursor);
+ xg_set_cursor (wscroll, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
SET_SCROLL_BAR_X_WINDOW (bar, scroll_id);
}
@@ -2960,8 +3106,8 @@ xg_tool_bar_item_expose_callback (w, event, client_data)
event->area.x -= width > event->area.width ? width-event->area.width : 0;
event->area.y -= height > event->area.height ? height-event->area.height : 0;
- event->area.x = max(0, event->area.x);
- event->area.y = max(0, event->area.y);
+ event->area.x = max (0, event->area.x);
+ event->area.y = max (0, event->area.y);
event->area.width = max (width, event->area.width);
event->area.height = max (height, event->area.height);
@@ -2983,7 +3129,7 @@ xg_tool_bar_expose_callback (w, event, client_data)
GdkEventExpose *event;
gpointer client_data;
{
- update_frame_tool_bar((FRAME_PTR)client_data);
+ update_frame_tool_bar ((FRAME_PTR) client_data);
return FALSE;
}
@@ -3116,11 +3262,12 @@ update_frame_tool_bar (f)
if (! wicon)
{
- GdkPixmap *gpix = gdk_pixmap_foreign_new (img->pixmap);
- GdkBitmap *gmask = img->mask ?
- (GdkBitmap*) gdk_pixmap_foreign_new (img->mask) : 0;
+ GdkPixmap *gpix;
+ GdkBitmap *gmask;
+ GtkWidget *w;
- GtkWidget *w = gtk_image_new_from_pixmap (gpix, gmask);
+ xg_get_gdk_pixmap_and_mask (f, img, &gpix, &gmask);
+ w = gtk_image_new_from_pixmap (gpix, gmask);
gtk_toolbar_append_item (GTK_TOOLBAR (x->toolbar_widget),
0, 0, 0,
w,
@@ -3178,10 +3325,10 @@ update_frame_tool_bar (f)
if (old_img != img->pixmap)
{
- GdkPixmap *gpix = gdk_pixmap_foreign_new (img->pixmap);
- GdkBitmap *gmask = img->mask ?
- (GdkBitmap*) gdk_pixmap_foreign_new (img->mask) : 0;
+ GdkPixmap *gpix;
+ GdkBitmap *gmask;
+ xg_get_gdk_pixmap_and_mask (f, img, &gpix, &gmask);
gtk_image_set_from_pixmap (wimage, gpix, gmask);
}
@@ -3249,9 +3396,7 @@ void
xg_initialize ()
{
xg_ignore_gtk_scrollbar = 0;
- xg_left_ptr_cursor = 0;
- xg_did_tearoff = 0;
-
+ xg_detached_menus = 0;
xg_menu_cb_list.prev = xg_menu_cb_list.next =
xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
@@ -3275,3 +3420,6 @@ xg_initialize ()
}
#endif /* USE_GTK */
+
+/* arch-tag: fe7104da-bc1e-4aba-9bd1-f349c528f7e3
+ (do not change this comment) */