]> code.delx.au - gnu-emacs/blobdiff - src/gtkutil.c
TODO update
[gnu-emacs] / src / gtkutil.c
index 44be22785233de941de600b5de4d24ad24245d75..e76b0a733a0bc1b4d8b98ac7d9c1210c7256d1a4 100644 (file)
@@ -1,6 +1,6 @@
 /* Functions for creating and updating GTK widgets.
-   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
-     Free Software Foundation, Inc.
+
+Copyright (C) 2003-2013 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -20,10 +20,11 @@ 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 <float.h>
 #include <stdio.h>
-#include <setjmp.h>
+
+#include <c-ctype.h>
+
 #include "lisp.h"
 #include "xterm.h"
 #include "blockinput.h"
@@ -35,24 +36,96 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "charset.h"
 #include "coding.h"
 #include <gdk/gdkkeysyms.h>
+#include "xsettings.h"
 
 #ifdef HAVE_XFT
 #include <X11/Xft/Xft.h>
 #endif
 
+#ifdef HAVE_GTK3
+#include <gtk/gtkx.h>
+#include "emacsgtkfixed.h"
+#endif
+
 #define FRAME_TOTAL_PIXEL_HEIGHT(f) \
   (FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
 
-/* Avoid "differ in sign" warnings */
-#define SSDATA(x)  ((char *) SDATA (x))
+#define FRAME_TOTAL_PIXEL_WIDTH(f) \
+  (FRAME_PIXEL_WIDTH (f) + FRAME_TOOLBAR_WIDTH (f))
+
+#ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
+#define gtk_widget_set_has_window(w, b) \
+  (gtk_fixed_set_has_window (GTK_FIXED (w), b))
+#endif
+#ifndef HAVE_GTK_DIALOG_GET_ACTION_AREA
+#define gtk_dialog_get_action_area(w) ((w)->action_area)
+#define gtk_dialog_get_content_area(w) ((w)->vbox)
+#endif
+#ifndef HAVE_GTK_WIDGET_GET_SENSITIVE
+#define gtk_widget_get_sensitive(w) (GTK_WIDGET_SENSITIVE (w))
+#endif
+#ifndef HAVE_GTK_ADJUSTMENT_GET_PAGE_SIZE
+#define gtk_adjustment_set_page_size(w, s) ((w)->page_size = (s))
+#define gtk_adjustment_set_page_increment(w, s) ((w)->page_increment = (s))
+#define gtk_adjustment_get_step_increment(w) ((w)->step_increment)
+#define gtk_adjustment_set_step_increment(w, s) ((w)->step_increment = (s))
+#endif
+#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 11
+#define remove_submenu(w) gtk_menu_item_set_submenu ((w), NULL)
+#else
+#define remove_submenu(w) gtk_menu_item_remove_submenu ((w))
+#endif
+
+#if GTK_MAJOR_VERSION > 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 2)
+#define USE_NEW_GTK_FONT_CHOOSER 1
+#else
+#define USE_NEW_GTK_FONT_CHOOSER 0
+#define gtk_font_chooser_dialog_new(x, y) \
+  gtk_font_selection_dialog_new (x)
+#undef GTK_FONT_CHOOSER
+#define GTK_FONT_CHOOSER(x) GTK_FONT_SELECTION_DIALOG (x)
+#define  gtk_font_chooser_set_font(x, y) \
+  gtk_font_selection_dialog_set_font_name (x, y)
+#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 gtk_box_new(ori, spacing)                                       \
+  ((ori) == GTK_ORIENTATION_HORIZONTAL                                  \
+   ? gtk_hbox_new (FALSE, (spacing)) : gtk_vbox_new (FALSE, (spacing)))
+#define gtk_scrollbar_new(ori, spacing)                                 \
+  ((ori) == GTK_ORIENTATION_HORIZONTAL                                  \
+   ? gtk_hscrollbar_new ((spacing)) : gtk_vscrollbar_new ((spacing)))
+#ifndef GDK_KEY_g
+#define GDK_KEY_g GDK_g
+#endif
+#endif /* HAVE_GTK3 */
+
+#define XG_BIN_CHILD(x) gtk_bin_get_child (GTK_BIN (x))
+
+static void update_theme_scrollbar_width (void);
+
+#define TB_INFO_KEY "xg_frame_tb_info"
+struct xg_frame_tb_info
+{
+  Lisp_Object last_tool_bar;
+  Lisp_Object style;
+  int n_last_items;
+  int hmargin, vmargin;
+  GtkTextDirection dir;
+};
 
 \f
 /***********************************************************************
                       Display handling functions
  ***********************************************************************/
 
-#ifdef HAVE_GTK_MULTIDISPLAY
-
 /* Keep track of the default display, or NULL if there is none.  Emacs
    may close all its displays.  */
 
@@ -63,11 +136,9 @@ static GdkDisplay *gdpy_def;
    W can be a GtkMenu or a GtkWindow widget.  */
 
 static void
-xg_set_screen (w, f)
-     GtkWidget *w;
-     FRAME_PTR f;
+xg_set_screen (GtkWidget *w, FRAME_PTR f)
 {
-  if (FRAME_X_DISPLAY (f) != GDK_DISPLAY ())
+  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);
@@ -80,33 +151,16 @@ xg_set_screen (w, f)
 }
 
 
-#else /* not HAVE_GTK_MULTIDISPLAY */
-
-/* Make some defines so we can use the GTK 2.2 functions when
-   compiling with GTK 2.0.  */
-
-#define xg_set_screen(w, f)
-#define gdk_xid_table_lookup_for_display(dpy, w)    gdk_xid_table_lookup (w)
-#define gdk_pixmap_foreign_new_for_display(dpy, p)  gdk_pixmap_foreign_new (p)
-#define gdk_cursor_new_for_display(dpy, c)          gdk_cursor_new (c)
-#define gdk_x11_lookup_xdisplay(dpy)                0
-#define GdkDisplay                                  void
-
-#endif /* not HAVE_GTK_MULTIDISPLAY */
-
 /* Open a display named by DISPLAY_NAME.  The display is returned in *DPY.
    *DPY is set to NULL if the display can't be opened.
 
    Returns non-zero if display could be opened, zero if display could not
    be opened, and less than zero if the GTK version doesn't support
-   multipe displays.  */
+   multiple displays.  */
 
-int
-xg_display_open (display_name, dpy)
-     char *display_name;
-     Display **dpy;
+void
+xg_display_open (char *display_name, Display **dpy)
 {
-#ifdef HAVE_GTK_MULTIDISPLAY
   GdkDisplay *gdpy;
 
   gdpy = gdk_display_open (display_name);
@@ -118,12 +172,6 @@ xg_display_open (display_name, dpy)
     }
 
   *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
-  return gdpy != NULL;
-
-#else /* not HAVE_GTK_MULTIDISPLAY */
-
-  return -1;
-#endif /* not HAVE_GTK_MULTIDISPLAY */
 }
 
 
@@ -132,7 +180,6 @@ xg_display_open (display_name, dpy)
 void
 xg_display_close (Display *dpy)
 {
-#ifdef HAVE_GTK_MULTIDISPLAY
   GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
 
   /* If this is the default display, try to change it before closing.
@@ -164,7 +211,6 @@ xg_display_close (Display *dpy)
   /* This seems to be fixed in GTK 2.10. */
   gdk_display_close (gdpy);
 #endif
-#endif /* HAVE_GTK_MULTIDISPLAY */
 }
 
 \f
@@ -181,7 +227,7 @@ static int malloc_cpt;
    Return a pointer to the allocated structure.  */
 
 widget_value *
-malloc_widget_value ()
+malloc_widget_value (void)
 {
   widget_value *wv;
   if (widget_value_free_list)
@@ -192,7 +238,7 @@ malloc_widget_value ()
     }
   else
     {
-      wv = (widget_value *) xmalloc (sizeof (widget_value));
+      wv = xmalloc (sizeof *wv);
       malloc_cpt++;
     }
   memset (wv, 0, sizeof (widget_value));
@@ -203,11 +249,10 @@ malloc_widget_value ()
    by malloc_widget_value, and no substructures.  */
 
 void
-free_widget_value (wv)
-     widget_value *wv;
+free_widget_value (widget_value *wv)
 {
   if (wv->free_list)
-    abort ();
+    emacs_abort ();
 
   if (malloc_cpt > 25)
     {
@@ -228,37 +273,61 @@ free_widget_value (wv)
    scroll bars on display DPY.  */
 
 GdkCursor *
-xg_create_default_cursor (dpy)
-     Display *dpy;
+xg_create_default_cursor (Display *dpy)
 {
   GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
   return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
 }
 
+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,
+                                      width,
+                                      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 (gpix, gmask, cmap)
-     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);
@@ -292,12 +361,10 @@ xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap)
 }
 
 static Lisp_Object
-file_for_image (image)
-     Lisp_Object image;
+file_for_image (Lisp_Object image)
 {
   Lisp_Object specified_file = Qnil;
   Lisp_Object tail;
-  extern Lisp_Object QCfile;
 
   for (tail = XCDR (image);
        NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
@@ -310,7 +377,7 @@ file_for_image (image)
 
 /* For the image defined in IMG, make and return a GtkImage.  For displays with
    8 planes or less we must make a GdkPixbuf and apply the mask manually.
-   Otherwise the highlightning and dimming the tool bar code in GTK does
+   Otherwise the highlighting and dimming the tool bar code in GTK does
    will look bad.  For display with more than 8 planes we just use the
    pixmap and mask directly.  For monochrome displays, GTK doesn't seem
    able to use external pixmaps, it looks bad whatever we do.
@@ -320,16 +387,11 @@ file_for_image (image)
    If OLD_WIDGET is not NULL, that widget is modified.  */
 
 static GtkWidget *
-xg_get_image_for_pixmap (f, img, widget, old_widget)
-     FRAME_PTR f;
-     struct image *img;
-     GtkWidget *widget;
-     GtkImage *old_widget;
+xg_get_image_for_pixmap (FRAME_PTR f,
+                         struct image *img,
+                         GtkWidget *widget,
+                         GtkImage *old_widget)
 {
-  GdkPixmap *gpix;
-  GdkPixmap *gmask;
-  GdkDisplay *gdpy;
-  GdkColormap *cmap;
   GdkPixbuf *icon_buf;
 
   /* If we have a file, let GTK do all the image handling.
@@ -357,10 +419,6 @@ xg_get_image_for_pixmap (f, img, widget, old_widget)
      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
@@ -370,18 +428,17 @@ xg_get_image_for_pixmap (f, img, widget, old_widget)
      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);
+  icon_buf = xg_get_pixbuf_from_pix_and_mask (f, img->pixmap, img->mask);
 
-  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 (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);
 }
@@ -392,13 +449,12 @@ xg_get_image_for_pixmap (f, img, widget, old_widget)
    and it is those widgets that are visible.  */
 
 static void
-xg_set_cursor (w, cursor)
-     GtkWidget *w;
-     GdkCursor *cursor;
+xg_set_cursor (GtkWidget *w, GdkCursor *cursor)
 {
-  GList *children = gdk_window_peek_children (w->window);
+  GdkWindow *window = gtk_widget_get_window (w);
+  GList *children = gdk_window_peek_children (window);
 
-  gdk_window_set_cursor (w->window, cursor);
+  gdk_window_set_cursor (window, cursor);
 
   /* The scroll bar widget has more than one GDK window (had to look at
      the source to figure this out), and there is no way to set cursor
@@ -441,63 +497,67 @@ xg_list_remove (xg_list_node *list, xg_list_node *node)
 }
 
 /* Allocate and return a utf8 version of STR.  If STR is already
-   utf8 or NULL, just return STR.
-   If not, a new string is allocated and the caller must free the result
+   utf8 or NULL, just return a copy of STR.
+   A new string is allocated and the caller must free the result
    with g_free.  */
 
 static char *
-get_utf8_string (str)
-     char *str;
+get_utf8_string (const char *str)
 {
-  char *utf8_str = str;
+  char *utf8_str;
 
   if (!str) return NULL;
 
   /* If not UTF-8, try current locale.  */
   if (!g_utf8_validate (str, -1, NULL))
     utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
+  else
+    return g_strdup (str);
 
   if (!utf8_str)
     {
       /* Probably some control characters in str.  Escape them. */
-      size_t nr_bad = 0;
+      ptrdiff_t len;
+      ptrdiff_t nr_bad = 0;
       gsize bytes_read;
       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);
 
-      up = utf8_str = xmalloc (strlen (str) + nr_bad * 4 + 1);
+      len = strlen (str);
+      if ((min (PTRDIFF_MAX, SIZE_MAX) - len - 1) / 4 < nr_bad)
+       memory_full (SIZE_MAX);
+      up = utf8_str = xmalloc (len + nr_bad * 4 + 1);
       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);
+          memcpy (up, 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)
@@ -505,15 +565,248 @@ get_utf8_string (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;
 }
 
+/* Check for special colors used in face spec for region face.
+   The colors are fetched from the Gtk+ theme.
+   Return true if color was found, false if not.  */
+
+bool
+xg_check_special_colors (struct frame *f,
+                         const char *color_name,
+                         XColor *color)
+{
+  bool success_p = 0;
+  bool get_bg = strcmp ("gtk_selection_bg_color", color_name) == 0;
+  bool 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[sizeof "rgbi://" + 3 * (DBL_MAX_10_EXP + sizeof "-1.000000" - 1)];
+    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)
+                != 0);
+#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;
+}
+
+
+\f
+/***********************************************************************
+                              Tooltips
+ ***********************************************************************/
+/* Gtk+ calls this callback when the parent of our tooltip dummy changes.
+   We use that to pop down the tooltip.  This happens if Gtk+ for some
+   reason wants to change or hide the tooltip.  */
+
+#ifdef USE_GTK_TOOLTIP
+
+static void
+hierarchy_ch_cb (GtkWidget *widget,
+                 GtkWidget *previous_toplevel,
+                 gpointer   user_data)
+{
+  FRAME_PTR f = (FRAME_PTR) user_data;
+  struct x_output *x = f->output_data.x;
+  GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl);
+
+  if (! top || ! GTK_IS_WINDOW (top))
+      gtk_widget_hide (previous_toplevel);
+}
+
+/* Callback called when Gtk+ thinks a tooltip should be displayed.
+   We use it to get the tooltip window and the tooltip widget so
+   we can manipulate the ourselves.
+
+   Return FALSE ensures that the tooltip is not shown.  */
+
+static gboolean
+qttip_cb (GtkWidget  *widget,
+          gint        xpos,
+          gint        ypos,
+          gboolean    keyboard_mode,
+          GtkTooltip *tooltip,
+          gpointer    user_data)
+{
+  FRAME_PTR f = (FRAME_PTR) user_data;
+  struct x_output *x = f->output_data.x;
+  if (x->ttip_widget == NULL)
+    {
+      GtkWidget *p;
+      GList *list, *iter;
+
+      g_object_set (G_OBJECT (widget), "has-tooltip", FALSE, NULL);
+      x->ttip_widget = tooltip;
+      g_object_ref (G_OBJECT (tooltip));
+      x->ttip_lbl = gtk_label_new ("");
+      g_object_ref (G_OBJECT (x->ttip_lbl));
+      gtk_tooltip_set_custom (tooltip, x->ttip_lbl);
+      x->ttip_window = GTK_WINDOW (gtk_widget_get_toplevel (x->ttip_lbl));
+
+      /* Change stupid Gtk+ default line wrapping.  */
+      p = gtk_widget_get_parent (x->ttip_lbl);
+      list = gtk_container_get_children (GTK_CONTAINER (p));
+      for (iter = list; iter; iter = g_list_next (iter))
+        {
+          GtkWidget *w = GTK_WIDGET (iter->data);
+          if (GTK_IS_LABEL (w))
+            gtk_label_set_line_wrap (GTK_LABEL (w), FALSE);
+        }
+      g_list_free (list);
+
+      /* ATK needs an empty title for some reason.  */
+      gtk_window_set_title (x->ttip_window, "");
+      /* Realize so we can safely get screen later on.  */
+      gtk_widget_realize (GTK_WIDGET (x->ttip_window));
+      gtk_widget_realize (x->ttip_lbl);
+
+      g_signal_connect (x->ttip_lbl, "hierarchy-changed",
+                        G_CALLBACK (hierarchy_ch_cb), f);
+    }
+  return FALSE;
+}
+
+#endif /* USE_GTK_TOOLTIP */
+
+/* Prepare a tooltip to be shown, i.e. calculate WIDTH and HEIGHT.
+   Return true if a system tooltip is available.  */
+
+bool
+xg_prepare_tooltip (FRAME_PTR f,
+                    Lisp_Object string,
+                    int *width,
+                    int *height)
+{
+#ifndef USE_GTK_TOOLTIP
+  return 0;
+#else
+  struct x_output *x = f->output_data.x;
+  GtkWidget *widget;
+  GdkWindow *gwin;
+  GdkScreen *screen;
+  GtkSettings *settings;
+  gboolean tt_enabled = TRUE;
+  GtkRequisition req;
+  Lisp_Object encoded_string;
+
+  if (!x->ttip_lbl) return 0;
+
+  block_input ();
+  encoded_string = ENCODE_UTF_8 (string);
+  widget = GTK_WIDGET (x->ttip_lbl);
+  gwin = gtk_widget_get_window (GTK_WIDGET (x->ttip_window));
+  screen = gdk_window_get_screen (gwin);
+  settings = gtk_settings_get_for_screen (screen);
+  g_object_get (settings, "gtk-enable-tooltips", &tt_enabled, NULL);
+  if (tt_enabled)
+    {
+      g_object_set (settings, "gtk-enable-tooltips", FALSE, NULL);
+      /* Record that we disabled it so it can be enabled again.  */
+      g_object_set_data (G_OBJECT (x->ttip_window), "restore-tt",
+                         (gpointer)f);
+    }
+
+  /* Prevent Gtk+ from hiding tooltip on mouse move and such.  */
+  g_object_set_data (G_OBJECT
+                     (gtk_widget_get_display (GTK_WIDGET (x->ttip_window))),
+                     "gdk-display-current-tooltip", NULL);
+
+  /* Put our dummy widget in so we can get callbacks for unrealize and
+     hierarchy-changed.  */
+  gtk_tooltip_set_custom (x->ttip_widget, widget);
+  gtk_tooltip_set_text (x->ttip_widget, 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;
+#endif /* USE_GTK_TOOLTIP */
+}
+
+/* Show the tooltip at ROOT_X and ROOT_Y.
+   xg_prepare_tooltip must have been called before this function.  */
+
+void
+xg_show_tooltip (FRAME_PTR f, int root_x, int root_y)
+{
+#ifdef USE_GTK_TOOLTIP
+  struct x_output *x = f->output_data.x;
+  if (x->ttip_window)
+    {
+      block_input ();
+      gtk_window_move (x->ttip_window, root_x, root_y);
+      gtk_widget_show_all (GTK_WIDGET (x->ttip_window));
+      unblock_input ();
+    }
+#endif
+}
+
+/* Hide tooltip if shown.  Do nothing if not shown.
+   Return true if tip was hidden, false if not (i.e. not using
+   system tooltips).  */
+
+bool
+xg_hide_tooltip (FRAME_PTR f)
+{
+  bool ret = 0;
+#ifdef USE_GTK_TOOLTIP
+  if (f->output_data.x->ttip_window)
+    {
+      GtkWindow *win = f->output_data.x->ttip_window;
+      block_input ();
+      gtk_widget_hide (GTK_WIDGET (win));
+
+      if (g_object_get_data (G_OBJECT (win), "restore-tt"))
+        {
+          GdkWindow *gwin = gtk_widget_get_window (GTK_WIDGET (win));
+          GdkScreen *screen = gdk_window_get_screen (gwin);
+          GtkSettings *settings = gtk_settings_get_for_screen (screen);
+          g_object_set (settings, "gtk-enable-tooltips", TRUE, NULL);
+        }
+      unblock_input ();
+
+      ret = 1;
+    }
+#endif
+  return ret;
+}
 
 \f
 /***********************************************************************
@@ -526,16 +819,15 @@ get_utf8_string (str)
    F is the frame we shall set geometry for.  */
 
 static void
-xg_set_geometry (f)
-     FRAME_PTR f;
+xg_set_geometry (FRAME_PTR f)
 {
-  if (f->size_hint_flags & USPosition)
+  if (f->size_hint_flags & (USPosition | PPosition))
     {
       int left = f->left_pos;
       int xneg = f->size_hint_flags & XNegative;
       int top = f->top_pos;
       int yneg = f->size_hint_flags & YNegative;
-      char geom_str[32];
+      char geom_str[sizeof "=x--" + 4 * INT_STRLEN_BOUND (int)];
 
       if (xneg)
         left = -left;
@@ -544,7 +836,7 @@ xg_set_geometry (f)
 
       sprintf (geom_str, "=%dx%d%c%d%c%d",
                FRAME_PIXEL_WIDTH (f),
-               FRAME_TOTAL_PIXEL_HEIGHT (f),
+               FRAME_PIXEL_HEIGHT (f),
                (xneg ? '-' : '+'), left,
                (yneg ? '-' : '+'), top);
 
@@ -552,17 +844,13 @@ xg_set_geometry (f)
                                       geom_str))
         fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
     }
-  else if (f->size_hint_flags & PPosition)
-    gtk_window_move (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                     f->left_pos, f->top_pos);
 }
 
 /* Clear under internal border if any.  As we use a mix of Gtk+ and X calls
    and use a GtkFixed widget, this doesn't happen automatically.  */
 
 static void
-xg_clear_under_internal_border (f)
-     FRAME_PTR f;
+xg_clear_under_internal_border (FRAME_PTR f)
 {
   if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
     {
@@ -600,20 +888,19 @@ xg_clear_under_internal_border (f)
    PIXELWIDTH, PIXELHEIGHT is the new size in pixels.  */
 
 void
-xg_frame_resized (f, pixelwidth, pixelheight)
-     FRAME_PTR f;
-     int pixelwidth, pixelheight;
+xg_frame_resized (FRAME_PTR f, int pixelwidth, int pixelheight)
 {
   int rows, columns;
 
   if (pixelwidth == -1 && pixelheight == -1)
     {
-      if (FRAME_GTK_WIDGET (f) && GTK_WIDGET_MAPPED (FRAME_GTK_WIDGET (f)))
-          gdk_window_get_geometry (FRAME_GTK_WIDGET (f)->window, 0, 0,
-                                   &pixelwidth, &pixelheight, 0);
+      if (FRAME_GTK_WIDGET (f) && gtk_widget_get_mapped (FRAME_GTK_WIDGET (f)))
+          gdk_window_get_geometry (gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
+                                   0, 0,
+                                   &pixelwidth, &pixelheight);
       else return;
     }
-  
+
 
   rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelheight);
   columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
@@ -633,14 +920,11 @@ xg_frame_resized (f, pixelwidth, pixelheight)
     }
 }
 
-/* Resize the outer window of frame F after chainging the height.
+/* Resize the outer window of frame F after changing the height.
    COLUMNS/ROWS is the size the edit area shall have after the resize.  */
 
 void
-xg_frame_set_char_size (f, cols, rows)
-     FRAME_PTR f;
-     int cols;
-     int rows;
+xg_frame_set_char_size (FRAME_PTR f, int cols, int rows)
 {
   int pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows)
     + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
@@ -661,7 +945,8 @@ xg_frame_set_char_size (f, cols, rows)
 
   /* FRAME_TEXT_COLS_TO_PIXEL_WIDTH uses scroll_bar_actual_width, so call it
      after calculating that value.  */
-  pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols);
+  pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols)
+    + FRAME_TOOLBAR_WIDTH (f);
 
 
   /* Do this before resize, as we don't know yet if we will be resized.  */
@@ -698,15 +983,15 @@ xg_frame_set_char_size (f, cols, rows)
      }
 }
 
-/* Handle height changes (i.e. add/remove menu/toolbar).
+/* Handle height/width changes (i.e. add/remove/move menu/toolbar).
    The policy is to keep the number of editable lines.  */
 
 static void
-xg_height_changed (f)
-     FRAME_PTR f;
+xg_height_or_width_changed (FRAME_PTR f)
 {
   gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                     FRAME_PIXEL_WIDTH (f), FRAME_TOTAL_PIXEL_HEIGHT (f));
+                     FRAME_TOTAL_PIXEL_WIDTH (f),
+                     FRAME_TOTAL_PIXEL_HEIGHT (f));
   f->output_data.x->hint_flags = 0;
   x_wm_set_size_hint (f, 0, 0);
 }
@@ -718,74 +1003,166 @@ xg_height_changed (f)
    Return 0 if no widget match WDESC.  */
 
 GtkWidget *
-xg_win_to_widget (dpy, wdesc)
-     Display *dpy;
-     Window wdesc;
+xg_win_to_widget (Display *dpy, Window wdesc)
 {
   gpointer gdkwin;
   GtkWidget *gwdesc = 0;
 
-  BLOCK_INPUT;
+  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;
       event.any.window = gdkwin;
+      event.any.type = GDK_NOTHING;
       gwdesc = gtk_get_event_widget (&event);
     }
 
-  UNBLOCK_INPUT;
+  unblock_input ();
   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 (w, pixel, c)
-     GtkWidget *w;
-     unsigned long 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/65535.0;
+      bg.green = (double)xbg.green/65535.0;
+      bg.blue = (double)xbg.blue/65535.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.
+   We notify lisp code so it can fix faces used for region for example.  */
+
+static void
+style_changed_cb (GObject *go,
+                  GParamSpec *spec,
+                  gpointer user_data)
+{
+  struct input_event event;
+  GdkDisplay *gdpy = (GdkDisplay *) user_data;
+  const char *display_name = gdk_display_get_name (gdpy);
+  Display *dpy = GDK_DISPLAY_XDISPLAY (gdpy);
+
+  EVENT_INIT (event);
+  event.kind = CONFIG_CHANGED_EVENT;
+  event.frame_or_window = build_string (display_name);
+  /* Theme doesn't change often, so intern is called seldom.  */
+  event.arg = intern ("theme-name");
+  kbd_buffer_store_event (&event);
+
+  update_theme_scrollbar_width ();
+
+  /* If scroll bar width changed, we need set the new size on all frames
+     on this display.  */
+  if (dpy)
+    {
+      Lisp_Object rest, frame;
+      FOR_EACH_FRAME (rest, frame)
+        {
+          FRAME_PTR f = XFRAME (frame);
+          if (FRAME_X_DISPLAY (f) == dpy)
+            {
+              x_set_scroll_bar_default_width (f);
+              xg_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (f));
+            }
+        }
+    }
+}
+
+/* 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.  */
+   Return true if creation succeeded.  */
 
-int
-xg_create_frame_widgets (f)
-     FRAME_PTR f;
+bool
+xg_create_frame_widgets (FRAME_PTR f)
 {
   GtkWidget *wtop;
-  GtkWidget *wvbox;
+  GtkWidget *wvbox, *whbox;
   GtkWidget *wfixed;
-  GdkColor bg;
+#ifndef HAVE_GTK3
   GtkRcStyle *style;
+#endif
   char *title = 0;
 
-  BLOCK_INPUT;
+  block_input ();
 
   if (FRAME_X_EMBEDDED_P (f))
-    wtop = gtk_plug_new (f->output_data.x->parent_desc);
+    {
+      GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
+      wtop = gtk_plug_new_for_display (gdpy, f->output_data.x->parent_desc);
+    }
   else
     wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 
+  /* gtk_window_set_has_resize_grip is a Gtk+ 3.0 function but Ubuntu
+     has backported it to Gtk+ 2.0 and they add the resize grip for
+     Gtk+ 2.0 applications also.  But it has a bug that makes Emacs loop
+     forever, so disable the grip.  */
+#if GTK_MAJOR_VERSION < 3 && defined (HAVE_GTK_WINDOW_SET_HAS_RESIZE_GRIP)
+  gtk_window_set_has_resize_grip (GTK_WINDOW (wtop), FALSE);
+#endif
+
   xg_set_screen (wtop, f);
 
-  wvbox = gtk_vbox_new (FALSE, 0);
-  wfixed = gtk_fixed_new ();  /* Must have this to place scroll bars  */
+  wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  whbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_box_set_homogeneous (GTK_BOX (wvbox), FALSE);
+  gtk_box_set_homogeneous (GTK_BOX (whbox), FALSE);
+
+#ifdef HAVE_GTK3
+  wfixed = emacs_fixed_new (f);
+#else
+  wfixed = gtk_fixed_new ();
+#endif
 
-  if (! wtop || ! wvbox || ! wfixed)
+  if (! wtop || ! wvbox || ! whbox || ! wfixed)
     {
       if (wtop) gtk_widget_destroy (wtop);
       if (wvbox) gtk_widget_destroy (wvbox);
+      if (whbox) gtk_widget_destroy (whbox);
       if (wfixed) gtk_widget_destroy (wfixed);
 
-      UNBLOCK_INPUT;
+      unblock_input ();
       return 0;
     }
 
@@ -795,19 +1172,23 @@ xg_create_frame_widgets (f)
   gtk_widget_set_name (wfixed, SSDATA (Vx_resource_name));
 
   /* If this frame has a title or name, set it in the title bar.  */
-  if (! NILP (f->title)) title = SSDATA (ENCODE_UTF_8 (f->title));
-  else if (! NILP (f->name)) title = SSDATA (ENCODE_UTF_8 (f->name));
+  if (! NILP (f->title))
+    title = SSDATA (ENCODE_UTF_8 (f->title));
+  else if (! NILP (f->name))
+    title = SSDATA (ENCODE_UTF_8 (f->name));
 
   if (title) gtk_window_set_title (GTK_WINDOW (wtop), title);
 
   FRAME_GTK_OUTER_WIDGET (f) = wtop;
   FRAME_GTK_WIDGET (f) = wfixed;
   f->output_data.x->vbox_widget = wvbox;
+  f->output_data.x->hbox_widget = whbox;
 
-  gtk_fixed_set_has_window (GTK_FIXED (wfixed), TRUE);
+  gtk_widget_set_has_window (wfixed, TRUE);
 
   gtk_container_add (GTK_CONTAINER (wtop), wvbox);
-  gtk_box_pack_end (GTK_BOX (wvbox), wfixed, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (wvbox), whbox, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (whbox), wfixed, TRUE, TRUE, 0);
 
   if (FRAME_EXTERNAL_TOOL_BAR (f))
     update_frame_tool_bar (f);
@@ -826,7 +1207,7 @@ xg_create_frame_widgets (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.
@@ -854,9 +1235,9 @@ xg_create_frame_widgets (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.  */
@@ -865,29 +1246,75 @@ xg_create_frame_widgets (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);
+  gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE);
+#endif
 
-  /* GTK does not set any border, and they look bad with GTK.  */
-  /* That they look bad is no excuse for imposing this here.  --Stef
-     It should be done by providing the proper default in Fx_create_Frame.
-  f->border_width = 0;
-  f->internal_border_width = 0; */
+#ifdef USE_GTK_TOOLTIP
+  /* Steal a tool tip window we can move ourselves.  */
+  f->output_data.x->ttip_widget = 0;
+  f->output_data.x->ttip_lbl = 0;
+  f->output_data.x->ttip_window = 0;
+  gtk_widget_set_tooltip_text (wtop, "Dummy text");
+  g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f);
+#endif
 
-  UNBLOCK_INPUT;
+  {
+    GdkScreen *screen = gtk_widget_get_screen (wtop);
+    GtkSettings *gs = gtk_settings_get_for_screen (screen);
+    /* Only connect this signal once per screen.  */
+    if (! g_signal_handler_find (G_OBJECT (gs),
+                                 G_SIGNAL_MATCH_FUNC,
+                                 0, 0, 0,
+                                 G_CALLBACK (style_changed_cb),
+                                 0))
+      {
+        g_signal_connect (G_OBJECT (gs), "notify::gtk-theme-name",
+                          G_CALLBACK (style_changed_cb),
+                          gdk_screen_get_display (screen));
+      }
+  }
+
+  unblock_input ();
 
   return 1;
 }
 
+void
+xg_free_frame_widgets (FRAME_PTR f)
+{
+  if (FRAME_GTK_OUTER_WIDGET (f))
+    {
+#ifdef USE_GTK_TOOLTIP
+      struct x_output *x = f->output_data.x;
+#endif
+      struct xg_frame_tb_info *tbinfo
+        = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                             TB_INFO_KEY);
+      if (tbinfo)
+        xfree (tbinfo);
+
+      gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
+      FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
+      FRAME_GTK_OUTER_WIDGET (f) = 0;
+#ifdef USE_GTK_TOOLTIP
+      if (x->ttip_lbl)
+        gtk_widget_destroy (x->ttip_lbl);
+      if (x->ttip_widget)
+        g_object_unref (G_OBJECT (x->ttip_widget));
+#endif
+    }
+}
+
 /* Set the normal size hints for the window manager, for frame F.
    FLAGS is the flags word to use--or 0 meaning preserve the flags
    that the window now has.
-   If USER_POSITION is nonzero, we set the User Position
+   If USER_POSITION, set the User Position
    flag (this is useful when FLAGS is 0).  */
 
 void
-x_wm_set_size_hint (f, flags, user_position)
-     FRAME_PTR f;
-     long flags;
-     int user_position;
+x_wm_set_size_hint (FRAME_PTR f, long int flags, bool user_position)
 {
   /* Must use GTK routines here, otherwise GTK resets the size hints
      to its own defaults.  */
@@ -920,11 +1347,15 @@ x_wm_set_size_hint (f, flags, user_position)
   size_hints.height_inc = FRAME_LINE_HEIGHT (f);
 
   hint_flags |= GDK_HINT_BASE_SIZE;
-  base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0);
-  base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0)
+  /* Use one row/col here so base_height/width does not become zero.
+     Gtk+ and/or Unity on Ubuntu 12.04 can't handle it.  */
+  base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TOOLBAR_WIDTH (f);
+  base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
     + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
 
   check_frame_size (f, &min_rows, &min_cols);
+  if (min_cols > 0) --min_cols; /* We used one col in base_width = ... 1); */
+  if (min_rows > 0) --min_rows; /* We used one row in base_height = ... 1); */
 
   size_hints.base_width = base_width;
   size_hints.base_height = base_height;
@@ -956,10 +1387,6 @@ x_wm_set_size_hint (f, flags, user_position)
   else if (win_gravity == StaticGravity)
     size_hints.win_gravity = GDK_GRAVITY_STATIC;
 
-  if (flags & PPosition) hint_flags |= GDK_HINT_POS;
-  if (flags & USPosition) hint_flags |= GDK_HINT_USER_POS;
-  if (flags & USSize) hint_flags |= GDK_HINT_USER_SIZE;
-
   if (user_position)
     {
       hint_flags &= ~GDK_HINT_POS;
@@ -971,12 +1398,12 @@ x_wm_set_size_hint (f, flags, user_position)
                 &f->output_data.x->size_hints,
                 sizeof (size_hints)) != 0)
     {
-      BLOCK_INPUT;
+      block_input ();
       gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                                    NULL, &size_hints, hint_flags);
+                                     NULL, &size_hints, hint_flags);
       f->output_data.x->size_hints = size_hints;
       f->output_data.x->hint_flags = hint_flags;
-      UNBLOCK_INPUT;
+      unblock_input ();
     }
 }
 
@@ -987,18 +1414,13 @@ x_wm_set_size_hint (f, flags, user_position)
    BG is the pixel value to change to.  */
 
 void
-xg_set_background_color (f, bg)
-     FRAME_PTR f;
-     unsigned long bg;
+xg_set_background_color (FRAME_PTR f, long unsigned int bg)
 {
   if (FRAME_GTK_WIDGET (f))
     {
-      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);
-      UNBLOCK_INPUT;
+      block_input ();
+      xg_set_widget_bg (f, FRAME_GTK_WIDGET (f), FRAME_BACKGROUND_PIXEL (f));
+      unblock_input ();
     }
 }
 
@@ -1007,16 +1429,12 @@ xg_set_background_color (f, bg)
    functions so GTK does not overwrite the icon.  */
 
 void
-xg_set_frame_icon (f, icon_pixmap, icon_mask)
-     FRAME_PTR f;
-     Pixmap icon_pixmap;
-     Pixmap icon_mask;
+xg_set_frame_icon (FRAME_PTR f, Pixmap icon_pixmap, Pixmap icon_mask)
 {
-    GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
-    GdkPixmap *gpix = gdk_pixmap_foreign_new_for_display (gdpy, icon_pixmap);
-    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);
 }
 
@@ -1028,10 +1446,10 @@ xg_set_frame_icon (f, icon_pixmap, icon_mask)
 /* Return the dialog title to use for a dialog of type KEY.
    This is the encoding used by lwlib.  We use the same for GTK.  */
 
-static char *
+static const char *
 get_dialog_title (char key)
 {
-  char *title = "";
+  const char *title = "";
 
   switch (key) {
   case 'E': case 'e':
@@ -1061,7 +1479,7 @@ get_dialog_title (char key)
 /* Callback for dialogs that get WM_DELETE_WINDOW.  We pop down
    the dialog, but return TRUE so the event does not propagate further
    in GTK.  This prevents GTK from destroying the dialog widget automatically
-   and we can always destrou the widget manually, regardles of how
+   and we can always destroy the widget manually, regardless of how
    it was popped down (button press or WM_DELETE_WINDOW).
    W is the dialog widget.
    EVENT is the GdkEvent that represents WM_DELETE_WINDOW (not used).
@@ -1070,10 +1488,7 @@ get_dialog_title (char key)
    Returns TRUE to end propagation of event.  */
 
 static gboolean
-dialog_delete_callback (w, event, user_data)
-     GtkWidget *w;
-     GdkEvent *event;
-     gpointer user_data;
+dialog_delete_callback (GtkWidget *w, GdkEvent *event, gpointer user_data)
 {
   gtk_widget_unmap (w);
   return TRUE;
@@ -1087,27 +1502,25 @@ dialog_delete_callback (w, event, user_data)
    Returns the GTK dialog widget.  */
 
 static GtkWidget *
-create_dialog (wv, select_cb, deactivate_cb)
-     widget_value *wv;
-     GCallback select_cb;
-     GCallback deactivate_cb;
+create_dialog (widget_value *wv,
+               GCallback select_cb,
+               GCallback deactivate_cb)
 {
-  char *title = get_dialog_title (wv->name[0]);
+  const char *title = get_dialog_title (wv->name[0]);
   int total_buttons = wv->name[1] - '0';
   int right_buttons = wv->name[4] - '0';
   int left_buttons;
   int button_nr = 0;
   int button_spacing = 10;
   GtkWidget *wdialog = gtk_dialog_new ();
+  GtkDialog *wd = GTK_DIALOG (wdialog);
+  GtkBox *cur_box = GTK_BOX (gtk_dialog_get_action_area (wd));
   widget_value *item;
-  GtkBox *cur_box;
-  GtkWidget *wvbox;
-  GtkWidget *whbox_up;
   GtkWidget *whbox_down;
 
   /* If the number of buttons is greater than 4, make two rows of buttons
      instead.  This looks better.  */
-  int make_two_rows = total_buttons > 4;
+  bool make_two_rows = total_buttons > 4;
 
   if (right_buttons == 0) right_buttons = total_buttons/2;
   left_buttons = total_buttons - right_buttons;
@@ -1115,13 +1528,15 @@ create_dialog (wv, select_cb, deactivate_cb)
   gtk_window_set_title (GTK_WINDOW (wdialog), title);
   gtk_widget_set_name (wdialog, "emacs-dialog");
 
-  cur_box = GTK_BOX (GTK_DIALOG (wdialog)->action_area);
 
   if (make_two_rows)
     {
-      wvbox = gtk_vbox_new (TRUE, button_spacing);
-      whbox_up = gtk_hbox_new (FALSE, 0);
-      whbox_down = gtk_hbox_new (FALSE, 0);
+      GtkWidget *wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, button_spacing);
+      GtkWidget *whbox_up = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+      gtk_box_set_homogeneous (GTK_BOX (wvbox), TRUE);
+      gtk_box_set_homogeneous (GTK_BOX (whbox_up), FALSE);
+      whbox_down = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+      gtk_box_set_homogeneous (GTK_BOX (whbox_down), FALSE);
 
       gtk_box_pack_start (cur_box, wvbox, FALSE, FALSE, 0);
       gtk_box_pack_start (GTK_BOX (wvbox), whbox_up, FALSE, FALSE, 0);
@@ -1147,21 +1562,18 @@ create_dialog (wv, select_cb, deactivate_cb)
 
       if (item->name && strcmp (item->name, "message") == 0)
         {
+          GtkBox *wvbox = GTK_BOX (gtk_dialog_get_content_area (wd));
           /* This is the text part of the dialog.  */
           w = gtk_label_new (utf8_label);
-          gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->vbox),
-                              gtk_label_new (""),
-                              FALSE, FALSE, 0);
-          gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->vbox), w,
-                              TRUE, TRUE, 0);
+          gtk_box_pack_start (wvbox, gtk_label_new (""), FALSE, FALSE, 0);
+          gtk_box_pack_start (wvbox, w, TRUE, TRUE, 0);
           gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5);
 
           /* Try to make dialog look better.  Must realize first so
              the widget can calculate the size it needs.  */
           gtk_widget_realize (w);
-          gtk_widget_size_request (w, &req);
-          gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (wdialog)->vbox),
-                               req.height);
+          gtk_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);
         }
@@ -1188,7 +1600,7 @@ create_dialog (wv, select_cb, deactivate_cb)
             }
         }
 
-     if (utf8_label && utf8_label != item->value)
+     if (utf8_label)
        g_free (utf8_label);
     }
 
@@ -1208,12 +1620,9 @@ struct xg_dialog_data
    USER_DATA is what we passed in to g_signal_connect.  */
 
 static void
-xg_dialog_response_cb (w,
-                       response,
-                       user_data)
-     GtkDialog *w;
-     gint response;
-     gpointer user_data;
+xg_dialog_response_cb (GtkDialog *w,
+                      gint response,
+                      gpointer user_data)
 {
   struct xg_dialog_data *dd = (struct xg_dialog_data *)user_data;
   dd->response = response;
@@ -1224,20 +1633,19 @@ xg_dialog_response_cb (w,
 /*  Destroy the dialog.  This makes it pop down.  */
 
 static Lisp_Object
-pop_down_dialog (arg)
-     Lisp_Object arg;
+pop_down_dialog (Lisp_Object arg)
 {
   struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
   struct xg_dialog_data *dd = (struct xg_dialog_data *) p->pointer;
 
-  BLOCK_INPUT;
+  block_input ();
   if (dd->w) gtk_widget_destroy (dd->w);
   if (dd->timerid != 0) g_source_remove (dd->timerid);
 
   g_main_loop_quit (dd->loop);
   g_main_loop_unref (dd->loop);
-  
-  UNBLOCK_INPUT;
+
+  unblock_input ();
 
   return Qnil;
 }
@@ -1246,37 +1654,33 @@ pop_down_dialog (arg)
     We pass in DATA as gpointer* so we can use this as a callback.  */
 
 static gboolean
-xg_maybe_add_timer (data)
-     gpointer data;
+xg_maybe_add_timer (gpointer data)
 {
   struct xg_dialog_data *dd = (struct xg_dialog_data *) data;
-  EMACS_TIME next_time = timer_check (1);
-  long secs = EMACS_SECS (next_time);
-  long usecs = EMACS_USECS (next_time);
+  EMACS_TIME next_time = timer_check ();
 
   dd->timerid = 0;
 
-  if (secs >= 0 && usecs >= 0 && secs < ((guint)-1)/1000)
+  if (EMACS_TIME_VALID_P (next_time))
     {
-      dd->timerid = g_timeout_add (secs * 1000 + usecs/1000,
-                                   xg_maybe_add_timer,
-                                   dd);
+      time_t s = EMACS_SECS (next_time);
+      int per_ms = EMACS_TIME_RESOLUTION / 1000;
+      int ms = (EMACS_NSECS (next_time) + per_ms - 1) / per_ms;
+      if (s <= ((guint) -1 - ms) / 1000)
+       dd->timerid = g_timeout_add (s * 1000 + ms, xg_maybe_add_timer, dd);
     }
   return FALSE;
 }
 
-     
+
 /* Pops up a modal dialog W and waits for response.
    We don't use gtk_dialog_run because we want to process emacs timers.
    The dialog W is not destroyed when this function returns.  */
 
 static int
-xg_dialog_run (f, w)
-     FRAME_PTR f;
-     GtkWidget *w;
-
+xg_dialog_run (FRAME_PTR f, GtkWidget *w)
 {
-  int count = SPECPDL_INDEX ();
+  ptrdiff_t count = SPECPDL_INDEX ();
   struct xg_dialog_data dd;
 
   xg_set_screen (w, f);
@@ -1302,7 +1706,7 @@ xg_dialog_run (f, w)
 
   (void) xg_maybe_add_timer (&dd);
   g_main_loop_run (dd.loop);
-  
+
   dd.w = 0;
   unbind_to (count, Qnil);
 
@@ -1313,37 +1717,26 @@ xg_dialog_run (f, w)
 /***********************************************************************
                       File dialog functions
  ***********************************************************************/
-/* Return non-zero if the old file selection dialog is being used.
-   Return zero if not.  */
+/* Return true if the old file selection dialog is being used.  */
 
-int
-xg_uses_old_file_dialog ()
+bool
+xg_uses_old_file_dialog (void)
 {
-#ifdef HAVE_GTK_FILE_BOTH
-  extern int x_gtk_use_old_file_dialog;
-  return x_gtk_use_old_file_dialog;
-#else /* ! HAVE_GTK_FILE_BOTH */
-
 #ifdef HAVE_GTK_FILE_SELECTION_NEW
-  return 1;
+  return x_gtk_use_old_file_dialog;
 #else
   return 0;
 #endif
-
-#endif /* ! HAVE_GTK_FILE_BOTH */
 }
 
 
-typedef char * (*xg_get_file_func) P_ ((GtkWidget *));
-
-#ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW
+typedef char * (*xg_get_file_func) (GtkWidget *);
 
 /* Return the selected file for file chooser dialog W.
    The returned string must be free:d.  */
 
 static char *
-xg_get_file_name_from_chooser (w)
-     GtkWidget *w;
+xg_get_file_name_from_chooser (GtkWidget *w)
 {
   return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
 }
@@ -1352,9 +1745,7 @@ xg_get_file_name_from_chooser (w)
    WIDGET is the toggle widget, DATA is the file chooser dialog.  */
 
 static void
-xg_toggle_visibility_cb (widget, data)
-     GtkWidget *widget;
-     gpointer data;
+xg_toggle_visibility_cb (GtkWidget *widget, gpointer data)
 {
   GtkFileChooser *dialog = GTK_FILE_CHOOSER (data);
   gboolean visible;
@@ -1370,13 +1761,8 @@ xg_toggle_visibility_cb (widget, data)
    changes that property by right clicking in the file list.  */
 
 static void
-xg_toggle_notify_cb (gobject, arg1, user_data)
-     GObject *gobject;
-     GParamSpec *arg1;
-     gpointer user_data;
+xg_toggle_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data)
 {
-  extern int x_gtk_show_hidden_files;
-
   if (strcmp (arg1->name, "show-hidden") == 0)
     {
       GtkWidget *wtoggle = GTK_WIDGET (user_data);
@@ -1404,31 +1790,27 @@ xg_toggle_notify_cb (gobject, arg1, user_data)
    F is the current frame.
    PROMPT is a prompt to show to the user.  May not be NULL.
    DEFAULT_FILENAME is a default selection to be displayed.  May be NULL.
-   If MUSTMATCH_P is non-zero, the returned file name must be an existing
-   file.  *FUNC is set to a function that can be used to retrieve the
-   selected file name from the returned widget.
+   If MUSTMATCH_P, the returned file name must be an existing
+   file.  (Actually, this only has cosmetic effects, the user can
+   still enter a non-existing file.)  *FUNC is set to a function that
+   can be used to retrieve the selected file name from the returned widget.
 
    Returns the created widget.  */
 
 static GtkWidget *
-xg_get_file_with_chooser (f, prompt, default_filename,
-                          mustmatch_p, only_dir_p, func)
-     FRAME_PTR f;
-     char *prompt;
-     char *default_filename;
-     int mustmatch_p, only_dir_p;
-     xg_get_file_func *func;
+xg_get_file_with_chooser (FRAME_PTR f,
+                         char *prompt,
+                         char *default_filename,
+                         bool mustmatch_p, bool 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;
@@ -1441,7 +1823,8 @@ xg_get_file_with_chooser (f, prompt, default_filename,
                                          NULL);
   gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
 
-  wbox = gtk_vbox_new (FALSE, 0);
+  wbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
   gtk_widget_show (wbox);
   wtoggle = gtk_check_button_new_with_label ("Show hidden files.");
 
@@ -1458,16 +1841,16 @@ xg_get_file_with_chooser (f, prompt, default_filename,
 
   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);
     }
 
@@ -1513,7 +1896,6 @@ xg_get_file_with_chooser (f, prompt, default_filename,
   *func = xg_get_file_name_from_chooser;
   return filewin;
 }
-#endif /* HAVE_GTK_FILE_CHOOSER_DIALOG_NEW */
 
 #ifdef HAVE_GTK_FILE_SELECTION_NEW
 
@@ -1521,8 +1903,7 @@ xg_get_file_with_chooser (f, prompt, default_filename,
    The returned string must be free:d.  */
 
 static char *
-xg_get_file_name_from_selector (w)
-     GtkWidget *w;
+xg_get_file_name_from_selector (GtkWidget *w)
 {
   GtkFileSelection *filesel = GTK_FILE_SELECTION (w);
   return xstrdup ((char*) gtk_file_selection_get_filename (filesel));
@@ -1532,20 +1913,18 @@ xg_get_file_name_from_selector (w)
    F is the current frame.
    PROMPT is a prompt to show to the user.  May not be NULL.
    DEFAULT_FILENAME is a default selection to be displayed.  May be NULL.
-   If MUSTMATCH_P is non-zero, the returned file name must be an existing
+   If MUSTMATCH_P, the returned file name must be an existing
    file.  *FUNC is set to a function that can be used to retrieve the
    selected file name from the returned widget.
 
    Returns the created widget.  */
 
 static GtkWidget *
-xg_get_file_with_selection (f, prompt, default_filename,
-                            mustmatch_p, only_dir_p, func)
-     FRAME_PTR f;
-     char *prompt;
-     char *default_filename;
-     int mustmatch_p, only_dir_p;
-     xg_get_file_func *func;
+xg_get_file_with_selection (FRAME_PTR f,
+                            char *prompt,
+                            char *default_filename,
+                            bool mustmatch_p, bool only_dir_p,
+                            xg_get_file_func *func)
 {
   GtkWidget *filewin;
   GtkFileSelection *filesel;
@@ -1576,32 +1955,25 @@ xg_get_file_with_selection (f, prompt, default_filename,
    F is the current frame.
    PROMPT is a prompt to show to the user.  May not be NULL.
    DEFAULT_FILENAME is a default selection to be displayed.  May be NULL.
-   If MUSTMATCH_P is non-zero, the returned file name must be an existing
+   If MUSTMATCH_P, the returned file name must be an existing
    file.
 
    Returns a file name or NULL if no file was selected.
    The returned string must be freed by the caller.  */
 
 char *
-xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p)
-     FRAME_PTR f;
-     char *prompt;
-     char *default_filename;
-     int mustmatch_p, only_dir_p;
+xg_get_file_name (FRAME_PTR f,
+                  char *prompt,
+                  char *default_filename,
+                  bool mustmatch_p,
+                  bool only_dir_p)
 {
   GtkWidget *w = 0;
   char *fn = 0;
   int filesel_done = 0;
   xg_get_file_func func;
 
-#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
-  /* I really don't know why this is needed, but without this the GLIBC add on
-     library linuxthreads hangs when the Gnome file chooser backend creates
-     threads.  */
-  sigblock (sigmask (__SIGRTMIN));
-#endif /* HAVE_GTK_AND_PTHREAD */
-
-#ifdef HAVE_GTK_FILE_BOTH
+#ifdef HAVE_GTK_FILE_SELECTION_NEW
 
   if (xg_uses_old_file_dialog ())
     w = xg_get_file_with_selection (f, prompt, default_filename,
@@ -1610,27 +1982,14 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p)
     w = xg_get_file_with_chooser (f, prompt, default_filename,
                                   mustmatch_p, only_dir_p, &func);
 
-#else /* not HAVE_GTK_FILE_BOTH */
-
-#ifdef HAVE_GTK_FILE_SELECTION_NEW
-  w = xg_get_file_with_selection (f, prompt, default_filename,
-                                  mustmatch_p, only_dir_p, &func);
-#endif
-#ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW
+#else /* not HAVE_GTK_FILE_SELECTION_NEW */
   w = xg_get_file_with_chooser (f, prompt, default_filename,
                                 mustmatch_p, only_dir_p, &func);
-#endif
-
-#endif /* HAVE_GTK_FILE_BOTH */
+#endif /* not HAVE_GTK_FILE_SELECTION_NEW */
 
   gtk_widget_set_name (w, "emacs-filedialog");
 
   filesel_done = xg_dialog_run (f, w);
-
-#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
-  sigunblock (sigmask (__SIGRTMIN));
-#endif
-
   if (filesel_done == GTK_RESPONSE_OK)
     fn = (*func) (w);
 
@@ -1638,7 +1997,35 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p)
   return fn;
 }
 
+/***********************************************************************
+                      GTK font chooser
+ ***********************************************************************/
+
 #ifdef HAVE_FREETYPE
+
+#if USE_NEW_GTK_FONT_CHOOSER
+
+#define XG_WEIGHT_TO_SYMBOL(w)                 \
+  (w <= PANGO_WEIGHT_THIN ? Qextra_light       \
+   : w <= PANGO_WEIGHT_ULTRALIGHT ? Qlight     \
+   : w <= PANGO_WEIGHT_LIGHT ? Qsemi_light     \
+   : w < PANGO_WEIGHT_MEDIUM ? Qnormal         \
+   : w <= PANGO_WEIGHT_SEMIBOLD ? Qsemi_bold   \
+   : w <= PANGO_WEIGHT_BOLD ? Qbold            \
+   : w <= PANGO_WEIGHT_HEAVY ? Qextra_bold     \
+   : Qultra_bold)
+
+#define XG_STYLE_TO_SYMBOL(s)                  \
+  (s == PANGO_STYLE_OBLIQUE ? Qoblique         \
+   : s == PANGO_STYLE_ITALIC ? Qitalic         \
+   : Qnormal)
+
+#endif /* USE_NEW_GTK_FONT_CHOOSER */
+
+
+static char *x_last_font_name;
+extern Lisp_Object Qxft;
+
 /* Pop up a GTK font selector and return the name of the font the user
    selects, as a C string.  The returned font name follows GTK's own
    format:
@@ -1648,39 +2035,90 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p)
    This can be parsed using font_parse_fcname in font.c.
    DEFAULT_NAME, if non-zero, is the default font name.  */
 
-char *
-xg_get_font_name (f, default_name)
-     FRAME_PTR f;
-     char *default_name;
+Lisp_Object
+xg_get_font (FRAME_PTR f, const char *default_name)
 {
   GtkWidget *w;
-  char *fontname = NULL;
   int done = 0;
+  Lisp_Object font = Qnil;
 
-#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
-  sigblock (sigmask (__SIGRTMIN));
-#endif /* HAVE_GTK_AND_PTHREAD */
+  w = gtk_font_chooser_dialog_new
+    ("Pick a font", GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
 
-  w = gtk_font_selection_dialog_new ("Pick a font");
-  if (!default_name)
-    default_name = "Monospace 10";
-  gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (w),
-                                           default_name);
+  if (default_name)
+    {
+      /* Convert fontconfig names to Gtk names, i.e. remove - before
+        number */
+      char *p = strrchr (default_name, '-');
+      if (p)
+        {
+          char *ep = p+1;
+          while (c_isdigit (*ep))
+            ++ep;
+          if (*ep == '\0') *p = ' ';
+        }
+    }
+  else if (x_last_font_name)
+    default_name = x_last_font_name;
 
-  gtk_widget_set_name (w, "emacs-fontdialog");
+  if (default_name)
+    gtk_font_chooser_set_font (GTK_FONT_CHOOSER (w), default_name);
 
+  gtk_widget_set_name (w, "emacs-fontdialog");
   done = xg_dialog_run (f, w);
+  if (done == GTK_RESPONSE_OK)
+    {
+#if USE_NEW_GTK_FONT_CHOOSER
+      /* Use the GTK3 font chooser.  */
+      PangoFontDescription *desc
+       = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (w));
 
-#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
-  sigunblock (sigmask (__SIGRTMIN));
-#endif
+      if (desc)
+       {
+         Lisp_Object args[10];
+         const char *name   = pango_font_description_get_family (desc);
+         gint        size   = pango_font_description_get_size (desc);
+         PangoWeight weight = pango_font_description_get_weight (desc);
+         PangoStyle  style  = pango_font_description_get_style (desc);
 
-  if (done == GTK_RESPONSE_OK)
-    fontname = gtk_font_selection_dialog_get_font_name
-      (GTK_FONT_SELECTION_DIALOG (w));
+         args[0] = QCname;
+         args[1] = build_string (name);
+
+         args[2] = QCsize;
+         args[3] = make_float (pango_units_to_double (size));
+
+         args[4] = QCweight;
+         args[5] = XG_WEIGHT_TO_SYMBOL (weight);
+
+         args[6] = QCslant;
+         args[7] = XG_STYLE_TO_SYMBOL (style);
+
+         args[8] = QCtype;
+         args[9] = Qxft;
+
+         font = Ffont_spec (8, args);
+
+         pango_font_description_free (desc);
+         xfree (x_last_font_name);
+         x_last_font_name = xstrdup (name);
+       }
+
+#else /* Use old font selector, which just returns the font name.  */
+
+      char *font_name
+       = gtk_font_selection_dialog_get_font_name (GTK_FONT_CHOOSER (w));
+
+      if (font_name)
+       {
+         font = build_string (font_name);
+         g_free (x_last_font_name);
+         x_last_font_name = font_name;
+       }
+#endif /* USE_NEW_GTK_FONT_CHOOSER */
+    }
 
   gtk_widget_destroy (w);
-  return fontname;
+  return font;
 }
 #endif /* HAVE_FREETYPE */
 
@@ -1716,14 +2154,11 @@ static xg_list_node xg_menu_item_cb_list;
    allocated xg_menu_cb_data if CL_DATA is NULL.  */
 
 static xg_menu_cb_data *
-make_cl_data (cl_data, f, highlight_cb)
-     xg_menu_cb_data *cl_data;
-     FRAME_PTR f;
-     GCallback highlight_cb;
+make_cl_data (xg_menu_cb_data *cl_data, FRAME_PTR f, GCallback highlight_cb)
 {
   if (! cl_data)
     {
-      cl_data = (xg_menu_cb_data*) xmalloc (sizeof (*cl_data));
+      cl_data = xmalloc (sizeof *cl_data);
       cl_data->f = f;
       cl_data->menu_bar_vector = f->menu_bar_vector;
       cl_data->menu_bar_items_used = f->menu_bar_items_used;
@@ -1750,10 +2185,9 @@ make_cl_data (cl_data, f, highlight_cb)
    creating the menu bar.  */
 
 static void
-update_cl_data (cl_data, f, highlight_cb)
-     xg_menu_cb_data *cl_data;
-     FRAME_PTR f;
-     GCallback highlight_cb;
+update_cl_data (xg_menu_cb_data *cl_data,
+                FRAME_PTR f,
+                GCallback highlight_cb)
 {
   if (cl_data)
     {
@@ -1768,8 +2202,7 @@ update_cl_data (cl_data, f, highlight_cb)
    If reference count is zero, free CL_DATA.  */
 
 static void
-unref_cl_data (cl_data)
-     xg_menu_cb_data *cl_data;
+unref_cl_data (xg_menu_cb_data *cl_data)
 {
   if (cl_data && cl_data->ref_count > 0)
     {
@@ -1785,9 +2218,10 @@ unref_cl_data (cl_data)
 /* Function that marks all lisp data during GC.  */
 
 void
-xg_mark_data ()
+xg_mark_data (void)
 {
   xg_list_node *iter;
+  Lisp_Object rest, frame;
 
   for (iter = xg_menu_cb_list.next; iter; iter = iter->next)
     mark_object (((xg_menu_cb_data *) iter)->menu_bar_vector);
@@ -1799,6 +2233,23 @@ xg_mark_data ()
       if (! NILP (cb_data->help))
         mark_object (cb_data->help);
     }
+
+  FOR_EACH_FRAME (rest, frame)
+    {
+      FRAME_PTR f = XFRAME (frame);
+
+      if (FRAME_X_P (f) && FRAME_GTK_OUTER_WIDGET (f))
+        {
+          struct xg_frame_tb_info *tbinfo
+            = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                                 TB_INFO_KEY);
+          if (tbinfo)
+            {
+              mark_object (tbinfo->last_tool_bar);
+              mark_object (tbinfo->style);
+            }
+        }
+    }
 }
 
 
@@ -1807,9 +2258,7 @@ xg_mark_data ()
    CLIENT_DATA points to the xg_menu_item_cb_data associated with the W.  */
 
 static void
-menuitem_destroy_callback (w, client_data)
-     GtkWidget *w;
-     gpointer client_data;
+menuitem_destroy_callback (GtkWidget *w, gpointer client_data)
 {
   if (client_data)
     {
@@ -1827,10 +2276,9 @@ menuitem_destroy_callback (w, client_data)
    Returns FALSE to tell GTK to keep processing this event.  */
 
 static gboolean
-menuitem_highlight_callback (w, event, client_data)
-     GtkWidget *w;
-     GdkEventCrossing *event;
-     gpointer client_data;
+menuitem_highlight_callback (GtkWidget *w,
+                             GdkEventCrossing *event,
+                             gpointer client_data)
 {
   GdkEvent ev;
   GtkWidget *subwidget;
@@ -1858,9 +2306,7 @@ menuitem_highlight_callback (w, event, client_data)
    CLIENT_DATA points to the xg_menu_cb_data associated with W.  */
 
 static void
-menu_destroy_callback (w, client_data)
-     GtkWidget *w;
-     gpointer client_data;
+menu_destroy_callback (GtkWidget *w, gpointer client_data)
 {
   unref_cl_data ((xg_menu_cb_data*) client_data);
 }
@@ -1871,15 +2317,14 @@ menu_destroy_callback (w, client_data)
    Returns the GtkHBox.  */
 
 static GtkWidget *
-make_widget_for_menu_item (utf8_label, utf8_key)
-     char *utf8_label;
-     char *utf8_key;
+make_widget_for_menu_item (const char *utf8_label, const char *utf8_key)
 {
   GtkWidget *wlbl;
   GtkWidget *wkey;
   GtkWidget *wbox;
 
-  wbox = gtk_hbox_new (FALSE, 0);
+  wbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
   wlbl = gtk_label_new (utf8_label);
   wkey = gtk_label_new (utf8_key);
 
@@ -1911,11 +2356,10 @@ make_widget_for_menu_item (utf8_label, utf8_key)
    but the MacOS X version doesn't either, so I guess that is OK.  */
 
 static GtkWidget *
-make_menu_item (utf8_label, utf8_key, item, group)
-     char *utf8_label;
-     char *utf8_key;
-     widget_value *item;
-     GSList **group;
+make_menu_item (const char *utf8_label,
+                const char *utf8_key,
+                widget_value *item,
+                GSList **group)
 {
   GtkWidget *w;
   GtkWidget *wtoadd = 0;
@@ -1957,60 +2401,12 @@ make_menu_item (utf8_label, utf8_key, item, group)
   return w;
 }
 
-/* Return non-zero if LABEL specifies a separator (GTK only has one
-   separator type)  */
-
-static const char* separator_names[] = {
-  "space",
-  "no-line",
-  "single-line",
-  "double-line",
-  "single-dashed-line",
-  "double-dashed-line",
-  "shadow-etched-in",
-  "shadow-etched-out",
-  "shadow-etched-in-dash",
-  "shadow-etched-out-dash",
-  "shadow-double-etched-in",
-  "shadow-double-etched-out",
-  "shadow-double-etched-in-dash",
-  "shadow-double-etched-out-dash",
-  0,
-};
-
-static int
-xg_separator_p (char *label)
-{
-  if (! label) return 0;
-  else if (strlen (label) > 3
-          && strncmp (label, "--", 2) == 0
-          && label[2] != '-')
-    {
-      int i;
-
-      label += 2;
-      for (i = 0; separator_names[i]; ++i)
-       if (strcmp (label, separator_names[i]) == 0)
-          return 1;
-    }
-  else
-    {
-      /* Old-style separator, maybe.  It's a separator if it contains
-        only dashes.  */
-      while (*label == '-')
-       ++label;
-      if (*label == 0) return 1;
-    }
-
-  return 0;
-}
-
 static int xg_detached_menus;
 
-/* Returns non-zero if there are detached menus.  */
+/* Return true if there are detached menus.  */
 
-int
-xg_have_tear_offs ()
+bool
+xg_have_tear_offs (void)
 {
   return xg_detached_menus > 0;
 }
@@ -2021,9 +2417,7 @@ xg_have_tear_offs ()
    CLIENT_DATA is not used.  */
 
 static void
-tearoff_remove (widget, client_data)
-     GtkWidget *widget;
-     gpointer client_data;
+tearoff_remove (GtkWidget *widget, gpointer client_data)
 {
   if (xg_detached_menus > 0) --xg_detached_menus;
 }
@@ -2034,9 +2428,7 @@ tearoff_remove (widget, client_data)
    CLIENT_DATA is not used.  */
 
 static void
-tearoff_activate (widget, client_data)
-     GtkWidget *widget;
-     gpointer client_data;
+tearoff_activate (GtkWidget *widget, gpointer client_data)
 {
   GtkWidget *menu = gtk_widget_get_parent (widget);
   if (gtk_menu_get_tearoff_state (GTK_MENU (menu)))
@@ -2050,7 +2442,7 @@ tearoff_activate (widget, client_data)
 
 
 /* Create a menu item widget, and connect the callbacks.
-   ITEM decribes the menu item.
+   ITEM describes the menu item.
    F is the frame the created menu belongs to.
    SELECT_CB is the callback to use when a menu item is selected.
    HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
@@ -2064,13 +2456,12 @@ tearoff_activate (widget, client_data)
    Returns the created GtkWidget.  */
 
 static GtkWidget *
-xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group)
-     widget_value *item;
-     FRAME_PTR f;
-     GCallback select_cb;
-     GCallback highlight_cb;
-     xg_menu_cb_data *cl_data;
-     GSList **group;
+xg_create_one_menuitem (widget_value *item,
+                        FRAME_PTR f,
+                        GCallback select_cb,
+                        GCallback highlight_cb,
+                        xg_menu_cb_data *cl_data,
+                        GSList **group)
 {
   char *utf8_label;
   char *utf8_key;
@@ -2082,10 +2473,10 @@ xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group)
 
   w = make_menu_item (utf8_label, utf8_key, item, group);
 
-  if (utf8_label && utf8_label != item->name) g_free (utf8_label);
-  if (utf8_key && utf8_key != item->key) g_free (utf8_key);
+  if (utf8_label) g_free (utf8_label);
+  if (utf8_key) g_free (utf8_key);
 
-  cb_data = xmalloc (sizeof (xg_menu_item_cb_data));
+  cb_data = xmalloc (sizeof *cb_data);
 
   xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
 
@@ -2113,19 +2504,14 @@ xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group)
   return w;
 }
 
-static GtkWidget *create_menus P_ ((widget_value *, FRAME_PTR, GCallback,
-                                   GCallback, GCallback, int, int, int,
-                                   GtkWidget *, xg_menu_cb_data *, char *));
-
 /* Create a full menu tree specified by DATA.
    F is the frame the created menu belongs to.
    SELECT_CB is the callback to use when a menu item is selected.
    DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
    HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
-   POP_UP_P is non-zero if we shall create a popup menu.
-   MENU_BAR_P is non-zero if we shall create a menu bar.
-   ADD_TEAROFF_P is non-zero if we shall add a teroff menu item.  Ignored
-   if MENU_BAR_P is non-zero.
+   If POP_UP_P, create a popup menu.
+   If MENU_BAR_P, create a menu bar.
+   If ADD_TEAROFF_P, add a tearoff menu item.  Ignored if MENU_BAR_P.
    TOPMENU is the topmost GtkWidget that others shall be placed under.
    It may be NULL, in that case we create the appropriate widget
    (menu bar or menu item depending on POP_UP_P and MENU_BAR_P)
@@ -2140,19 +2526,17 @@ static GtkWidget *create_menus P_ ((widget_value *, FRAME_PTR, GCallback,
    This function calls itself to create submenus.  */
 
 static GtkWidget *
-create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
-              pop_up_p, menu_bar_p, add_tearoff_p, topmenu, cl_data, name)
-     widget_value *data;
-     FRAME_PTR f;
-     GCallback select_cb;
-     GCallback deactivate_cb;
-     GCallback highlight_cb;
-     int pop_up_p;
-     int menu_bar_p;
-     int add_tearoff_p;
-     GtkWidget *topmenu;
-     xg_menu_cb_data *cl_data;
-     char *name;
+create_menus (widget_value *data,
+              FRAME_PTR f,
+              GCallback select_cb,
+              GCallback deactivate_cb,
+              GCallback highlight_cb,
+              bool pop_up_p,
+              bool menu_bar_p,
+              bool add_tearoff_p,
+              GtkWidget *topmenu,
+              xg_menu_cb_data *cl_data,
+              const char *name)
 {
   widget_value *item;
   GtkWidget *wmenu = topmenu;
@@ -2214,7 +2598,7 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
       GtkWidget *w;
 
       if (pop_up_p && !item->contents && !item->call_data
-          && !xg_separator_p (item->name))
+          && !menu_separator_name_p (item->name))
         {
           char *utf8_label;
           /* A title for a popup.  We do the same as GTK does when
@@ -2225,9 +2609,9 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
           gtk_menu_set_title (GTK_MENU (wmenu), utf8_label);
           w = gtk_menu_item_new_with_label (utf8_label);
           gtk_widget_set_sensitive (w, FALSE);
-          if (utf8_label && utf8_label != item->name) g_free (utf8_label);
+          if (utf8_label) g_free (utf8_label);
         }
-      else if (xg_separator_p (item->name))
+      else if (menu_separator_name_p (item->name))
         {
           group = NULL;
           /* GTK only have one separator type.  */
@@ -2284,19 +2668,13 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
    Returns the widget created.  */
 
 GtkWidget *
-xg_create_widget (type, name, f, val,
-                  select_cb, deactivate_cb, highlight_cb)
-     char *type;
-     char *name;
-     FRAME_PTR f;
-     widget_value *val;
-     GCallback select_cb;
-     GCallback deactivate_cb;
-     GCallback highlight_cb;
+xg_create_widget (const char *type, const char *name, FRAME_PTR f, widget_value *val,
+                  GCallback select_cb, GCallback deactivate_cb,
+                 GCallback highlight_cb)
 {
   GtkWidget *w = 0;
-  int menu_bar_p = strcmp (type, "menubar") == 0;
-  int pop_up_p = strcmp (type, "popup") == 0;
+  bool menu_bar_p = strcmp (type, "menubar") == 0;
+  bool pop_up_p = strcmp (type, "popup") == 0;
 
   if (strcmp (type, "dialog") == 0)
     {
@@ -2343,21 +2721,18 @@ xg_create_widget (type, name, f, val,
 /* Return the label for menu item WITEM.  */
 
 static const char *
-xg_get_menu_item_label (witem)
-     GtkMenuItem *witem;
+xg_get_menu_item_label (GtkMenuItem *witem)
 {
-  GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem)));
+  GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
   return gtk_label_get_label (wlabel);
 }
 
-/* Return non-zero if the menu item WITEM has the text LABEL.  */
+/* Return true if the menu item WITEM has the text LABEL.  */
 
-static int
-xg_item_label_same_p (witem, label)
-     GtkMenuItem *witem;
-     char *label;
+static bool
+xg_item_label_same_p (GtkMenuItem *witem, const char *label)
 {
-  int is_same = 0;
+  bool is_same = 0;
   char *utf8_label = get_utf8_string (label);
   const char *old_label = witem ? xg_get_menu_item_label (witem) : 0;
 
@@ -2366,7 +2741,7 @@ xg_item_label_same_p (witem, label)
   else if (old_label && utf8_label)
     is_same = strcmp (utf8_label, old_label) == 0;
 
-  if (utf8_label && utf8_label != label) g_free (utf8_label);
+  if (utf8_label) g_free (utf8_label);
 
   return is_same;
 }
@@ -2374,8 +2749,7 @@ xg_item_label_same_p (witem, label)
 /* Destroy widgets in LIST.  */
 
 static void
-xg_destroy_widgets (list)
-     GList *list;
+xg_destroy_widgets (GList *list)
 {
   GList *iter;
 
@@ -2401,18 +2775,16 @@ xg_destroy_widgets (list)
    This function calls itself to walk through the menu bar names.  */
 
 static void
-xg_update_menubar (menubar, f, list, iter, pos, val,
-                   select_cb, deactivate_cb, highlight_cb, cl_data)
-     GtkWidget *menubar;
-     FRAME_PTR f;
-     GList **list;
-     GList *iter;
-     int pos;
-     widget_value *val;
-     GCallback select_cb;
-     GCallback deactivate_cb;
-     GCallback highlight_cb;
-     xg_menu_cb_data *cl_data;
+xg_update_menubar (GtkWidget *menubar,
+                  FRAME_PTR f,
+                  GList **list,
+                  GList *iter,
+                  int pos,
+                  widget_value *val,
+                  GCallback select_cb,
+                  GCallback deactivate_cb,
+                  GCallback highlight_cb,
+                  xg_menu_cb_data *cl_data)
 {
   if (! iter && ! val)
     return;
@@ -2451,8 +2823,8 @@ xg_update_menubar (menubar, f, list, iter, pos, val,
     {
       GtkMenuItem *witem = GTK_MENU_ITEM (iter->data);
       GtkMenuItem *witem2 = 0;
-      int val_in_menubar = 0;
-      int iter_in_new_menubar = 0;
+      bool val_in_menubar = 0;
+      bool iter_in_new_menubar = 0;
       GList *iter2;
       widget_value *cur;
 
@@ -2479,7 +2851,7 @@ xg_update_menubar (menubar, f, list, iter, pos, val,
                 New:      A C
               Remove B.  */
 
-          gtk_widget_ref (GTK_WIDGET (witem));
+          g_object_ref (G_OBJECT (witem));
           gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem));
           gtk_widget_destroy (GTK_WIDGET (witem));
 
@@ -2504,7 +2876,7 @@ xg_update_menubar (menubar, f, list, iter, pos, val,
                 Rename X to B (minibuf to C-mode menu).
               If the X menu hasn't been invoked, the menu under B
               is up to date when leaving the minibuffer.  */
-          GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem)));
+          GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
           char *utf8_label = get_utf8_string (val->name);
           GtkWidget *submenu = gtk_menu_item_get_submenu (witem);
 
@@ -2516,6 +2888,7 @@ xg_update_menubar (menubar, f, list, iter, pos, val,
             /* Set the title of the detached window.  */
             gtk_menu_set_title (GTK_MENU (submenu), utf8_label);
 
+          if (utf8_label) g_free (utf8_label);
           iter = g_list_next (iter);
           val = val->next;
           ++pos;
@@ -2528,7 +2901,7 @@ xg_update_menubar (menubar, f, list, iter, pos, val,
               Insert X.  */
 
           int nr = pos;
-          GList *group = 0;
+          GSList *group = 0;
           GtkWidget *w = xg_create_one_menuitem (val,
                                                  f,
                                                  select_cb,
@@ -2561,11 +2934,11 @@ xg_update_menubar (menubar, f, list, iter, pos, val,
                 New:      A C B
               Move C before B  */
 
-          gtk_widget_ref (GTK_WIDGET (witem2));
+          g_object_ref (G_OBJECT (witem2));
           gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem2));
           gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
                                  GTK_WIDGET (witem2), pos);
-          gtk_widget_unref (GTK_WIDGET (witem2));
+          g_object_unref (G_OBJECT (witem2));
 
           g_list_free (*list);
           *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
@@ -2587,12 +2960,11 @@ xg_update_menubar (menubar, f, list, iter, pos, val,
    CL_DATA is the data to set in the widget for menu invocation.  */
 
 static void
-xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data)
-     widget_value *val;
-     GtkWidget *w;
-     GCallback select_cb;
-     GCallback highlight_cb;
-     xg_menu_cb_data *cl_data;
+xg_update_menu_item (widget_value *val,
+                     GtkWidget *w,
+                     GCallback select_cb,
+                     GCallback highlight_cb,
+                     xg_menu_cb_data *cl_data)
 {
   GtkWidget *wchild;
   GtkLabel *wlbl = 0;
@@ -2603,12 +2975,12 @@ xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data)
   const char *old_key = 0;
   xg_menu_item_cb_data *cb_data;
 
-  wchild = gtk_bin_get_child (GTK_BIN (w));
+  wchild = XG_BIN_CHILD (w);
   utf8_label = get_utf8_string (val->name);
   utf8_key = get_utf8_string (val->key);
 
   /* See if W is a menu item with a key.  See make_menu_item above.  */
-  if (GTK_IS_HBOX (wchild))
+  if (GTK_IS_BOX (wchild))
     {
       GList *list = gtk_container_get_children (GTK_CONTAINER (wchild));
 
@@ -2619,9 +2991,10 @@ xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data)
       if (! utf8_key)
         {
           /* Remove the key and keep just the label.  */
-          gtk_widget_ref (GTK_WIDGET (wlbl));
+          g_object_ref (G_OBJECT (wlbl));
           gtk_container_remove (GTK_CONTAINER (w), wchild);
           gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl));
+          g_object_unref (G_OBJECT (wlbl));
           wkey = 0;
         }
 
@@ -2655,12 +3028,12 @@ xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data)
   if (! old_label || strcmp (utf8_label, old_label) != 0)
     gtk_label_set_text (wlbl, utf8_label);
 
-  if (utf8_key && utf8_key != val->key) g_free (utf8_key);
-  if (utf8_label && utf8_label != val->name) g_free (utf8_label);
+  if (utf8_key) g_free (utf8_key);
+  if (utf8_label) g_free (utf8_label);
 
-  if (! val->enabled && GTK_WIDGET_SENSITIVE (w))
+  if (! val->enabled && gtk_widget_get_sensitive (w))
     gtk_widget_set_sensitive (w, FALSE);
-  else if (val->enabled && ! GTK_WIDGET_SENSITIVE (w))
+  else if (val->enabled && ! gtk_widget_get_sensitive (w))
     gtk_widget_set_sensitive (w, TRUE);
 
   cb_data = (xg_menu_item_cb_data*) g_object_get_data (G_OBJECT (w),
@@ -2691,9 +3064,7 @@ xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data)
 /* Update the toggle menu item W so it corresponds to VAL.  */
 
 static void
-xg_update_toggle_item (val, w)
-     widget_value *val;
-     GtkWidget *w;
+xg_update_toggle_item (widget_value *val, GtkWidget *w)
 {
   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
 }
@@ -2701,9 +3072,7 @@ xg_update_toggle_item (val, w)
 /* Update the radio menu item W so it corresponds to VAL.  */
 
 static void
-xg_update_radio_item (val, w)
-     widget_value *val;
-     GtkWidget *w;
+xg_update_radio_item (widget_value *val, GtkWidget *w)
 {
   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
 }
@@ -2721,21 +3090,19 @@ xg_update_radio_item (val, w)
    was NULL.  */
 
 static GtkWidget *
-xg_update_submenu (submenu, f, val,
-                   select_cb, deactivate_cb, highlight_cb, cl_data)
-     GtkWidget *submenu;
-     FRAME_PTR f;
-     widget_value *val;
-     GCallback select_cb;
-     GCallback deactivate_cb;
-     GCallback highlight_cb;
-     xg_menu_cb_data *cl_data;
+xg_update_submenu (GtkWidget *submenu,
+                  FRAME_PTR f,
+                  widget_value *val,
+                  GCallback select_cb,
+                  GCallback deactivate_cb,
+                  GCallback highlight_cb,
+                  xg_menu_cb_data *cl_data)
 {
   GtkWidget *newsub = submenu;
   GList *list = 0;
   GList *iter;
   widget_value *cur;
-  int has_tearoff_p = 0;
+  bool has_tearoff_p = 0;
   GList *first_radio = 0;
 
   if (submenu)
@@ -2767,7 +3134,7 @@ xg_update_submenu (submenu, f, val,
 
     if (GTK_IS_SEPARATOR_MENU_ITEM (w))
       {
-        if (! xg_separator_p (cur->name))
+        if (! menu_separator_name_p (cur->name))
           break;
       }
     else if (GTK_IS_CHECK_MENU_ITEM (w))
@@ -2790,7 +3157,7 @@ xg_update_submenu (submenu, f, val,
         GtkWidget *sub;
 
         if (cur->button_type != BUTTON_TYPE_NONE ||
-            xg_separator_p (cur->name))
+            menu_separator_name_p (cur->name))
           break;
 
         xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
@@ -2799,8 +3166,8 @@ xg_update_submenu (submenu, f, val,
         if (sub && ! cur->contents)
           {
             /* Not a submenu anymore.  */
-            gtk_widget_ref (sub);
-            gtk_menu_item_remove_submenu (witem);
+            g_object_ref (G_OBJECT (sub));
+            remove_submenu (witem);
             gtk_widget_destroy (sub);
           }
         else if (cur->contents)
@@ -2824,7 +3191,7 @@ xg_update_submenu (submenu, f, val,
       }
   }
 
-  /* Remove widgets from first structual change.  */
+  /* Remove widgets from first structural change.  */
   if (iter)
     {
       /* If we are adding new menu items below, we must remove from
@@ -2857,22 +3224,17 @@ xg_update_submenu (submenu, f, val,
 /* Update the MENUBAR.
    F is the frame the menu bar belongs to.
    VAL describes the contents of the menu bar.
-   If DEEP_P is non-zero, rebuild all but the top level menu names in
+   If DEEP_P, rebuild all but the top level menu names in
    the MENUBAR.  If DEEP_P is zero, just rebuild the names in the menubar.
    SELECT_CB is the callback to use when a menu item is selected.
    DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
    HIGHLIGHT_CB is the callback to call when entering/leaving menu items.  */
 
 void
-xg_modify_menubar_widgets (menubar, f, val, deep_p,
-                           select_cb, deactivate_cb, highlight_cb)
-     GtkWidget *menubar;
-     FRAME_PTR f;
-     widget_value *val;
-     int deep_p;
-     GCallback select_cb;
-     GCallback deactivate_cb;
-     GCallback highlight_cb;
+xg_modify_menubar_widgets (GtkWidget *menubar, FRAME_PTR f, widget_value *val,
+                          bool deep_p,
+                           GCallback select_cb, GCallback deactivate_cb,
+                          GCallback highlight_cb)
 {
   xg_menu_cb_data *cl_data;
   GList *list = gtk_container_get_children (GTK_CONTAINER (menubar));
@@ -2901,7 +3263,7 @@ xg_modify_menubar_widgets (menubar, f, val, deep_p,
           GList *iter;
           GtkWidget *sub = 0;
           GtkWidget *newsub;
-          GtkMenuItem *witem;
+          GtkMenuItem *witem = 0;
 
           /* Find sub menu that corresponds to val and update it.  */
           for (iter = list ; iter; iter = g_list_next (iter))
@@ -2924,7 +3286,7 @@ xg_modify_menubar_widgets (menubar, f, val, deep_p,
           /* sub may still be NULL.  If we just updated non deep and added
              a new menu bar item, it has no sub menu yet.  So we set the
              newly created sub menu under witem.  */
-          if (newsub != sub)
+          if (newsub != sub && witem != 0)
             {
               xg_set_screen (newsub, f);
               gtk_menu_item_set_submenu (witem, newsub);
@@ -2945,31 +3307,30 @@ menubar_map_cb (GtkWidget *w, gpointer user_data)
 {
   GtkRequisition req;
   FRAME_PTR f = (FRAME_PTR) user_data;
-  gtk_widget_size_request (w, &req);
-  if (FRAME_MENUBAR_HEIGHT (f) != req.height) 
+  gtk_widget_get_preferred_size (w, NULL, &req);
+  if (FRAME_MENUBAR_HEIGHT (f) != req.height)
     {
       FRAME_MENUBAR_HEIGHT (f) = req.height;
-      xg_height_changed (f);
+      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.  */
+   changed.  */
 
-int
-xg_update_frame_menubar (f)
-     FRAME_PTR f;
+void
+xg_update_frame_menubar (FRAME_PTR f)
 {
   struct x_output *x = f->output_data.x;
   GtkRequisition req;
 
-  if (!x->menubar_widget || GTK_WIDGET_MAPPED (x->menubar_widget))
-    return 0;
+  if (!x->menubar_widget || gtk_widget_get_mapped (x->menubar_widget))
+    return;
 
   if (x->menubar_widget && gtk_widget_get_parent (x->menubar_widget))
-    return 0; /* Already done this, happens for frames created invisible.  */
+    return; /* Already done this, happens for frames created invisible.  */
 
-  BLOCK_INPUT;
+  block_input ();
 
   gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
                       FALSE, FALSE, 0);
@@ -2977,7 +3338,8 @@ xg_update_frame_menubar (f)
 
   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);
+  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)
@@ -2986,37 +3348,34 @@ xg_update_frame_menubar (f)
   if (FRAME_MENUBAR_HEIGHT (f) != req.height)
     {
       FRAME_MENUBAR_HEIGHT (f) = req.height;
-      xg_height_changed (f);
+      xg_height_or_width_changed (f);
     }
-  UNBLOCK_INPUT;
-
-  return 1;
+  unblock_input ();
 }
 
 /* Get rid of the menu bar of frame F, and free its storage.
    This is used when deleting a frame, and when turning off the menu bar.  */
 
 void
-free_frame_menubar (f)
-     FRAME_PTR f;
+free_frame_menubar (FRAME_PTR f)
 {
   struct x_output *x = f->output_data.x;
 
   if (x->menubar_widget)
     {
-      BLOCK_INPUT;
+      block_input ();
 
       gtk_container_remove (GTK_CONTAINER (x->vbox_widget), x->menubar_widget);
        /* The menubar and its children shall be deleted when removed from
           the container.  */
       x->menubar_widget = 0;
       FRAME_MENUBAR_HEIGHT (f) = 0;
-      xg_height_changed (f);
-      UNBLOCK_INPUT;
+      xg_height_or_width_changed (f);
+      unblock_input ();
     }
 }
 
-int
+bool
 xg_event_is_for_menubar (FRAME_PTR f, XEvent *event)
 {
   struct x_output *x = f->output_data.x;
@@ -3038,9 +3397,10 @@ 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;
+  gevent.any.type = GDK_NOTHING;
   gwdesc = gtk_get_event_widget (&gevent);
   if (! gwdesc) return 0;
   if (! GTK_IS_MENU_BAR (gwdesc)
@@ -3058,11 +3418,11 @@ xg_event_is_for_menubar (FRAME_PTR f, XEvent *event)
   for (iter = list ; iter; iter = g_list_next (iter))
     {
       GtkWidget *w = GTK_WIDGET (iter->data);
-      if (GTK_WIDGET_MAPPED (w) && gtk_widget_intersect (w, &rec, NULL))
+      if (gtk_widget_get_mapped (w) && gtk_widget_intersect (w, &rec, NULL))
         break;
     }
   g_list_free (list);
-  return iter == 0 ? 0 : 1;
+  return iter != 0;
 }
 
 
@@ -3075,7 +3435,11 @@ xg_event_is_for_menubar (FRAME_PTR f, XEvent *event)
 /* Setting scroll bar values invokes the callback.  Use this variable
    to indicate that callback should do nothing.  */
 
-int xg_ignore_gtk_scrollbar;
+bool xg_ignore_gtk_scrollbar;
+
+/* The width of the scroll bar for the current theme.  */
+
+static int scroll_bar_width_for_theme;
 
 /* Xlib's `Window' fits in 32 bits.  But we want to store pointers, and they
    may be larger than 32 bits.  Keep a mapping from integer index to widget
@@ -3084,8 +3448,8 @@ int xg_ignore_gtk_scrollbar;
 static struct
 {
   GtkWidget **widgets;
-  int max_size;
-  int used;
+  ptrdiff_t max_size;
+  ptrdiff_t used;
 } id_to_widget;
 
 /* Grow this much every time we need to allocate more  */
@@ -3094,18 +3458,20 @@ static struct
 
 /* Store the widget pointer W in id_to_widget and return the integer index.  */
 
-static int
-xg_store_widget_in_map (w)
-     GtkWidget *w;
+static ptrdiff_t
+xg_store_widget_in_map (GtkWidget *w)
 {
-  int i;
+  ptrdiff_t i;
 
   if (id_to_widget.max_size == id_to_widget.used)
     {
-      int new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR;
+      ptrdiff_t new_size;
+      if (TYPE_MAXIMUM (Window) - ID_TO_WIDGET_INCR < id_to_widget.max_size)
+       memory_full (SIZE_MAX);
 
-      id_to_widget.widgets = xrealloc (id_to_widget.widgets,
-                                       sizeof (GtkWidget *)*new_size);
+      new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR;
+      id_to_widget.widgets = xnrealloc (id_to_widget.widgets,
+                                       new_size, sizeof (GtkWidget *));
 
       for (i = id_to_widget.max_size; i < new_size; ++i)
         id_to_widget.widgets[i] = 0;
@@ -3127,15 +3493,14 @@ xg_store_widget_in_map (w)
     }
 
   /* Should never end up here  */
-  abort ();
+  emacs_abort ();
 }
 
 /* Remove pointer at IDX from id_to_widget.
    Called when scroll bar is destroyed.  */
 
 static void
-xg_remove_widget_from_map (idx)
-     int idx;
+xg_remove_widget_from_map (ptrdiff_t idx)
 {
   if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
     {
@@ -3147,8 +3512,7 @@ xg_remove_widget_from_map (idx)
 /* Get the widget pointer at IDX from id_to_widget. */
 
 static GtkWidget *
-xg_get_widget_from_map (idx)
-     int idx;
+xg_get_widget_from_map (ptrdiff_t idx)
 {
   if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
     return id_to_widget.widgets[idx];
@@ -3156,15 +3520,41 @@ xg_get_widget_from_map (idx)
   return 0;
 }
 
+static void
+update_theme_scrollbar_width (void)
+{
+#ifdef HAVE_GTK3
+  GtkAdjustment *vadj;
+#else
+  GtkObject *vadj;
+#endif
+  GtkWidget *wscroll;
+  int w = 0, b = 0;
+
+  vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX, 0.1, 0.1, 0.1);
+  wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
+  g_object_ref_sink (G_OBJECT (wscroll));
+  gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
+  gtk_widget_destroy (wscroll);
+  g_object_unref (G_OBJECT (wscroll));
+  w += 2*b;
+  if (w < 16) w = 16;
+  scroll_bar_width_for_theme = w;
+}
+
+int
+xg_get_default_scrollbar_width (void)
+{
+  return scroll_bar_width_for_theme;
+}
+
 /* Return the scrollbar id for X Window WID on display DPY.
    Return -1 if WID not in id_to_widget.  */
 
-int
-xg_get_scroll_id_for_window (dpy, wid)
-     Display *dpy;
-     Window wid;
+ptrdiff_t
+xg_get_scroll_id_for_window (Display *dpy, Window wid)
 {
-  int idx;
+  ptrdiff_t idx;
   GtkWidget *w;
 
   w = xg_win_to_widget (dpy, wid);
@@ -3184,11 +3574,9 @@ xg_get_scroll_id_for_window (dpy, wid)
    We free pointer to last scroll bar values here and remove the index.  */
 
 static void
-xg_gtk_scroll_destroy (widget, data)
-     GtkWidget *widget;
-     gpointer data;
+xg_gtk_scroll_destroy (GtkWidget *widget, gpointer data)
 {
-  int id = (int) (EMACS_INT) data; /* The EMACS_INT cast avoids a warning. */
+  intptr_t id = (intptr_t) data;
   xg_remove_widget_from_map (id);
 }
 
@@ -3201,35 +3589,40 @@ xg_gtk_scroll_destroy (widget, data)
    to set resources for the widget.  */
 
 void
-xg_create_scroll_bar (f, bar, scroll_callback, end_callback, scroll_bar_name)
-     FRAME_PTR f;
-     struct scroll_bar *bar;
-     GCallback scroll_callback, end_callback;
-     char *scroll_bar_name;
+xg_create_scroll_bar (FRAME_PTR f,
+                      struct scroll_bar *bar,
+                      GCallback scroll_callback,
+                      GCallback end_callback,
+                      const char *scroll_bar_name)
 {
   GtkWidget *wscroll;
   GtkWidget *webox;
+  intptr_t 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. */
   vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
                              0.1, 0.1, 0.1);
 
-  wscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT (vadj));
+  wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, 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,
@@ -3238,7 +3631,7 @@ xg_create_scroll_bar (f, bar, scroll_callback, end_callback, scroll_bar_name)
                     "button-release-event",
                     end_callback,
                     (gpointer) bar);
-  
+
   /* The scroll bar widget does not draw on a window of its own.  Instead
      it draws on the parent window, in this case the edit widget.  So
      whenever the edit widget is cleared, the scroll bar needs to redraw
@@ -3255,23 +3648,10 @@ xg_create_scroll_bar (f, bar, scroll_callback, end_callback, scroll_bar_name)
   bar->x_window = scroll_id;
 }
 
-/* Make the scroll bar represented by SCROLLBAR_ID visible.  */
-
-void
-xg_show_scroll_bar (scrollbar_id)
-     int scrollbar_id;
-{
-  GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
-  if (w)
-    gtk_widget_show_all (gtk_widget_get_parent (w));
-}
-
 /* Remove the scroll bar represented by SCROLLBAR_ID from the frame F.  */
 
 void
-xg_remove_scroll_bar (f, scrollbar_id)
-     FRAME_PTR f;
-     int scrollbar_id;
+xg_remove_scroll_bar (FRAME_PTR f, ptrdiff_t scrollbar_id)
 {
   GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
   if (w)
@@ -3289,13 +3669,12 @@ xg_remove_scroll_bar (f, scrollbar_id)
    WIDTH, HEIGHT is the size in pixels the bar shall have.  */
 
 void
-xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height)
-     FRAME_PTR f;
-     int scrollbar_id;
-     int top;
-     int left;
-     int width;
-     int height;
+xg_update_scrollbar_pos (FRAME_PTR f,
+                         ptrdiff_t scrollbar_id,
+                         int top,
+                         int left,
+                         int width,
+                         int height)
 {
 
   GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
@@ -3304,30 +3683,35 @@ xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height)
     {
       GtkWidget *wfixed = f->output_data.x->edit_widget;
       GtkWidget *wparent = gtk_widget_get_parent (wscroll);
-      GtkFixed *wf = GTK_FIXED (wfixed);
+      gint msl;
 
       /* Clear out old position.  */
-      GList *iter;
       int oldx = -1, oldy = -1, oldw, oldh;
-      for (iter = wf->children; iter; iter = iter->next)
-        if (((GtkFixedChild *)iter->data)->widget == wparent)
-          {
-            GtkFixedChild *ch = (GtkFixedChild *)iter->data;
-            if (ch->x != left || ch->y != top)
-              {
-                oldx = ch->x;
-                oldy = ch->y;
-                gtk_widget_get_size_request (wscroll, &oldw, &oldh);
-              }
-            break;
-          }
+      if (gtk_widget_get_parent (wparent) == wfixed)
+        {
+          gtk_container_child_get (GTK_CONTAINER (wfixed), wparent,
+                                   "x", &oldx, "y", &oldy, NULL);
+          gtk_widget_get_size_request (wscroll, &oldw, &oldh);
+        }
 
       /* Move and resize to new values.  */
       gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
-      gtk_widget_set_size_request (wscroll, width, height);
+      gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL);
+      if (msl > height)
+        {
+          /* No room.  Hide scroll bar as some themes output a warning if
+             the height is less than the min size.  */
+          gtk_widget_hide (wparent);
+          gtk_widget_hide (wscroll);
+        }
+      else
+        {
+          gtk_widget_show_all (wparent);
+          gtk_widget_set_size_request (wscroll, width, height);
+        }
       gtk_widget_queue_draw (wfixed);
       gdk_window_process_all_updates ();
-      if (oldx != -1
+      if (oldx != -1 && oldw > 0 && oldh > 0)
         {
           /* Clear under old scroll bar position.  This must be done after
              the gtk_widget_queue_draw and gdk_window_process_all_updates
@@ -3336,24 +3720,34 @@ xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height)
                         FRAME_X_WINDOW (f),
                         oldx, oldy, oldw, oldh, 0);
         }
-      
+
       /* GTK does not redraw until the main loop is entered again, but
          if there are no X events pending we will not enter it.  So we sync
          here to get some events.  */
-            
+
       x_sync (f);
       SET_FRAME_GARBAGED (f);
       cancel_mouse_face (f);
     }
 }
 
+/* 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);
+}
+
+
 /* Set the thumb size and position of scroll bar BAR.  We are currently
    displaying PORTION out of a whole WHOLE, and our position POSITION.  */
 
 void
-xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
-     struct scroll_bar *bar;
-     int portion, position, whole;
+xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
+                                 int portion,
+                                 int position,
+                                 int whole)
 {
   GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
 
@@ -3365,8 +3759,9 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
       gdouble shown;
       gdouble top;
       int size, value;
+      int old_size;
       int new_step;
-      int changed = 0;
+      bool changed = 0;
 
       adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
 
@@ -3397,66 +3792,73 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
       /* Assume all lines are of equal size.  */
       new_step = size / max (1, FRAME_LINES (f));
 
-      if ((int) adj->page_size != size
-          || (int) adj->step_increment != new_step)
-        {
-          adj->page_size = size;
-          adj->step_increment = new_step;
-          /* Assume a page increment is about 95% of the page size  */
-          adj->page_increment = (int) (0.95*adj->page_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, size - size / 20);
+             changed = 1;
+           }
+       }
+
+      if (changed || int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
       {
-        BLOCK_INPUT;
+        block_input ();
 
         /* gtk_range_set_value invokes the callback.  Set
            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);
 
         xg_ignore_gtk_scrollbar = 0;
 
-        UNBLOCK_INPUT;
+        unblock_input ();
       }
     }
 }
 
-/* Return non-zero if EVENT is for a scroll bar in frame F.
+/* Return true if EVENT is for a scroll bar in frame F.
    When the same X window is used for several Gtk+ widgets, we cannot
    say for sure based on the X window alone if an event is for the
-   frame.  This function does additional checks.
+   frame.  This function does additional checks.  */
 
-   Return non-zero if the event is for a scroll bar, zero otherwise.  */
-
-int
-xg_event_is_for_scrollbar (f, event)
-     FRAME_PTR f;
-     XEvent *event;
+bool
+xg_event_is_for_scrollbar (FRAME_PTR f, XEvent *event)
 {
-  int retval = 0;
+  bool retval = 0;
 
   if (f && event->type == ButtonPress && event->xbutton.button < 4)
     {
       /* Check if press occurred outside the edit widget.  */
       GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
-      retval = gdk_display_get_window_at_pointer (gdpy, NULL, NULL)
-        != f->output_data.x->edit_widget->window;
+      GdkWindow *gwin;
+#ifdef HAVE_GTK3
+      GdkDevice *gdev = gdk_device_manager_get_client_pointer
+        (gdk_display_get_device_manager (gdpy));
+      gwin = gdk_device_get_window_at_position (gdev, NULL, NULL);
+#else
+      gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL);
+#endif
+      retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget);
     }
   else if (f
            && ((event->type == ButtonRelease && event->xbutton.button < 4)
                || event->type == MotionNotify))
     {
       /* If we are releasing or moving the scroll bar, it has the grab.  */
-      retval = gtk_grab_get_current () != 0
-        && gtk_grab_get_current () != f->output_data.x->edit_widget;
+      GtkWidget *w = gtk_grab_get_current ();
+      retval = w != 0 && GTK_IS_SCROLLBAR (w);
     }
-  
+
   return retval;
 }
 
@@ -3492,13 +3894,12 @@ xg_event_is_for_scrollbar (f, event)
    tool bar.  0 is the first button.  */
 
 static gboolean
-xg_tool_bar_button_cb (widget, event, user_data)
-    GtkWidget      *widget;
-    GdkEventButton *event;
-    gpointer        user_data;
+xg_tool_bar_button_cb (GtkWidget *widget,
+                       GdkEventButton *event,
+                       gpointer user_data)
 {
-  /* Casts to avoid warnings when gpointer is 64 bits and int is 32 bits */
-  gpointer ptr = (gpointer) (EMACS_INT) event->state;
+  intptr_t state = event->state;
+  gpointer ptr = (gpointer) state;
   g_object_set_data (G_OBJECT (widget), XG_TOOL_BAR_LAST_MODIFIER, ptr);
   return FALSE;
 }
@@ -3510,14 +3911,11 @@ xg_tool_bar_button_cb (widget, event, user_data)
    tool bar.  0 is the first button.  */
 
 static void
-xg_tool_bar_callback (w, client_data)
-     GtkWidget *w;
-     gpointer client_data;
+xg_tool_bar_callback (GtkWidget *w, gpointer client_data)
 {
-  /* The EMACS_INT cast avoids a warning. */
-  int idx = (int) (EMACS_INT) client_data;
-  int mod = (int) (EMACS_INT) g_object_get_data (G_OBJECT (w),
-                                                 XG_TOOL_BAR_LAST_MODIFIER);
+  intptr_t idx = (intptr_t) client_data;
+  gpointer gmod = g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_LAST_MODIFIER);
+  intptr_t mod = (intptr_t) gmod;
 
   FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
   Lisp_Object key, frame;
@@ -3547,7 +3945,7 @@ xg_tool_bar_callback (w, client_data)
      this is written.  */
   event.modifiers = x_x_to_emacs_modifiers (FRAME_X_DISPLAY_INFO (f), mod);
   kbd_buffer_store_event (&event);
+
    /* Return focus to the frame after we have clicked on a detached
       tool bar button. */
    Fx_focus_frame (frame);
@@ -3561,9 +3959,7 @@ xg_tool_bar_callback (w, client_data)
    tool bar.  0 is the first button.  */
 
 static void
-xg_tool_bar_proxy_callback (w, client_data)
-     GtkWidget *w;
-     gpointer client_data;
+xg_tool_bar_proxy_callback (GtkWidget *w, gpointer client_data)
 {
   GtkWidget *wbutton = GTK_WIDGET (g_object_get_data (G_OBJECT (w),
                                                       XG_TOOL_BAR_PROXY_BUTTON));
@@ -3572,25 +3968,36 @@ xg_tool_bar_proxy_callback (w, client_data)
 
 
 static gboolean
-xg_tool_bar_help_callback P_ ((GtkWidget *w,
-                               GdkEventCrossing *event,
-                               gpointer client_data));
+xg_tool_bar_help_callback (GtkWidget *w,
+                           GdkEventCrossing *event,
+                           gpointer client_data);
 
 /* This callback is called when a help is to be shown for an item in
    the detached tool bar when the detached tool bar it is not expanded.  */
 
 static gboolean
-xg_tool_bar_proxy_help_callback (w, event, client_data)
-     GtkWidget *w;
-     GdkEventCrossing *event;
-     gpointer client_data;
+xg_tool_bar_proxy_help_callback (GtkWidget *w,
+                                 GdkEventCrossing *event,
+                                 gpointer client_data)
 {
   GtkWidget *wbutton = GTK_WIDGET (g_object_get_data (G_OBJECT (w),
                                                       XG_TOOL_BAR_PROXY_BUTTON));
-  
+
   return xg_tool_bar_help_callback (wbutton, event, client_data);
 }
 
+static GtkWidget *
+xg_get_tool_bar_widgets (GtkWidget *vb, GtkWidget **wimage)
+{
+  GList *clist = gtk_container_get_children (GTK_CONTAINER (vb));
+  GtkWidget *c1 = (GtkWidget *) clist->data;
+  GtkWidget *c2 = clist->next ? (GtkWidget *) clist->next->data : NULL;
+
+  *wimage = GTK_IS_IMAGE (c1) ? c1 : c2;
+  g_list_free (clist);
+  return GTK_IS_LABEL (c1) ? c1 : c2;
+}
+
 
 /* This callback is called when a tool item should create a proxy item,
    such as for the overflow menu.  Also called when the tool bar is detached.
@@ -3598,21 +4005,23 @@ xg_tool_bar_proxy_help_callback (w, event, client_data)
    blank.  */
 
 static gboolean
-xg_tool_bar_menu_proxy (toolitem, user_data)
-     GtkToolItem *toolitem;
-     gpointer user_data;
+xg_tool_bar_menu_proxy (GtkToolItem *toolitem, gpointer user_data)
 {
-  GtkWidget *weventbox = gtk_bin_get_child (GTK_BIN (toolitem));
-  GtkButton *wbutton = GTK_BUTTON (gtk_bin_get_child (GTK_BIN (weventbox)));
-  GtkWidget *wmenuitem = gtk_image_menu_item_new_with_label ("");
+  GtkButton *wbutton = GTK_BUTTON (XG_BIN_CHILD (XG_BIN_CHILD (toolitem)));
+  GtkWidget *vb = XG_BIN_CHILD (wbutton);
+  GtkWidget *c1;
+  GtkLabel *wlbl = GTK_LABEL (xg_get_tool_bar_widgets (vb, &c1));
+  GtkImage *wimage = GTK_IMAGE (c1);
+  GtkWidget *wmenuitem = gtk_image_menu_item_new_with_label
+    (wlbl ? gtk_label_get_text (wlbl) : "");
   GtkWidget *wmenuimage;
 
+
   if (gtk_button_get_use_stock (wbutton))
     wmenuimage = gtk_image_new_from_stock (gtk_button_get_label (wbutton),
                                            GTK_ICON_SIZE_MENU);
   else
     {
-      GtkImage *wimage = GTK_IMAGE (gtk_bin_get_child (GTK_BIN (wbutton)));
       GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (wbutton));
       GtkImageType store_type = gtk_image_get_storage_type (wimage);
 
@@ -3650,7 +4059,7 @@ xg_tool_bar_menu_proxy (toolitem, user_data)
           else
             {
               fprintf (stderr, "internal error: GTK_IMAGE_PIXBUF failed\n");
-              abort ();
+              emacs_abort ();
             }
         }
       else if (store_type == GTK_IMAGE_ICON_NAME)
@@ -3665,7 +4074,7 @@ xg_tool_bar_menu_proxy (toolitem, user_data)
       else
         {
           fprintf (stderr, "internal error: store_type is %d\n", store_type);
-          abort ();
+          emacs_abort ();
         }
     }
   if (wmenuimage)
@@ -3676,11 +4085,12 @@ xg_tool_bar_menu_proxy (toolitem, user_data)
                     G_CALLBACK (xg_tool_bar_proxy_callback),
                     user_data);
 
-  
+
   g_object_set_data (G_OBJECT (wmenuitem), XG_TOOL_BAR_PROXY_BUTTON,
                      (gpointer) wbutton);
   gtk_tool_item_set_proxy_menu_item (toolitem, "Emacs toolbar item", wmenuitem);
-  gtk_widget_set_sensitive (wmenuitem, GTK_WIDGET_SENSITIVE (wbutton));
+  gtk_widget_set_sensitive (wmenuitem,
+                            gtk_widget_get_sensitive (GTK_WIDGET (wbutton)));
 
   /* Use enter/leave notify to show help.  We use the events
      rather than the GtkButton specific signals "enter" and
@@ -3706,26 +4116,32 @@ xg_tool_bar_menu_proxy (toolitem, user_data)
    CLIENT_DATA is a pointer to the frame the tool bar belongs to.  */
 
 static void
-xg_tool_bar_detach_callback (wbox, w, client_data)
-     GtkHandleBox *wbox;
-     GtkWidget *w;
-     gpointer client_data;
+xg_tool_bar_detach_callback (GtkHandleBox *wbox,
+                             GtkWidget *w,
+                             gpointer client_data)
 {
   FRAME_PTR f = (FRAME_PTR) client_data;
-  extern int x_gtk_whole_detached_tool_bar;
 
   g_object_set (G_OBJECT (w), "show-arrow", !x_gtk_whole_detached_tool_bar,
                NULL);
 
   if (f)
     {
+      GtkRequisition req, req2;
       FRAME_X_OUTPUT (f)->toolbar_detached = 1;
-
-      /* When detaching a tool bar, not everything dissapear.  There are
-         a few pixels left that are used to drop the tool bar back into
-         place.  */
-      FRAME_TOOLBAR_HEIGHT (f) = 4;
-      xg_height_changed (f);
+      gtk_widget_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)
+        FRAME_TOOLBAR_TOP_HEIGHT (f) = req.height;
+      else if (FRAME_TOOLBAR_BOTTOM_HEIGHT (f) != 0)
+        FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = req.height;
+      else if (FRAME_TOOLBAR_RIGHT_WIDTH (f) != 0)
+        FRAME_TOOLBAR_RIGHT_WIDTH (f) = req.width;
+      else if (FRAME_TOOLBAR_LEFT_WIDTH (f) != 0)
+        FRAME_TOOLBAR_LEFT_WIDTH (f) = req.width;
+      xg_height_or_width_changed (f);
     }
 }
 
@@ -3737,23 +4153,30 @@ xg_tool_bar_detach_callback (wbox, w, client_data)
    CLIENT_DATA is a pointer to the frame the tool bar belongs to.  */
 
 static void
-xg_tool_bar_attach_callback (wbox, w, client_data)
-     GtkHandleBox *wbox;
-     GtkWidget *w;
-     gpointer client_data;
+xg_tool_bar_attach_callback (GtkHandleBox *wbox,
+                             GtkWidget *w,
+                             gpointer client_data)
 {
   FRAME_PTR f = (FRAME_PTR) client_data;
   g_object_set (G_OBJECT (w), "show-arrow", TRUE, NULL);
 
   if (f)
     {
-      GtkRequisition req;
-
+      GtkRequisition req, req2;
       FRAME_X_OUTPUT (f)->toolbar_detached = 0;
-
-      gtk_widget_size_request (w, &req);
-      FRAME_TOOLBAR_HEIGHT (f) = req.height;
-      xg_height_changed (f);
+      gtk_widget_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)
+        FRAME_TOOLBAR_TOP_HEIGHT (f) = req.height;
+      else if (FRAME_TOOLBAR_BOTTOM_HEIGHT (f) != 0)
+        FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = req.height;
+      else if (FRAME_TOOLBAR_RIGHT_WIDTH (f) != 0)
+        FRAME_TOOLBAR_RIGHT_WIDTH (f) = req.width;
+      else if (FRAME_TOOLBAR_LEFT_WIDTH (f) != 0)
+        FRAME_TOOLBAR_LEFT_WIDTH (f) = req.width;
+      xg_height_or_width_changed (f);
     }
 }
 
@@ -3767,13 +4190,11 @@ xg_tool_bar_attach_callback (wbox, w, client_data)
    Returns FALSE to tell GTK to keep processing this event.  */
 
 static gboolean
-xg_tool_bar_help_callback (w, event, client_data)
-     GtkWidget *w;
-     GdkEventCrossing *event;
-     gpointer client_data;
+xg_tool_bar_help_callback (GtkWidget *w,
+                           GdkEventCrossing *event,
+                           gpointer client_data)
 {
-  /* The EMACS_INT cast avoids a warning. */
-  int idx = (int) (EMACS_INT) client_data;
+  intptr_t idx = (intptr_t) client_data;
   FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
   Lisp_Object help, frame;
 
@@ -3808,16 +4229,15 @@ xg_tool_bar_help_callback (w, event, client_data)
 
    Returns FALSE to tell GTK to keep processing this event.  */
 
+#ifndef HAVE_GTK3
 static gboolean
-xg_tool_bar_item_expose_callback (w, event, client_data)
-     GtkWidget *w;
-     GdkEventExpose *event;
-     gpointer client_data;
+xg_tool_bar_item_expose_callback (GtkWidget *w,
+                                  GdkEventExpose *event,
+                                  gpointer client_data)
 {
   gint width, height;
 
   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;
 
@@ -3829,56 +4249,104 @@ xg_tool_bar_item_expose_callback (w, event, client_data)
 
   return FALSE;
 }
+#endif
+
+#ifdef HAVE_GTK_ORIENTABLE_SET_ORIENTATION
+#define toolbar_set_orientation(w, o) \
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (w), o)
+#else
+#define toolbar_set_orientation(w, o) \
+  gtk_toolbar_set_orientation (GTK_TOOLBAR (w), o)
+#endif
 
 /* Attach a tool bar to frame F.  */
 
 static void
-xg_pack_tool_bar (f)
-     FRAME_PTR f;
+xg_pack_tool_bar (FRAME_PTR f, Lisp_Object pos)
 {
   struct x_output *x = f->output_data.x;
-  int vbox_pos = x->menubar_widget ? 1 : 0;
-
-  x->handlebox_widget = gtk_handle_box_new ();
-  g_signal_connect (G_OBJECT (x->handlebox_widget), "child-detached",
-                    G_CALLBACK (xg_tool_bar_detach_callback), f);
-  g_signal_connect (G_OBJECT (x->handlebox_widget), "child-attached",
-                    G_CALLBACK (xg_tool_bar_attach_callback), f);
+  bool into_hbox = EQ (pos, Qleft) || EQ (pos, Qright);
 
-  gtk_container_add (GTK_CONTAINER (x->handlebox_widget),
-                     x->toolbar_widget);
-
-  gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->handlebox_widget,
-                      FALSE, FALSE, 0);
+  toolbar_set_orientation (x->toolbar_widget,
+                           into_hbox
+                           ? GTK_ORIENTATION_VERTICAL
+                           : GTK_ORIENTATION_HORIZONTAL);
+  if (!x->handlebox_widget)
+    {
+      x->handlebox_widget = gtk_handle_box_new ();
+      g_signal_connect (G_OBJECT (x->handlebox_widget), "child-detached",
+                        G_CALLBACK (xg_tool_bar_detach_callback), f);
+      g_signal_connect (G_OBJECT (x->handlebox_widget), "child-attached",
+                        G_CALLBACK (xg_tool_bar_attach_callback), f);
+      gtk_container_add (GTK_CONTAINER (x->handlebox_widget),
+                         x->toolbar_widget);
+    }
 
-  gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->handlebox_widget,
-                         vbox_pos);
-  gtk_widget_show_all (x->handlebox_widget);
+  if (into_hbox)
+    {
+      gtk_handle_box_set_handle_position (GTK_HANDLE_BOX (x->handlebox_widget),
+                                          GTK_POS_TOP);
+      gtk_box_pack_start (GTK_BOX (x->hbox_widget), x->handlebox_widget,
+                          FALSE, FALSE, 0);
+
+      if (EQ (pos, Qleft))
+        gtk_box_reorder_child (GTK_BOX (x->hbox_widget),
+                               x->handlebox_widget,
+                               0);
+      x->toolbar_in_hbox = 1;
+    }
+  else
+    {
+      bool vbox_pos = x->menubar_widget != 0;
+      gtk_handle_box_set_handle_position (GTK_HANDLE_BOX (x->handlebox_widget),
+                                          GTK_POS_LEFT);
+      gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->handlebox_widget,
+                          FALSE, FALSE, 0);
+
+      if (EQ (pos, Qtop))
+        gtk_box_reorder_child (GTK_BOX (x->vbox_widget),
+                               x->handlebox_widget,
+                               vbox_pos);
+      x->toolbar_in_hbox = 0;
+    }
 }
 
 /* Create a tool bar for frame F.  */
 
 static void
-xg_create_tool_bar (f)
-     FRAME_PTR f;
+xg_create_tool_bar (FRAME_PTR f)
 {
   struct x_output *x = f->output_data.x;
+#if GTK_CHECK_VERSION (3, 3, 6)
+  GtkStyleContext *gsty;
+#endif
+  struct xg_frame_tb_info *tbinfo
+    = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                         TB_INFO_KEY);
+  if (! tbinfo)
+    {
+      tbinfo = xmalloc (sizeof (*tbinfo));
+      tbinfo->last_tool_bar = Qnil;
+      tbinfo->style = Qnil;
+      tbinfo->hmargin = tbinfo->vmargin = 0;
+      tbinfo->dir = GTK_TEXT_DIR_NONE;
+      tbinfo->n_last_items = 0;
+      g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                         TB_INFO_KEY,
+                         tbinfo);
+    }
 
   x->toolbar_widget = gtk_toolbar_new ();
   x->toolbar_detached = 0;
 
   gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
 
-  /* We only have icons, so override any user setting.  We could
-     use the caption property of the toolbar item (see update_frame_tool_bar
-     below), but some of those strings are long, making the toolbar so
-     long it does not fit on the screen.  The GtkToolbar widget makes every
-     item equal size, so the longest caption determine the size of every
-     tool bar item.  I think the creators of the GtkToolbar widget
-     counted on 4 or 5 character long strings.  */
   gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
-  gtk_toolbar_set_orientation (GTK_TOOLBAR (x->toolbar_widget),
-                               GTK_ORIENTATION_HORIZONTAL);
+  toolbar_set_orientation (x->toolbar_widget, GTK_ORIENTATION_HORIZONTAL);
+#if GTK_CHECK_VERSION (3, 3, 6)
+  gsty = gtk_widget_get_style_context (x->toolbar_widget);
+  gtk_style_context_add_class (gsty, "primary-toolbar");
+#endif
 }
 
 
@@ -3888,10 +4356,7 @@ xg_create_tool_bar (f)
    Returns IMAGE if RTL is not found.  */
 
 static Lisp_Object
-find_rtl_image (f, image, rtl)
-     FRAME_PTR f;
-     Lisp_Object image;
-     Lisp_Object rtl;
+find_rtl_image (FRAME_PTR f, Lisp_Object image, Lisp_Object rtl)
 {
   int i;
   Lisp_Object file, rtl_name;
@@ -3907,7 +4372,7 @@ find_rtl_image (f, image, rtl)
         {
           file = call1 (intern ("file-name-sans-extension"),
                        Ffile_name_nondirectory (file));
-          if (EQ (Fequal (file, rtl_name), Qt))
+          if (! NILP (Fequal (file, rtl_name)))
             {
               image = rtl_image;
               break;
@@ -3918,42 +4383,246 @@ find_rtl_image (f, image, rtl)
   return image;
 }
 
-/* Update the tool bar for frame F.  Add new buttons and remove old.  */
+static GtkToolItem *
+xg_make_tool_item (FRAME_PTR f,
+                   GtkWidget *wimage,
+                   GtkWidget **wbutton,
+                   const char *label,
+                   int i, bool horiz, bool text_image)
+{
+  GtkToolItem *ti = gtk_tool_item_new ();
+  GtkWidget *vb = gtk_box_new (horiz
+                               ? GTK_ORIENTATION_HORIZONTAL
+                               : GTK_ORIENTATION_VERTICAL,
+                               0);
+  GtkWidget *wb = gtk_button_new ();
+  /* The eventbox is here so we can have tooltips on disabled items.  */
+  GtkWidget *weventbox = gtk_event_box_new ();
+#if GTK_CHECK_VERSION (3, 3, 6)
+  GtkCssProvider *css_prov = gtk_css_provider_new ();
+  GtkStyleContext *gsty;
+
+  gtk_css_provider_load_from_data (css_prov,
+                                  "GtkEventBox {"
+                                  "    background-color: transparent;"
+                                  "}",
+                                  -1, NULL);
+
+  gsty = gtk_widget_get_style_context (weventbox);
+  gtk_style_context_add_provider (gsty,
+                                 GTK_STYLE_PROVIDER (css_prov),
+                                 GTK_STYLE_PROVIDER_PRIORITY_USER);
+  g_object_unref (css_prov);
+#endif
+
+  gtk_box_set_homogeneous (GTK_BOX (vb), FALSE);
 
-extern Lisp_Object Qx_gtk_map_stock;
+  if (wimage && !text_image)
+    gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
+  if (label)
+    gtk_box_pack_start (GTK_BOX (vb), gtk_label_new (label), TRUE, TRUE, 0);
+  if (wimage && text_image)
+    gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
+
+  gtk_button_set_focus_on_click (GTK_BUTTON (wb), FALSE);
+  gtk_button_set_relief (GTK_BUTTON (wb), GTK_RELIEF_NONE);
+  gtk_container_add (GTK_CONTAINER (wb), vb);
+  gtk_container_add (GTK_CONTAINER (weventbox), wb);
+  gtk_container_add (GTK_CONTAINER (ti), weventbox);
+
+  if (wimage || label)
+    {
+      intptr_t ii = i;
+      gpointer gi = (gpointer) ii;
+
+      g_signal_connect (G_OBJECT (ti), "create-menu-proxy",
+                        G_CALLBACK (xg_tool_bar_menu_proxy),
+                        gi);
+
+      g_signal_connect (G_OBJECT (wb), "clicked",
+                        G_CALLBACK (xg_tool_bar_callback),
+                        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 modifier mask (Shift/Control, etc).  GTK makes
+         no distinction based on modifiers in the activate callback,
+         so we have to do it ourselves.  */
+      g_signal_connect (wb, "button-release-event",
+                        G_CALLBACK (xg_tool_bar_button_cb),
+                        NULL);
+
+      g_object_set_data (G_OBJECT (wb), XG_FRAME_DATA, (gpointer)f);
+
+      /* Use enter/leave notify to show help.  We use the events
+         rather than the GtkButton specific signals "enter" and
+         "leave", so we can have only one callback.  The event
+         will tell us what kind of event it is.  */
+      g_signal_connect (G_OBJECT (weventbox),
+                        "enter-notify-event",
+                        G_CALLBACK (xg_tool_bar_help_callback),
+                        gi);
+      g_signal_connect (G_OBJECT (weventbox),
+                        "leave-notify-event",
+                        G_CALLBACK (xg_tool_bar_help_callback),
+                        gi);
+    }
+
+  if (wbutton) *wbutton = wb;
+
+  return ti;
+}
+
+static bool
+is_box_type (GtkWidget *vb, bool is_horizontal)
+{
+#ifdef HAVE_GTK3
+  bool ret = 0;
+  if (GTK_IS_BOX (vb))
+    {
+      GtkOrientation ori = gtk_orientable_get_orientation (GTK_ORIENTABLE (vb));
+      ret = (ori == GTK_ORIENTATION_HORIZONTAL && is_horizontal)
+        || (ori == GTK_ORIENTATION_VERTICAL && ! is_horizontal);
+    }
+  return ret;
+#else
+  return is_horizontal ? GTK_IS_VBOX (vb) : GTK_IS_HBOX (vb);
+#endif
+}
+
+
+static bool
+xg_tool_item_stale_p (GtkWidget *wbutton, const char *stock_name,
+                     const char *icon_name, const struct image *img,
+                     const char *label, bool horiz)
+{
+  gpointer old;
+  GtkWidget *wimage;
+  GtkWidget *vb = XG_BIN_CHILD (wbutton);
+  GtkWidget *wlbl = xg_get_tool_bar_widgets (vb, &wimage);
+
+  /* Check if the tool icon matches.  */
+  if (stock_name && wimage)
+    {
+      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 && wimage)
+    {
+      old = g_object_get_data (G_OBJECT (wimage),
+                              XG_TOOL_BAR_ICON_NAME);
+      if (!old || strcmp (old, icon_name))
+       return 1;
+    }
+  else if (wimage)
+    {
+      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;
+    }
+
+  /* Check button configuration and label.  */
+  if (is_box_type (vb, horiz)
+      || (label ? (wlbl == NULL) : (wlbl != NULL)))
+    return 1;
+
+  /* Ensure label is correct.  */
+  if (label && wlbl)
+    gtk_label_set_text (GTK_LABEL (wlbl), label);
+  return 0;
+}
+
+static bool
+xg_update_tool_bar_sizes (FRAME_PTR f)
+{
+  struct x_output *x = f->output_data.x;
+  GtkRequisition req;
+  int nl = 0, nr = 0, nt = 0, nb = 0;
+
+  gtk_widget_get_preferred_size (GTK_WIDGET (x->handlebox_widget), NULL, &req);
+  if (x->toolbar_in_hbox)
+    {
+      int pos;
+      gtk_container_child_get (GTK_CONTAINER (x->hbox_widget),
+                               x->handlebox_widget,
+                               "position", &pos, NULL);
+      if (pos == 0) nl = req.width;
+      else nr = req.width;
+    }
+  else
+    {
+      int pos;
+      gtk_container_child_get (GTK_CONTAINER (x->vbox_widget),
+                               x->handlebox_widget,
+                               "position", &pos, NULL);
+      if (pos == 0 || (pos == 1 && x->menubar_widget)) nt = req.height;
+      else nb = req.height;
+    }
+
+  if (nl != FRAME_TOOLBAR_LEFT_WIDTH (f)
+      || nr != FRAME_TOOLBAR_RIGHT_WIDTH (f)
+      || nt != FRAME_TOOLBAR_TOP_HEIGHT (f)
+      || nb != FRAME_TOOLBAR_BOTTOM_HEIGHT (f))
+    {
+      FRAME_TOOLBAR_RIGHT_WIDTH (f) = FRAME_TOOLBAR_LEFT_WIDTH (f)
+        = FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
+      FRAME_TOOLBAR_LEFT_WIDTH (f) = nl;
+      FRAME_TOOLBAR_RIGHT_WIDTH (f) = nr;
+      FRAME_TOOLBAR_TOP_HEIGHT (f) = nt;
+      FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = nb;
+      return 1;
+    }
+
+  return 0;
+}
+
+
+/* Update the tool bar for frame F.  Add new buttons and remove old.  */
 
 void
-update_frame_tool_bar (f)
-     FRAME_PTR f;
+update_frame_tool_bar (FRAME_PTR f)
 {
-  int i;
-  GtkRequisition old_req, new_req;
+  int i, j;
   struct x_output *x = f->output_data.x;
   int hmargin = 0, vmargin = 0;
   GtkToolbar *wtoolbar;
   GtkToolItem *ti;
   GtkTextDirection dir;
-  int pack_tool_bar = x->handlebox_widget == NULL;
+  bool pack_tool_bar = x->handlebox_widget == NULL;
+  Lisp_Object style;
+  bool text_image, horiz;
+  struct xg_frame_tb_info *tbinfo;
 
   if (! FRAME_GTK_WIDGET (f))
     return;
 
-  BLOCK_INPUT;
+  block_input ();
 
-  if (INTEGERP (Vtool_bar_button_margin)
-      && XINT (Vtool_bar_button_margin) > 0)
+  if (RANGED_INTEGERP (1, Vtool_bar_button_margin, INT_MAX))
     {
       hmargin = XFASTINT (Vtool_bar_button_margin);
       vmargin = XFASTINT (Vtool_bar_button_margin);
     }
   else if (CONSP (Vtool_bar_button_margin))
     {
-      if (INTEGERP (XCAR (Vtool_bar_button_margin))
-          && XINT (XCAR (Vtool_bar_button_margin)) > 0)
+      if (RANGED_INTEGERP (1, XCAR (Vtool_bar_button_margin), INT_MAX))
         hmargin = XFASTINT (XCAR (Vtool_bar_button_margin));
 
-      if (INTEGERP (XCDR (Vtool_bar_button_margin))
-          && XINT (XCDR (Vtool_bar_button_margin)) > 0)
+      if (RANGED_INTEGERP (1, XCDR (Vtool_bar_button_margin), INT_MAX))
         vmargin = XFASTINT (XCDR (Vtool_bar_button_margin));
     }
 
@@ -3968,15 +4637,41 @@ update_frame_tool_bar (f)
     xg_create_tool_bar (f);
 
   wtoolbar = GTK_TOOLBAR (x->toolbar_widget);
-  gtk_widget_size_request (GTK_WIDGET (wtoolbar), &old_req);
-  dir = gtk_widget_get_direction (x->toolbar_widget);
+  dir = gtk_widget_get_direction (GTK_WIDGET (wtoolbar));
 
-  for (i = 0; i < f->n_tool_bar_items; ++i)
+  style = Ftool_bar_get_system_style ();
+
+  /* Are we up to date? */
+  tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                              TB_INFO_KEY);
+
+  if (! NILP (tbinfo->last_tool_bar) && ! NILP (f->tool_bar_items)
+      && tbinfo->n_last_items == f->n_tool_bar_items
+      && tbinfo->hmargin == hmargin && tbinfo->vmargin == vmargin
+      && tbinfo->dir == dir
+      && ! NILP (Fequal (tbinfo->style, style))
+      && ! NILP (Fequal (tbinfo->last_tool_bar, f->tool_bar_items)))
     {
-      int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
-      int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
+      unblock_input ();
+      return;
+    }
+
+  tbinfo->last_tool_bar = f->tool_bar_items;
+  tbinfo->n_last_items = f->n_tool_bar_items;
+  tbinfo->style = style;
+  tbinfo->hmargin = hmargin;
+  tbinfo->vmargin = vmargin;
+  tbinfo->dir = dir;
+
+  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)
+    {
+      bool enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
+      bool selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
       int idx;
-      int img_id;
+      ptrdiff_t img_id;
       int icon_size = 0;
       struct image *img = NULL;
       Lisp_Object image;
@@ -3986,23 +4681,48 @@ update_frame_tool_bar (f)
       char *icon_name = NULL;
       Lisp_Object rtl;
       GtkWidget *wbutton = NULL;
-      GtkWidget *weventbox;
       Lisp_Object specified_file;
-
-      ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (x->toolbar_widget), i);
-
-      if (ti)
-        {
-          weventbox = gtk_bin_get_child (GTK_BIN (ti));
-          wbutton = gtk_bin_get_child (GTK_BIN (weventbox));
-        }
-
-      image = PROP (TOOL_BAR_ITEM_IMAGES);
+      bool 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;
         }
 
@@ -4028,7 +4748,7 @@ update_frame_tool_bar (f)
                 icon_size = gtk_toolbar_get_icon_size (wtoolbar);
             }
           else if (gtk_stock_lookup (SSDATA (stock), &stock_item))
-              icon_size = gtk_toolbar_get_icon_size (wtoolbar);
+            icon_size = gtk_toolbar_get_icon_size (wtoolbar);
           else
             {
               stock = Qnil;
@@ -4038,16 +4758,13 @@ update_frame_tool_bar (f)
 
       if (stock_name == NULL && icon_name == NULL)
         {
-          /* No stock image, or stock item not known.  Try regular image.  */
-
-          /* If image is a vector, choose the image according to the
+          /* No stock image, or stock item not known.  Try regular
+             image.  If image is a vector, choose it according to the
              button state.  */
           if (dir == GTK_TEXT_DIR_RTL
               && !NILP (rtl = PROP (TOOL_BAR_ITEM_RTL_IMAGE))
               && STRINGP (rtl))
-            {
-              image = find_rtl_image (f, image, rtl);
-            }
+           image = find_rtl_image (f, image, rtl);
 
           if (VECTORP (image))
             {
@@ -4060,7 +4777,7 @@ update_frame_tool_bar (f)
                        ? TOOL_BAR_IMAGE_DISABLED_SELECTED
                        : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
 
-              xassert (ASIZE (image) >= idx);
+              eassert (ASIZE (image) >= idx);
               image = AREF (image, idx);
             }
           else
@@ -4072,29 +4789,32 @@ update_frame_tool_bar (f)
 
           if (img->load_failed_p || img->pixmap == None)
             {
-                if (ti)
-                    gtk_widget_hide_all (GTK_WIDGET (ti));
-                else
-                {
-                    /* Insert an empty (non-image) button */
-                    weventbox = gtk_event_box_new ();
-                    wbutton = gtk_button_new ();
-                    gtk_button_set_focus_on_click (GTK_BUTTON (wbutton), FALSE);
-                    gtk_button_set_relief (GTK_BUTTON (wbutton),
-                                           GTK_RELIEF_NONE);
-                    gtk_container_add (GTK_CONTAINER (weventbox), wbutton);
-                    ti = gtk_tool_item_new ();
-                    gtk_container_add (GTK_CONTAINER (ti), weventbox);
-                    gtk_toolbar_insert (GTK_TOOLBAR (x->toolbar_widget), ti, -1);
-                }
-                continue;
+              if (ti)
+               gtk_container_remove (GTK_CONTAINER (wtoolbar),
+                                     GTK_WIDGET (ti));
+              continue;
             }
         }
 
+      /* If there is an existing widget, check if it's stale; if so,
+        remove it and make a new tool item from scratch.  */
+      if (ti && xg_tool_item_stale_p (wbutton, stock_name, icon_name,
+                                     img, label, horiz))
+       {
+         gtk_container_remove (GTK_CONTAINER (wtoolbar),
+                               GTK_WIDGET (ti));
+         ti = NULL;
+       }
+
       if (ti == NULL)
         {
           GtkWidget *w;
-          if (stock_name)
+
+         /* Save the image so we can see if an update is needed the
+            next time we call xg_tool_item_match_p.  */
+         if (EQ (style, Qtext))
+           w = NULL;
+         else if (stock_name)
             {
               w = gtk_image_new_from_stock (stock_name, icon_size);
               g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_STOCK_NAME,
@@ -4111,187 +4831,123 @@ update_frame_tool_bar (f)
           else
             {
               w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
-              /* Save the image so we can see if an update is needed when
-                 this function is called again.  */
               g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
                                  (gpointer)img->pixmap);
             }
 
-          gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
-          wbutton = gtk_button_new ();
-          gtk_button_set_focus_on_click (GTK_BUTTON (wbutton), FALSE);
-          gtk_button_set_relief (GTK_BUTTON (wbutton), GTK_RELIEF_NONE);
-          gtk_container_add (GTK_CONTAINER (wbutton), w);
-          weventbox = gtk_event_box_new ();
-          gtk_container_add (GTK_CONTAINER (weventbox), wbutton);
-          ti = gtk_tool_item_new ();
-          gtk_container_add (GTK_CONTAINER (ti), weventbox);
-          gtk_toolbar_insert (GTK_TOOLBAR (x->toolbar_widget), ti, -1);
-
-
-          /* The EMACS_INT cast avoids a warning. */
-          g_signal_connect (G_OBJECT (ti), "create-menu-proxy",
-                            G_CALLBACK (xg_tool_bar_menu_proxy),
-                            (gpointer) (EMACS_INT) i);
-
-          g_signal_connect (G_OBJECT (wbutton), "clicked",
-                            G_CALLBACK (xg_tool_bar_callback),
-                            (gpointer) (EMACS_INT) i);
-
-          gtk_widget_show_all (GTK_WIDGET (ti));
-
-
-          g_object_set_data (G_OBJECT (weventbox), XG_FRAME_DATA, (gpointer)f);
-
-          /* Catch expose events to overcome an annoying redraw bug, see
-             comment for xg_tool_bar_item_expose_callback.  */
-          g_signal_connect (G_OBJECT (ti),
-                            "expose-event",
-                            G_CALLBACK (xg_tool_bar_item_expose_callback),
-                            0);
-
-          gtk_widget_set_sensitive (wbutton, enabled_p);
-          gtk_tool_item_set_homogeneous (ti, FALSE);
-
-          /* Callback to save modifyer mask (Shift/Control, etc).  GTK makes
-             no distinction based on modifiers in the activate callback,
-             so we have to do it ourselves.  */
-          g_signal_connect (wbutton, "button-release-event",
-                            G_CALLBACK (xg_tool_bar_button_cb),
-                            NULL);
-
-          g_object_set_data (G_OBJECT (wbutton), XG_FRAME_DATA, (gpointer)f);
-          
-          /* Use enter/leave notify to show help.  We use the events
-             rather than the GtkButton specific signals "enter" and
-             "leave", so we can have only one callback.  The event
-             will tell us what kind of event it is.  */
-          /* The EMACS_INT cast avoids a warning. */
-          g_signal_connect (G_OBJECT (weventbox),
-                            "enter-notify-event",
-                            G_CALLBACK (xg_tool_bar_help_callback),
-                            (gpointer) (EMACS_INT) i);
-          g_signal_connect (G_OBJECT (weventbox),
-                            "leave-notify-event",
-                            G_CALLBACK (xg_tool_bar_help_callback),
-                            (gpointer) (EMACS_INT) i);
+         if (w) gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
+          ti = xg_make_tool_item (f, w, &wbutton, label, i, horiz, text_image);
+          gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
         }
-      else
-        {
-          GtkWidget *wimage = gtk_bin_get_child (GTK_BIN (wbutton));
-          Pixmap old_img = (Pixmap)g_object_get_data (G_OBJECT (wimage),
-                                                      XG_TOOL_BAR_IMAGE_DATA);
-          gpointer old_stock_name = g_object_get_data (G_OBJECT (wimage),
-                                                       XG_TOOL_BAR_STOCK_NAME);
-          gpointer old_icon_name = g_object_get_data (G_OBJECT (wimage),
-                                                      XG_TOOL_BAR_ICON_NAME);
-          if (stock_name &&
-              (! old_stock_name || strcmp (old_stock_name, stock_name) != 0))
-            {
-              gtk_image_set_from_stock (GTK_IMAGE (wimage),
-                                        stock_name, icon_size);
-              g_object_set_data_full (G_OBJECT (wimage), XG_TOOL_BAR_STOCK_NAME,
-                                      (gpointer) xstrdup (stock_name),
-                                      (GDestroyNotify) xfree);
-              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
-                                 NULL);
-              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_ICON_NAME, NULL);
-            }
-          else if (icon_name &&
-                   (! old_icon_name || strcmp (old_icon_name, icon_name) != 0))
-            {
-              gtk_image_set_from_icon_name (GTK_IMAGE (wimage),
-                                            icon_name, icon_size);
-              g_object_set_data_full (G_OBJECT (wimage), XG_TOOL_BAR_ICON_NAME,
-                                      (gpointer) xstrdup (icon_name),
-                                      (GDestroyNotify) xfree);
-              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
-                                 NULL);
-              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_STOCK_NAME,
-                                 NULL);
-            }
-          else if (img && old_img != img->pixmap)
-            {
-              (void) xg_get_image_for_pixmap (f, img, x->widget, wimage);
-              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
-                                 (gpointer)img->pixmap);
-
-              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_STOCK_NAME,
-                                 NULL);
-              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_ICON_NAME, NULL);
-            }
-
-          gtk_misc_set_padding (GTK_MISC (wimage), hmargin, vmargin);
-
-          gtk_widget_set_sensitive (wbutton, enabled_p);
-          gtk_widget_show_all (GTK_WIDGET (ti));
-       }
 
 #undef PROP
+
+      gtk_widget_set_sensitive (wbutton, enabled_p);
+      j++;
     }
 
-  /* Remove buttons not longer needed.  We just hide them so they
-     can be reused later on.  */
+  /* Remove buttons not longer needed.  */
   do
     {
-      ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (x->toolbar_widget), i++);
-      if (ti) gtk_widget_hide_all (GTK_WIDGET (ti));
+      ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
+      if (ti)
+       gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
     } while (ti != NULL);
 
-  new_req.height = 0;
-  if (pack_tool_bar && f->n_tool_bar_items != 0)
-      xg_pack_tool_bar (f);
-  
-
-  gtk_widget_size_request (GTK_WIDGET (x->toolbar_widget), &new_req);
-  if (old_req.height != new_req.height
-      && ! FRAME_X_OUTPUT (f)->toolbar_detached)
+  if (f->n_tool_bar_items != 0)
     {
-      FRAME_TOOLBAR_HEIGHT (f) = new_req.height;
-      xg_height_changed (f);
+      if (pack_tool_bar)
+        xg_pack_tool_bar (f, f->tool_bar_position);
+      gtk_widget_show_all (GTK_WIDGET (x->handlebox_widget));
+      if (xg_update_tool_bar_sizes (f))
+        xg_height_or_width_changed (f);
     }
-  UNBLOCK_INPUT;
+
+  unblock_input ();
 }
 
 /* Deallocate all resources for the tool bar on frame F.
    Remove the tool bar.  */
 
 void
-free_frame_tool_bar (f)
-     FRAME_PTR f;
+free_frame_tool_bar (FRAME_PTR f)
 {
   struct x_output *x = f->output_data.x;
 
   if (x->toolbar_widget)
     {
-      int is_packed = x->handlebox_widget != 0;
-      BLOCK_INPUT;
+      struct xg_frame_tb_info *tbinfo;
+      bool is_packed = x->handlebox_widget != 0;
+      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)
-        gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
-                              x->handlebox_widget);
+        {
+          if (x->toolbar_in_hbox)
+            gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
+                                  x->handlebox_widget);
+          else
+            gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
+                                  x->handlebox_widget);
+        }
       else
         gtk_widget_destroy (x->toolbar_widget);
 
       x->toolbar_widget = 0;
       x->handlebox_widget = 0;
-      FRAME_TOOLBAR_HEIGHT (f) = 0;
-      xg_height_changed (f);
+      FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
+      FRAME_TOOLBAR_LEFT_WIDTH (f) = FRAME_TOOLBAR_RIGHT_WIDTH (f) = 0;
+
+      tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                                  TB_INFO_KEY);
+      if (tbinfo)
+        {
+          xfree (tbinfo);
+          g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                             TB_INFO_KEY,
+                             NULL);
+        }
 
-      UNBLOCK_INPUT;
+      xg_height_or_width_changed (f);
+
+      unblock_input ();
     }
 }
 
+void
+xg_change_toolbar_position (FRAME_PTR f, Lisp_Object pos)
+{
+  struct x_output *x = f->output_data.x;
+
+  if (! x->toolbar_widget || ! x->handlebox_widget)
+    return;
+
+  block_input ();
+  g_object_ref (x->handlebox_widget);
+  if (x->toolbar_in_hbox)
+    gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
+                          x->handlebox_widget);
+  else
+    gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
+                          x->handlebox_widget);
+  xg_pack_tool_bar (f, pos);
+  g_object_unref (x->handlebox_widget);
+  if (xg_update_tool_bar_sizes (f))
+    xg_height_or_width_changed (f);
+
+  unblock_input ();
+}
+
 
 \f
 /***********************************************************************
                       Initializing
- ***********************************************************************/
+***********************************************************************/
 void
-xg_initialize ()
+xg_initialize (void)
 {
   GtkBindingSet *binding_set;
+  GtkSettings *settings;
 
 #if HAVE_XFT
   /* Work around a bug with corrupted data if libXft gets unloaded.  This way
@@ -4308,17 +4964,19 @@ xg_initialize ()
   id_to_widget.max_size = id_to_widget.used = 0;
   id_to_widget.widgets = 0;
 
+  settings = gtk_settings_get_for_screen (gdk_display_get_default_screen
+                                          (gdk_display_get_default ()));
   /* Remove F10 as a menu accelerator, it does not mix well with Emacs key
      bindings.  It doesn't seem to be any way to remove properties,
      so we set it to VoidSymbol which in X means "no key".  */
-  gtk_settings_set_string_property (gtk_settings_get_default (),
+  gtk_settings_set_string_property (settings,
                                     "gtk-menu-bar-accel",
                                     "VoidSymbol",
                                     EMACS_CLASS);
 
   /* Make GTK text input widgets use Emacs style keybindings.  This is
      Emacs after all.  */
-  gtk_settings_set_string_property (gtk_settings_get_default (),
+  gtk_settings_set_string_property (settings,
                                     "gtk-key-theme-name",
                                     "Emacs",
                                     EMACS_CLASS);
@@ -4326,17 +4984,19 @@ xg_initialize ()
   /* 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);
+  update_theme_scrollbar_width ();
+
+#ifdef HAVE_FREETYPE
+  x_last_font_name = NULL;
+#endif
 }
 
 #endif /* USE_GTK */
-
-/* arch-tag: fe7104da-bc1e-4aba-9bd1-f349c528f7e3
-   (do not change this comment) */