X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/2b5b82db4975fdfa2818d9184a026d041a941263..95df8112a0cbdb06addbac5fbea03b37d4440418:/src/gtkutil.c diff --git a/src/gtkutil.c b/src/gtkutil.c index 7a25bbb1e3..6367949a64 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -1,6 +1,6 @@ /* Functions for creating and updating GTK widgets. - Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 - Free Software Foundation, Inc. + +Copyright (C) 2003-2011 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -20,7 +20,6 @@ along with GNU Emacs. If not, see . */ #include #ifdef USE_GTK -#include #include #include #include @@ -35,6 +34,7 @@ along with GNU Emacs. If not, see . */ #include "charset.h" #include "coding.h" #include +#include "xsettings.h" #ifdef HAVE_XFT #include @@ -43,16 +43,39 @@ along with GNU Emacs. If not, see . */ #define FRAME_TOTAL_PIXEL_HEIGHT(f) \ (FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f)) -/* Avoid "differ in sign" warnings */ -#define SSDATA(x) ((char *) SDATA (x)) +#define FRAME_TOTAL_PIXEL_WIDTH(f) \ + (FRAME_PIXEL_WIDTH (f) + FRAME_TOOLBAR_WIDTH (f)) + +#ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW +#define gtk_widget_set_has_window(w, b) \ + (gtk_fixed_set_has_window (GTK_FIXED (w), b)) +#endif +#ifndef HAVE_GTK_DIALOG_GET_ACTION_AREA +#define gtk_dialog_get_action_area(w) ((w)->action_area) +#define gtk_dialog_get_content_area(w) ((w)->vbox) +#endif +#ifndef HAVE_GTK_WIDGET_GET_SENSITIVE +#define gtk_widget_get_sensitive(w) (GTK_WIDGET_SENSITIVE (w)) +#endif +#ifndef HAVE_GTK_ADJUSTMENT_GET_PAGE_SIZE +#define gtk_adjustment_set_page_size(w, s) ((w)->page_size = (s)) +#define gtk_adjustment_set_page_increment(w, s) ((w)->page_increment = (s)) +#define gtk_adjustment_get_step_increment(w) ((w)->step_increment) +#define gtk_adjustment_set_step_increment(w, s) ((w)->step_increment = (s)) +#endif +#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 11 +#define remove_submenu(w) gtk_menu_item_set_submenu ((w), NULL) +#else +#define remove_submenu(w) gtk_menu_item_remove_submenu ((w)) +#endif + +#define XG_BIN_CHILD(x) gtk_bin_get_child (GTK_BIN (x)) /*********************************************************************** Display handling functions ***********************************************************************/ -#ifdef HAVE_GTK_MULTIDISPLAY - /* Keep track of the default display, or NULL if there is none. Emacs may close all its displays. */ @@ -63,9 +86,7 @@ static GdkDisplay *gdpy_def; W can be a GtkMenu or a GtkWindow widget. */ static void -xg_set_screen (w, f) - GtkWidget *w; - FRAME_PTR f; +xg_set_screen (GtkWidget *w, FRAME_PTR f) { if (FRAME_X_DISPLAY (f) != GDK_DISPLAY ()) { @@ -80,20 +101,6 @@ xg_set_screen (w, f) } -#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. @@ -101,12 +108,9 @@ xg_set_screen (w, f) 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; +void +xg_display_open (char *display_name, Display **dpy) { -#ifdef HAVE_GTK_MULTIDISPLAY GdkDisplay *gdpy; gdpy = gdk_display_open (display_name); @@ -118,12 +122,6 @@ xg_display_open (display_name, dpy) } *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL; - return gdpy != NULL; - -#else /* not HAVE_GTK_MULTIDISPLAY */ - - return -1; -#endif /* not HAVE_GTK_MULTIDISPLAY */ } @@ -132,7 +130,6 @@ xg_display_open (display_name, dpy) void xg_display_close (Display *dpy) { -#ifdef HAVE_GTK_MULTIDISPLAY GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy); /* If this is the default display, try to change it before closing. @@ -164,7 +161,6 @@ xg_display_close (Display *dpy) /* This seems to be fixed in GTK 2.10. */ gdk_display_close (gdpy); #endif -#endif /* HAVE_GTK_MULTIDISPLAY */ } @@ -181,7 +177,7 @@ static int malloc_cpt; Return a pointer to the allocated structure. */ widget_value * -malloc_widget_value () +malloc_widget_value (void) { widget_value *wv; if (widget_value_free_list) @@ -203,8 +199,7 @@ malloc_widget_value () by malloc_widget_value, and no substructures. */ void -free_widget_value (wv) - widget_value *wv; +free_widget_value (widget_value *wv) { if (wv->free_list) abort (); @@ -228,8 +223,7 @@ free_widget_value (wv) scroll bars on display DPY. */ GdkCursor * -xg_create_default_cursor (dpy) - Display *dpy; +xg_create_default_cursor (Display *dpy) { GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy); return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR); @@ -238,10 +232,9 @@ xg_create_default_cursor (dpy) /* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel. */ static GdkPixbuf * -xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap) - GdkPixmap *gpix; - GdkPixmap *gmask; - GdkColormap *cmap; +xg_get_pixbuf_from_pix_and_mask (GdkPixmap *gpix, + GdkPixmap *gmask, + GdkColormap *cmap) { int width, height; GdkPixbuf *icon_buf, *tmp_buf; @@ -292,12 +285,10 @@ xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap) } static Lisp_Object -file_for_image (image) - Lisp_Object image; +file_for_image (Lisp_Object image) { Lisp_Object specified_file = Qnil; Lisp_Object tail; - extern Lisp_Object QCfile; for (tail = XCDR (image); NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail)); @@ -320,11 +311,10 @@ file_for_image (image) If OLD_WIDGET is not NULL, that widget is modified. */ static GtkWidget * -xg_get_image_for_pixmap (f, img, widget, old_widget) - FRAME_PTR f; - struct image *img; - GtkWidget *widget; - GtkImage *old_widget; +xg_get_image_for_pixmap (FRAME_PTR f, + struct image *img, + GtkWidget *widget, + GtkImage *old_widget) { GdkPixmap *gpix; GdkPixmap *gmask; @@ -392,13 +382,12 @@ xg_get_image_for_pixmap (f, img, widget, old_widget) and it is those widgets that are visible. */ static void -xg_set_cursor (w, cursor) - GtkWidget *w; - GdkCursor *cursor; +xg_set_cursor (GtkWidget *w, GdkCursor *cursor) { - GList *children = gdk_window_peek_children (w->window); + GdkWindow *window = gtk_widget_get_window(w); + GList *children = gdk_window_peek_children (window); - gdk_window_set_cursor (w->window, cursor); + gdk_window_set_cursor (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 @@ -441,21 +430,22 @@ xg_list_remove (xg_list_node *list, xg_list_node *node) } /* Allocate and return a utf8 version of STR. If STR is already - utf8 or NULL, just return STR. - If not, a new string is allocated and the caller must free the result + utf8 or NULL, just return a copy of STR. + A new string is allocated and the caller must free the result with g_free. */ static char * -get_utf8_string (str) - char *str; +get_utf8_string (const char *str) { - char *utf8_str = str; + char *utf8_str; if (!str) return NULL; /* If not UTF-8, try current locale. */ if (!g_utf8_validate (str, -1, NULL)) utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0); + else + return g_strdup (str); if (!utf8_str) { @@ -514,6 +504,209 @@ get_utf8_string (str) return utf8_str; } +/* Check for special colors used in face spec for region face. + The colors are fetched from the Gtk+ theme. + Return 1 if color was found, 0 if not. */ + +int +xg_check_special_colors (struct frame *f, + const char *color_name, + XColor *color) +{ + int success_p = 0; + if (FRAME_GTK_WIDGET (f)) + { + if (strcmp ("gtk_selection_bg_color", color_name) == 0) + { + GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f)); + color->red = gsty->bg[GTK_STATE_SELECTED].red; + color->green = gsty->bg[GTK_STATE_SELECTED].green; + color->blue = gsty->bg[GTK_STATE_SELECTED].blue; + color->pixel = gsty->bg[GTK_STATE_SELECTED].pixel; + success_p = 1; + } + else if (strcmp ("gtk_selection_fg_color", color_name) == 0) + { + GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f)); + color->red = gsty->fg[GTK_STATE_SELECTED].red; + color->green = gsty->fg[GTK_STATE_SELECTED].green; + color->blue = gsty->fg[GTK_STATE_SELECTED].blue; + color->pixel = gsty->fg[GTK_STATE_SELECTED].pixel; + success_p = 1; + } + } + + return success_p; +} + + + +/*********************************************************************** + Tooltips + ***********************************************************************/ +/* Gtk+ calls this callback when the parent of our tooltip dummy changes. + We use that to pop down the tooltip. This happens if Gtk+ for some + reason wants to change or hide the tooltip. */ + +#ifdef USE_GTK_TOOLTIP + +static void +hierarchy_ch_cb (GtkWidget *widget, + GtkWidget *previous_toplevel, + gpointer user_data) +{ + FRAME_PTR f = (FRAME_PTR) user_data; + struct x_output *x = f->output_data.x; + GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl); + + if (! top || ! GTK_IS_WINDOW (top)) + gtk_widget_hide (previous_toplevel); +} + +/* Callback called when Gtk+ thinks a tooltip should be displayed. + We use it to get the tooltip window and the tooltip widget so + we can manipulate the ourselves. + + Return FALSE ensures that the tooltip is not shown. */ + +static gboolean +qttip_cb (GtkWidget *widget, + gint xpos, + gint ypos, + gboolean keyboard_mode, + GtkTooltip *tooltip, + gpointer user_data) +{ + FRAME_PTR f = (FRAME_PTR) user_data; + struct x_output *x = f->output_data.x; + if (x->ttip_widget == NULL) + { + g_object_set (G_OBJECT (widget), "has-tooltip", FALSE, NULL); + x->ttip_widget = tooltip; + g_object_ref (G_OBJECT (tooltip)); + x->ttip_lbl = gtk_label_new (""); + g_object_ref (G_OBJECT (x->ttip_lbl)); + gtk_tooltip_set_custom (tooltip, x->ttip_lbl); + x->ttip_window = GTK_WINDOW (gtk_widget_get_toplevel (x->ttip_lbl)); + /* ATK needs an empty title for some reason. */ + gtk_window_set_title (x->ttip_window, ""); + /* Realize so we can safely get screen later on. */ + gtk_widget_realize (GTK_WIDGET (x->ttip_window)); + gtk_widget_realize (x->ttip_lbl); + + g_signal_connect (x->ttip_lbl, "hierarchy-changed", + G_CALLBACK (hierarchy_ch_cb), f); + } + return FALSE; +} + +#endif /* USE_GTK_TOOLTIP */ + +/* Prepare a tooltip to be shown, i.e. calculate WIDTH and HEIGHT. + Return zero if no system tooltip available, non-zero otherwise. */ + +int +xg_prepare_tooltip (FRAME_PTR f, + Lisp_Object string, + int *width, + int *height) +{ +#ifndef USE_GTK_TOOLTIP + return 0; +#else + struct x_output *x = f->output_data.x; + GtkWidget *widget; + GdkWindow *gwin; + GdkScreen *screen; + GtkSettings *settings; + gboolean tt_enabled = TRUE; + GtkRequisition req; + Lisp_Object encoded_string; + + if (!x->ttip_lbl) return 0; + + BLOCK_INPUT; + encoded_string = ENCODE_UTF_8 (string); + widget = GTK_WIDGET (x->ttip_lbl); + gwin = gtk_widget_get_window (GTK_WIDGET (x->ttip_window)); + screen = gdk_drawable_get_screen (gwin); + settings = gtk_settings_get_for_screen (screen); + g_object_get (settings, "gtk-enable-tooltips", &tt_enabled, NULL); + if (tt_enabled) + { + g_object_set (settings, "gtk-enable-tooltips", FALSE, NULL); + /* Record that we disabled it so it can be enabled again. */ + g_object_set_data (G_OBJECT (x->ttip_window), "restore-tt", + (gpointer)f); + } + + /* Prevent Gtk+ from hiding tooltip on mouse move and such. */ + g_object_set_data (G_OBJECT + (gtk_widget_get_display (GTK_WIDGET (x->ttip_window))), + "gdk-display-current-tooltip", NULL); + + /* Put out dummy widget in so we can get callbacks for unrealize and + hierarchy-changed. */ + gtk_tooltip_set_custom (x->ttip_widget, widget); + + gtk_tooltip_set_text (x->ttip_widget, SDATA (encoded_string)); + gtk_widget_size_request (GTK_WIDGET (x->ttip_window), &req); + if (width) *width = req.width; + if (height) *height = req.height; + + UNBLOCK_INPUT; + + return 1; +#endif /* USE_GTK_TOOLTIP */ +} + +/* Show the tooltip at ROOT_X and ROOT_Y. + xg_prepare_tooltip must have been called before this function. */ + +void +xg_show_tooltip (FRAME_PTR f, int root_x, int root_y) +{ +#ifdef USE_GTK_TOOLTIP + struct x_output *x = f->output_data.x; + if (x->ttip_window) + { + BLOCK_INPUT; + gtk_window_move (x->ttip_window, root_x, root_y); + gtk_widget_show_all (GTK_WIDGET (x->ttip_window)); + UNBLOCK_INPUT; + } +#endif +} + +/* Hide tooltip if shown. Do nothing if not shown. + Return non-zero if tip was hidden, non-ero if not (i.e. not using + system tooltips). */ + +int +xg_hide_tooltip (FRAME_PTR f) +{ + int ret = 0; +#ifdef USE_GTK_TOOLTIP + if (f->output_data.x->ttip_window) + { + GtkWindow *win = f->output_data.x->ttip_window; + BLOCK_INPUT; + gtk_widget_hide (GTK_WIDGET (win)); + + if (g_object_get_data (G_OBJECT (win), "restore-tt")) + { + GdkWindow *gwin = gtk_widget_get_window (GTK_WIDGET (win)); + GdkScreen *screen = gdk_drawable_get_screen (gwin); + GtkSettings *settings = gtk_settings_get_for_screen (screen); + g_object_set (settings, "gtk-enable-tooltips", TRUE, NULL); + } + UNBLOCK_INPUT; + + ret = 1; + } +#endif + return ret; +} /*********************************************************************** @@ -526,10 +719,9 @@ get_utf8_string (str) F is the frame we shall set geometry for. */ static void -xg_set_geometry (f) - FRAME_PTR f; +xg_set_geometry (FRAME_PTR f) { - if (f->size_hint_flags & USPosition) + if (f->size_hint_flags & (USPosition | PPosition)) { int left = f->left_pos; int xneg = f->size_hint_flags & XNegative; @@ -544,7 +736,7 @@ xg_set_geometry (f) sprintf (geom_str, "=%dx%d%c%d%c%d", FRAME_PIXEL_WIDTH (f), - FRAME_TOTAL_PIXEL_HEIGHT (f), + FRAME_PIXEL_HEIGHT (f), (xneg ? '-' : '+'), left, (yneg ? '-' : '+'), top); @@ -552,17 +744,13 @@ xg_set_geometry (f) geom_str)) fprintf (stderr, "Failed to parse: '%s'\n", geom_str); } - else if (f->size_hint_flags & PPosition) - gtk_window_move (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), - f->left_pos, f->top_pos); } /* Clear under internal border if any. As we use a mix of Gtk+ and X calls and use a GtkFixed widget, this doesn't happen automatically. */ static void -xg_clear_under_internal_border (f) - FRAME_PTR f; +xg_clear_under_internal_border (FRAME_PTR f) { if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0) { @@ -600,20 +788,19 @@ xg_clear_under_internal_border (f) PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */ void -xg_frame_resized (f, pixelwidth, pixelheight) - FRAME_PTR f; - int pixelwidth, pixelheight; +xg_frame_resized (FRAME_PTR f, int pixelwidth, int pixelheight) { int rows, columns; if (pixelwidth == -1 && pixelheight == -1) { - if (FRAME_GTK_WIDGET (f) && GTK_WIDGET_MAPPED (FRAME_GTK_WIDGET (f))) - gdk_window_get_geometry (FRAME_GTK_WIDGET (f)->window, 0, 0, + if (FRAME_GTK_WIDGET (f) && gtk_widget_get_mapped (FRAME_GTK_WIDGET (f))) + gdk_window_get_geometry (gtk_widget_get_window (FRAME_GTK_WIDGET (f)), + 0, 0, &pixelwidth, &pixelheight, 0); else return; } - + rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelheight); columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth); @@ -637,10 +824,7 @@ xg_frame_resized (f, pixelwidth, pixelheight) COLUMNS/ROWS is the size the edit area shall have after the resize. */ void -xg_frame_set_char_size (f, cols, rows) - FRAME_PTR f; - int cols; - int rows; +xg_frame_set_char_size (FRAME_PTR f, int cols, int rows) { int pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f); @@ -661,7 +845,8 @@ xg_frame_set_char_size (f, cols, rows) /* FRAME_TEXT_COLS_TO_PIXEL_WIDTH uses scroll_bar_actual_width, so call it after calculating that value. */ - pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols); + pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols) + + FRAME_TOOLBAR_WIDTH (f); /* Do this before resize, as we don't know yet if we will be resized. */ @@ -698,15 +883,15 @@ xg_frame_set_char_size (f, cols, rows) } } -/* Handle height changes (i.e. add/remove menu/toolbar). +/* Handle height/width changes (i.e. add/remove/move menu/toolbar). The policy is to keep the number of editable lines. */ static void -xg_height_changed (f) - FRAME_PTR f; +xg_height_or_width_changed (FRAME_PTR f) { gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), - FRAME_PIXEL_WIDTH (f), FRAME_TOTAL_PIXEL_HEIGHT (f)); + FRAME_TOTAL_PIXEL_WIDTH (f), + FRAME_TOTAL_PIXEL_HEIGHT (f)); f->output_data.x->hint_flags = 0; x_wm_set_size_hint (f, 0, 0); } @@ -718,9 +903,7 @@ xg_height_changed (f) Return 0 if no widget match WDESC. */ GtkWidget * -xg_win_to_widget (dpy, wdesc) - Display *dpy; - Window wdesc; +xg_win_to_widget (Display *dpy, Window wdesc) { gpointer gdkwin; GtkWidget *gwdesc = 0; @@ -744,24 +927,40 @@ xg_win_to_widget (dpy, wdesc) W is the widget that color will be used for. Used to find colormap. */ static void -xg_pix_to_gcolor (w, pixel, c) - GtkWidget *w; - unsigned long pixel; - GdkColor *c; +xg_pix_to_gcolor (GtkWidget *w, long unsigned int pixel, GdkColor *c) { GdkColormap *map = gtk_widget_get_colormap (w); gdk_colormap_query_color (map, pixel, c); } +/* Callback called when the gtk theme changes. + We notify lisp code so it can fix faces used for region for example. */ + +static void +style_changed_cb (GObject *go, + GParamSpec *spec, + gpointer user_data) +{ + struct input_event event; + GdkDisplay *gdpy = (GdkDisplay *) user_data; + const char *display_name = gdk_display_get_name (gdpy); + + EVENT_INIT (event); + event.kind = CONFIG_CHANGED_EVENT; + event.frame_or_window = make_string (display_name, strlen (display_name)); + /* Theme doesn't change often, so intern is called seldom. */ + event.arg = intern ("theme-name"); + kbd_buffer_store_event (&event); +} + /* Create and set up the GTK widgets for frame F. Return 0 if creation failed, non-zero otherwise. */ int -xg_create_frame_widgets (f) - FRAME_PTR f; +xg_create_frame_widgets (FRAME_PTR f) { GtkWidget *wtop; - GtkWidget *wvbox; + GtkWidget *wvbox, *whbox; GtkWidget *wfixed; GdkColor bg; GtkRcStyle *style; @@ -777,12 +976,14 @@ xg_create_frame_widgets (f) xg_set_screen (wtop, f); wvbox = gtk_vbox_new (FALSE, 0); + whbox = gtk_hbox_new (FALSE, 0); wfixed = gtk_fixed_new (); /* Must have this to place scroll bars */ - if (! wtop || ! wvbox || ! wfixed) + if (! wtop || ! wvbox || ! whbox || ! wfixed) { if (wtop) gtk_widget_destroy (wtop); if (wvbox) gtk_widget_destroy (wvbox); + if (whbox) gtk_widget_destroy (whbox); if (wfixed) gtk_widget_destroy (wfixed); UNBLOCK_INPUT; @@ -803,11 +1004,13 @@ xg_create_frame_widgets (f) FRAME_GTK_OUTER_WIDGET (f) = wtop; FRAME_GTK_WIDGET (f) = wfixed; f->output_data.x->vbox_widget = wvbox; + f->output_data.x->hbox_widget = whbox; - gtk_fixed_set_has_window (GTK_FIXED (wfixed), TRUE); + gtk_widget_set_has_window (wfixed, TRUE); gtk_container_add (GTK_CONTAINER (wtop), wvbox); - gtk_box_pack_end (GTK_BOX (wvbox), wfixed, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (wvbox), whbox, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (whbox), wfixed, TRUE, TRUE, 0); if (FRAME_EXTERNAL_TOOL_BAR (f)) update_frame_tool_bar (f); @@ -866,17 +1069,54 @@ xg_create_frame_widgets (f) style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup (""); gtk_widget_modify_style (wfixed, style); - /* GTK does not set any border, and they look bad with GTK. */ - /* That they look bad is no excuse for imposing this here. --Stef - It should be done by providing the proper default in Fx_create_Frame. - f->border_width = 0; - f->internal_border_width = 0; */ +#ifdef USE_GTK_TOOLTIP + /* Steal a tool tip window we can move ourselves. */ + f->output_data.x->ttip_widget = 0; + f->output_data.x->ttip_lbl = 0; + f->output_data.x->ttip_window = 0; + gtk_widget_set_tooltip_text (wtop, "Dummy text"); + g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f); +#endif + + { + GdkScreen *screen = gtk_widget_get_screen (wtop); + GtkSettings *gs = gtk_settings_get_for_screen (screen); + /* Only connect this signal once per screen. */ + if (! g_signal_handler_find (G_OBJECT (gs), + G_SIGNAL_MATCH_FUNC, + 0, 0, 0, + G_CALLBACK (style_changed_cb), + 0)) + { + g_signal_connect (G_OBJECT (gs), "notify::gtk-theme-name", + G_CALLBACK (style_changed_cb), + gdk_screen_get_display (screen)); + } + } UNBLOCK_INPUT; return 1; } +void +xg_free_frame_widgets (FRAME_PTR f) +{ + if (FRAME_GTK_OUTER_WIDGET (f)) + { + struct x_output *x = f->output_data.x; + gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f)); + FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */ + FRAME_GTK_OUTER_WIDGET (f) = 0; +#ifdef USE_GTK_TOOLTIP + if (x->ttip_lbl) + gtk_widget_destroy (x->ttip_lbl); + if (x->ttip_widget) + g_object_unref (G_OBJECT (x->ttip_widget)); +#endif + } +} + /* Set the normal size hints for the window manager, for frame F. FLAGS is the flags word to use--or 0 meaning preserve the flags that the window now has. @@ -884,10 +1124,7 @@ xg_create_frame_widgets (f) flag (this is useful when FLAGS is 0). */ void -x_wm_set_size_hint (f, flags, user_position) - FRAME_PTR f; - long flags; - int user_position; +x_wm_set_size_hint (FRAME_PTR f, long int flags, int user_position) { /* Must use GTK routines here, otherwise GTK resets the size hints to its own defaults. */ @@ -920,7 +1157,7 @@ x_wm_set_size_hint (f, flags, user_position) size_hints.height_inc = FRAME_LINE_HEIGHT (f); hint_flags |= GDK_HINT_BASE_SIZE; - base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0); + base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0) + FRAME_TOOLBAR_WIDTH (f); base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f); @@ -956,10 +1193,6 @@ x_wm_set_size_hint (f, flags, user_position) else if (win_gravity == StaticGravity) size_hints.win_gravity = GDK_GRAVITY_STATIC; - if (flags & PPosition) hint_flags |= GDK_HINT_POS; - if (flags & USPosition) hint_flags |= GDK_HINT_USER_POS; - if (flags & USSize) hint_flags |= GDK_HINT_USER_SIZE; - if (user_position) { hint_flags &= ~GDK_HINT_POS; @@ -987,9 +1220,7 @@ x_wm_set_size_hint (f, flags, user_position) BG is the pixel value to change to. */ void -xg_set_background_color (f, bg) - FRAME_PTR f; - unsigned long bg; +xg_set_background_color (FRAME_PTR f, long unsigned int bg) { if (FRAME_GTK_WIDGET (f)) { @@ -1007,10 +1238,7 @@ xg_set_background_color (f, bg) functions so GTK does not overwrite the icon. */ void -xg_set_frame_icon (f, icon_pixmap, icon_mask) - FRAME_PTR f; - Pixmap icon_pixmap; - Pixmap icon_mask; +xg_set_frame_icon (FRAME_PTR f, Pixmap icon_pixmap, Pixmap icon_mask) { GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f)); GdkPixmap *gpix = gdk_pixmap_foreign_new_for_display (gdpy, icon_pixmap); @@ -1028,10 +1256,10 @@ xg_set_frame_icon (f, icon_pixmap, icon_mask) /* Return the dialog title to use for a dialog of type KEY. This is the encoding used by lwlib. We use the same for GTK. */ -static char * +static const char * get_dialog_title (char key) { - char *title = ""; + const char *title = ""; switch (key) { case 'E': case 'e': @@ -1070,10 +1298,7 @@ get_dialog_title (char key) Returns TRUE to end propagation of event. */ static gboolean -dialog_delete_callback (w, event, user_data) - GtkWidget *w; - GdkEvent *event; - gpointer user_data; +dialog_delete_callback (GtkWidget *w, GdkEvent *event, gpointer user_data) { gtk_widget_unmap (w); return TRUE; @@ -1087,20 +1312,20 @@ dialog_delete_callback (w, event, user_data) Returns the GTK dialog widget. */ static GtkWidget * -create_dialog (wv, select_cb, deactivate_cb) - widget_value *wv; - GCallback select_cb; - GCallback deactivate_cb; +create_dialog (widget_value *wv, + GCallback select_cb, + GCallback deactivate_cb) { - char *title = get_dialog_title (wv->name[0]); + const char *title = get_dialog_title (wv->name[0]); int total_buttons = wv->name[1] - '0'; int right_buttons = wv->name[4] - '0'; int left_buttons; int button_nr = 0; int button_spacing = 10; GtkWidget *wdialog = gtk_dialog_new (); + GtkDialog *wd = GTK_DIALOG (wdialog); + GtkBox *cur_box = GTK_BOX (gtk_dialog_get_action_area (wd)); widget_value *item; - GtkBox *cur_box; GtkWidget *wvbox; GtkWidget *whbox_up; GtkWidget *whbox_down; @@ -1115,7 +1340,6 @@ create_dialog (wv, select_cb, deactivate_cb) gtk_window_set_title (GTK_WINDOW (wdialog), title); gtk_widget_set_name (wdialog, "emacs-dialog"); - cur_box = GTK_BOX (GTK_DIALOG (wdialog)->action_area); if (make_two_rows) { @@ -1147,21 +1371,18 @@ create_dialog (wv, select_cb, deactivate_cb) if (item->name && strcmp (item->name, "message") == 0) { + GtkBox *wvbox = GTK_BOX (gtk_dialog_get_content_area (wd)); /* This is the text part of the dialog. */ w = gtk_label_new (utf8_label); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->vbox), - gtk_label_new (""), - FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->vbox), w, - TRUE, TRUE, 0); + gtk_box_pack_start (wvbox, gtk_label_new (""), FALSE, FALSE, 0); + gtk_box_pack_start (wvbox, w, TRUE, TRUE, 0); gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5); /* Try to make dialog look better. Must realize first so the widget can calculate the size it needs. */ gtk_widget_realize (w); gtk_widget_size_request (w, &req); - gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (wdialog)->vbox), - req.height); + gtk_box_set_spacing (wvbox, req.height); if (item->value && strlen (item->value) > 0) button_spacing = 2*req.width/strlen (item->value); } @@ -1188,7 +1409,7 @@ create_dialog (wv, select_cb, deactivate_cb) } } - if (utf8_label && utf8_label != item->value) + if (utf8_label) g_free (utf8_label); } @@ -1208,12 +1429,9 @@ struct xg_dialog_data USER_DATA is what we passed in to g_signal_connect. */ static void -xg_dialog_response_cb (w, - response, - user_data) - GtkDialog *w; - gint response; - gpointer user_data; +xg_dialog_response_cb (GtkDialog *w, + gint response, + gpointer user_data) { struct xg_dialog_data *dd = (struct xg_dialog_data *)user_data; dd->response = response; @@ -1224,8 +1442,7 @@ xg_dialog_response_cb (w, /* Destroy the dialog. This makes it pop down. */ static Lisp_Object -pop_down_dialog (arg) - Lisp_Object arg; +pop_down_dialog (Lisp_Object arg) { struct Lisp_Save_Value *p = XSAVE_VALUE (arg); struct xg_dialog_data *dd = (struct xg_dialog_data *) p->pointer; @@ -1236,7 +1453,7 @@ pop_down_dialog (arg) g_main_loop_quit (dd->loop); g_main_loop_unref (dd->loop); - + UNBLOCK_INPUT; return Qnil; @@ -1246,8 +1463,7 @@ pop_down_dialog (arg) We pass in DATA as gpointer* so we can use this as a callback. */ static gboolean -xg_maybe_add_timer (data) - gpointer data; +xg_maybe_add_timer (gpointer data) { struct xg_dialog_data *dd = (struct xg_dialog_data *) data; EMACS_TIME next_time = timer_check (1); @@ -1265,16 +1481,13 @@ xg_maybe_add_timer (data) return FALSE; } - + /* Pops up a modal dialog W and waits for response. We don't use gtk_dialog_run because we want to process emacs timers. The dialog W is not destroyed when this function returns. */ static int -xg_dialog_run (f, w) - FRAME_PTR f; - GtkWidget *w; - +xg_dialog_run (FRAME_PTR f, GtkWidget *w) { int count = SPECPDL_INDEX (); struct xg_dialog_data dd; @@ -1302,7 +1515,7 @@ xg_dialog_run (f, w) (void) xg_maybe_add_timer (&dd); g_main_loop_run (dd.loop); - + dd.w = 0; unbind_to (count, Qnil); @@ -1317,33 +1530,23 @@ xg_dialog_run (f, w) Return zero if not. */ int -xg_uses_old_file_dialog () +xg_uses_old_file_dialog (void) { -#ifdef HAVE_GTK_FILE_BOTH - extern int x_gtk_use_old_file_dialog; - return x_gtk_use_old_file_dialog; -#else /* ! HAVE_GTK_FILE_BOTH */ - #ifdef HAVE_GTK_FILE_SELECTION_NEW - return 1; + return x_gtk_use_old_file_dialog; #else return 0; #endif - -#endif /* ! HAVE_GTK_FILE_BOTH */ } -typedef char * (*xg_get_file_func) P_ ((GtkWidget *)); - -#ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW +typedef char * (*xg_get_file_func) (GtkWidget *); /* Return the selected file for file chooser dialog W. The returned string must be free:d. */ static char * -xg_get_file_name_from_chooser (w) - GtkWidget *w; +xg_get_file_name_from_chooser (GtkWidget *w) { return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w)); } @@ -1352,9 +1555,7 @@ xg_get_file_name_from_chooser (w) WIDGET is the toggle widget, DATA is the file chooser dialog. */ static void -xg_toggle_visibility_cb (widget, data) - GtkWidget *widget; - gpointer data; +xg_toggle_visibility_cb (GtkWidget *widget, gpointer data) { GtkFileChooser *dialog = GTK_FILE_CHOOSER (data); gboolean visible; @@ -1370,13 +1571,8 @@ xg_toggle_visibility_cb (widget, data) changes that property by right clicking in the file list. */ static void -xg_toggle_notify_cb (gobject, arg1, user_data) - GObject *gobject; - GParamSpec *arg1; - gpointer user_data; +xg_toggle_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data) { - extern int x_gtk_show_hidden_files; - if (strcmp (arg1->name, "show-hidden") == 0) { GtkWidget *wtoggle = GTK_WIDGET (user_data); @@ -1411,13 +1607,11 @@ xg_toggle_notify_cb (gobject, arg1, user_data) Returns the created widget. */ static GtkWidget * -xg_get_file_with_chooser (f, prompt, default_filename, - mustmatch_p, only_dir_p, func) - FRAME_PTR f; - char *prompt; - char *default_filename; - int mustmatch_p, only_dir_p; - xg_get_file_func *func; +xg_get_file_with_chooser (FRAME_PTR f, + char *prompt, + char *default_filename, + int mustmatch_p, int only_dir_p, + xg_get_file_func *func) { char message[1024]; @@ -1426,9 +1620,6 @@ xg_get_file_with_chooser (f, prompt, default_filename, GtkFileChooserAction action = (mustmatch_p ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE); - extern int x_gtk_show_hidden_files; - extern int x_gtk_file_dialog_help_text; - if (only_dir_p) action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; @@ -1513,7 +1704,6 @@ xg_get_file_with_chooser (f, prompt, default_filename, *func = xg_get_file_name_from_chooser; return filewin; } -#endif /* HAVE_GTK_FILE_CHOOSER_DIALOG_NEW */ #ifdef HAVE_GTK_FILE_SELECTION_NEW @@ -1521,8 +1711,7 @@ xg_get_file_with_chooser (f, prompt, default_filename, The returned string must be free:d. */ static char * -xg_get_file_name_from_selector (w) - GtkWidget *w; +xg_get_file_name_from_selector (GtkWidget *w) { GtkFileSelection *filesel = GTK_FILE_SELECTION (w); return xstrdup ((char*) gtk_file_selection_get_filename (filesel)); @@ -1539,13 +1728,11 @@ xg_get_file_name_from_selector (w) Returns the created widget. */ static GtkWidget * -xg_get_file_with_selection (f, prompt, default_filename, - mustmatch_p, only_dir_p, func) - FRAME_PTR f; - char *prompt; - char *default_filename; - int mustmatch_p, only_dir_p; - xg_get_file_func *func; +xg_get_file_with_selection (FRAME_PTR f, + char *prompt, + char *default_filename, + int mustmatch_p, int only_dir_p, + xg_get_file_func *func) { GtkWidget *filewin; GtkFileSelection *filesel; @@ -1583,11 +1770,11 @@ xg_get_file_with_selection (f, prompt, default_filename, The returned string must be freed by the caller. */ char * -xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p) - FRAME_PTR f; - char *prompt; - char *default_filename; - int mustmatch_p, only_dir_p; +xg_get_file_name (FRAME_PTR f, + char *prompt, + char *default_filename, + int mustmatch_p, + int only_dir_p) { GtkWidget *w = 0; char *fn = 0; @@ -1601,7 +1788,7 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p) sigblock (sigmask (__SIGRTMIN)); #endif /* HAVE_GTK_AND_PTHREAD */ -#ifdef HAVE_GTK_FILE_BOTH +#ifdef HAVE_GTK_FILE_SELECTION_NEW if (xg_uses_old_file_dialog ()) w = xg_get_file_with_selection (f, prompt, default_filename, @@ -1610,18 +1797,10 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p) w = xg_get_file_with_chooser (f, prompt, default_filename, mustmatch_p, only_dir_p, &func); -#else /* not HAVE_GTK_FILE_BOTH */ - -#ifdef HAVE_GTK_FILE_SELECTION_NEW - w = xg_get_file_with_selection (f, prompt, default_filename, - mustmatch_p, only_dir_p, &func); -#endif -#ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW +#else /* not HAVE_GTK_FILE_SELECTION_NEW */ w = xg_get_file_with_chooser (f, prompt, default_filename, mustmatch_p, only_dir_p, &func); -#endif - -#endif /* HAVE_GTK_FILE_BOTH */ +#endif /* not HAVE_GTK_FILE_SELECTION_NEW */ gtk_widget_set_name (w, "emacs-filedialog"); @@ -1649,9 +1828,7 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p) DEFAULT_NAME, if non-zero, is the default font name. */ char * -xg_get_font_name (f, default_name) - FRAME_PTR f; - char *default_name; +xg_get_font_name (FRAME_PTR f, const char *default_name) { GtkWidget *w; char *fontname = NULL; @@ -1716,10 +1893,7 @@ static xg_list_node xg_menu_item_cb_list; allocated xg_menu_cb_data if CL_DATA is NULL. */ static xg_menu_cb_data * -make_cl_data (cl_data, f, highlight_cb) - xg_menu_cb_data *cl_data; - FRAME_PTR f; - GCallback highlight_cb; +make_cl_data (xg_menu_cb_data *cl_data, FRAME_PTR f, GCallback highlight_cb) { if (! cl_data) { @@ -1750,10 +1924,9 @@ make_cl_data (cl_data, f, highlight_cb) creating the menu bar. */ static void -update_cl_data (cl_data, f, highlight_cb) - xg_menu_cb_data *cl_data; - FRAME_PTR f; - GCallback highlight_cb; +update_cl_data (xg_menu_cb_data *cl_data, + FRAME_PTR f, + GCallback highlight_cb) { if (cl_data) { @@ -1768,8 +1941,7 @@ update_cl_data (cl_data, f, highlight_cb) If reference count is zero, free CL_DATA. */ static void -unref_cl_data (cl_data) - xg_menu_cb_data *cl_data; +unref_cl_data (xg_menu_cb_data *cl_data) { if (cl_data && cl_data->ref_count > 0) { @@ -1785,7 +1957,7 @@ unref_cl_data (cl_data) /* Function that marks all lisp data during GC. */ void -xg_mark_data () +xg_mark_data (void) { xg_list_node *iter; @@ -1807,9 +1979,7 @@ xg_mark_data () CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */ static void -menuitem_destroy_callback (w, client_data) - GtkWidget *w; - gpointer client_data; +menuitem_destroy_callback (GtkWidget *w, gpointer client_data) { if (client_data) { @@ -1827,10 +1997,9 @@ menuitem_destroy_callback (w, client_data) Returns FALSE to tell GTK to keep processing this event. */ static gboolean -menuitem_highlight_callback (w, event, client_data) - GtkWidget *w; - GdkEventCrossing *event; - gpointer client_data; +menuitem_highlight_callback (GtkWidget *w, + GdkEventCrossing *event, + gpointer client_data) { GdkEvent ev; GtkWidget *subwidget; @@ -1858,9 +2027,7 @@ menuitem_highlight_callback (w, event, client_data) CLIENT_DATA points to the xg_menu_cb_data associated with W. */ static void -menu_destroy_callback (w, client_data) - GtkWidget *w; - gpointer client_data; +menu_destroy_callback (GtkWidget *w, gpointer client_data) { unref_cl_data ((xg_menu_cb_data*) client_data); } @@ -1871,9 +2038,7 @@ menu_destroy_callback (w, client_data) Returns the GtkHBox. */ static GtkWidget * -make_widget_for_menu_item (utf8_label, utf8_key) - char *utf8_label; - char *utf8_key; +make_widget_for_menu_item (const char *utf8_label, const char *utf8_key) { GtkWidget *wlbl; GtkWidget *wkey; @@ -1911,11 +2076,10 @@ make_widget_for_menu_item (utf8_label, utf8_key) but the MacOS X version doesn't either, so I guess that is OK. */ static GtkWidget * -make_menu_item (utf8_label, utf8_key, item, group) - char *utf8_label; - char *utf8_key; - widget_value *item; - GSList **group; +make_menu_item (const char *utf8_label, + const char *utf8_key, + widget_value *item, + GSList **group) { GtkWidget *w; GtkWidget *wtoadd = 0; @@ -1957,60 +2121,12 @@ make_menu_item (utf8_label, utf8_key, item, group) return w; } -/* Return non-zero if LABEL specifies a separator (GTK only has one - separator type) */ - -static const char* separator_names[] = { - "space", - "no-line", - "single-line", - "double-line", - "single-dashed-line", - "double-dashed-line", - "shadow-etched-in", - "shadow-etched-out", - "shadow-etched-in-dash", - "shadow-etched-out-dash", - "shadow-double-etched-in", - "shadow-double-etched-out", - "shadow-double-etched-in-dash", - "shadow-double-etched-out-dash", - 0, -}; - -static int -xg_separator_p (char *label) -{ - if (! label) return 0; - else if (strlen (label) > 3 - && strncmp (label, "--", 2) == 0 - && label[2] != '-') - { - int i; - - label += 2; - for (i = 0; separator_names[i]; ++i) - if (strcmp (label, separator_names[i]) == 0) - return 1; - } - else - { - /* Old-style separator, maybe. It's a separator if it contains - only dashes. */ - while (*label == '-') - ++label; - if (*label == 0) return 1; - } - - return 0; -} - static int xg_detached_menus; /* Returns non-zero if there are detached menus. */ int -xg_have_tear_offs () +xg_have_tear_offs (void) { return xg_detached_menus > 0; } @@ -2021,9 +2137,7 @@ xg_have_tear_offs () CLIENT_DATA is not used. */ static void -tearoff_remove (widget, client_data) - GtkWidget *widget; - gpointer client_data; +tearoff_remove (GtkWidget *widget, gpointer client_data) { if (xg_detached_menus > 0) --xg_detached_menus; } @@ -2034,9 +2148,7 @@ tearoff_remove (widget, client_data) CLIENT_DATA is not used. */ static void -tearoff_activate (widget, client_data) - GtkWidget *widget; - gpointer client_data; +tearoff_activate (GtkWidget *widget, gpointer client_data) { GtkWidget *menu = gtk_widget_get_parent (widget); if (gtk_menu_get_tearoff_state (GTK_MENU (menu))) @@ -2064,13 +2176,12 @@ tearoff_activate (widget, client_data) Returns the created GtkWidget. */ static GtkWidget * -xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group) - widget_value *item; - FRAME_PTR f; - GCallback select_cb; - GCallback highlight_cb; - xg_menu_cb_data *cl_data; - GSList **group; +xg_create_one_menuitem (widget_value *item, + FRAME_PTR f, + GCallback select_cb, + GCallback highlight_cb, + xg_menu_cb_data *cl_data, + GSList **group) { char *utf8_label; char *utf8_key; @@ -2082,8 +2193,8 @@ xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group) w = make_menu_item (utf8_label, utf8_key, item, group); - if (utf8_label && utf8_label != item->name) g_free (utf8_label); - if (utf8_key && utf8_key != item->key) g_free (utf8_key); + if (utf8_label) g_free (utf8_label); + if (utf8_key) g_free (utf8_key); cb_data = xmalloc (sizeof (xg_menu_item_cb_data)); @@ -2113,10 +2224,6 @@ xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group) return w; } -static GtkWidget *create_menus P_ ((widget_value *, FRAME_PTR, GCallback, - GCallback, GCallback, int, int, int, - GtkWidget *, xg_menu_cb_data *, char *)); - /* Create a full menu tree specified by DATA. F is the frame the created menu belongs to. SELECT_CB is the callback to use when a menu item is selected. @@ -2140,19 +2247,17 @@ static GtkWidget *create_menus P_ ((widget_value *, FRAME_PTR, GCallback, This function calls itself to create submenus. */ static GtkWidget * -create_menus (data, f, select_cb, deactivate_cb, highlight_cb, - pop_up_p, menu_bar_p, add_tearoff_p, topmenu, cl_data, name) - widget_value *data; - FRAME_PTR f; - GCallback select_cb; - GCallback deactivate_cb; - GCallback highlight_cb; - int pop_up_p; - int menu_bar_p; - int add_tearoff_p; - GtkWidget *topmenu; - xg_menu_cb_data *cl_data; - char *name; +create_menus (widget_value *data, + FRAME_PTR f, + GCallback select_cb, + GCallback deactivate_cb, + GCallback highlight_cb, + int pop_up_p, + int menu_bar_p, + int add_tearoff_p, + GtkWidget *topmenu, + xg_menu_cb_data *cl_data, + const char *name) { widget_value *item; GtkWidget *wmenu = topmenu; @@ -2214,7 +2319,7 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb, GtkWidget *w; if (pop_up_p && !item->contents && !item->call_data - && !xg_separator_p (item->name)) + && !menu_separator_name_p (item->name)) { char *utf8_label; /* A title for a popup. We do the same as GTK does when @@ -2225,9 +2330,9 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb, gtk_menu_set_title (GTK_MENU (wmenu), utf8_label); w = gtk_menu_item_new_with_label (utf8_label); gtk_widget_set_sensitive (w, FALSE); - if (utf8_label && utf8_label != item->name) g_free (utf8_label); + if (utf8_label) g_free (utf8_label); } - else if (xg_separator_p (item->name)) + else if (menu_separator_name_p (item->name)) { group = NULL; /* GTK only have one separator type. */ @@ -2284,15 +2389,9 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb, Returns the widget created. */ GtkWidget * -xg_create_widget (type, name, f, val, - select_cb, deactivate_cb, highlight_cb) - char *type; - char *name; - FRAME_PTR f; - widget_value *val; - GCallback select_cb; - GCallback deactivate_cb; - GCallback highlight_cb; +xg_create_widget (const char *type, const char *name, FRAME_PTR f, widget_value *val, + GCallback select_cb, GCallback deactivate_cb, + GCallback highlight_cb) { GtkWidget *w = 0; int menu_bar_p = strcmp (type, "menubar") == 0; @@ -2343,19 +2442,16 @@ xg_create_widget (type, name, f, val, /* Return the label for menu item WITEM. */ static const char * -xg_get_menu_item_label (witem) - GtkMenuItem *witem; +xg_get_menu_item_label (GtkMenuItem *witem) { - GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem))); + GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem)); return gtk_label_get_label (wlabel); } /* Return non-zero if the menu item WITEM has the text LABEL. */ static int -xg_item_label_same_p (witem, label) - GtkMenuItem *witem; - char *label; +xg_item_label_same_p (GtkMenuItem *witem, const char *label) { int is_same = 0; char *utf8_label = get_utf8_string (label); @@ -2366,7 +2462,7 @@ xg_item_label_same_p (witem, label) else if (old_label && utf8_label) is_same = strcmp (utf8_label, old_label) == 0; - if (utf8_label && utf8_label != label) g_free (utf8_label); + if (utf8_label) g_free (utf8_label); return is_same; } @@ -2374,8 +2470,7 @@ xg_item_label_same_p (witem, label) /* Destroy widgets in LIST. */ static void -xg_destroy_widgets (list) - GList *list; +xg_destroy_widgets (GList *list) { GList *iter; @@ -2401,18 +2496,16 @@ xg_destroy_widgets (list) This function calls itself to walk through the menu bar names. */ static void -xg_update_menubar (menubar, f, list, iter, pos, val, - select_cb, deactivate_cb, highlight_cb, cl_data) - GtkWidget *menubar; - FRAME_PTR f; - GList **list; - GList *iter; - int pos; - widget_value *val; - GCallback select_cb; - GCallback deactivate_cb; - GCallback highlight_cb; - xg_menu_cb_data *cl_data; +xg_update_menubar (GtkWidget *menubar, + FRAME_PTR f, + GList **list, + GList *iter, + int pos, + widget_value *val, + GCallback select_cb, + GCallback deactivate_cb, + GCallback highlight_cb, + xg_menu_cb_data *cl_data) { if (! iter && ! val) return; @@ -2479,7 +2572,7 @@ xg_update_menubar (menubar, f, list, iter, pos, val, New: A C Remove B. */ - gtk_widget_ref (GTK_WIDGET (witem)); + g_object_ref (G_OBJECT (witem)); gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem)); gtk_widget_destroy (GTK_WIDGET (witem)); @@ -2504,7 +2597,7 @@ xg_update_menubar (menubar, f, list, iter, pos, val, Rename X to B (minibuf to C-mode menu). If the X menu hasn't been invoked, the menu under B is up to date when leaving the minibuffer. */ - GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem))); + GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem)); char *utf8_label = get_utf8_string (val->name); GtkWidget *submenu = gtk_menu_item_get_submenu (witem); @@ -2516,6 +2609,7 @@ xg_update_menubar (menubar, f, list, iter, pos, val, /* Set the title of the detached window. */ gtk_menu_set_title (GTK_MENU (submenu), utf8_label); + if (utf8_label) g_free (utf8_label); iter = g_list_next (iter); val = val->next; ++pos; @@ -2528,7 +2622,7 @@ xg_update_menubar (menubar, f, list, iter, pos, val, Insert X. */ int nr = pos; - GList *group = 0; + GSList *group = 0; GtkWidget *w = xg_create_one_menuitem (val, f, select_cb, @@ -2561,11 +2655,11 @@ xg_update_menubar (menubar, f, list, iter, pos, val, New: A C B Move C before B */ - gtk_widget_ref (GTK_WIDGET (witem2)); + g_object_ref (G_OBJECT (witem2)); gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem2)); gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), GTK_WIDGET (witem2), pos); - gtk_widget_unref (GTK_WIDGET (witem2)); + g_object_unref (G_OBJECT (witem2)); g_list_free (*list); *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar)); @@ -2587,12 +2681,11 @@ xg_update_menubar (menubar, f, list, iter, pos, val, CL_DATA is the data to set in the widget for menu invocation. */ static void -xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data) - widget_value *val; - GtkWidget *w; - GCallback select_cb; - GCallback highlight_cb; - xg_menu_cb_data *cl_data; +xg_update_menu_item (widget_value *val, + GtkWidget *w, + GCallback select_cb, + GCallback highlight_cb, + xg_menu_cb_data *cl_data) { GtkWidget *wchild; GtkLabel *wlbl = 0; @@ -2603,7 +2696,7 @@ xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data) const char *old_key = 0; xg_menu_item_cb_data *cb_data; - wchild = gtk_bin_get_child (GTK_BIN (w)); + wchild = XG_BIN_CHILD (w); utf8_label = get_utf8_string (val->name); utf8_key = get_utf8_string (val->key); @@ -2619,9 +2712,10 @@ xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data) if (! utf8_key) { /* Remove the key and keep just the label. */ - gtk_widget_ref (GTK_WIDGET (wlbl)); + g_object_ref (G_OBJECT (wlbl)); gtk_container_remove (GTK_CONTAINER (w), wchild); gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl)); + g_object_unref (G_OBJECT (wlbl)); wkey = 0; } @@ -2655,12 +2749,12 @@ xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data) if (! old_label || strcmp (utf8_label, old_label) != 0) gtk_label_set_text (wlbl, utf8_label); - if (utf8_key && utf8_key != val->key) g_free (utf8_key); - if (utf8_label && utf8_label != val->name) g_free (utf8_label); + if (utf8_key) g_free (utf8_key); + if (utf8_label) g_free (utf8_label); - if (! val->enabled && GTK_WIDGET_SENSITIVE (w)) + if (! val->enabled && gtk_widget_get_sensitive (w)) gtk_widget_set_sensitive (w, FALSE); - else if (val->enabled && ! GTK_WIDGET_SENSITIVE (w)) + else if (val->enabled && ! gtk_widget_get_sensitive (w)) gtk_widget_set_sensitive (w, TRUE); cb_data = (xg_menu_item_cb_data*) g_object_get_data (G_OBJECT (w), @@ -2691,9 +2785,7 @@ xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data) /* Update the toggle menu item W so it corresponds to VAL. */ static void -xg_update_toggle_item (val, w) - widget_value *val; - GtkWidget *w; +xg_update_toggle_item (widget_value *val, GtkWidget *w) { gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected); } @@ -2701,9 +2793,7 @@ xg_update_toggle_item (val, w) /* Update the radio menu item W so it corresponds to VAL. */ static void -xg_update_radio_item (val, w) - widget_value *val; - GtkWidget *w; +xg_update_radio_item (widget_value *val, GtkWidget *w) { gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected); } @@ -2721,15 +2811,13 @@ xg_update_radio_item (val, w) was NULL. */ static GtkWidget * -xg_update_submenu (submenu, f, val, - select_cb, deactivate_cb, highlight_cb, cl_data) - GtkWidget *submenu; - FRAME_PTR f; - widget_value *val; - GCallback select_cb; - GCallback deactivate_cb; - GCallback highlight_cb; - xg_menu_cb_data *cl_data; +xg_update_submenu (GtkWidget *submenu, + FRAME_PTR f, + widget_value *val, + GCallback select_cb, + GCallback deactivate_cb, + GCallback highlight_cb, + xg_menu_cb_data *cl_data) { GtkWidget *newsub = submenu; GList *list = 0; @@ -2767,7 +2855,7 @@ xg_update_submenu (submenu, f, val, if (GTK_IS_SEPARATOR_MENU_ITEM (w)) { - if (! xg_separator_p (cur->name)) + if (! menu_separator_name_p (cur->name)) break; } else if (GTK_IS_CHECK_MENU_ITEM (w)) @@ -2790,7 +2878,7 @@ xg_update_submenu (submenu, f, val, GtkWidget *sub; if (cur->button_type != BUTTON_TYPE_NONE || - xg_separator_p (cur->name)) + menu_separator_name_p (cur->name)) break; xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data); @@ -2799,8 +2887,8 @@ xg_update_submenu (submenu, f, val, if (sub && ! cur->contents) { /* Not a submenu anymore. */ - gtk_widget_ref (sub); - gtk_menu_item_remove_submenu (witem); + g_object_ref (G_OBJECT (sub)); + remove_submenu (witem); gtk_widget_destroy (sub); } else if (cur->contents) @@ -2864,15 +2952,10 @@ xg_update_submenu (submenu, f, val, HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */ void -xg_modify_menubar_widgets (menubar, f, val, deep_p, - select_cb, deactivate_cb, highlight_cb) - GtkWidget *menubar; - FRAME_PTR f; - widget_value *val; - int deep_p; - GCallback select_cb; - GCallback deactivate_cb; - GCallback highlight_cb; +xg_modify_menubar_widgets (GtkWidget *menubar, FRAME_PTR f, widget_value *val, + int deep_p, + GCallback select_cb, GCallback deactivate_cb, + GCallback highlight_cb) { xg_menu_cb_data *cl_data; GList *list = gtk_container_get_children (GTK_CONTAINER (menubar)); @@ -2901,7 +2984,7 @@ xg_modify_menubar_widgets (menubar, f, val, deep_p, GList *iter; GtkWidget *sub = 0; GtkWidget *newsub; - GtkMenuItem *witem; + GtkMenuItem *witem = 0; /* Find sub menu that corresponds to val and update it. */ for (iter = list ; iter; iter = g_list_next (iter)) @@ -2924,7 +3007,7 @@ xg_modify_menubar_widgets (menubar, f, val, deep_p, /* sub may still be NULL. If we just updated non deep and added a new menu bar item, it has no sub menu yet. So we set the newly created sub menu under witem. */ - if (newsub != sub) + if (newsub != sub && witem != 0) { xg_set_screen (newsub, f); gtk_menu_item_set_submenu (witem, newsub); @@ -2936,17 +3019,33 @@ xg_modify_menubar_widgets (menubar, f, val, deep_p, gtk_widget_show_all (menubar); } +/* Callback called when the menu bar W is mapped. + Used to find the height of the menu bar if we didn't get it + after showing the widget. */ + +static void +menubar_map_cb (GtkWidget *w, gpointer user_data) +{ + GtkRequisition req; + FRAME_PTR f = (FRAME_PTR) user_data; + gtk_widget_size_request (w, &req); + if (FRAME_MENUBAR_HEIGHT (f) != req.height) + { + FRAME_MENUBAR_HEIGHT (f) = req.height; + xg_height_or_width_changed (f); + } +} + /* Recompute all the widgets of frame F, when the menu bar has been changed. Value is non-zero if widgets were updated. */ int -xg_update_frame_menubar (f) - FRAME_PTR f; +xg_update_frame_menubar (FRAME_PTR f) { struct x_output *x = f->output_data.x; GtkRequisition req; - if (!x->menubar_widget || GTK_WIDGET_MAPPED (x->menubar_widget)) + if (!x->menubar_widget || gtk_widget_get_mapped (x->menubar_widget)) return 0; if (x->menubar_widget && gtk_widget_get_parent (x->menubar_widget)) @@ -2958,10 +3057,20 @@ xg_update_frame_menubar (f) FALSE, FALSE, 0); gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->menubar_widget, 0); + g_signal_connect (x->menubar_widget, "map", G_CALLBACK (menubar_map_cb), f); gtk_widget_show_all (x->menubar_widget); gtk_widget_size_request (x->menubar_widget, &req); - FRAME_MENUBAR_HEIGHT (f) = req.height; - xg_height_changed (f); + + /* If menu bar doesn't know its height yet, cheat a little so the frame + doesn't jump so much when resized later in menubar_map_cb. */ + if (req.height == 0) + req.height = 23; + + if (FRAME_MENUBAR_HEIGHT (f) != req.height) + { + FRAME_MENUBAR_HEIGHT (f) = req.height; + xg_height_or_width_changed (f); + } UNBLOCK_INPUT; return 1; @@ -2971,8 +3080,7 @@ xg_update_frame_menubar (f) This is used when deleting a frame, and when turning off the menu bar. */ void -free_frame_menubar (f) - FRAME_PTR f; +free_frame_menubar (FRAME_PTR f) { struct x_output *x = f->output_data.x; @@ -2985,7 +3093,7 @@ free_frame_menubar (f) the container. */ x->menubar_widget = 0; FRAME_MENUBAR_HEIGHT (f) = 0; - xg_height_changed (f); + xg_height_or_width_changed (f); UNBLOCK_INPUT; } } @@ -3032,7 +3140,7 @@ xg_event_is_for_menubar (FRAME_PTR f, XEvent *event) for (iter = list ; iter; iter = g_list_next (iter)) { GtkWidget *w = GTK_WIDGET (iter->data); - if (GTK_WIDGET_MAPPED (w) && gtk_widget_intersect (w, &rec, NULL)) + if (gtk_widget_get_mapped (w) && gtk_widget_intersect (w, &rec, NULL)) break; } g_list_free (list); @@ -3069,8 +3177,7 @@ static struct /* Store the widget pointer W in id_to_widget and return the integer index. */ static int -xg_store_widget_in_map (w) - GtkWidget *w; +xg_store_widget_in_map (GtkWidget *w) { int i; @@ -3108,8 +3215,7 @@ xg_store_widget_in_map (w) Called when scroll bar is destroyed. */ static void -xg_remove_widget_from_map (idx) - int idx; +xg_remove_widget_from_map (int idx) { if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0) { @@ -3121,8 +3227,7 @@ xg_remove_widget_from_map (idx) /* Get the widget pointer at IDX from id_to_widget. */ static GtkWidget * -xg_get_widget_from_map (idx) - int idx; +xg_get_widget_from_map (int idx) { if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0) return id_to_widget.widgets[idx]; @@ -3134,9 +3239,7 @@ xg_get_widget_from_map (idx) Return -1 if WID not in id_to_widget. */ int -xg_get_scroll_id_for_window (dpy, wid) - Display *dpy; - Window wid; +xg_get_scroll_id_for_window (Display *dpy, Window wid) { int idx; GtkWidget *w; @@ -3158,9 +3261,7 @@ xg_get_scroll_id_for_window (dpy, wid) We free pointer to last scroll bar values here and remove the index. */ static void -xg_gtk_scroll_destroy (widget, data) - GtkWidget *widget; - gpointer data; +xg_gtk_scroll_destroy (GtkWidget *widget, gpointer data) { int id = (int) (EMACS_INT) data; /* The EMACS_INT cast avoids a warning. */ xg_remove_widget_from_map (id); @@ -3175,11 +3276,11 @@ xg_gtk_scroll_destroy (widget, data) to set resources for the widget. */ void -xg_create_scroll_bar (f, bar, scroll_callback, end_callback, scroll_bar_name) - FRAME_PTR f; - struct scroll_bar *bar; - GCallback scroll_callback, end_callback; - char *scroll_bar_name; +xg_create_scroll_bar (FRAME_PTR f, + struct scroll_bar *bar, + GCallback scroll_callback, + GCallback end_callback, + const char *scroll_bar_name) { GtkWidget *wscroll; GtkWidget *webox; @@ -3212,7 +3313,7 @@ xg_create_scroll_bar (f, bar, scroll_callback, end_callback, scroll_bar_name) "button-release-event", end_callback, (gpointer) bar); - + /* The scroll bar widget does not draw on a window of its own. Instead it draws on the parent window, in this case the edit widget. So whenever the edit widget is cleared, the scroll bar needs to redraw @@ -3229,23 +3330,10 @@ xg_create_scroll_bar (f, bar, scroll_callback, end_callback, scroll_bar_name) bar->x_window = scroll_id; } -/* Make the scroll bar represented by SCROLLBAR_ID visible. */ - -void -xg_show_scroll_bar (scrollbar_id) - int scrollbar_id; -{ - GtkWidget *w = xg_get_widget_from_map (scrollbar_id); - if (w) - gtk_widget_show_all (gtk_widget_get_parent (w)); -} - /* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */ void -xg_remove_scroll_bar (f, scrollbar_id) - FRAME_PTR f; - int scrollbar_id; +xg_remove_scroll_bar (FRAME_PTR f, int scrollbar_id) { GtkWidget *w = xg_get_widget_from_map (scrollbar_id); if (w) @@ -3263,13 +3351,12 @@ xg_remove_scroll_bar (f, scrollbar_id) WIDTH, HEIGHT is the size in pixels the bar shall have. */ void -xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height) - FRAME_PTR f; - int scrollbar_id; - int top; - int left; - int width; - int height; +xg_update_scrollbar_pos (FRAME_PTR f, + int scrollbar_id, + int top, + int left, + int width, + int height) { GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id); @@ -3278,30 +3365,35 @@ xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height) { GtkWidget *wfixed = f->output_data.x->edit_widget; GtkWidget *wparent = gtk_widget_get_parent (wscroll); - GtkFixed *wf = GTK_FIXED (wfixed); + gint msl; /* Clear out old position. */ - GList *iter; int oldx = -1, oldy = -1, oldw, oldh; - for (iter = wf->children; iter; iter = iter->next) - if (((GtkFixedChild *)iter->data)->widget == wparent) - { - GtkFixedChild *ch = (GtkFixedChild *)iter->data; - if (ch->x != left || ch->y != top) - { - oldx = ch->x; - oldy = ch->y; - gtk_widget_get_size_request (wscroll, &oldw, &oldh); - } - break; - } + if (gtk_widget_get_parent (wparent) == wfixed) + { + gtk_container_child_get (GTK_CONTAINER (wfixed), wparent, + "x", &oldx, "y", &oldy, NULL); + gtk_widget_get_size_request (wscroll, &oldw, &oldh); + } /* Move and resize to new values. */ gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top); - gtk_widget_set_size_request (wscroll, width, height); + gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL); + if (msl > height) + { + /* No room. Hide scroll bar as some themes output a warning if + the height is less than the min size. */ + gtk_widget_hide (wparent); + gtk_widget_hide (wscroll); + } + else + { + gtk_widget_show_all (wparent); + gtk_widget_set_size_request (wscroll, width, height); + } gtk_widget_queue_draw (wfixed); gdk_window_process_all_updates (); - if (oldx != -1) + if (oldx != -1 && oldw > 0 && oldh > 0) { /* Clear under old scroll bar position. This must be done after the gtk_widget_queue_draw and gdk_window_process_all_updates @@ -3310,11 +3402,11 @@ xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height) FRAME_X_WINDOW (f), oldx, oldy, oldw, oldh, 0); } - + /* GTK does not redraw until the main loop is entered again, but if there are no X events pending we will not enter it. So we sync here to get some events. */ - + x_sync (f); SET_FRAME_GARBAGED (f); cancel_mouse_face (f); @@ -3325,9 +3417,10 @@ xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height) displaying PORTION out of a whole WHOLE, and our position POSITION. */ void -xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole) - struct scroll_bar *bar; - int portion, position, whole; +xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar, + int portion, + int position, + int whole) { GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window); @@ -3371,13 +3464,13 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole) /* Assume all lines are of equal size. */ new_step = size / max (1, FRAME_LINES (f)); - if ((int) adj->page_size != size - || (int) adj->step_increment != new_step) + if ((int) gtk_adjustment_get_page_size (adj) != size + || (int) gtk_adjustment_get_step_increment (adj) != new_step) { - adj->page_size = size; - adj->step_increment = new_step; + gtk_adjustment_set_page_size (adj, size); + gtk_adjustment_set_step_increment (adj, new_step); /* Assume a page increment is about 95% of the page size */ - adj->page_increment = (int) (0.95*adj->page_size); + gtk_adjustment_set_page_increment (adj,(int) (0.95*size)); changed = 1; } @@ -3409,9 +3502,7 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole) Return non-zero if the event is for a scroll bar, zero otherwise. */ int -xg_event_is_for_scrollbar (f, event) - FRAME_PTR f; - XEvent *event; +xg_event_is_for_scrollbar (FRAME_PTR f, XEvent *event) { int retval = 0; @@ -3420,17 +3511,17 @@ xg_event_is_for_scrollbar (f, event) /* Check if press occurred outside the edit widget. */ GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f)); retval = gdk_display_get_window_at_pointer (gdpy, NULL, NULL) - != f->output_data.x->edit_widget->window; + != gtk_widget_get_window (f->output_data.x->edit_widget); } else if (f && ((event->type == ButtonRelease && event->xbutton.button < 4) || event->type == MotionNotify)) { /* If we are releasing or moving the scroll bar, it has the grab. */ - retval = gtk_grab_get_current () != 0 - && gtk_grab_get_current () != f->output_data.x->edit_widget; + GtkWidget *w = gtk_grab_get_current (); + retval = w != 0 && GTK_IS_SCROLLBAR (w); } - + return retval; } @@ -3466,10 +3557,9 @@ xg_event_is_for_scrollbar (f, event) tool bar. 0 is the first button. */ static gboolean -xg_tool_bar_button_cb (widget, event, user_data) - GtkWidget *widget; - GdkEventButton *event; - gpointer user_data; +xg_tool_bar_button_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) { /* Casts to avoid warnings when gpointer is 64 bits and int is 32 bits */ gpointer ptr = (gpointer) (EMACS_INT) event->state; @@ -3484,9 +3574,7 @@ xg_tool_bar_button_cb (widget, event, user_data) tool bar. 0 is the first button. */ static void -xg_tool_bar_callback (w, client_data) - GtkWidget *w; - gpointer client_data; +xg_tool_bar_callback (GtkWidget *w, gpointer client_data) { /* The EMACS_INT cast avoids a warning. */ int idx = (int) (EMACS_INT) client_data; @@ -3521,7 +3609,7 @@ xg_tool_bar_callback (w, client_data) this is written. */ event.modifiers = x_x_to_emacs_modifiers (FRAME_X_DISPLAY_INFO (f), mod); kbd_buffer_store_event (&event); - + /* Return focus to the frame after we have clicked on a detached tool bar button. */ Fx_focus_frame (frame); @@ -3535,9 +3623,7 @@ xg_tool_bar_callback (w, client_data) tool bar. 0 is the first button. */ static void -xg_tool_bar_proxy_callback (w, client_data) - GtkWidget *w; - gpointer client_data; +xg_tool_bar_proxy_callback (GtkWidget *w, gpointer client_data) { GtkWidget *wbutton = GTK_WIDGET (g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_PROXY_BUTTON)); @@ -3546,25 +3632,36 @@ xg_tool_bar_proxy_callback (w, client_data) static gboolean -xg_tool_bar_help_callback P_ ((GtkWidget *w, - GdkEventCrossing *event, - gpointer client_data)); +xg_tool_bar_help_callback (GtkWidget *w, + GdkEventCrossing *event, + gpointer client_data); /* This callback is called when a help is to be shown for an item in the detached tool bar when the detached tool bar it is not expanded. */ static gboolean -xg_tool_bar_proxy_help_callback (w, event, client_data) - GtkWidget *w; - GdkEventCrossing *event; - gpointer client_data; +xg_tool_bar_proxy_help_callback (GtkWidget *w, + GdkEventCrossing *event, + gpointer client_data) { GtkWidget *wbutton = GTK_WIDGET (g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_PROXY_BUTTON)); - + return xg_tool_bar_help_callback (wbutton, event, client_data); } +static GtkWidget * +xg_get_tool_bar_widgets (GtkWidget *vb, GtkWidget **wimage) +{ + GList *clist = gtk_container_get_children (GTK_CONTAINER (vb)); + GtkWidget *c1 = (GtkWidget *) clist->data; + GtkWidget *c2 = clist->next ? (GtkWidget *) clist->next->data : NULL; + + *wimage = GTK_IS_IMAGE (c1) ? c1 : c2; + g_list_free (clist); + return GTK_IS_LABEL (c1) ? c1 : c2; +} + /* This callback is called when a tool item should create a proxy item, such as for the overflow menu. Also called when the tool bar is detached. @@ -3572,21 +3669,23 @@ xg_tool_bar_proxy_help_callback (w, event, client_data) blank. */ static gboolean -xg_tool_bar_menu_proxy (toolitem, user_data) - GtkToolItem *toolitem; - gpointer user_data; +xg_tool_bar_menu_proxy (GtkToolItem *toolitem, gpointer user_data) { - GtkWidget *weventbox = gtk_bin_get_child (GTK_BIN (toolitem)); - GtkButton *wbutton = GTK_BUTTON (gtk_bin_get_child (GTK_BIN (weventbox))); - GtkWidget *wmenuitem = gtk_image_menu_item_new_with_label (""); + GtkButton *wbutton = GTK_BUTTON (XG_BIN_CHILD (XG_BIN_CHILD (toolitem))); + GtkWidget *vb = XG_BIN_CHILD (wbutton); + GtkWidget *c1; + GtkLabel *wlbl = GTK_LABEL (xg_get_tool_bar_widgets (vb, &c1)); + GtkImage *wimage = GTK_IMAGE (c1); + GtkWidget *wmenuitem = gtk_image_menu_item_new_with_label + (gtk_label_get_text (wlbl)); GtkWidget *wmenuimage; + if (gtk_button_get_use_stock (wbutton)) wmenuimage = gtk_image_new_from_stock (gtk_button_get_label (wbutton), GTK_ICON_SIZE_MENU); else { - GtkImage *wimage = GTK_IMAGE (gtk_bin_get_child (GTK_BIN (wbutton))); GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (wbutton)); GtkImageType store_type = gtk_image_get_storage_type (wimage); @@ -3650,11 +3749,12 @@ xg_tool_bar_menu_proxy (toolitem, user_data) G_CALLBACK (xg_tool_bar_proxy_callback), user_data); - + g_object_set_data (G_OBJECT (wmenuitem), XG_TOOL_BAR_PROXY_BUTTON, (gpointer) wbutton); gtk_tool_item_set_proxy_menu_item (toolitem, "Emacs toolbar item", wmenuitem); - gtk_widget_set_sensitive (wmenuitem, GTK_WIDGET_SENSITIVE (wbutton)); + gtk_widget_set_sensitive (wmenuitem, + gtk_widget_get_sensitive (GTK_WIDGET (wbutton))); /* Use enter/leave notify to show help. We use the events rather than the GtkButton specific signals "enter" and @@ -3680,26 +3780,32 @@ xg_tool_bar_menu_proxy (toolitem, user_data) CLIENT_DATA is a pointer to the frame the tool bar belongs to. */ static void -xg_tool_bar_detach_callback (wbox, w, client_data) - GtkHandleBox *wbox; - GtkWidget *w; - gpointer client_data; +xg_tool_bar_detach_callback (GtkHandleBox *wbox, + GtkWidget *w, + gpointer client_data) { FRAME_PTR f = (FRAME_PTR) client_data; - extern int x_gtk_whole_detached_tool_bar; g_object_set (G_OBJECT (w), "show-arrow", !x_gtk_whole_detached_tool_bar, NULL); if (f) { + GtkRequisition req, req2; FRAME_X_OUTPUT (f)->toolbar_detached = 1; - - /* When detaching a tool bar, not everything dissapear. There are - a few pixels left that are used to drop the tool bar back into - place. */ - FRAME_TOOLBAR_HEIGHT (f) = 4; - xg_height_changed (f); + gtk_widget_size_request (GTK_WIDGET (wbox), &req); + gtk_widget_size_request (w, &req2); + req.width -= req2.width; + req.height -= req2.height; + if (FRAME_TOOLBAR_TOP_HEIGHT (f) != 0) + FRAME_TOOLBAR_TOP_HEIGHT (f) = req.height; + else if (FRAME_TOOLBAR_BOTTOM_HEIGHT (f) != 0) + FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = req.height; + else if (FRAME_TOOLBAR_RIGHT_WIDTH (f) != 0) + FRAME_TOOLBAR_RIGHT_WIDTH (f) = req.width; + else if (FRAME_TOOLBAR_LEFT_WIDTH (f) != 0) + FRAME_TOOLBAR_LEFT_WIDTH (f) = req.width; + xg_height_or_width_changed (f); } } @@ -3711,23 +3817,30 @@ xg_tool_bar_detach_callback (wbox, w, client_data) CLIENT_DATA is a pointer to the frame the tool bar belongs to. */ static void -xg_tool_bar_attach_callback (wbox, w, client_data) - GtkHandleBox *wbox; - GtkWidget *w; - gpointer client_data; +xg_tool_bar_attach_callback (GtkHandleBox *wbox, + GtkWidget *w, + gpointer client_data) { FRAME_PTR f = (FRAME_PTR) client_data; g_object_set (G_OBJECT (w), "show-arrow", TRUE, NULL); if (f) { - GtkRequisition req; - + GtkRequisition req, req2; FRAME_X_OUTPUT (f)->toolbar_detached = 0; - - gtk_widget_size_request (w, &req); - FRAME_TOOLBAR_HEIGHT (f) = req.height; - xg_height_changed (f); + gtk_widget_size_request (GTK_WIDGET (wbox), &req); + gtk_widget_size_request (w, &req2); + req.width += req2.width; + req.height += req2.height; + if (FRAME_TOOLBAR_TOP_HEIGHT (f) != 0) + FRAME_TOOLBAR_TOP_HEIGHT (f) = req.height; + else if (FRAME_TOOLBAR_BOTTOM_HEIGHT (f) != 0) + FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = req.height; + else if (FRAME_TOOLBAR_RIGHT_WIDTH (f) != 0) + FRAME_TOOLBAR_RIGHT_WIDTH (f) = req.width; + else if (FRAME_TOOLBAR_LEFT_WIDTH (f) != 0) + FRAME_TOOLBAR_LEFT_WIDTH (f) = req.width; + xg_height_or_width_changed (f); } } @@ -3741,10 +3854,9 @@ xg_tool_bar_attach_callback (wbox, w, client_data) Returns FALSE to tell GTK to keep processing this event. */ static gboolean -xg_tool_bar_help_callback (w, event, client_data) - GtkWidget *w; - GdkEventCrossing *event; - gpointer client_data; +xg_tool_bar_help_callback (GtkWidget *w, + GdkEventCrossing *event, + gpointer client_data) { /* The EMACS_INT cast avoids a warning. */ int idx = (int) (EMACS_INT) client_data; @@ -3783,10 +3895,9 @@ xg_tool_bar_help_callback (w, event, client_data) Returns FALSE to tell GTK to keep processing this event. */ static gboolean -xg_tool_bar_item_expose_callback (w, event, client_data) - GtkWidget *w; - GdkEventExpose *event; - gpointer client_data; +xg_tool_bar_item_expose_callback (GtkWidget *w, + GdkEventExpose *event, + gpointer client_data) { gint width, height; @@ -3804,37 +3915,70 @@ xg_tool_bar_item_expose_callback (w, event, client_data) return FALSE; } +#ifdef HAVE_GTK_ORIENTABLE_SET_ORIENTATION +#define toolbar_set_orientation(w, o) \ + gtk_orientable_set_orientation (GTK_ORIENTABLE (w), o) +#else +#define toolbar_set_orientation(w, o) \ + gtk_toolbar_set_orientation (GTK_TOOLBAR (w), o) +#endif + /* Attach a tool bar to frame F. */ static void -xg_pack_tool_bar (f) - FRAME_PTR f; +xg_pack_tool_bar (FRAME_PTR f, Lisp_Object pos) { struct x_output *x = f->output_data.x; - int vbox_pos = x->menubar_widget ? 1 : 0; - - x->handlebox_widget = gtk_handle_box_new (); - g_signal_connect (G_OBJECT (x->handlebox_widget), "child-detached", - G_CALLBACK (xg_tool_bar_detach_callback), f); - g_signal_connect (G_OBJECT (x->handlebox_widget), "child-attached", - G_CALLBACK (xg_tool_bar_attach_callback), f); - - gtk_container_add (GTK_CONTAINER (x->handlebox_widget), - x->toolbar_widget); + int into_hbox = EQ (pos, Qleft) || EQ (pos, Qright); - gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->handlebox_widget, - FALSE, FALSE, 0); + toolbar_set_orientation (x->toolbar_widget, + into_hbox + ? GTK_ORIENTATION_VERTICAL + : GTK_ORIENTATION_HORIZONTAL); + if (!x->handlebox_widget) + { + x->handlebox_widget = gtk_handle_box_new (); + g_signal_connect (G_OBJECT (x->handlebox_widget), "child-detached", + G_CALLBACK (xg_tool_bar_detach_callback), f); + g_signal_connect (G_OBJECT (x->handlebox_widget), "child-attached", + G_CALLBACK (xg_tool_bar_attach_callback), f); + gtk_container_add (GTK_CONTAINER (x->handlebox_widget), + x->toolbar_widget); + } - gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->handlebox_widget, - vbox_pos); - gtk_widget_show_all (x->handlebox_widget); + if (into_hbox) + { + gtk_handle_box_set_handle_position (GTK_HANDLE_BOX (x->handlebox_widget), + GTK_POS_TOP); + gtk_box_pack_start (GTK_BOX (x->hbox_widget), x->handlebox_widget, + FALSE, FALSE, 0); + + if (EQ (pos, Qleft)) + gtk_box_reorder_child (GTK_BOX (x->hbox_widget), + x->handlebox_widget, + 0); + x->toolbar_in_hbox = 1; + } + else + { + int vbox_pos = x->menubar_widget ? 1 : 0; + gtk_handle_box_set_handle_position (GTK_HANDLE_BOX (x->handlebox_widget), + GTK_POS_LEFT); + gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->handlebox_widget, + FALSE, FALSE, 0); + + if (EQ (pos, Qtop)) + gtk_box_reorder_child (GTK_BOX (x->vbox_widget), + x->handlebox_widget, + vbox_pos); + x->toolbar_in_hbox = 0; + } } /* Create a tool bar for frame F. */ static void -xg_create_tool_bar (f) - FRAME_PTR f; +xg_create_tool_bar (FRAME_PTR f) { struct x_output *x = f->output_data.x; @@ -3843,16 +3987,8 @@ xg_create_tool_bar (f) gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar"); - /* We only have icons, so override any user setting. We could - use the caption property of the toolbar item (see update_frame_tool_bar - below), but some of those strings are long, making the toolbar so - long it does not fit on the screen. The GtkToolbar widget makes every - item equal size, so the longest caption determine the size of every - tool bar item. I think the creators of the GtkToolbar widget - counted on 4 or 5 character long strings. */ gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS); - gtk_toolbar_set_orientation (GTK_TOOLBAR (x->toolbar_widget), - GTK_ORIENTATION_HORIZONTAL); + toolbar_set_orientation (x->toolbar_widget, GTK_ORIENTATION_HORIZONTAL); } @@ -3862,10 +3998,7 @@ xg_create_tool_bar (f) Returns IMAGE if RTL is not found. */ static Lisp_Object -find_rtl_image (f, image, rtl) - FRAME_PTR f; - Lisp_Object image; - Lisp_Object rtl; +find_rtl_image (FRAME_PTR f, Lisp_Object image, Lisp_Object rtl) { int i; Lisp_Object file, rtl_name; @@ -3892,16 +4025,178 @@ find_rtl_image (f, image, rtl) return image; } -/* Update the tool bar for frame F. Add new buttons and remove old. */ +static GtkToolItem * +xg_make_tool_item (FRAME_PTR f, + GtkWidget *wimage, + GtkWidget **wbutton, + const char *label, + int i, int horiz, int text_image) +{ + GtkToolItem *ti = gtk_tool_item_new (); + GtkWidget *vb = horiz ? gtk_hbox_new (FALSE, 0) : gtk_vbox_new (FALSE, 0); + GtkWidget *wb = gtk_button_new (); + GtkWidget *weventbox = gtk_event_box_new (); + + if (wimage && !text_image) + gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0); + if (label) + gtk_box_pack_start (GTK_BOX (vb), gtk_label_new (label), TRUE, TRUE, 0); + if (wimage && text_image) + gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0); + + gtk_button_set_focus_on_click (GTK_BUTTON (wb), FALSE); + gtk_button_set_relief (GTK_BUTTON (wb), GTK_RELIEF_NONE); + gtk_container_add (GTK_CONTAINER (wb), vb); + gtk_container_add (GTK_CONTAINER (weventbox), wb); + gtk_container_add (GTK_CONTAINER (ti), weventbox); + + if (wimage) + { + /* The EMACS_INT cast avoids a warning. */ + g_signal_connect (G_OBJECT (ti), "create-menu-proxy", + G_CALLBACK (xg_tool_bar_menu_proxy), + (gpointer) (EMACS_INT) i); + + g_signal_connect (G_OBJECT (wb), "clicked", + G_CALLBACK (xg_tool_bar_callback), + (gpointer) (EMACS_INT) i); + + g_object_set_data (G_OBJECT (weventbox), XG_FRAME_DATA, (gpointer)f); + + /* Catch expose events to overcome an annoying redraw bug, see + comment for xg_tool_bar_item_expose_callback. */ + g_signal_connect (G_OBJECT (ti), + "expose-event", + G_CALLBACK (xg_tool_bar_item_expose_callback), + 0); + + gtk_tool_item_set_homogeneous (ti, FALSE); + + /* Callback to save modifyer mask (Shift/Control, etc). GTK makes + no distinction based on modifiers in the activate callback, + so we have to do it ourselves. */ + g_signal_connect (wb, "button-release-event", + G_CALLBACK (xg_tool_bar_button_cb), + NULL); + + g_object_set_data (G_OBJECT (wb), XG_FRAME_DATA, (gpointer)f); + + /* Use enter/leave notify to show help. We use the events + rather than the GtkButton specific signals "enter" and + "leave", so we can have only one callback. The event + will tell us what kind of event it is. */ + /* The EMACS_INT cast avoids a warning. */ + g_signal_connect (G_OBJECT (weventbox), + "enter-notify-event", + G_CALLBACK (xg_tool_bar_help_callback), + (gpointer) (EMACS_INT) i); + g_signal_connect (G_OBJECT (weventbox), + "leave-notify-event", + G_CALLBACK (xg_tool_bar_help_callback), + (gpointer) (EMACS_INT) i); + } + + if (wbutton) *wbutton = wb; + + return ti; +} + +static int +xg_tool_item_stale_p (GtkWidget *wbutton, const char *stock_name, + const char *icon_name, const struct image *img, + const char *label, int horiz) +{ + gpointer old; + GtkWidget *wimage; + GtkWidget *vb = XG_BIN_CHILD (wbutton); + GtkWidget *wlbl = xg_get_tool_bar_widgets (vb, &wimage); + + /* Check if the tool icon matches. */ + if (stock_name) + { + old = g_object_get_data (G_OBJECT (wimage), + XG_TOOL_BAR_STOCK_NAME); + if (!old || strcmp (old, stock_name)) + return 1; + } + else if (icon_name) + { + old = g_object_get_data (G_OBJECT (wimage), + XG_TOOL_BAR_ICON_NAME); + if (!old || strcmp (old, icon_name)) + return 1; + } + else + { + Pixmap old_img + = (Pixmap) g_object_get_data (G_OBJECT (wimage), + XG_TOOL_BAR_IMAGE_DATA); + if (old_img != img->pixmap) + return 1; + } -extern Lisp_Object Qx_gtk_map_stock; + /* Check button configuration and label. */ + if ((horiz ? GTK_IS_VBOX (vb) : GTK_IS_HBOX (vb)) + || (label ? (wlbl == NULL) : (wlbl != NULL))) + return 1; + + /* Ensure label is correct. */ + if (label) + gtk_label_set_text (GTK_LABEL (wlbl), label); + return 0; +} + +static int +xg_update_tool_bar_sizes (FRAME_PTR f) +{ + struct x_output *x = f->output_data.x; + GtkRequisition req; + int nl = 0, nr = 0, nt = 0, nb = 0; + + gtk_widget_size_request (GTK_WIDGET (x->handlebox_widget), &req); + if (x->toolbar_in_hbox) + { + int pos; + gtk_container_child_get (GTK_CONTAINER (x->hbox_widget), + x->handlebox_widget, + "position", &pos, NULL); + if (pos == 0) nl = req.width; + else nr = req.width; + } + else + { + int pos; + gtk_container_child_get (GTK_CONTAINER (x->vbox_widget), + x->handlebox_widget, + "position", &pos, NULL); + if (pos == 0 || (pos == 1 && x->menubar_widget)) nt = req.height; + else nb = req.height; + } + + if (nl != FRAME_TOOLBAR_LEFT_WIDTH (f) + || nr != FRAME_TOOLBAR_RIGHT_WIDTH (f) + || nt != FRAME_TOOLBAR_TOP_HEIGHT (f) + || nb != FRAME_TOOLBAR_BOTTOM_HEIGHT (f)) + { + FRAME_TOOLBAR_RIGHT_WIDTH (f) = FRAME_TOOLBAR_LEFT_WIDTH (f) + = FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0; + FRAME_TOOLBAR_LEFT_WIDTH (f) = nl; + FRAME_TOOLBAR_RIGHT_WIDTH (f) = nr; + FRAME_TOOLBAR_TOP_HEIGHT (f) = nt; + FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = nb; + return 1; + } + + return 0; +} + + +/* Update the tool bar for frame F. Add new buttons and remove old. */ void -update_frame_tool_bar (f) - FRAME_PTR f; +update_frame_tool_bar (FRAME_PTR f) { - int i; - GtkRequisition old_req, new_req; + int i, j; struct x_output *x = f->output_data.x; int hmargin = 0, vmargin = 0; GtkToolbar *wtoolbar; @@ -3909,6 +4204,9 @@ update_frame_tool_bar (f) GtkTextDirection dir; int pack_tool_bar = x->handlebox_widget == NULL; + Lisp_Object style; + int text_image, horiz; + if (! FRAME_GTK_WIDGET (f)) return; @@ -3942,10 +4240,13 @@ update_frame_tool_bar (f) xg_create_tool_bar (f); wtoolbar = GTK_TOOLBAR (x->toolbar_widget); - gtk_widget_size_request (GTK_WIDGET (wtoolbar), &old_req); - dir = gtk_widget_get_direction (x->toolbar_widget); + dir = gtk_widget_get_direction (GTK_WIDGET (wtoolbar)); - for (i = 0; i < f->n_tool_bar_items; ++i) + style = Ftool_bar_get_system_style (); + text_image = EQ (style, Qtext_image_horiz); + horiz = EQ (style, Qboth_horiz) || text_image; + + for (i = j = 0; i < f->n_tool_bar_items; ++i) { int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P)); int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P)); @@ -3960,23 +4261,48 @@ update_frame_tool_bar (f) char *icon_name = NULL; Lisp_Object rtl; GtkWidget *wbutton = NULL; - GtkWidget *weventbox; Lisp_Object specified_file; - - ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (x->toolbar_widget), i); - - if (ti) - { - weventbox = gtk_bin_get_child (GTK_BIN (ti)); - wbutton = gtk_bin_get_child (GTK_BIN (weventbox)); - } - - image = PROP (TOOL_BAR_ITEM_IMAGES); + int vert_only = ! NILP (PROP (TOOL_BAR_ITEM_VERT_ONLY)); + const char *label + = (EQ (style, Qimage) || (vert_only && horiz)) ? NULL + : STRINGP (PROP (TOOL_BAR_ITEM_LABEL)) + ? SSDATA (PROP (TOOL_BAR_ITEM_LABEL)) + : ""; + + ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j); + + /* If this is a separator, use a gtk separator item. */ + if (EQ (PROP (TOOL_BAR_ITEM_TYPE), Qt)) + { + if (ti == NULL || !GTK_IS_SEPARATOR_TOOL_ITEM (ti)) + { + if (ti) + gtk_container_remove (GTK_CONTAINER (wtoolbar), + GTK_WIDGET (ti)); + ti = gtk_separator_tool_item_new (); + gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j); + } + j++; + continue; + } + + /* Otherwise, the tool-bar item is an ordinary button. */ + + if (ti && GTK_IS_SEPARATOR_TOOL_ITEM (ti)) + { + gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti)); + ti = NULL; + } + + if (ti) wbutton = XG_BIN_CHILD (XG_BIN_CHILD (ti)); /* Ignore invalid image specifications. */ + image = PROP (TOOL_BAR_ITEM_IMAGES); if (!valid_image_p (image)) { - if (wbutton) gtk_widget_hide (wbutton); + if (ti) + gtk_container_remove (GTK_CONTAINER (wtoolbar), + GTK_WIDGET (ti)); continue; } @@ -4002,7 +4328,7 @@ update_frame_tool_bar (f) icon_size = gtk_toolbar_get_icon_size (wtoolbar); } else if (gtk_stock_lookup (SSDATA (stock), &stock_item)) - icon_size = gtk_toolbar_get_icon_size (wtoolbar); + icon_size = gtk_toolbar_get_icon_size (wtoolbar); else { stock = Qnil; @@ -4012,16 +4338,13 @@ update_frame_tool_bar (f) if (stock_name == NULL && icon_name == NULL) { - /* No stock image, or stock item not known. Try regular image. */ - - /* If image is a vector, choose the image according to the + /* No stock image, or stock item not known. Try regular + image. If image is a vector, choose it according to the button state. */ if (dir == GTK_TEXT_DIR_RTL && !NILP (rtl = PROP (TOOL_BAR_ITEM_RTL_IMAGE)) && STRINGP (rtl)) - { - image = find_rtl_image (f, image, rtl); - } + image = find_rtl_image (f, image, rtl); if (VECTORP (image)) { @@ -4046,29 +4369,32 @@ update_frame_tool_bar (f) if (img->load_failed_p || img->pixmap == None) { - if (ti) - gtk_widget_hide_all (GTK_WIDGET (ti)); - else - { - /* Insert an empty (non-image) button */ - weventbox = gtk_event_box_new (); - wbutton = gtk_button_new (); - gtk_button_set_focus_on_click (GTK_BUTTON (wbutton), FALSE); - gtk_button_set_relief (GTK_BUTTON (wbutton), - GTK_RELIEF_NONE); - gtk_container_add (GTK_CONTAINER (weventbox), wbutton); - ti = gtk_tool_item_new (); - gtk_container_add (GTK_CONTAINER (ti), weventbox); - gtk_toolbar_insert (GTK_TOOLBAR (x->toolbar_widget), ti, -1); - } - continue; + if (ti) + gtk_container_remove (GTK_CONTAINER (wtoolbar), + GTK_WIDGET (ti)); + continue; } } + /* If there is an existing widget, check if it's stale; if so, + remove it and make a new tool item from scratch. */ + if (ti && xg_tool_item_stale_p (wbutton, stock_name, icon_name, + img, label, horiz)) + { + gtk_container_remove (GTK_CONTAINER (wtoolbar), + GTK_WIDGET (ti)); + ti = NULL; + } + if (ti == NULL) { GtkWidget *w; - if (stock_name) + + /* Save the image so we can see if an update is needed the + next time we call xg_tool_item_match_p. */ + if (EQ (style, Qtext)) + w = NULL; + else if (stock_name) { w = gtk_image_new_from_stock (stock_name, icon_size); g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_STOCK_NAME, @@ -4085,145 +4411,38 @@ update_frame_tool_bar (f) else { w = xg_get_image_for_pixmap (f, img, x->widget, NULL); - /* Save the image so we can see if an update is needed when - this function is called again. */ g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA, (gpointer)img->pixmap); } - gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin); - wbutton = gtk_button_new (); - gtk_button_set_focus_on_click (GTK_BUTTON (wbutton), FALSE); - gtk_button_set_relief (GTK_BUTTON (wbutton), GTK_RELIEF_NONE); - gtk_container_add (GTK_CONTAINER (wbutton), w); - weventbox = gtk_event_box_new (); - gtk_container_add (GTK_CONTAINER (weventbox), wbutton); - ti = gtk_tool_item_new (); - gtk_container_add (GTK_CONTAINER (ti), weventbox); - gtk_toolbar_insert (GTK_TOOLBAR (x->toolbar_widget), ti, -1); - - - /* The EMACS_INT cast avoids a warning. */ - g_signal_connect (G_OBJECT (ti), "create-menu-proxy", - G_CALLBACK (xg_tool_bar_menu_proxy), - (gpointer) (EMACS_INT) i); - - g_signal_connect (G_OBJECT (wbutton), "clicked", - G_CALLBACK (xg_tool_bar_callback), - (gpointer) (EMACS_INT) i); - - gtk_widget_show_all (GTK_WIDGET (ti)); - - - g_object_set_data (G_OBJECT (weventbox), XG_FRAME_DATA, (gpointer)f); - - /* Catch expose events to overcome an annoying redraw bug, see - comment for xg_tool_bar_item_expose_callback. */ - g_signal_connect (G_OBJECT (ti), - "expose-event", - G_CALLBACK (xg_tool_bar_item_expose_callback), - 0); - - gtk_widget_set_sensitive (wbutton, enabled_p); - gtk_tool_item_set_homogeneous (ti, FALSE); - - /* Callback to save modifyer mask (Shift/Control, etc). GTK makes - no distinction based on modifiers in the activate callback, - so we have to do it ourselves. */ - g_signal_connect (wbutton, "button-release-event", - G_CALLBACK (xg_tool_bar_button_cb), - NULL); - - g_object_set_data (G_OBJECT (wbutton), XG_FRAME_DATA, (gpointer)f); - - /* Use enter/leave notify to show help. We use the events - rather than the GtkButton specific signals "enter" and - "leave", so we can have only one callback. The event - will tell us what kind of event it is. */ - /* The EMACS_INT cast avoids a warning. */ - g_signal_connect (G_OBJECT (weventbox), - "enter-notify-event", - G_CALLBACK (xg_tool_bar_help_callback), - (gpointer) (EMACS_INT) i); - g_signal_connect (G_OBJECT (weventbox), - "leave-notify-event", - G_CALLBACK (xg_tool_bar_help_callback), - (gpointer) (EMACS_INT) i); + if (w) gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin); + ti = xg_make_tool_item (f, w, &wbutton, label, i, horiz, text_image); + gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j); } - else - { - GtkWidget *wimage = gtk_bin_get_child (GTK_BIN (wbutton)); - Pixmap old_img = (Pixmap)g_object_get_data (G_OBJECT (wimage), - XG_TOOL_BAR_IMAGE_DATA); - gpointer old_stock_name = g_object_get_data (G_OBJECT (wimage), - XG_TOOL_BAR_STOCK_NAME); - gpointer old_icon_name = g_object_get_data (G_OBJECT (wimage), - XG_TOOL_BAR_ICON_NAME); - if (stock_name && - (! old_stock_name || strcmp (old_stock_name, stock_name) != 0)) - { - gtk_image_set_from_stock (GTK_IMAGE (wimage), - stock_name, icon_size); - g_object_set_data_full (G_OBJECT (wimage), XG_TOOL_BAR_STOCK_NAME, - (gpointer) xstrdup (stock_name), - (GDestroyNotify) xfree); - g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA, - NULL); - g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_ICON_NAME, NULL); - } - else if (icon_name && - (! old_icon_name || strcmp (old_icon_name, icon_name) != 0)) - { - gtk_image_set_from_icon_name (GTK_IMAGE (wimage), - icon_name, icon_size); - g_object_set_data_full (G_OBJECT (wimage), XG_TOOL_BAR_ICON_NAME, - (gpointer) xstrdup (icon_name), - (GDestroyNotify) xfree); - g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA, - NULL); - g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_STOCK_NAME, - NULL); - } - else if (img && old_img != img->pixmap) - { - (void) xg_get_image_for_pixmap (f, img, x->widget, wimage); - g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA, - (gpointer)img->pixmap); - - g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_STOCK_NAME, - NULL); - g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_ICON_NAME, NULL); - } - - gtk_misc_set_padding (GTK_MISC (wimage), hmargin, vmargin); - - gtk_widget_set_sensitive (wbutton, enabled_p); - gtk_widget_show_all (GTK_WIDGET (ti)); - } #undef PROP + + gtk_widget_set_sensitive (wbutton, enabled_p); + j++; } - /* Remove buttons not longer needed. We just hide them so they - can be reused later on. */ + /* Remove buttons not longer needed. */ do { - ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (x->toolbar_widget), i++); - if (ti) gtk_widget_hide_all (GTK_WIDGET (ti)); + ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j); + if (ti) + gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti)); } while (ti != NULL); - new_req.height = 0; - if (pack_tool_bar && f->n_tool_bar_items != 0) - xg_pack_tool_bar (f); - - - gtk_widget_size_request (GTK_WIDGET (x->toolbar_widget), &new_req); - if (old_req.height != new_req.height - && ! FRAME_X_OUTPUT (f)->toolbar_detached) + if (f->n_tool_bar_items != 0) { - FRAME_TOOLBAR_HEIGHT (f) = new_req.height; - xg_height_changed (f); + if (pack_tool_bar) + xg_pack_tool_bar (f, f->tool_bar_position); + gtk_widget_show_all (GTK_WIDGET (x->handlebox_widget)); + if (xg_update_tool_bar_sizes (f)) + xg_height_or_width_changed (f); } + UNBLOCK_INPUT; } @@ -4231,8 +4450,7 @@ update_frame_tool_bar (f) Remove the tool bar. */ void -free_frame_tool_bar (f) - FRAME_PTR f; +free_frame_tool_bar (FRAME_PTR f) { struct x_output *x = f->output_data.x; @@ -4243,27 +4461,60 @@ free_frame_tool_bar (f) /* We may have created the toolbar_widget in xg_create_tool_bar, but not the x->handlebox_widget which is created in xg_pack_tool_bar. */ if (is_packed) - gtk_container_remove (GTK_CONTAINER (x->vbox_widget), - x->handlebox_widget); + { + if (x->toolbar_in_hbox) + gtk_container_remove (GTK_CONTAINER (x->hbox_widget), + x->handlebox_widget); + else + gtk_container_remove (GTK_CONTAINER (x->vbox_widget), + x->handlebox_widget); + } else gtk_widget_destroy (x->toolbar_widget); x->toolbar_widget = 0; x->handlebox_widget = 0; - FRAME_TOOLBAR_HEIGHT (f) = 0; - xg_height_changed (f); + FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0; + FRAME_TOOLBAR_LEFT_WIDTH (f) = FRAME_TOOLBAR_RIGHT_WIDTH (f) = 0; + + xg_height_or_width_changed (f); UNBLOCK_INPUT; } } +int +xg_change_toolbar_position (FRAME_PTR f, Lisp_Object pos) +{ + struct x_output *x = f->output_data.x; + + if (! x->toolbar_widget || ! x->handlebox_widget) + return 1; + + BLOCK_INPUT; + g_object_ref (x->handlebox_widget); + if (x->toolbar_in_hbox) + gtk_container_remove (GTK_CONTAINER (x->hbox_widget), + x->handlebox_widget); + else + gtk_container_remove (GTK_CONTAINER (x->vbox_widget), + x->handlebox_widget); + xg_pack_tool_bar (f, pos); + g_object_unref (x->handlebox_widget); + if (xg_update_tool_bar_sizes (f)) + xg_height_or_width_changed (f); + + UNBLOCK_INPUT; + return 1; +} + /*********************************************************************** Initializing - ***********************************************************************/ +***********************************************************************/ void -xg_initialize () +xg_initialize (void) { GtkBindingSet *binding_set; @@ -4311,6 +4562,3 @@ xg_initialize () } #endif /* USE_GTK */ - -/* arch-tag: fe7104da-bc1e-4aba-9bd1-f349c528f7e3 - (do not change this comment) */