Prefer intptr_t/uintptr_t for integers the same widths as pointers.
[bpt/emacs.git] / src / gtkutil.c
index fd89015..ff33a3f 100644 (file)
@@ -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 <http://www.gnu.org/licenses/>.  */
 #include <config.h>
 
 #ifdef USE_GTK
-#include <string.h>
 #include <signal.h>
 #include <stdio.h>
 #include <setjmp.h>
@@ -41,15 +40,16 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <X11/Xft/Xft.h>
 #endif
 
+#ifdef HAVE_GTK3
+#include <gtk/gtkx.h>
+#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,28 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #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)
+#ifndef GDK_KEY_g
+#define GDK_KEY_g GDK_g
+#endif
+#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);
+}
+
 \f
 /***********************************************************************
                       Display handling functions
@@ -90,7 +112,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 +253,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 +366,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 +393,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 +402,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 +496,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 +519,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 +536,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 +555,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 +614,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 +635,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 +644,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 +685,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))),
@@ -649,11 +705,11 @@ xg_prepare_tooltip (FRAME_PTR f,
      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);
+  gtk_tooltip_set_text (x->ttip_widget, SSDATA (encoded_string));
+  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 +752,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 +853,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 +966,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 +979,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 +1024,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 +1055,6 @@ xg_create_frame_widgets (FRAME_PTR f)
   GtkWidget *wtop;
   GtkWidget *wvbox, *whbox;
   GtkWidget *wfixed;
-  GdkColor bg;
   GtkRcStyle *style;
   char *title = 0;
 
@@ -1029,7 +1121,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 +1149,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 +1160,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 ("<none>");
   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 +1199,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 +1321,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 +1334,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 +1419,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 +1434,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 +1472,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 +1544,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 +1557,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 +1572,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 +1606,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 +1624,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 +1664,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 +1704,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;
+  GtkWidget *filewin, *wtoggle, *wbox, *wmessage IF_LINT (= NULL);
   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 +1740,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 +2212,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 +2410,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 +2423,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 +2535,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 +2688,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 +2787,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 +2946,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 +2969,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 +3110,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 +3148,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 +3211,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);
@@ -3290,7 +3354,7 @@ xg_get_scroll_id_for_window (Display *dpy, Window wid)
 static void
 xg_gtk_scroll_destroy (GtkWidget *widget, gpointer data)
 {
-  int id = (int) (EMACS_INT) data; /* The EMACS_INT cast avoids a warning. */
+  int id = (EMACS_INTPTR) data;
   xg_remove_widget_from_map (id);
 }
 
@@ -3311,8 +3375,12 @@ xg_create_scroll_bar (FRAME_PTR f,
 {
   GtkWidget *wscroll;
   GtkWidget *webox;
+  EMACS_INTPTR scroll_id;
+#ifdef HAVE_GTK3
+  GtkAdjustment *vadj;
+#else
   GtkObject *vadj;
-  int scroll_id;
+#endif
 
   /* Page, step increment values are not so important here, they
      will be corrected in x_set_toolkit_scroll_bar_thumb. */
@@ -3322,16 +3390,17 @@ 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);
 
-  /* The EMACS_INT cast avoids a warning. */
   g_signal_connect (G_OBJECT (wscroll),
                     "destroy",
                     G_CALLBACK (xg_gtk_scroll_destroy),
-                    (gpointer) (EMACS_INT) scroll_id);
+                    (gpointer) scroll_id);
   g_signal_connect (G_OBJECT (wscroll),
                     "change-value",
                     scroll_callback,
@@ -3340,7 +3409,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 +3498,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);
@@ -3459,6 +3528,7 @@ xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
       gdouble shown;
       gdouble top;
       int size, value;
+      int old_size;
       int new_step;
       int changed = 0;
 
@@ -3491,17 +3561,21 @@ xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
       /* Assume all lines are of equal size.  */
       new_step = size / max (1, FRAME_LINES (f));
 
-      if ((int) gtk_adjustment_get_page_size (adj) != size
-          || (int) gtk_adjustment_get_step_increment (adj) != 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  */
-          gtk_adjustment_set_page_increment (adj,(int) (0.95*size));
-          changed = 1;
-        }
-
-      if (changed || (int) gtk_range_get_value (GTK_RANGE (wscroll)) != value)
+      old_size = gtk_adjustment_get_page_size (adj);
+      if (old_size != size)
+       {
+         int old_step = gtk_adjustment_get_step_increment (adj);
+         if (old_step != 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  */
+             gtk_adjustment_set_page_increment (adj,(int) (0.95*size));
+             changed = 1;
+           }
+       }
+
+      if (changed || int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
       {
         BLOCK_INPUT;
 
@@ -3509,7 +3583,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 +3622,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;
 }
 
@@ -3588,8 +3662,8 @@ 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;
+  EMACS_INTPTR state = event->state;
+  gpointer ptr = (gpointer) state;
   g_object_set_data (G_OBJECT (widget), XG_TOOL_BAR_LAST_MODIFIER, ptr);
   return FALSE;
 }
@@ -3603,10 +3677,9 @@ xg_tool_bar_button_cb (GtkWidget *widget,
 static void
 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);
+  EMACS_INTPTR idx = (EMACS_INTPTR) client_data;
+  gpointer gmod = g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_LAST_MODIFIER);
+  EMACS_INTPTR mod = (EMACS_INTPTR) gmod;
 
   FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
   Lisp_Object key, frame;
@@ -3636,7 +3709,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 +3746,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 +3755,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 +3771,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 +3789,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 +3849,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 +3885,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 +3893,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 +3928,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)
@@ -3884,8 +3958,7 @@ 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;
+  EMACS_INTPTR idx = (EMACS_INTPTR) client_data;
   FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
   Lisp_Object help, frame;
 
@@ -3920,6 +3993,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 +4002,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 +4013,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 +4046,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 +4062,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 +4130,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);
 
@@ -4080,24 +4152,27 @@ xg_make_tool_item (FRAME_PTR f,
 
   if (wimage)
     {
-      /* The EMACS_INT cast avoids a warning. */
+      EMACS_INTPTR ii = i;
+      gpointer gi = (gpointer) ii;
+
       g_signal_connect (G_OBJECT (ti), "create-menu-proxy",
                         G_CALLBACK (xg_tool_bar_menu_proxy),
-                        (gpointer) (EMACS_INT) i);
+                        gi);
 
       g_signal_connect (G_OBJECT (wb), "clicked",
                         G_CALLBACK (xg_tool_bar_callback),
-                        (gpointer) (EMACS_INT) i);
+                        gi);
 
       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 +4183,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
@@ -4117,69 +4192,61 @@ xg_make_tool_item (FRAME_PTR f,
       g_signal_connect (G_OBJECT (weventbox),
                         "enter-notify-event",
                         G_CALLBACK (xg_tool_bar_help_callback),
-                        (gpointer) (EMACS_INT) i);
+                        gi);
       g_signal_connect (G_OBJECT (weventbox),
                         "leave-notify-event",
                         G_CALLBACK (xg_tool_bar_help_callback),
-                        (gpointer) (EMACS_INT) i);
+                        gi);
     }
-  
+
   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 +4256,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 +4275,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 +4299,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 +4343,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,25 +4363,48 @@ update_frame_tool_bar (FRAME_PTR f)
       char *icon_name = NULL;
       Lisp_Object rtl;
       GtkWidget *wbutton = NULL;
-      GtkWidget *weventbox;
       Lisp_Object specified_file;
-      char *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;
         }
 
@@ -4344,16 +4440,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))
             {
@@ -4379,21 +4472,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,
@@ -4410,92 +4513,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);
+         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 *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);
-        }
-      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);
     }
@@ -4517,7 +4562,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),
@@ -4608,17 +4653,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) */