X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/e1e18511e3a9c914db7a7a6fbae5737f7cf96553..c5101a77a4066d979698d356c3a9c7f387007359:/src/gtkutil.c diff --git a/src/gtkutil.c b/src/gtkutil.c index dbd48eb527..4e5ecce76c 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 @@ -41,15 +40,16 @@ along with GNU Emacs. If not, see . */ #include #endif +#ifdef HAVE_GTK3 +#include +#endif + #define FRAME_TOTAL_PIXEL_HEIGHT(f) \ (FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f)) #define FRAME_TOTAL_PIXEL_WIDTH(f) \ (FRAME_PIXEL_WIDTH (f) + FRAME_TOOLBAR_WIDTH (f)) -/* Avoid "differ in sign" warnings */ -#define SSDATA(x) ((char *) SDATA (x)) - #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW #define gtk_widget_set_has_window(w, b) \ (gtk_fixed_set_has_window (GTK_FIXED (w), b)) @@ -73,6 +73,26 @@ along with GNU Emacs. If not, see . */ #define remove_submenu(w) gtk_menu_item_remove_submenu ((w)) #endif +#ifndef HAVE_GTK3 +#ifdef USE_GTK_TOOLTIP +#define gdk_window_get_screen(w) gdk_drawable_get_screen (w) +#endif +#define gdk_window_get_geometry(w, a, b, c, d) \ + gdk_window_get_geometry (w, a, b, c, d, 0) +#define gdk_x11_window_lookup_for_display(d, w) \ + gdk_xid_table_lookup_for_display (d, w) +#define GDK_KEY_g GDK_g +#endif + +#define XG_BIN_CHILD(x) gtk_bin_get_child (GTK_BIN (x)) + +/* Get the current value of the range, truncated to an integer. */ +static int +int_gtk_range_get_value (GtkRange *range) +{ + return gtk_range_get_value (range); +} + /*********************************************************************** Display handling functions @@ -90,7 +110,7 @@ static GdkDisplay *gdpy_def; static void xg_set_screen (GtkWidget *w, FRAME_PTR f) { - if (FRAME_X_DISPLAY (f) != GDK_DISPLAY ()) + if (FRAME_X_DISPLAY (f) != DEFAULT_GDK_DISPLAY ()) { GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f)); GdkScreen *gscreen = gdk_display_get_default_screen (gdpy); @@ -231,29 +251,55 @@ xg_create_default_cursor (Display *dpy) return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR); } +static GdkPixbuf * +xg_get_pixbuf_from_pixmap (FRAME_PTR f, Pixmap pix) +{ + int iunused; + GdkPixbuf *tmp_buf; + Window wunused; + unsigned int width, height, uunused; + XImage *xim; + + XGetGeometry (FRAME_X_DISPLAY (f), pix, &wunused, &iunused, &iunused, + &width, &height, &uunused, &uunused); + + xim = XGetImage (FRAME_X_DISPLAY (f), pix, 0, 0, width, height, + ~0, XYPixmap); + if (!xim) return 0; + + tmp_buf = gdk_pixbuf_new_from_data ((guchar *) xim->data, + GDK_COLORSPACE_RGB, + FALSE, + xim->bitmap_unit, + (int) width, + (int) height, + xim->bytes_per_line, + NULL, + NULL); + XDestroyImage (xim); + return tmp_buf; +} + /* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel. */ static GdkPixbuf * -xg_get_pixbuf_from_pix_and_mask (GdkPixmap *gpix, - GdkPixmap *gmask, - GdkColormap *cmap) +xg_get_pixbuf_from_pix_and_mask (FRAME_PTR f, + Pixmap pix, + Pixmap mask) { int width, height; GdkPixbuf *icon_buf, *tmp_buf; - gdk_drawable_get_size (gpix, &width, &height); - tmp_buf = gdk_pixbuf_get_from_drawable (NULL, gpix, cmap, - 0, 0, 0, 0, width, height); + tmp_buf = xg_get_pixbuf_from_pixmap (f, pix); icon_buf = gdk_pixbuf_add_alpha (tmp_buf, FALSE, 0, 0, 0); g_object_unref (G_OBJECT (tmp_buf)); - if (gmask) + width = gdk_pixbuf_get_width (icon_buf); + height = gdk_pixbuf_get_height (icon_buf); + + if (mask) { - GdkPixbuf *mask_buf = gdk_pixbuf_get_from_drawable (NULL, - gmask, - NULL, - 0, 0, 0, 0, - width, height); + GdkPixbuf *mask_buf = xg_get_pixbuf_from_pixmap (f, mask); guchar *pixels = gdk_pixbuf_get_pixels (icon_buf); guchar *mask_pixels = gdk_pixbuf_get_pixels (mask_buf); int rowstride = gdk_pixbuf_get_rowstride (icon_buf); @@ -318,10 +364,6 @@ xg_get_image_for_pixmap (FRAME_PTR f, GtkWidget *widget, GtkImage *old_widget) { - GdkPixmap *gpix; - GdkPixmap *gmask; - GdkDisplay *gdpy; - GdkColormap *cmap; GdkPixbuf *icon_buf; /* If we have a file, let GTK do all the image handling. @@ -349,10 +391,6 @@ xg_get_image_for_pixmap (FRAME_PTR f, on a monochrome display, and sometimes bad on all displays with certain themes. */ - gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f)); - gpix = gdk_pixmap_foreign_new_for_display (gdpy, img->pixmap); - gmask = img->mask ? gdk_pixmap_foreign_new_for_display (gdpy, img->mask) : 0; - /* This is a workaround to make icons look good on pseudo color displays. Apparently GTK expects the images to have an alpha channel. If they don't, insensitive and activated icons will @@ -362,18 +400,17 @@ xg_get_image_for_pixmap (FRAME_PTR f, not associated with the img->pixmap. The img->pixmap may be removed by clearing the image cache and then the tool bar redraw fails, since Gtk+ assumes the pixmap is always there. */ - cmap = gtk_widget_get_colormap (widget); - icon_buf = xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap); - - if (! old_widget) - old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf)); - else - gtk_image_set_from_pixbuf (old_widget, icon_buf); + icon_buf = xg_get_pixbuf_from_pix_and_mask (f, img->pixmap, img->mask); - g_object_unref (G_OBJECT (icon_buf)); + if (icon_buf) + { + if (! old_widget) + old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf)); + else + gtk_image_set_from_pixbuf (old_widget, icon_buf); - g_object_unref (G_OBJECT (gpix)); - if (gmask) g_object_unref (G_OBJECT (gmask)); + g_object_unref (G_OBJECT (icon_buf)); + } return GTK_WIDGET (old_widget); } @@ -457,22 +494,22 @@ get_utf8_string (const char *str) gsize bytes_written; unsigned char *p = (unsigned char *)str; char *cp, *up; - GError *error = NULL; + GError *err = NULL; while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read, - &bytes_written, &error)) - && error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE) + &bytes_written, &err)) + && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE) { ++nr_bad; p += bytes_written+1; - g_error_free (error); - error = NULL; + g_error_free (err); + err = NULL; } - if (error) + if (err) { - g_error_free (error); - error = NULL; + g_error_free (err); + err = NULL; } if (cp) g_free (cp); @@ -480,16 +517,16 @@ get_utf8_string (const char *str) p = (unsigned char *)str; while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read, - &bytes_written, &error)) - && error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE) + &bytes_written, &err)) + && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE) { strncpy (up, (char *)p, bytes_written); sprintf (up + bytes_written, "\\%03o", p[bytes_written]); up[bytes_written+4] = '\0'; up += bytes_written+4; p += bytes_written+1; - g_error_free (error); - error = NULL; + g_error_free (err); + err = NULL; } if (cp) @@ -497,10 +534,10 @@ get_utf8_string (const char *str) strcat (utf8_str, cp); g_free (cp); } - if (error) + if (err) { - g_error_free (error); - error = NULL; + g_error_free (err); + err = NULL; } } return utf8_str; @@ -516,28 +553,43 @@ xg_check_special_colors (struct frame *f, 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; - } - } + int get_bg = strcmp ("gtk_selection_bg_color", color_name) == 0; + int get_fg = !get_bg && strcmp ("gtk_selection_fg_color", color_name) == 0; + if (! FRAME_GTK_WIDGET (f) || ! (get_bg || get_fg)) + return success_p; + + BLOCK_INPUT; + { +#ifdef HAVE_GTK3 + GtkStyleContext *gsty + = gtk_widget_get_style_context (FRAME_GTK_OUTER_WIDGET (f)); + GdkRGBA col; + char buf[64]; + int state = GTK_STATE_FLAG_SELECTED|GTK_STATE_FLAG_FOCUSED; + if (get_fg) + gtk_style_context_get_color (gsty, state, &col); + else + gtk_style_context_get_background_color (gsty, state, &col); + + sprintf (buf, "rgbi:%lf/%lf/%lf", col.red, col.green, col.blue); + success_p = XParseColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), + buf, color); +#else + GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f)); + GdkColor *grgb = get_bg + ? &gsty->bg[GTK_STATE_SELECTED] + : &gsty->fg[GTK_STATE_SELECTED]; + + color->red = grgb->red; + color->green = grgb->green; + color->blue = grgb->blue; + color->pixel = grgb->pixel; + success_p = 1; +#endif + + } + UNBLOCK_INPUT; return success_p; } @@ -560,7 +612,7 @@ hierarchy_ch_cb (GtkWidget *widget, 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); } @@ -581,7 +633,7 @@ qttip_cb (GtkWidget *widget, { FRAME_PTR f = (FRAME_PTR) user_data; struct x_output *x = f->output_data.x; - if (x->ttip_widget == NULL) + if (x->ttip_widget == NULL) { g_object_set (G_OBJECT (widget), "has-tooltip", FALSE, NULL); x->ttip_widget = tooltip; @@ -590,6 +642,8 @@ qttip_cb (GtkWidget *widget, 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); @@ -629,17 +683,17 @@ xg_prepare_tooltip (FRAME_PTR f, 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); + screen = gdk_window_get_screen (gwin); settings = gtk_settings_get_for_screen (screen); g_object_get (settings, "gtk-enable-tooltips", &tt_enabled, NULL); - if (tt_enabled) + 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))), @@ -650,10 +704,10 @@ xg_prepare_tooltip (FRAME_PTR f, 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); + gtk_widget_get_preferred_size (GTK_WIDGET (x->ttip_window), NULL, &req); if (width) *width = req.width; if (height) *height = req.height; - + UNBLOCK_INPUT; return 1; @@ -696,7 +750,7 @@ xg_hide_tooltip (FRAME_PTR f) 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); + GdkScreen *screen = gdk_window_get_screen (gwin); GtkSettings *settings = gtk_settings_get_for_screen (screen); g_object_set (settings, "gtk-enable-tooltips", TRUE, NULL); } @@ -797,10 +851,10 @@ xg_frame_resized (FRAME_PTR f, int pixelwidth, int pixelheight) 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); + &pixelwidth, &pixelheight); else return; } - + rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelheight); columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth); @@ -910,8 +964,8 @@ xg_win_to_widget (Display *dpy, Window wdesc) BLOCK_INPUT; - gdkwin = gdk_xid_table_lookup_for_display (gdk_x11_lookup_xdisplay (dpy), - wdesc); + gdkwin = gdk_x11_window_lookup_for_display (gdk_x11_lookup_xdisplay (dpy), + wdesc); if (gdkwin) { GdkEvent event; @@ -923,14 +977,29 @@ xg_win_to_widget (Display *dpy, Window wdesc) return gwdesc; } -/* Fill in the GdkColor C so that it represents PIXEL. - W is the widget that color will be used for. Used to find colormap. */ +/* Set the background of widget W to PIXEL. */ static void -xg_pix_to_gcolor (GtkWidget *w, long unsigned int pixel, GdkColor *c) +xg_set_widget_bg (FRAME_PTR f, GtkWidget *w, long unsigned int pixel) { +#ifdef HAVE_GTK3 + GdkRGBA bg; + XColor xbg; + xbg.pixel = pixel; + if (XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &xbg)) + { + bg.red = (double)xbg.red/65536.0; + bg.green = (double)xbg.green/65536.0; + bg.blue = (double)xbg.blue/65536.0; + bg.alpha = 1.0; + gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &bg); + } +#else + GdkColor bg; GdkColormap *map = gtk_widget_get_colormap (w); - gdk_colormap_query_color (map, pixel, c); + gdk_colormap_query_color (map, pixel, &bg); + gtk_widget_modify_bg (FRAME_GTK_WIDGET (f), GTK_STATE_NORMAL, &bg); +#endif } /* Callback called when the gtk theme changes. @@ -953,6 +1022,28 @@ style_changed_cb (GObject *go, kbd_buffer_store_event (&event); } +/* Called when a delete-event occurs on WIDGET. */ + +static gboolean +delete_cb (GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ +#ifdef HAVE_GTK3 + /* The event doesn't arrive in the normal event loop. Send event + here. */ + FRAME_PTR f = (FRAME_PTR) user_data; + struct input_event ie; + + EVENT_INIT (ie); + ie.kind = DELETE_WINDOW_EVENT; + XSETFRAME (ie.frame_or_window, f); + kbd_buffer_store_event (&ie); +#endif + + return TRUE; +} + /* Create and set up the GTK widgets for frame F. Return 0 if creation failed, non-zero otherwise. */ @@ -962,7 +1053,6 @@ xg_create_frame_widgets (FRAME_PTR f) GtkWidget *wtop; GtkWidget *wvbox, *whbox; GtkWidget *wfixed; - GdkColor bg; GtkRcStyle *style; char *title = 0; @@ -1029,7 +1119,7 @@ xg_create_frame_widgets (FRAME_PTR f) /* Add callback to do nothing on WM_DELETE_WINDOW. The default in GTK is to destroy the widget. We want Emacs to do that instead. */ g_signal_connect (G_OBJECT (wtop), "delete-event", - G_CALLBACK (gtk_true), 0); + G_CALLBACK (delete_cb), f); /* Convert our geometry parameters into a geometry string and specify it. @@ -1057,9 +1147,9 @@ xg_create_frame_widgets (FRAME_PTR f) /* Since GTK clears its window by filling with the background color, we must keep X and GTK background in sync. */ - xg_pix_to_gcolor (wfixed, FRAME_BACKGROUND_PIXEL (f), &bg); - gtk_widget_modify_bg (wfixed, GTK_STATE_NORMAL, &bg); + xg_set_widget_bg (f, wfixed, FRAME_BACKGROUND_PIXEL (f)); +#ifndef HAVE_GTK3 /* Also, do not let any background pixmap to be set, this looks very bad as Emacs overwrites the background pixmap with its own idea of background color. */ @@ -1068,13 +1158,16 @@ xg_create_frame_widgets (FRAME_PTR f) /* Must use g_strdup because gtk_widget_modify_style does g_free. */ style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup (""); gtk_widget_modify_style (wfixed, style); +#else + gtk_widget_set_can_focus (wfixed, TRUE); +#endif #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"); + gtk_widget_set_tooltip_text (wtop, "Dummy text"); g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f); #endif @@ -1104,7 +1197,9 @@ xg_free_frame_widgets (FRAME_PTR f) { if (FRAME_GTK_OUTER_WIDGET (f)) { +#ifdef USE_GTK_TOOLTIP struct x_output *x = f->output_data.x; +#endif 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; @@ -1224,11 +1319,8 @@ xg_set_background_color (FRAME_PTR f, long unsigned int bg) { if (FRAME_GTK_WIDGET (f)) { - GdkColor gdk_bg; - BLOCK_INPUT; - xg_pix_to_gcolor (FRAME_GTK_WIDGET (f), bg, &gdk_bg); - gtk_widget_modify_bg (FRAME_GTK_WIDGET (f), GTK_STATE_NORMAL, &gdk_bg); + xg_set_widget_bg (f, FRAME_GTK_WIDGET (f), FRAME_BACKGROUND_PIXEL (f)); UNBLOCK_INPUT; } } @@ -1240,11 +1332,10 @@ xg_set_background_color (FRAME_PTR f, long unsigned int bg) void 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); - GdkPixmap *gmask = gdk_pixmap_foreign_new_for_display (gdpy, icon_mask); - GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (gpix, gmask, NULL); - + GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (f, + icon_pixmap, + icon_mask); + if (gp) gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp); } @@ -1326,8 +1417,6 @@ create_dialog (widget_value *wv, GtkDialog *wd = GTK_DIALOG (wdialog); GtkBox *cur_box = GTK_BOX (gtk_dialog_get_action_area (wd)); widget_value *item; - GtkWidget *wvbox; - GtkWidget *whbox_up; GtkWidget *whbox_down; /* If the number of buttons is greater than 4, make two rows of buttons @@ -1343,8 +1432,8 @@ create_dialog (widget_value *wv, if (make_two_rows) { - wvbox = gtk_vbox_new (TRUE, button_spacing); - whbox_up = gtk_hbox_new (FALSE, 0); + GtkWidget *wvbox = gtk_vbox_new (TRUE, button_spacing); + GtkWidget *whbox_up = gtk_hbox_new (FALSE, 0); whbox_down = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (cur_box, wvbox, FALSE, FALSE, 0); @@ -1381,7 +1470,7 @@ create_dialog (widget_value *wv, /* 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_widget_get_preferred_size (w, NULL, &req); gtk_box_set_spacing (wvbox, req.height); if (item->value && strlen (item->value) > 0) button_spacing = 2*req.width/strlen (item->value); @@ -1453,7 +1542,7 @@ pop_down_dialog (Lisp_Object arg) g_main_loop_quit (dd->loop); g_main_loop_unref (dd->loop); - + UNBLOCK_INPUT; return Qnil; @@ -1466,7 +1555,7 @@ static gboolean xg_maybe_add_timer (gpointer data) { struct xg_dialog_data *dd = (struct xg_dialog_data *) data; - EMACS_TIME next_time = timer_check (1); + EMACS_TIME next_time = timer_check (); long secs = EMACS_SECS (next_time); long usecs = EMACS_USECS (next_time); @@ -1481,7 +1570,7 @@ xg_maybe_add_timer (gpointer 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. */ @@ -1515,7 +1604,7 @@ xg_dialog_run (FRAME_PTR f, GtkWidget *w) (void) xg_maybe_add_timer (&dd); g_main_loop_run (dd.loop); - + dd.w = 0; unbind_to (count, Qnil); @@ -1533,7 +1622,6 @@ int xg_uses_old_file_dialog (void) { #ifdef HAVE_GTK_FILE_SELECTION_NEW - extern int x_gtk_use_old_file_dialog; return x_gtk_use_old_file_dialog; #else return 0; @@ -1574,8 +1662,6 @@ xg_toggle_visibility_cb (GtkWidget *widget, gpointer data) static void 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); @@ -1616,16 +1702,13 @@ xg_get_file_with_chooser (FRAME_PTR f, int mustmatch_p, int only_dir_p, xg_get_file_func *func) { - char message[1024]; + char msgbuf[1024]; GtkWidget *filewin, *wtoggle, *wbox, *wmessage; GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)); 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; @@ -1655,16 +1738,16 @@ xg_get_file_with_chooser (FRAME_PTR f, if (x_gtk_file_dialog_help_text) { - message[0] = '\0'; + msgbuf[0] = '\0'; /* Gtk+ 2.10 has the file name text entry box integrated in the dialog. Show the C-l help text only for versions < 2.10. */ if (gtk_check_version (2, 10, 0) && action != GTK_FILE_CHOOSER_ACTION_SAVE) - strcat (message, "\nType C-l to display a file name text entry box.\n"); - strcat (message, "\nIf you don't like this file selector, use the " + strcat (msgbuf, "\nType C-l to display a file name text entry box.\n"); + strcat (msgbuf, "\nIf you don't like this file selector, use the " "corresponding\nkey binding or customize " "use-file-dialog to turn it off."); - wmessage = gtk_label_new (message); + wmessage = gtk_label_new (msgbuf); gtk_widget_show (wmessage); } @@ -2127,54 +2210,6 @@ make_menu_item (const char *utf8_label, 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 (const 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. */ @@ -2373,7 +2408,7 @@ create_menus (widget_value *data, 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 @@ -2386,7 +2421,7 @@ create_menus (widget_value *data, gtk_widget_set_sensitive (w, FALSE); 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. */ @@ -2498,7 +2533,7 @@ xg_create_widget (const char *type, const char *name, FRAME_PTR f, widget_value static const char * 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); } @@ -2651,7 +2686,7 @@ xg_update_menubar (GtkWidget *menubar, 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); @@ -2750,7 +2785,7 @@ xg_update_menu_item (widget_value *val, 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); @@ -2909,7 +2944,7 @@ xg_update_submenu (GtkWidget *submenu, 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)) @@ -2932,7 +2967,7 @@ xg_update_submenu (GtkWidget *submenu, 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); @@ -3073,6 +3108,23 @@ xg_modify_menubar_widgets (GtkWidget *menubar, FRAME_PTR f, widget_value *val, 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_get_preferred_size (w, NULL, &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. */ @@ -3094,10 +3146,20 @@ xg_update_frame_menubar (FRAME_PTR 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_or_width_changed (f); + gtk_widget_get_preferred_size (x->menubar_widget, NULL, &req); + + /* 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; @@ -3147,7 +3209,7 @@ xg_event_is_for_menubar (FRAME_PTR f, XEvent *event) return 0; gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f)); - gw = gdk_xid_table_lookup_for_display (gdpy, event->xbutton.window); + gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window); if (! gw) return 0; gevent.any.window = gw; gwdesc = gtk_get_event_widget (&gevent); @@ -3311,8 +3373,12 @@ xg_create_scroll_bar (FRAME_PTR f, { GtkWidget *wscroll; GtkWidget *webox; - GtkObject *vadj; int scroll_id; +#ifdef HAVE_GTK3 + GtkAdjustment *vadj; +#else + GtkObject *vadj; +#endif /* Page, step increment values are not so important here, they will be corrected in x_set_toolkit_scroll_bar_thumb. */ @@ -3322,7 +3388,9 @@ xg_create_scroll_bar (FRAME_PTR f, wscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT (vadj)); webox = gtk_event_box_new (); gtk_widget_set_name (wscroll, scroll_bar_name); +#ifndef HAVE_GTK3 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS); +#endif g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f); scroll_id = xg_store_widget_in_map (wscroll); @@ -3340,7 +3408,7 @@ xg_create_scroll_bar (FRAME_PTR f, "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 @@ -3429,11 +3497,11 @@ xg_update_scrollbar_pos (FRAME_PTR f, 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); @@ -3501,7 +3569,7 @@ xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar, changed = 1; } - if (changed || (int) gtk_range_get_value (GTK_RANGE (wscroll)) != value) + if (changed || int_gtk_range_get_value (GTK_RANGE (wscroll)) != value) { BLOCK_INPUT; @@ -3509,7 +3577,7 @@ xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar, ignore_gtk_scrollbar to make the callback do nothing */ xg_ignore_gtk_scrollbar = 1; - if ((int) gtk_range_get_value (GTK_RANGE (wscroll)) != value) + if (int_gtk_range_get_value (GTK_RANGE (wscroll)) != value) gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value); else if (changed) gtk_adjustment_changed (adj); @@ -3548,7 +3616,7 @@ xg_event_is_for_scrollbar (FRAME_PTR f, XEvent *event) GtkWidget *w = gtk_grab_get_current (); retval = w != 0 && GTK_IS_SCROLLBAR (w); } - + return retval; } @@ -3605,8 +3673,8 @@ xg_tool_bar_callback (GtkWidget *w, gpointer client_data) { /* The EMACS_INT cast avoids a warning. */ int idx = (int) (EMACS_INT) client_data; - int mod = (int) (EMACS_INT) g_object_get_data (G_OBJECT (w), - XG_TOOL_BAR_LAST_MODIFIER); + gpointer gmod = g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_LAST_MODIFIER); + int mod = (int) (EMACS_INT) gmod; FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA); Lisp_Object key, frame; @@ -3636,7 +3704,7 @@ xg_tool_bar_callback (GtkWidget *w, gpointer 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); @@ -3673,7 +3741,7 @@ xg_tool_bar_proxy_help_callback (GtkWidget *w, { 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); } @@ -3682,7 +3750,8 @@ 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 = (GtkWidget *) clist->next->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; @@ -3697,14 +3766,13 @@ xg_get_tool_bar_widgets (GtkWidget *vb, GtkWidget **wimage) static gboolean 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 *vb = gtk_bin_get_child (GTK_BIN (wbutton)); + 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)); + (wlbl ? gtk_label_get_text (wlbl) : ""); GtkWidget *wmenuimage; @@ -3716,6 +3784,8 @@ xg_tool_bar_menu_proxy (GtkToolItem *toolitem, gpointer user_data) GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (wbutton)); GtkImageType store_type = gtk_image_get_storage_type (wimage); + g_object_set (G_OBJECT (settings), "gtk-menu-images", TRUE, NULL); + if (store_type == GTK_IMAGE_STOCK) { gchar *stock_id; @@ -3774,7 +3844,7 @@ xg_tool_bar_menu_proxy (GtkToolItem *toolitem, gpointer 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); @@ -3810,7 +3880,6 @@ xg_tool_bar_detach_callback (GtkHandleBox *wbox, 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); @@ -3819,8 +3888,8 @@ xg_tool_bar_detach_callback (GtkHandleBox *wbox, { GtkRequisition req, req2; FRAME_X_OUTPUT (f)->toolbar_detached = 1; - gtk_widget_size_request (GTK_WIDGET (wbox), &req); - gtk_widget_size_request (w, &req2); + gtk_widget_get_preferred_size (GTK_WIDGET (wbox), NULL, &req); + gtk_widget_get_preferred_size (w, NULL, &req2); req.width -= req2.width; req.height -= req2.height; if (FRAME_TOOLBAR_TOP_HEIGHT (f) != 0) @@ -3854,8 +3923,8 @@ xg_tool_bar_attach_callback (GtkHandleBox *wbox, { GtkRequisition req, req2; FRAME_X_OUTPUT (f)->toolbar_detached = 0; - gtk_widget_size_request (GTK_WIDGET (wbox), &req); - gtk_widget_size_request (w, &req2); + gtk_widget_get_preferred_size (GTK_WIDGET (wbox), NULL, &req); + gtk_widget_get_preferred_size (w, NULL, &req2); req.width += req2.width; req.height += req2.height; if (FRAME_TOOLBAR_TOP_HEIGHT (f) != 0) @@ -3920,6 +3989,7 @@ xg_tool_bar_help_callback (GtkWidget *w, Returns FALSE to tell GTK to keep processing this event. */ +#ifndef HAVE_GTK3 static gboolean xg_tool_bar_item_expose_callback (GtkWidget *w, GdkEventExpose *event, @@ -3928,7 +3998,6 @@ xg_tool_bar_item_expose_callback (GtkWidget *w, gint width, height; gdk_drawable_get_size (event->window, &width, &height); - event->area.x -= width > event->area.width ? width-event->area.width : 0; event->area.y -= height > event->area.height ? height-event->area.height : 0; @@ -3940,6 +4009,7 @@ xg_tool_bar_item_expose_callback (GtkWidget *w, return FALSE; } +#endif #ifdef HAVE_GTK_ORIENTABLE_SET_ORIENTATION #define toolbar_set_orientation(w, o) \ @@ -3972,8 +4042,10 @@ xg_pack_tool_bar (FRAME_PTR f, Lisp_Object pos) x->toolbar_widget); } - if (into_hbox) + 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); @@ -3986,6 +4058,8 @@ xg_pack_tool_bar (FRAME_PTR f, Lisp_Object pos) 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); @@ -4052,23 +4126,17 @@ xg_make_tool_item (FRAME_PTR f, GtkWidget *wimage, GtkWidget **wbutton, const char *label, - int i) + int i, int horiz, int text_image) { GtkToolItem *ti = gtk_tool_item_new (); - Lisp_Object style = Ftool_bar_get_system_style (); - int both_horiz = EQ (style, Qboth_horiz); - int text_image = EQ (style, Qtext_image_horiz); - - GtkWidget *vb = both_horiz || text_image - ? gtk_hbox_new (FALSE, 0) : gtk_vbox_new (FALSE, 0); + 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) + if (wimage && !text_image) gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0); - - gtk_box_pack_start (GTK_BOX (vb), gtk_label_new (label), 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); @@ -4091,13 +4159,14 @@ xg_make_tool_item (FRAME_PTR f, g_object_set_data (G_OBJECT (weventbox), XG_FRAME_DATA, (gpointer)f); +#ifndef HAVE_GTK3 /* 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); - +#endif gtk_tool_item_set_homogeneous (ti, FALSE); /* Callback to save modifyer mask (Shift/Control, etc). GTK makes @@ -4108,7 +4177,7 @@ xg_make_tool_item (FRAME_PTR f, 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 @@ -4123,63 +4192,55 @@ xg_make_tool_item (FRAME_PTR f, G_CALLBACK (xg_tool_bar_help_callback), (gpointer) (EMACS_INT) i); } - + if (wbutton) *wbutton = wb; return ti; } -static void -xg_show_toolbar_item (GtkToolItem *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) { - Lisp_Object style = Ftool_bar_get_system_style (); - int both_horiz = EQ (style, Qboth_horiz); - int text_image = EQ (style, Qtext_image_horiz); - - int horiz = both_horiz || text_image; - int show_label = ! EQ (style, Qimage); - int show_image = ! EQ (style, Qtext); - - GtkWidget *weventbox = gtk_bin_get_child (GTK_BIN (ti)); - GtkWidget *wbutton = gtk_bin_get_child (GTK_BIN (weventbox)); - GtkWidget *vb = gtk_bin_get_child (GTK_BIN (wbutton)); + gpointer old; GtkWidget *wimage; + GtkWidget *vb = XG_BIN_CHILD (wbutton); GtkWidget *wlbl = xg_get_tool_bar_widgets (vb, &wimage); - GtkWidget *new_box = NULL; - if (GTK_IS_VBOX (vb) && horiz) - new_box = gtk_hbox_new (FALSE, 0); - else if (GTK_IS_HBOX (vb) && !horiz && show_label && show_image) - new_box = gtk_vbox_new (FALSE, 0); - - if (!new_box && horiz) - gtk_box_reorder_child (GTK_BOX (vb), wlbl, text_image ? 0 : 1); - else if (new_box) + /* 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 { - g_object_ref (G_OBJECT (wimage)); - g_object_ref (G_OBJECT (wlbl)); - gtk_container_remove (GTK_CONTAINER (vb), wimage); - gtk_container_remove (GTK_CONTAINER (vb), wlbl); - gtk_widget_destroy (GTK_WIDGET (vb)); - if (! text_image) - gtk_box_pack_start (GTK_BOX (new_box), wimage, TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (new_box), wlbl, TRUE, TRUE, 0); - if (text_image) - gtk_box_pack_start (GTK_BOX (new_box), wimage, TRUE, TRUE, 0); - gtk_container_add (GTK_CONTAINER (wbutton), new_box); - g_object_unref (G_OBJECT (wimage)); - g_object_unref (G_OBJECT (wlbl)); - vb = new_box; + gpointer gold_img = g_object_get_data (G_OBJECT (wimage), + XG_TOOL_BAR_IMAGE_DATA); + Pixmap old_img = (Pixmap) gold_img; + if (old_img != img->pixmap) + return 1; } - if (show_label) gtk_widget_show (wlbl); - else gtk_widget_hide (wlbl); - if (show_image) gtk_widget_show (wimage); - else gtk_widget_hide (wimage); - gtk_widget_show (GTK_WIDGET (weventbox)); - gtk_widget_show (GTK_WIDGET (vb)); - gtk_widget_show (GTK_WIDGET (wbutton)); - gtk_widget_show (GTK_WIDGET (ti)); + /* 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 @@ -4189,7 +4250,7 @@ xg_update_tool_bar_sizes (FRAME_PTR f) GtkRequisition req; int nl = 0, nr = 0, nt = 0, nb = 0; - gtk_widget_size_request (GTK_WIDGET (x->handlebox_widget), &req); + gtk_widget_get_preferred_size (GTK_WIDGET (x->handlebox_widget), NULL, &req); if (x->toolbar_in_hbox) { int pos; @@ -4208,7 +4269,7 @@ xg_update_tool_bar_sizes (FRAME_PTR f) 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) @@ -4232,13 +4293,15 @@ xg_update_tool_bar_sizes (FRAME_PTR f) void update_frame_tool_bar (FRAME_PTR f) { - int i; + int i, j; struct x_output *x = f->output_data.x; int hmargin = 0, vmargin = 0; GtkToolbar *wtoolbar; GtkToolItem *ti; GtkTextDirection dir; int pack_tool_bar = x->handlebox_widget == NULL; + Lisp_Object style; + int text_image, horiz; if (! FRAME_GTK_WIDGET (f)) return; @@ -4274,8 +4337,12 @@ update_frame_tool_bar (FRAME_PTR f) wtoolbar = GTK_TOOLBAR (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)); @@ -4290,26 +4357,48 @@ update_frame_tool_bar (FRAME_PTR f) char *icon_name = NULL; Lisp_Object rtl; GtkWidget *wbutton = NULL; - GtkWidget *weventbox; Lisp_Object specified_file; - const char *label = (STRINGP (PROP (TOOL_BAR_ITEM_LABEL)) - ? SSDATA (PROP (TOOL_BAR_ITEM_LABEL)) : ""); - - ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), 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; } @@ -4345,16 +4434,13 @@ update_frame_tool_bar (FRAME_PTR 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)) { @@ -4380,21 +4466,31 @@ update_frame_tool_bar (FRAME_PTR 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 */ - ti = xg_make_tool_item (f, NULL, NULL, "", i); - gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, -1); - } + 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, @@ -4411,92 +4507,34 @@ update_frame_tool_bar (FRAME_PTR 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); - ti = xg_make_tool_item (f, w, &wbutton, label, i); - gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, -1); - gtk_widget_set_sensitive (wbutton, enabled_p); - } - else - { - GtkWidget *vb = gtk_bin_get_child (GTK_BIN (wbutton)); - GtkWidget *wimage; - GtkWidget *wlbl = xg_get_tool_bar_widgets (vb, &wimage); - - 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); - gtk_label_set_text (GTK_LABEL (wlbl), label); - 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, - GTK_IMAGE (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); + 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); } - xg_show_toolbar_item (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 (wtoolbar), 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); if (f->n_tool_bar_items != 0) { if (pack_tool_bar) xg_pack_tool_bar (f, f->tool_bar_position); - gtk_widget_show (x->toolbar_widget); - gtk_widget_show (x->handlebox_widget); + gtk_widget_show_all (GTK_WIDGET (x->handlebox_widget)); if (xg_update_tool_bar_sizes (f)) xg_height_or_width_changed (f); } @@ -4518,7 +4556,7 @@ free_frame_tool_bar (FRAME_PTR f) BLOCK_INPUT; /* 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) + if (is_packed) { if (x->toolbar_in_hbox) gtk_container_remove (GTK_CONTAINER (x->hbox_widget), @@ -4609,17 +4647,14 @@ xg_initialize (void) /* Make dialogs close on C-g. Since file dialog inherits from dialog, this works for them also. */ binding_set = gtk_binding_set_by_class (g_type_class_ref (GTK_TYPE_DIALOG)); - gtk_binding_entry_add_signal (binding_set, GDK_g, GDK_CONTROL_MASK, + gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK, "close", 0); /* Make menus close on C-g. */ binding_set = gtk_binding_set_by_class (g_type_class_ref (GTK_TYPE_MENU_SHELL)); - gtk_binding_entry_add_signal (binding_set, GDK_g, GDK_CONTROL_MASK, + gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK, "cancel", 0); } #endif /* USE_GTK */ - -/* arch-tag: fe7104da-bc1e-4aba-9bd1-f349c528f7e3 - (do not change this comment) */