]> code.delx.au - gnu-emacs/blobdiff - src/gtkutil.c
js-mode: Don't indent inside a multiline string literal
[gnu-emacs] / src / gtkutil.c
index 237d031295c59389f67a5e23883d80f6e2c3e403..41cc7a746643c2332974c740fd81c145ddfeebdc 100644 (file)
@@ -1,6 +1,6 @@
 /* Functions for creating and updating GTK widgets.
 
-Copyright (C) 2003-2013 Free Software Foundation, Inc.
+Copyright (C) 2003-2015 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -30,11 +30,14 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "blockinput.h"
 #include "syssignal.h"
 #include "window.h"
+#include "buffer.h"
 #include "gtkutil.h"
 #include "termhooks.h"
 #include "keyboard.h"
 #include "charset.h"
 #include "coding.h"
+#include "font.h"
+
 #include <gdk/gdkkeysyms.h>
 #include "xsettings.h"
 
@@ -47,12 +50,6 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "emacsgtkfixed.h"
 #endif
 
-#define FRAME_TOTAL_PIXEL_HEIGHT(f) \
-  (FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
-
-#define FRAME_TOTAL_PIXEL_WIDTH(f) \
-  (FRAME_PIXEL_WIDTH (f) + FRAME_TOOLBAR_WIDTH (f))
-
 #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
 #define gtk_widget_set_has_window(w, b) \
   (gtk_fixed_set_has_window (GTK_FIXED (w), b))
@@ -76,6 +73,21 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #define remove_submenu(w) gtk_menu_item_remove_submenu ((w))
 #endif
 
+#if ! GTK_CHECK_VERSION (2, 14, 0)
+#define gtk_adjustment_configure(adj, xvalue, xlower,            \
+                                 xupper, xstep_increment,        \
+                                 xpage_increment, xpagesize)     \
+  do {                                                           \
+    adj->lower = xlower;                                         \
+    adj->upper = xupper;                                         \
+    adj->page_size = xpagesize;                                  \
+    gtk_adjustment_set_value (adj, xvalue);                      \
+    adj->page_increment = xpage_increment;                       \
+    adj->step_increment = xstep_increment;                       \
+  } while (0)
+#endif /* < Gtk+ 2.14 */
+
+#ifdef HAVE_FREETYPE
 #if GTK_CHECK_VERSION (3, 2, 0)
 #define USE_NEW_GTK_FONT_CHOOSER 1
 #else
@@ -87,6 +99,17 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #define  gtk_font_chooser_set_font(x, y) \
   gtk_font_selection_dialog_set_font_name (x, y)
 #endif
+#endif /* HAVE_FREETYPE */
+
+#if GTK_CHECK_VERSION (3, 10, 0)
+#define XG_TEXT_CANCEL "Cancel"
+#define XG_TEXT_OK     "OK"
+#define XG_TEXT_OPEN   "Open"
+#else
+#define XG_TEXT_CANCEL GTK_STOCK_CANCEL
+#define XG_TEXT_OK     GTK_STOCK_OK
+#define XG_TEXT_OPEN   GTK_STOCK_OPEN
+#endif
 
 #ifndef HAVE_GTK3
 #ifdef USE_GTK_TOOLTIP
@@ -110,6 +133,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #define XG_BIN_CHILD(x) gtk_bin_get_child (GTK_BIN (x))
 
 static void update_theme_scrollbar_width (void);
+static void update_theme_scrollbar_height (void);
 
 #define TB_INFO_KEY "xg_frame_tb_info"
 struct xg_frame_tb_info
@@ -163,7 +187,9 @@ xg_display_open (char *display_name, Display **dpy)
 {
   GdkDisplay *gdpy;
 
+  unrequest_sigio ();  // See comment in x_display_ok, xterm.c.
   gdpy = gdk_display_open (display_name);
+  request_sigio ();
   if (!gdpy_def && gdpy)
     {
       gdpy_def = gdpy;
@@ -217,57 +243,6 @@ xg_display_close (Display *dpy)
 /***********************************************************************
                       Utility functions
  ***********************************************************************/
-/* The next two variables and functions are taken from lwlib.  */
-static widget_value *widget_value_free_list;
-static int malloc_cpt;
-
-/* Allocate a widget_value structure, either by taking one from the
-   widget_value_free_list or by malloc:ing a new one.
-
-   Return a pointer to the allocated structure.  */
-
-widget_value *
-malloc_widget_value (void)
-{
-  widget_value *wv;
-  if (widget_value_free_list)
-    {
-      wv = widget_value_free_list;
-      widget_value_free_list = wv->free_list;
-      wv->free_list = 0;
-    }
-  else
-    {
-      wv = xmalloc (sizeof *wv);
-      malloc_cpt++;
-    }
-  memset (wv, 0, sizeof (widget_value));
-  return wv;
-}
-
-/* This is analogous to free.  It frees only what was allocated
-   by malloc_widget_value, and no substructures.  */
-
-void
-free_widget_value (widget_value *wv)
-{
-  if (wv->free_list)
-    emacs_abort ();
-
-  if (malloc_cpt > 25)
-    {
-      /* When the number of already allocated cells is too big,
-        We free it.  */
-      xfree (wv);
-      malloc_cpt--;
-    }
-  else
-    {
-      wv->free_list = widget_value_free_list;
-      widget_value_free_list = wv;
-    }
-}
-
 
 /* Create and return the cursor to be used for popup menus and
    scroll bars on display DPY.  */
@@ -553,16 +528,16 @@ get_utf8_string (const char *str)
              && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
         {
           memcpy (up, p, bytes_written);
-          sprintf (up + bytes_written, "\\%03o", p[bytes_written]);
-          up += bytes_written+4;
-          p += bytes_written+1;
+          up += bytes_written;
+          up += sprintf (up, "\\%03o", p[bytes_written]);
+          p += bytes_written + 1;
           g_error_free (err);
           err = NULL;
         }
 
       if (cp)
         {
-          strcat (utf8_str, cp);
+          strcpy (up, cp);
           g_free (cp);
         }
       if (err)
@@ -596,14 +571,17 @@ xg_check_special_colors (struct frame *f,
     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)];
+    char buf[sizeof "rgb://rrrr/gggg/bbbb"];
     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);
+    sprintf (buf, "rgb:%04x/%04x/%04x",
+             (int)(col.red * 65535),
+             (int)(col.green * 65535),
+             (int)(col.blue * 65535));
     success_p = (XParseColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f),
                              buf, color)
                 != 0);
@@ -641,7 +619,7 @@ hierarchy_ch_cb (GtkWidget *widget,
                  GtkWidget *previous_toplevel,
                  gpointer   user_data)
 {
-  struct frame *f = (struct frame *) user_data;
+  struct frame *f = user_data;
   struct x_output *x = f->output_data.x;
   GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl);
 
@@ -663,7 +641,7 @@ qttip_cb (GtkWidget  *widget,
           GtkTooltip *tooltip,
           gpointer    user_data)
 {
-  struct frame *f = (struct frame *) user_data;
+  struct frame *f = user_data;
   struct x_output *x = f->output_data.x;
   if (x->ttip_widget == NULL)
     {
@@ -864,35 +842,29 @@ xg_set_geometry (struct frame *f)
 /* 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
+void
 xg_clear_under_internal_border (struct frame *f)
 {
   if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
     {
       GtkWidget *wfixed = f->output_data.x->edit_widget;
+
       gtk_widget_queue_draw (wfixed);
       gdk_window_process_all_updates ();
-      x_clear_area (FRAME_X_DISPLAY (f),
-                    FRAME_X_WINDOW (f),
-                    0, 0,
-                    FRAME_PIXEL_WIDTH (f),
-                    FRAME_INTERNAL_BORDER_WIDTH (f), 0);
-      x_clear_area (FRAME_X_DISPLAY (f),
-                    FRAME_X_WINDOW (f),
-                    0, 0,
-                    FRAME_INTERNAL_BORDER_WIDTH (f),
-                    FRAME_PIXEL_HEIGHT (f), 0);
-      x_clear_area (FRAME_X_DISPLAY (f),
-                    FRAME_X_WINDOW (f),
-                    0, FRAME_PIXEL_HEIGHT (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
-                    FRAME_PIXEL_WIDTH (f),
-                    FRAME_INTERNAL_BORDER_WIDTH (f), 0);
-      x_clear_area (FRAME_X_DISPLAY (f),
-                    FRAME_X_WINDOW (f),
-                    FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
-                    0,
-                    FRAME_INTERNAL_BORDER_WIDTH (f),
-                    FRAME_PIXEL_HEIGHT (f), 0);
+
+      x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), 0, 0,
+                   FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f));
+
+      x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), 0, 0,
+                   FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
+
+      x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), 0,
+                   FRAME_PIXEL_HEIGHT (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
+                   FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f));
+
+      x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                   FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
+                   0, FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
     }
 }
 
@@ -905,33 +877,34 @@ xg_clear_under_internal_border (struct frame *f)
 void
 xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight)
 {
-  int rows, columns;
+  int width, height;
 
   if (pixelwidth == -1 && pixelheight == -1)
     {
       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;
+       gdk_window_get_geometry (gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
+                                0, 0, &pixelwidth, &pixelheight);
+      else
+       return;
     }
 
+  width = FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth);
+  height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight);
 
-  rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelheight);
-  columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
+  frame_size_history_add
+    (f, Qxg_frame_resized, width, height, Qnil);
 
-  if (columns != FRAME_COLS (f)
-      || rows != FRAME_LINES (f)
+  if (width != FRAME_TEXT_WIDTH (f)
+      || height != FRAME_TEXT_HEIGHT (f)
       || pixelwidth != FRAME_PIXEL_WIDTH (f)
       || pixelheight != FRAME_PIXEL_HEIGHT (f))
     {
-      FRAME_PIXEL_WIDTH (f) = pixelwidth;
-      FRAME_PIXEL_HEIGHT (f) = pixelheight;
-
       xg_clear_under_internal_border (f);
-      change_frame_size (f, rows, columns, 0, 1, 0);
+      change_frame_size (f, width, height, 0, 1, 0, 1);
       SET_FRAME_GARBAGED (f);
       cancel_mouse_face (f);
+
+      do_pending_window_change (0);
     }
 }
 
@@ -939,43 +912,75 @@ xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight)
    COLUMNS/ROWS is the size the edit area shall have after the resize.  */
 
 void
-xg_frame_set_char_size (struct frame *f, int cols, int rows)
+xg_frame_set_char_size (struct frame *f, int width, int height)
 {
-  int pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows)
-    + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
-  int pixelwidth;
+  int pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
+  int pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
+  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+  gint gwidth, gheight;
 
   if (FRAME_PIXEL_HEIGHT (f) == 0)
     return;
 
-  /* Take into account the size of the scroll bar.  Always use the
-     number of columns occupied by the scroll bar here otherwise we
-     might end up with a frame width that is not a multiple of the
-     frame's character width which is bad for vertically split
-     windows.  */
-  f->scroll_bar_actual_width
-    = FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f);
-
-  compute_fringe_widths (f, 0);
-
-  /* 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)
-    + FRAME_TOOLBAR_WIDTH (f);
-
+  gtk_window_get_size (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                      &gwidth, &gheight);
 
   /* Do this before resize, as we don't know yet if we will be resized.  */
   xg_clear_under_internal_border (f);
 
-  /* Must resize our top level widget.  Font size may have changed,
-     but not rows/cols.  */
-  gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                     pixelwidth, pixelheight);
-  x_wm_set_size_hint (f, 0, 0);
+  /* Resize the top level widget so rows and columns remain constant.
+
+     When the frame is fullheight and we only want to change the width
+     or it is fullwidth and we only want to change the height we should
+     be able to preserve the fullscreen property.  However, due to the
+     fact that we have to send a resize request anyway, the window
+     manager will abolish it.  At least the respective size should
+     remain unchanged but giving the frame back its normal size will
+     be broken ... */
+  if (EQ (fullscreen, Qfullwidth) && width == FRAME_TEXT_WIDTH (f))
+    {
+      frame_size_history_add
+       (f, Qxg_frame_set_char_size_1, width, height,
+        list2 (make_number (gheight),
+               make_number (pixelheight + FRAME_TOOLBAR_HEIGHT (f)
+                            + FRAME_MENUBAR_HEIGHT (f))));
+
+      gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                        gwidth,
+                        pixelheight + FRAME_TOOLBAR_HEIGHT (f)
+                        + FRAME_MENUBAR_HEIGHT (f));
+    }
+  else if (EQ (fullscreen, Qfullheight) && height == FRAME_TEXT_HEIGHT (f))
+    {
+      frame_size_history_add
+       (f, Qxg_frame_set_char_size_2, width, height,
+        list2 (make_number (gwidth),
+               make_number (pixelwidth + FRAME_TOOLBAR_WIDTH (f))));
+
+      gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                        pixelwidth + FRAME_TOOLBAR_WIDTH (f),
+                        gheight);
+    }
+
+  else
+    {
+      frame_size_history_add
+       (f, Qxg_frame_set_char_size_3, width, height,
+        list2 (make_number (pixelwidth + FRAME_TOOLBAR_WIDTH (f)),
+               make_number (pixelheight + FRAME_TOOLBAR_HEIGHT (f)
+                            + FRAME_MENUBAR_HEIGHT (f))));
+
+      gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                        pixelwidth + FRAME_TOOLBAR_WIDTH (f),
+                        pixelheight + FRAME_TOOLBAR_HEIGHT (f)
+                        + FRAME_MENUBAR_HEIGHT (f));
+      fullscreen = Qnil;
+    }
 
   SET_FRAME_GARBAGED (f);
   cancel_mouse_face (f);
 
+  x_wm_set_size_hint (f, 0, 0);
   /* We can not call change_frame_size for a mapped frame,
      we can not set pixel width/height either.  The window manager may
      override our resize request, XMonad does this all the time.
@@ -989,18 +994,23 @@ xg_frame_set_char_size (struct frame *f, int cols, int rows)
       (void)gtk_events_pending ();
       gdk_flush ();
       x_wait_for_event (f, ConfigureNotify);
+
+      if (!NILP (fullscreen))
+       /* Try to restore fullscreen state.  */
+       {
+         store_frame_param (f, Qfullscreen, fullscreen);
+         x_set_fullscreen (f, fullscreen, fullscreen);
+       }
     }
   else
-    {
-      change_frame_size (f, rows, cols, 0, 1, 0);
-      FRAME_PIXEL_WIDTH (f) = pixelwidth;
-      FRAME_PIXEL_HEIGHT (f) = pixelheight;
-     }
+    adjust_frame_size (f, width, height, 5, 0, Qxg_frame_set_char_size);
+
 }
 
 /* Handle height/width changes (i.e. add/remove/move menu/toolbar).
    The policy is to keep the number of editable lines.  */
 
+#if 0
 static void
 xg_height_or_width_changed (struct frame *f)
 {
@@ -1010,6 +1020,7 @@ xg_height_or_width_changed (struct frame *f)
   f->output_data.x->hint_flags = 0;
   x_wm_set_size_hint (f, 0, 0);
 }
+#endif
 
 /* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
    Must be done like this, because GtkWidget:s can have "hidden"
@@ -1042,7 +1053,7 @@ xg_win_to_widget (Display *dpy, Window wdesc)
 /* Set the background of widget W to PIXEL.  */
 
 static void
-xg_set_widget_bg (struct frame *f, GtkWidget *w, long unsigned int pixel)
+xg_set_widget_bg (struct frame *f, GtkWidget *w, unsigned long pixel)
 {
 #ifdef HAVE_GTK3
   GdkRGBA bg;
@@ -1073,7 +1084,7 @@ style_changed_cb (GObject *go,
                   gpointer user_data)
 {
   struct input_event event;
-  GdkDisplay *gdpy = (GdkDisplay *) user_data;
+  GdkDisplay *gdpy = user_data;
   const char *display_name = gdk_display_get_name (gdpy);
   Display *dpy = GDK_DISPLAY_XDISPLAY (gdpy);
 
@@ -1085,6 +1096,7 @@ style_changed_cb (GObject *go,
   kbd_buffer_store_event (&event);
 
   update_theme_scrollbar_width ();
+  update_theme_scrollbar_height ();
 
   /* If scroll bar width changed, we need set the new size on all frames
      on this display.  */
@@ -1099,7 +1111,8 @@ style_changed_cb (GObject *go,
               && FRAME_X_DISPLAY (f) == dpy)
             {
               x_set_scroll_bar_default_width (f);
-              xg_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (f));
+              x_set_scroll_bar_default_height (f);
+              xg_frame_set_char_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f));
             }
         }
     }
@@ -1115,7 +1128,7 @@ delete_cb (GtkWidget *widget,
 #ifdef HAVE_GTK3
   /* The event doesn't arrive in the normal event loop.  Send event
      here.  */
-  struct frame *f = (struct frame *) user_data;
+  struct frame *f = user_data;
   struct input_event ie;
 
   EVENT_INIT (ie);
@@ -1341,6 +1354,7 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
   int base_width, base_height;
   int min_rows = 0, min_cols = 0;
   int win_gravity = f->win_gravity;
+  Lisp_Object fs_state, frame;
 
   /* Don't set size hints during initialization; that apparently leads
      to a race condition.  See the thread at
@@ -1348,6 +1362,16 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
   if (NILP (Vafter_init_time) || !FRAME_GTK_OUTER_WIDGET (f))
     return;
 
+  XSETFRAME (frame, f);
+  fs_state = Fframe_parameter (frame, Qfullscreen);
+  if (EQ (fs_state, Qmaximized) || EQ (fs_state, Qfullboth))
+    {
+      /* Don't set hints when maximized or fullscreen.  Apparently KWin and
+         Gtk3 don't get along and the frame shrinks (!).
+      */
+      return;
+    }
+
   if (flags)
     {
       memset (&size_hints, 0, sizeof (size_hints));
@@ -1361,8 +1385,8 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
   hint_flags = f->output_data.x->hint_flags;
 
   hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
-  size_hints.width_inc = FRAME_COLUMN_WIDTH (f);
-  size_hints.height_inc = FRAME_LINE_HEIGHT (f);
+  size_hints.width_inc = frame_resize_pixelwise ? 1 : FRAME_COLUMN_WIDTH (f);
+  size_hints.height_inc = frame_resize_pixelwise ? 1 : FRAME_LINE_HEIGHT (f);
 
   hint_flags |= GDK_HINT_BASE_SIZE;
   /* Use one row/col here so base_height/width does not become zero.
@@ -1371,14 +1395,13 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
   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;
-  size_hints.min_width  = base_width + min_cols * size_hints.width_inc;
-  size_hints.min_height = base_height + min_rows * size_hints.height_inc;
+  size_hints.min_width  = base_width + min_cols * FRAME_COLUMN_WIDTH (f);
+  size_hints.min_height = base_height + min_rows * FRAME_LINE_HEIGHT (f);
 
   /* These currently have a one to one mapping with the X values, but I
      don't think we should rely on that.  */
@@ -1432,7 +1455,7 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
    BG is the pixel value to change to.  */
 
 void
-xg_set_background_color (struct frame *f, long unsigned int bg)
+xg_set_background_color (struct frame *f, unsigned long bg)
 {
   if (FRAME_GTK_WIDGET (f))
     {
@@ -1532,7 +1555,6 @@ create_dialog (widget_value *wv,
   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;
   GtkWidget *whbox_down;
 
@@ -1540,6 +1562,17 @@ create_dialog (widget_value *wv,
      instead.  This looks better.  */
   bool make_two_rows = total_buttons > 4;
 
+#if GTK_CHECK_VERSION (3, 12, 0)
+  GtkBuilder *gbld = gtk_builder_new ();
+  GObject *go = gtk_buildable_get_internal_child (GTK_BUILDABLE (wd),
+                                                  gbld,
+                                                  "action_area");
+  GtkBox *cur_box = GTK_BOX (go);
+  g_object_unref (G_OBJECT (gbld));
+#else
+  GtkBox *cur_box = GTK_BOX (gtk_dialog_get_action_area (wd));
+#endif
+
   if (right_buttons == 0) right_buttons = total_buttons/2;
   left_buttons = total_buttons - right_buttons;
 
@@ -1585,8 +1618,12 @@ create_dialog (widget_value *wv,
           w = gtk_label_new (utf8_label);
           gtk_box_pack_start (wvbox, gtk_label_new (""), FALSE, FALSE, 0);
           gtk_box_pack_start (wvbox, w, TRUE, TRUE, 0);
+#if GTK_CHECK_VERSION (3, 14, 0)
+          gtk_widget_set_halign (w, GTK_ALIGN_START);
+          gtk_widget_set_valign (w, GTK_ALIGN_CENTER);
+#else
           gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5);
-
+#endif
           /* Try to make dialog look better.  Must realize first so
              the widget can calculate the size it needs.  */
           gtk_widget_realize (w);
@@ -1594,6 +1631,7 @@ create_dialog (widget_value *wv,
           gtk_box_set_spacing (wvbox, req.height);
          if (item->value && strlen (item->value) > 0)
             button_spacing = 2*req.width/strlen (item->value);
+          if (button_spacing < 10) button_spacing = 10;
         }
       else
         {
@@ -1610,11 +1648,6 @@ create_dialog (widget_value *wv,
             {
               if (make_two_rows)
                 cur_box = GTK_BOX (whbox_down);
-              else
-                gtk_box_pack_start (cur_box,
-                                    gtk_label_new (""),
-                                    TRUE, TRUE,
-                                    button_spacing);
             }
         }
 
@@ -1642,7 +1675,7 @@ xg_dialog_response_cb (GtkDialog *w,
                       gint response,
                       gpointer user_data)
 {
-  struct xg_dialog_data *dd = (struct xg_dialog_data *)user_data;
+  struct xg_dialog_data *dd = user_data;
   dd->response = response;
   g_main_loop_quit (dd->loop);
 }
@@ -1671,16 +1704,16 @@ pop_down_dialog (void *arg)
 static gboolean
 xg_maybe_add_timer (gpointer data)
 {
-  struct xg_dialog_data *dd = (struct xg_dialog_data *) data;
-  EMACS_TIME next_time = timer_check ();
+  struct xg_dialog_data *dd = data;
+  struct timespec next_time = timer_check ();
 
   dd->timerid = 0;
 
-  if (EMACS_TIME_VALID_P (next_time))
+  if (timespec_valid_p (next_time))
     {
-      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;
+      time_t s = next_time.tv_sec;
+      int per_ms = TIMESPEC_RESOLUTION / 1000;
+      int ms = (next_time.tv_nsec + per_ms - 1) / per_ms;
       if (s <= ((guint) -1 - ms) / 1000)
        dd->timerid = g_timeout_add (s * 1000 + ms, xg_maybe_add_timer, dd);
     }
@@ -1831,9 +1864,9 @@ xg_get_file_with_chooser (struct frame *f,
     action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
 
   filewin = gtk_file_chooser_dialog_new (prompt, gwin, action,
-                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                         XG_TEXT_CANCEL, GTK_RESPONSE_CANCEL,
                                          (mustmatch_p || only_dir_p ?
-                                          GTK_STOCK_OPEN : GTK_STOCK_OK),
+                                          XG_TEXT_OPEN : XG_TEXT_OK),
                                          GTK_RESPONSE_OK,
                                          NULL);
   gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
@@ -1856,12 +1889,12 @@ xg_get_file_with_chooser (struct frame *f,
 
   if (x_gtk_file_dialog_help_text)
     {
-      msgbuf[0] = '\0';
+      char *z = msgbuf;
       /* 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 (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 "
+        z = stpcpy (z, "\nType C-l to display a file name text entry box.\n");
+      strcpy (z, "\nIf you don't like this file selector, use the "
               "corresponding\nkey binding or customize "
               "use-file-dialog to turn it off.");
 
@@ -1921,7 +1954,7 @@ static char *
 xg_get_file_name_from_selector (GtkWidget *w)
 {
   GtkFileSelection *filesel = GTK_FILE_SELECTION (w);
-  return xstrdup ((char*) gtk_file_selection_get_filename (filesel));
+  return xstrdup (gtk_file_selection_get_filename (filesel));
 }
 
 /* Create a file selection dialog.
@@ -2039,7 +2072,6 @@ xg_get_file_name (struct frame *f,
 
 
 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
@@ -2090,32 +2122,20 @@ xg_get_font (struct frame *f, const char *default_name)
 
       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);
 
-         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);
+         font = CALLN (Ffont_spec,
+                       QCname, build_string (name),
+                       QCsize, make_float (pango_units_to_double (size)),
+                       QCweight, XG_WEIGHT_TO_SYMBOL (weight),
+                       QCslant, XG_STYLE_TO_SYMBOL (style),
+                       QCtype, Qxft);
 
          pango_font_description_free (desc);
-         xfree (x_last_font_name);
-         x_last_font_name = xstrdup (name);
+         dupstring (&x_last_font_name, name);
        }
 
 #else /* Use old font selector, which just returns the font name.  */
@@ -2277,7 +2297,7 @@ menuitem_destroy_callback (GtkWidget *w, gpointer client_data)
 {
   if (client_data)
     {
-      xg_menu_item_cb_data *data = (xg_menu_item_cb_data*) client_data;
+      xg_menu_item_cb_data *data = client_data;
       xg_list_remove (&xg_menu_item_cb_list, &data->ptrs);
       xfree (data);
     }
@@ -2301,8 +2321,7 @@ menuitem_highlight_callback (GtkWidget *w,
 
   ev.crossing = *event;
   subwidget = gtk_get_event_widget (&ev);
-  data = (xg_menu_item_cb_data *) g_object_get_data (G_OBJECT (subwidget),
-                                                     XG_ITEM_DATA);
+  data = g_object_get_data (G_OBJECT (subwidget), XG_ITEM_DATA);
   if (data)
     {
       if (! NILP (data->help) && data->cl_data->highlight_cb)
@@ -2323,7 +2342,7 @@ menuitem_highlight_callback (GtkWidget *w,
 static void
 menu_destroy_callback (GtkWidget *w, gpointer client_data)
 {
-  unref_cl_data ((xg_menu_cb_data*) client_data);
+  unref_cl_data (client_data);
 }
 
 /* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
@@ -2343,9 +2362,15 @@ make_widget_for_menu_item (const char *utf8_label, const char *utf8_key)
   wlbl = gtk_label_new (utf8_label);
   wkey = gtk_label_new (utf8_key);
 
+#if GTK_CHECK_VERSION (3, 14, 0)
+  gtk_widget_set_halign (wlbl, GTK_ALIGN_START);
+  gtk_widget_set_valign (wlbl, GTK_ALIGN_CENTER);
+  gtk_widget_set_halign (wkey, GTK_ALIGN_START);
+  gtk_widget_set_valign (wkey, GTK_ALIGN_CENTER);
+#else
   gtk_misc_set_alignment (GTK_MISC (wlbl), 0.0, 0.5);
   gtk_misc_set_alignment (GTK_MISC (wkey), 0.0, 0.5);
-
+#endif
   gtk_box_pack_start (GTK_BOX (wbox), wlbl, TRUE, TRUE, 0);
   gtk_box_pack_start (GTK_BOX (wbox), wkey, FALSE, FALSE, 0);
 
@@ -2416,54 +2441,6 @@ make_menu_item (const char *utf8_label,
   return w;
 }
 
-#ifdef HAVE_GTK_TEAROFF_MENU_ITEM_NEW
-
-static int xg_detached_menus;
-
-/* Return true if there are detached menus.  */
-
-bool
-xg_have_tear_offs (void)
-{
-  return xg_detached_menus > 0;
-}
-
-/* Callback invoked when a detached menu window is removed.  Here we
-   decrease the xg_detached_menus count.
-   WIDGET is the top level window that is removed (the parent of the menu).
-   CLIENT_DATA is not used.  */
-
-static void
-tearoff_remove (GtkWidget *widget, gpointer client_data)
-{
-  if (xg_detached_menus > 0) --xg_detached_menus;
-}
-
-/* Callback invoked when a menu is detached.  It increases the
-   xg_detached_menus count.
-   WIDGET is the GtkTearoffMenuItem.
-   CLIENT_DATA is not used.  */
-
-static void
-tearoff_activate (GtkWidget *widget, gpointer client_data)
-{
-  GtkWidget *menu = gtk_widget_get_parent (widget);
-  if (gtk_menu_get_tearoff_state (GTK_MENU (menu)))
-    {
-      ++xg_detached_menus;
-      g_signal_connect (G_OBJECT (gtk_widget_get_toplevel (widget)),
-                        "destroy",
-                        G_CALLBACK (tearoff_remove), 0);
-    }
-}
-#else /* ! HAVE_GTK_TEAROFF_MENU_ITEM_NEW */
-bool
-xg_have_tear_offs (void)
-{
-  return false;
-}
-#endif /* ! HAVE_GTK_TEAROFF_MENU_ITEM_NEW */
-
 /* Create a menu item widget, and connect the callbacks.
    ITEM describes the menu item.
    F is the frame the created menu belongs to.
@@ -2534,8 +2511,6 @@ xg_create_one_menuitem (widget_value *item,
    HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
    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 or
-   the Gtk+ version used does not have tearoffs.
    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)
@@ -2557,7 +2532,6 @@ create_menus (widget_value *data,
               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)
@@ -2608,17 +2582,6 @@ create_menus (widget_value *data,
                           "selection-done", deactivate_cb, 0);
     }
 
-#ifdef HAVE_GTK_TEAROFF_MENU_ITEM_NEW
-  if (! menu_bar_p && add_tearoff_p)
-    {
-      GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
-      gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), tearoff);
-
-      g_signal_connect (G_OBJECT (tearoff), "activate",
-                        G_CALLBACK (tearoff_activate), 0);
-    }
-#endif
-
   for (item = data; item; item = item->next)
     {
       GtkWidget *w;
@@ -2632,7 +2595,6 @@ create_menus (widget_value *data,
           group = NULL;
           utf8_label = get_utf8_string (item->name);
 
-          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) g_free (utf8_label);
@@ -2663,7 +2625,6 @@ create_menus (widget_value *data,
                                                  highlight_cb,
                                                  0,
                                                  0,
-                                                 add_tearoff_p,
                                                  0,
                                                  cl_data,
                                                  0);
@@ -2721,7 +2682,6 @@ xg_create_widget (const char *type, const char *name, struct frame *f,
                         highlight_cb,
                         pop_up_p,
                         menu_bar_p,
-                        menu_bar_p,
                         0,
                         0,
                         name);
@@ -2732,7 +2692,7 @@ xg_create_widget (const char *type, const char *name, struct frame *f,
         {
           /* Must realize so the GdkWindow inside the widget is created.  */
           gtk_widget_realize (w);
-          xg_set_cursor (w, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
+          xg_set_cursor (w, FRAME_DISPLAY_INFO (f)->xg_cursor);
         }
     }
   else
@@ -2831,7 +2791,7 @@ xg_update_menubar (GtkWidget *menubar,
     {
       /* Item(s) added.  Add all new items in one call.  */
       create_menus (val, f, select_cb, deactivate_cb, highlight_cb,
-                    0, 1, 0, menubar, cl_data, 0);
+                    0, 1, menubar, cl_data, 0);
 
       /* All updated.  */
       val = 0;
@@ -2904,18 +2864,16 @@ xg_update_menubar (GtkWidget *menubar,
               is up to date when leaving the minibuffer.  */
           GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
           char *utf8_label = get_utf8_string (val->name);
-          GtkWidget *submenu = gtk_menu_item_get_submenu (witem);
 
+          /* GTK menu items don't notice when their labels have been
+             changed from underneath them, so we have to explicitly
+             use g_object_notify to tell listeners (e.g., a GMenuModel
+             bridge that might be loaded) that the item's label has
+             changed.  */
           gtk_label_set_text (wlabel, utf8_label);
-
-#ifdef HAVE_GTK_TEAROFF_MENU_ITEM_NEW
-          /* If this item has a submenu that has been detached, change
-             the title in the WM decorations also.  */
-          if (submenu && gtk_menu_get_tearoff_state (GTK_MENU (submenu)))
-            /* Set the title of the detached window.  */
-            gtk_menu_set_title (GTK_MENU (submenu), utf8_label);
+#if GTK_CHECK_VERSION (2, 16, 0)
+          g_object_notify (G_OBJECT (witem), "label");
 #endif
-
           if (utf8_label) g_free (utf8_label);
           iter = g_list_next (iter);
           val = val->next;
@@ -2942,7 +2900,8 @@ xg_update_menubar (GtkWidget *menubar,
           GtkWidget *submenu = create_menus (NULL, f,
                                              select_cb, deactivate_cb,
                                              highlight_cb,
-                                             0, 0, 0, 0, cl_data, 0);
+                                             0, 0, 0, cl_data, 0);
+
           gtk_widget_set_name (w, MENU_ITEM_NAME);
           gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
           gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
@@ -3002,6 +2961,7 @@ xg_update_menu_item (widget_value *val,
   const char *old_label = 0;
   const char *old_key = 0;
   xg_menu_item_cb_data *cb_data;
+  bool label_changed = false;
 
   wchild = XG_BIN_CHILD (w);
   utf8_label = get_utf8_string (val->name);
@@ -3046,15 +3006,20 @@ xg_update_menu_item (widget_value *val,
         }
     }
 
-
   if (wkey) old_key = gtk_label_get_label (wkey);
   if (wlbl) old_label = gtk_label_get_label (wlbl);
 
   if (wkey && utf8_key && (! old_key || strcmp (utf8_key, old_key) != 0))
-    gtk_label_set_text (wkey, utf8_key);
+    {
+      label_changed = true;
+      gtk_label_set_text (wkey, utf8_key);
+    }
 
   if (! old_label || strcmp (utf8_label, old_label) != 0)
-    gtk_label_set_text (wlbl, utf8_label);
+    {
+      label_changed = true;
+      gtk_label_set_text (wlbl, utf8_label);
+    }
 
   if (utf8_key) g_free (utf8_key);
   if (utf8_label) g_free (utf8_label);
@@ -3064,8 +3029,7 @@ xg_update_menu_item (widget_value *val,
   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),
-                                                       XG_ITEM_DATA);
+  cb_data = g_object_get_data (G_OBJECT (w), XG_ITEM_DATA);
   if (cb_data)
     {
       cb_data->call_data = val->call_data;
@@ -3087,6 +3051,11 @@ xg_update_menu_item (widget_value *val,
           cb_data->select_id = 0;
         }
     }
+
+#if GTK_CHECK_VERSION (2, 16, 0)
+  if (label_changed) /* See comment in xg_update_menubar.  */
+    g_object_notify (G_OBJECT (w), "label");
+#endif
 }
 
 /* Update the toggle menu item W so it corresponds to VAL.  */
@@ -3130,7 +3099,6 @@ xg_update_submenu (GtkWidget *submenu,
   GList *list = 0;
   GList *iter;
   widget_value *cur;
-  bool has_tearoff_p = 0;
   GList *first_radio = 0;
 
   if (submenu)
@@ -3142,17 +3110,6 @@ xg_update_submenu (GtkWidget *submenu,
   {
     GtkWidget *w = GTK_WIDGET (iter->data);
 
-#ifdef HAVE_GTK_TEAROFF_MENU_ITEM_NEW
-  /* Skip tearoff items, they have no counterpart in val.  */
-    if (GTK_IS_TEAROFF_MENU_ITEM (w))
-      {
-        has_tearoff_p = 1;
-        iter = g_list_next (iter);
-        if (iter) w = GTK_WIDGET (iter->data);
-        else break;
-      }
-#endif
-
     /* Remember first radio button in a group.  If we get a mismatch in
        a radio group we must rebuild the whole group so that the connections
        in GTK becomes correct.  */
@@ -3240,7 +3197,6 @@ xg_update_submenu (GtkWidget *submenu,
                              highlight_cb,
                              0,
                              0,
-                             ! has_tearoff_p,
                              submenu,
                              cl_data,
                              0);
@@ -3271,8 +3227,7 @@ xg_modify_menubar_widgets (GtkWidget *menubar, struct frame *f,
 
   if (! list) return;
 
-  cl_data = (xg_menu_cb_data*) g_object_get_data (G_OBJECT (menubar),
-                                                  XG_FRAME_DATA);
+  cl_data = g_object_get_data (G_OBJECT (menubar), XG_FRAME_DATA);
 
   xg_update_menubar (menubar, f, &list, list, 0, val->contents,
                      select_cb, deactivate_cb, highlight_cb, cl_data);
@@ -3336,12 +3291,12 @@ static void
 menubar_map_cb (GtkWidget *w, gpointer user_data)
 {
   GtkRequisition req;
-  struct frame *f = (struct frame *) user_data;
+  struct frame *f = user_data;
   gtk_widget_get_preferred_size (w, NULL, &req);
   if (FRAME_MENUBAR_HEIGHT (f) != req.height)
     {
       FRAME_MENUBAR_HEIGHT (f) = req.height;
-      xg_height_or_width_changed (f);
+      adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
     }
 }
 
@@ -3370,15 +3325,10 @@ xg_update_frame_menubar (struct frame *f)
   gtk_widget_show_all (x->menubar_widget);
   gtk_widget_get_preferred_size (x->menubar_widget, NULL, &req);
 
-  /* If menu bar doesn't know its height yet, cheat a little so the frame
-     doesn't jump so much when resized later in menubar_map_cb.  */
-  if (req.height == 0)
-    req.height = 23;
-
   if (FRAME_MENUBAR_HEIGHT (f) != req.height)
     {
       FRAME_MENUBAR_HEIGHT (f) = req.height;
-      xg_height_or_width_changed (f);
+      adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
     }
   unblock_input ();
 }
@@ -3400,13 +3350,13 @@ free_frame_menubar (struct frame *f)
           the container.  */
       x->menubar_widget = 0;
       FRAME_MENUBAR_HEIGHT (f) = 0;
-      xg_height_or_width_changed (f);
+      adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
       unblock_input ();
     }
 }
 
 bool
-xg_event_is_for_menubar (struct frame *f, XEvent *event)
+xg_event_is_for_menubar (struct frame *f, const XEvent *event)
 {
   struct x_output *x = f->output_data.x;
   GList *iter;
@@ -3422,7 +3372,7 @@ xg_event_is_for_menubar (struct frame *f, XEvent *event)
   if (! (event->xbutton.x >= 0
          && event->xbutton.x < FRAME_PIXEL_WIDTH (f)
          && event->xbutton.y >= 0
-         && event->xbutton.y < f->output_data.x->menubar_height
+         && event->xbutton.y < FRAME_MENUBAR_HEIGHT (f)
          && event->xbutton.same_screen))
     return 0;
 
@@ -3467,9 +3417,9 @@ xg_event_is_for_menubar (struct frame *f, XEvent *event)
 
 bool xg_ignore_gtk_scrollbar;
 
-/* The width of the scroll bar for the current theme.  */
-
+/* Width and height of scroll bars for the current theme.  */
 static int scroll_bar_width_for_theme;
+static int scroll_bar_height_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
@@ -3568,16 +3518,47 @@ update_theme_scrollbar_width (void)
   gtk_widget_destroy (wscroll);
   g_object_unref (G_OBJECT (wscroll));
   w += 2*b;
+#ifndef HAVE_GTK3
   if (w < 16) w = 16;
+#endif
   scroll_bar_width_for_theme = w;
 }
 
+static void
+update_theme_scrollbar_height (void)
+{
+#ifdef HAVE_GTK3
+  GtkAdjustment *hadj;
+#else
+  GtkObject *hadj;
+#endif
+  GtkWidget *wscroll;
+  int w = 0, b = 0;
+
+  hadj = gtk_adjustment_new (YG_SB_MIN, YG_SB_MIN, YG_SB_MAX, 0.1, 0.1, 0.1);
+  wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (hadj));
+  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 < 12) w = 12;
+  scroll_bar_height_for_theme = w;
+}
+
 int
 xg_get_default_scrollbar_width (void)
 {
   return scroll_bar_width_for_theme;
 }
 
+int
+xg_get_default_scrollbar_height (void)
+{
+  /* Apparently there's no default height for themes.  */
+  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.  */
 
@@ -3673,9 +3654,77 @@ xg_create_scroll_bar (struct frame *f,
 
 
   /* Set the cursor to an arrow.  */
-  xg_set_cursor (webox, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
+  xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
 
   bar->x_window = scroll_id;
+  bar->horizontal = 0;
+}
+
+/* Create a horizontal scroll bar widget for frame F.  Store the scroll
+   bar in BAR.  SCROLL_CALLBACK is the callback to invoke when the value
+   of the bar changes.  END_CALLBACK is the callback to invoke when
+   scrolling ends.  SCROLL_BAR_NAME is the name we use for the scroll
+   bar.  Can be used to set resources for the widget.  */
+
+void
+xg_create_horizontal_scroll_bar (struct frame *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 *hadj;
+#else
+  GtkObject *hadj;
+#endif
+
+  /* Page, step increment values are not so important here, they
+     will be corrected in x_set_toolkit_scroll_bar_thumb. */
+  hadj = gtk_adjustment_new (YG_SB_MIN, YG_SB_MIN, YG_SB_MAX,
+                             0.1, 0.1, 0.1);
+
+  wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (hadj));
+  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);
+
+  g_signal_connect (G_OBJECT (wscroll),
+                    "destroy",
+                    G_CALLBACK (xg_gtk_scroll_destroy),
+                    (gpointer) scroll_id);
+  g_signal_connect (G_OBJECT (wscroll),
+                    "change-value",
+                    scroll_callback,
+                    (gpointer) bar);
+  g_signal_connect (G_OBJECT (wscroll),
+                    "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
+     also, which causes flicker.  Put an event box between the edit widget
+     and the scroll bar, so the scroll bar instead draws itself on the
+     event box window.  */
+  gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
+  gtk_container_add (GTK_CONTAINER (webox), wscroll);
+
+
+  /* Set the cursor to an arrow.  */
+  xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
+
+  bar->x_window = scroll_id;
+  bar->horizontal = 1;
 }
 
 /* Remove the scroll bar represented by SCROLLBAR_ID from the frame F.  */
@@ -3742,14 +3791,77 @@ xg_update_scrollbar_pos (struct frame *f,
       gtk_widget_queue_draw (wfixed);
       gdk_window_process_all_updates ();
       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
+          above.  */
+       x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                     oldx, oldy, oldw, oldh);
+
+      /* 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);
+    }
+}
+
+
+/* Update the position of the horizontal scroll bar represented by SCROLLBAR_ID
+   in frame F.
+   TOP/LEFT are the new pixel positions where the bar shall appear.
+   WIDTH, HEIGHT is the size in pixels the bar shall have.  */
+
+void
+xg_update_horizontal_scrollbar_pos (struct frame *f,
+                                   ptrdiff_t scrollbar_id,
+                                   int top,
+                                   int left,
+                                   int width,
+                                   int height)
+{
+
+  GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
+
+  if (wscroll)
+    {
+      GtkWidget *wfixed = f->output_data.x->edit_widget;
+      GtkWidget *wparent = gtk_widget_get_parent (wscroll);
+      gint msl;
+
+      /* Clear out old position.  */
+      int oldx = -1, oldy = -1, oldw, oldh;
+      if (gtk_widget_get_parent (wparent) == wfixed)
         {
-          /* Clear under old scroll bar position.  This must be done after
-             the gtk_widget_queue_draw and gdk_window_process_all_updates
-             above.  */
-          x_clear_area (FRAME_X_DISPLAY (f),
-                        FRAME_X_WINDOW (f),
-                        oldx, oldy, oldw, oldh, 0);
+          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_style_get (wscroll, "min-slider-length", &msl, NULL);
+      if (msl > width)
+        {
+          /* No room.  Hide scroll bar as some themes output a warning if
+             the width 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 && 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
+          above.  */
+       x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                     oldx, oldy, oldw, oldh);
 
       /* 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
@@ -3761,6 +3873,7 @@ xg_update_scrollbar_pos (struct frame *f,
     }
 }
 
+
 /* Get the current value of the range, truncated to an integer.  */
 
 static int
@@ -3783,7 +3896,7 @@ xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
 
   struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
 
-  if (wscroll && NILP (bar->dragging))
+  if (wscroll && bar->dragging == -1)
     {
       GtkAdjustment *adj;
       gdouble shown;
@@ -3855,13 +3968,46 @@ xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
     }
 }
 
+/* Set the thumb size and position of horizontal scroll bar BAR.  We are
+   currently displaying PORTION out of a whole WHOLE, and our position
+   POSITION.  */
+void
+xg_set_toolkit_horizontal_scroll_bar_thumb (struct scroll_bar *bar,
+                                           int portion,
+                                           int position,
+                                           int whole)
+{
+  GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
+
+  if (wscroll && bar->dragging == -1)
+    {
+      GtkAdjustment *adj;
+      int lower = 0;
+      int upper = max (whole - 1, 0);
+      int pagesize = min (upper, max (portion, 0));
+      int value = max (0, min (position, upper - pagesize));
+      /* These should be set to something more <portion, whole>
+        related.  */
+      int page_increment = 4;
+      int step_increment = 1;
+
+      block_input ();
+      adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
+      gtk_adjustment_configure (adj, (gdouble) value, (gdouble) lower,
+                               (gdouble) upper, (gdouble) step_increment,
+                               (gdouble) page_increment, (gdouble) pagesize);
+      gtk_adjustment_changed (adj);
+      unblock_input ();
+    }
+}
+
 /* 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.  */
 
 bool
-xg_event_is_for_scrollbar (struct frame *f, XEvent *event)
+xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
 {
   bool retval = 0;
 
@@ -3905,9 +4051,6 @@ xg_event_is_for_scrollbar (struct frame *f, XEvent *event)
    get them.  */
 #define XG_TOOL_BAR_LAST_MODIFIER "emacs-tool-bar-modifier"
 
-/* The key for storing the button widget in its proxy menu item. */
-#define XG_TOOL_BAR_PROXY_BUTTON "emacs-tool-bar-proxy-button"
-
 /* The key for the data we put in the GtkImage widgets.  The data is
    the stock name used by Emacs.  We use this to see if we need to update
    the GtkImage with a new image.  */
@@ -3946,8 +4089,7 @@ xg_tool_bar_callback (GtkWidget *w, gpointer client_data)
   gpointer gmod = g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_LAST_MODIFIER);
   intptr_t mod = (intptr_t) gmod;
 
-  struct frame *f = (struct frame *) g_object_get_data (G_OBJECT (w),
-                                                       XG_FRAME_DATA);
+  struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
   Lisp_Object key, frame;
   struct input_event event;
   EVENT_INIT (event);
@@ -3973,55 +4115,20 @@ xg_tool_bar_callback (GtkWidget *w, gpointer client_data)
   /* Convert between the modifier bits GDK uses and the modifier bits
      Emacs uses.  This assumes GDK and X masks are the same, which they are when
      this is written.  */
-  event.modifiers = x_x_to_emacs_modifiers (FRAME_X_DISPLAY_INFO (f), mod);
+  event.modifiers = x_x_to_emacs_modifiers (FRAME_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);
-}
-
-/* Callback function invoked when a tool bar item is pressed in a detached
-   tool bar or the overflow drop down menu.
-   We just call xg_tool_bar_callback.
-   W is the menu item widget that got pressed,
-   CLIENT_DATA is an integer that is the index of the button in the
-   tool bar.  0 is the first button.  */
-
-static void
-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));
-  xg_tool_bar_callback (wbutton, client_data);
-}
-
-
-static gboolean
-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 (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);
+  /* Return focus to the frame after we have clicked on a detached
+     tool bar button. */
+  x_focus_frame (f);
 }
 
 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;
+  GtkWidget *c1 = clist->data;
+  GtkWidget *c2 = clist->next ? clist->next->data : NULL;
 
   *wimage = GTK_IS_IMAGE (c1) ? c1 : c2;
   g_list_free (clist);
@@ -4029,187 +4136,6 @@ xg_get_tool_bar_widgets (GtkWidget *vb, GtkWidget **wimage)
 }
 
 
-/* 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.
-   If we don't create a proxy menu item, the detached tool bar will be
-   blank.  */
-
-static gboolean
-xg_tool_bar_menu_proxy (GtkToolItem *toolitem, gpointer user_data)
-{
-  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
-    {
-      GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (wbutton));
-      GtkImageType store_type = gtk_image_get_storage_type (wimage);
-
-      g_object_set (G_OBJECT (settings), "gtk-menu-images", TRUE, NULL);
-
-      if (store_type == GTK_IMAGE_STOCK)
-        {
-          gchar *stock_id;
-          gtk_image_get_stock (wimage, &stock_id, NULL);
-          wmenuimage = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
-        }
-      else if (store_type == GTK_IMAGE_ICON_SET)
-        {
-          GtkIconSet *icon_set;
-          gtk_image_get_icon_set (wimage, &icon_set, NULL);
-          wmenuimage = gtk_image_new_from_icon_set (icon_set,
-                                                    GTK_ICON_SIZE_MENU);
-        }
-      else if (store_type == GTK_IMAGE_PIXBUF)
-        {
-          gint width, height;
-
-          if (settings &&
-              gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
-                                                 &width, &height))
-            {
-              GdkPixbuf *src_pixbuf, *dest_pixbuf;
-
-              src_pixbuf = gtk_image_get_pixbuf (wimage);
-              dest_pixbuf = gdk_pixbuf_scale_simple (src_pixbuf, width, height,
-                                                     GDK_INTERP_BILINEAR);
-
-              wmenuimage = gtk_image_new_from_pixbuf (dest_pixbuf);
-            }
-          else
-            {
-              fprintf (stderr, "internal error: GTK_IMAGE_PIXBUF failed\n");
-              emacs_abort ();
-            }
-        }
-      else if (store_type == GTK_IMAGE_ICON_NAME)
-        {
-          const gchar *icon_name;
-          GtkIconSize icon_size;
-
-          gtk_image_get_icon_name (wimage, &icon_name, &icon_size);
-          wmenuimage = gtk_image_new_from_icon_name (icon_name,
-                                                     GTK_ICON_SIZE_MENU);
-        }
-      else
-        {
-          fprintf (stderr, "internal error: store_type is %d\n", store_type);
-          emacs_abort ();
-        }
-    }
-  if (wmenuimage)
-    gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (wmenuitem), wmenuimage);
-
-  g_signal_connect (G_OBJECT (wmenuitem),
-                    "activate",
-                    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_get_sensitive (GTK_WIDGET (wbutton)));
-
-  /* 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 (wmenuitem),
-                    "enter-notify-event",
-                    G_CALLBACK (xg_tool_bar_proxy_help_callback),
-                    user_data);
-  g_signal_connect (G_OBJECT (wmenuitem),
-                    "leave-notify-event",
-                    G_CALLBACK (xg_tool_bar_proxy_help_callback),
-                    user_data);
-
-  return TRUE;
-}
-
-/* This callback is called when a tool bar is detached.  We must set
-   the height of the tool bar to zero when this happens so frame sizes
-   are correctly calculated.
-   WBOX is the handle box widget that enables detach/attach of the tool bar.
-   W is the tool bar widget.
-   CLIENT_DATA is a pointer to the frame the tool bar belongs to.  */
-
-static void
-xg_tool_bar_detach_callback (GtkHandleBox *wbox,
-                             GtkWidget *w,
-                             gpointer client_data)
-{
-  struct frame *f = (struct frame *) client_data;
-
-  g_object_set (G_OBJECT (w), "show-arrow", !x_gtk_whole_detached_tool_bar,
-               NULL);
-
-  if (f)
-    {
-      GtkRequisition req, req2;
-
-      gtk_widget_get_preferred_size (GTK_WIDGET (wbox), NULL, &req);
-      gtk_widget_get_preferred_size (w, NULL, &req2);
-      req.width -= req2.width;
-      req.height -= req2.height;
-      if (FRAME_TOOLBAR_TOP_HEIGHT (f) != 0)
-        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);
-    }
-}
-
-/* This callback is called when a tool bar is reattached.  We must set
-   the height of the tool bar when this happens so frame sizes
-   are correctly calculated.
-   WBOX is the handle box widget that enables detach/attach of the tool bar.
-   W is the tool bar widget.
-   CLIENT_DATA is a pointer to the frame the tool bar belongs to.  */
-
-static void
-xg_tool_bar_attach_callback (GtkHandleBox *wbox,
-                             GtkWidget *w,
-                             gpointer client_data)
-{
-  struct frame *f = (struct frame *) client_data;
-  g_object_set (G_OBJECT (w), "show-arrow", TRUE, NULL);
-
-  if (f)
-    {
-      GtkRequisition req, req2;
-
-      gtk_widget_get_preferred_size (GTK_WIDGET (wbox), NULL, &req);
-      gtk_widget_get_preferred_size (w, NULL, &req2);
-      req.width += req2.width;
-      req.height += req2.height;
-      if (FRAME_TOOLBAR_TOP_HEIGHT (f) != 0)
-        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);
-    }
-}
-
 /* This callback is called when the mouse enters or leaves a tool bar item.
    It is used for displaying and hiding the help text.
    W is the tool bar item, a button.
@@ -4225,8 +4151,7 @@ xg_tool_bar_help_callback (GtkWidget *w,
                            gpointer client_data)
 {
   intptr_t idx = (intptr_t) client_data;
-  struct frame *f = (struct frame *) g_object_get_data (G_OBJECT (w),
-                                                       XG_FRAME_DATA);
+  struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
   Lisp_Object help, frame;
 
   if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
@@ -4290,12 +4215,6 @@ xg_tool_bar_item_expose_callback (GtkWidget *w,
   gtk_toolbar_set_orientation (GTK_TOOLBAR (w), o)
 #endif
 
-#ifdef HAVE_GTK_HANDLE_BOX_NEW
-#define TOOLBAR_TOP_WIDGET(x) ((x)->handlebox_widget)
-#else
-#define TOOLBAR_TOP_WIDGET(x) ((x)->toolbar_widget)
-#endif
-
 /* Attach a tool bar to frame F.  */
 
 static void
@@ -4303,31 +4222,15 @@ xg_pack_tool_bar (struct frame *f, Lisp_Object pos)
 {
   struct x_output *x = f->output_data.x;
   bool into_hbox = EQ (pos, Qleft) || EQ (pos, Qright);
-  GtkWidget *top_widget = TOOLBAR_TOP_WIDGET (x);
+  GtkWidget *top_widget = x->toolbar_widget;
 
   toolbar_set_orientation (x->toolbar_widget,
                            into_hbox
                            ? GTK_ORIENTATION_VERTICAL
                            : GTK_ORIENTATION_HORIZONTAL);
-#ifdef HAVE_GTK_HANDLE_BOX_NEW
-  if (!x->handlebox_widget)
-    {
-      top_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);
-    }
-#endif
 
   if (into_hbox)
     {
-#ifdef HAVE_GTK_HANDLE_BOX_NEW
-      gtk_handle_box_set_handle_position (GTK_HANDLE_BOX (x->handlebox_widget),
-                                          GTK_POS_TOP);
-#endif
       gtk_box_pack_start (GTK_BOX (x->hbox_widget), top_widget,
                           FALSE, FALSE, 0);
 
@@ -4340,10 +4243,6 @@ xg_pack_tool_bar (struct frame *f, Lisp_Object pos)
   else
     {
       bool vbox_pos = x->menubar_widget != 0;
-#ifdef HAVE_GTK_HANDLE_BOX_NEW
-      gtk_handle_box_set_handle_position (GTK_HANDLE_BOX (x->handlebox_widget),
-                                          GTK_POS_LEFT);
-#endif
       gtk_box_pack_start (GTK_BOX (x->vbox_widget), top_widget,
                           FALSE, FALSE, 0);
 
@@ -4366,9 +4265,13 @@ tb_size_cb (GtkWidget    *widget,
   /* When tool bar is created it has one preferred size.  But when size is
      allocated between widgets, it may get another.  So we must update
      size hints if tool bar size changes.  Seen on Fedora 18 at least.  */
-  struct frame *f = (struct frame *) user_data;
+  struct frame *f = user_data;
+
   if (xg_update_tool_bar_sizes (f))
-    x_wm_set_size_hint (f, 0, 0);
+    {
+      frame_size_history_add (f, Qtb_size_cb, 0, 0, Qnil);
+      adjust_frame_size (f, -1, -1, 5, 0, Qtool_bar_lines);
+    }
 }
 
 /* Create a tool bar for frame F.  */
@@ -4496,10 +4399,6 @@ xg_make_tool_item (struct frame *f,
       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);
@@ -4613,7 +4512,7 @@ xg_update_tool_bar_sizes (struct frame *f)
   struct x_output *x = f->output_data.x;
   GtkRequisition req;
   int nl = 0, nr = 0, nt = 0, nb = 0;
-  GtkWidget *top_widget = TOOLBAR_TOP_WIDGET (x);
+  GtkWidget *top_widget = x->toolbar_widget;
 
   gtk_widget_get_preferred_size (GTK_WIDGET (top_widget), NULL, &req);
   if (x->toolbar_in_hbox)
@@ -4646,10 +4545,47 @@ xg_update_tool_bar_sizes (struct frame *f)
       FRAME_TOOLBAR_RIGHT_WIDTH (f) = nr;
       FRAME_TOOLBAR_TOP_HEIGHT (f) = nt;
       FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = nb;
-      return 1;
+
+      return true;
     }
+  else
+    return false;
+}
 
-  return 0;
+static char *
+find_icon_from_name (char *name,
+                     GtkIconTheme *icon_theme,
+                     char **icon_name)
+{
+#if ! GTK_CHECK_VERSION (3, 10, 0)
+  GtkStockItem stock_item;
+#endif
+
+  if (name[0] == 'n' && name[1] == ':')
+    {
+      *icon_name = name + 2;
+      name = NULL;
+
+      if (! gtk_icon_theme_has_icon (icon_theme, *icon_name))
+        *icon_name = NULL;
+    }
+
+#if ! GTK_CHECK_VERSION (3, 10, 0)
+  else if (gtk_stock_lookup (name, &stock_item))
+    *icon_name = NULL;
+#endif
+  else if (gtk_icon_theme_has_icon (icon_theme, name))
+    {
+      *icon_name = name;
+      name = NULL;
+    }
+  else
+    {
+      name = NULL;
+      *icon_name = NULL;
+    }
+
+  return name;
 }
 
 
@@ -4667,6 +4603,9 @@ update_frame_tool_bar (struct frame *f)
   Lisp_Object style;
   bool text_image, horiz;
   struct xg_frame_tb_info *tbinfo;
+  GdkScreen *screen;
+  GtkIconTheme *icon_theme;
+
 
   if (! FRAME_GTK_WIDGET (f))
     return;
@@ -4701,6 +4640,8 @@ update_frame_tool_bar (struct frame *f)
   dir = gtk_widget_get_direction (GTK_WIDGET (wtoolbar));
 
   style = Ftool_bar_get_system_style ();
+  screen = gtk_widget_get_screen (GTK_WIDGET (wtoolbar));
+  icon_theme = gtk_icon_theme_get_for_screen (screen);
 
   /* Are we up to date? */
   tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
@@ -4737,7 +4678,6 @@ update_frame_tool_bar (struct frame *f)
       struct image *img = NULL;
       Lisp_Object image;
       Lisp_Object stock = Qnil;
-      GtkStockItem stock_item;
       char *stock_name = NULL;
       char *icon_name = NULL;
       Lisp_Object rtl;
@@ -4791,31 +4731,27 @@ update_frame_tool_bar (struct frame *f)
       if (!NILP (specified_file) && !NILP (Ffboundp (Qx_gtk_map_stock)))
         stock = call1 (Qx_gtk_map_stock, specified_file);
 
-      if (STRINGP (stock))
+      if (CONSP (stock))
         {
-          stock_name = SSDATA (stock);
-          if (stock_name[0] == 'n' && stock_name[1] == ':')
-            {
-              GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (wtoolbar));
-              GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (screen);
-
-              icon_name = stock_name + 2;
-              stock_name = NULL;
-              stock = Qnil;
-
-              if (! gtk_icon_theme_has_icon (icon_theme, icon_name))
-                icon_name = NULL;
-              else
-                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);
-          else
-            {
-              stock = Qnil;
-              stock_name = NULL;
-            }
+          Lisp_Object tem;
+          for (tem = stock; CONSP (tem); tem = XCDR (tem))
+            if (! NILP (tem) && STRINGP (XCAR (tem)))
+              {
+                stock_name = find_icon_from_name (SSDATA (XCAR (tem)),
+                                                  icon_theme,
+                                                  &icon_name);
+                if (stock_name || icon_name) break;
+              }
         }
+      else if (STRINGP (stock))
+        {
+          stock_name = find_icon_from_name (SSDATA (stock),
+                                            icon_theme,
+                                            &icon_name);
+        }
+
+      if (stock_name || icon_name)
+        icon_size = gtk_toolbar_get_icon_size (wtoolbar);
 
       if (stock_name == NULL && icon_name == NULL)
         {
@@ -4877,7 +4813,12 @@ update_frame_tool_bar (struct frame *f)
            w = NULL;
          else if (stock_name)
             {
+
+#if GTK_CHECK_VERSION (3, 10, 0)
+              w = gtk_image_new_from_icon_name (stock_name, icon_size);
+#else
               w = gtk_image_new_from_stock (stock_name, icon_size);
+#endif
               g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_STOCK_NAME,
                                       (gpointer) xstrdup (stock_name),
                                       (GDestroyNotify) xfree);
@@ -4896,7 +4837,17 @@ update_frame_tool_bar (struct frame *f)
                                  (gpointer)img->pixmap);
             }
 
+#if GTK_CHECK_VERSION (3, 14, 0)
+          if (w)
+            {
+              gtk_widget_set_margin_start (w, hmargin);
+              gtk_widget_set_margin_end (w, hmargin);
+              gtk_widget_set_margin_top (w, vmargin);
+              gtk_widget_set_margin_bottom (w, vmargin);
+            }
+#else
          if (w) gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
+#endif
           ti = xg_make_tool_item (f, w, &wbutton, label, i, horiz, text_image);
           gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
         }
@@ -4918,10 +4869,13 @@ update_frame_tool_bar (struct frame *f)
   if (f->n_tool_bar_items != 0)
     {
       if (! x->toolbar_is_packed)
-        xg_pack_tool_bar (f, f->tool_bar_position);
-      gtk_widget_show_all (TOOLBAR_TOP_WIDGET (x));
+        xg_pack_tool_bar (f, FRAME_TOOL_BAR_POSITION (f));
+      gtk_widget_show_all (x->toolbar_widget);
       if (xg_update_tool_bar_sizes (f))
-        xg_height_or_width_changed (f);
+       {
+         frame_size_history_add (f, Qupdate_frame_tool_bar, 0, 0, Qnil);
+         adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
+       }
     }
 
   unblock_input ();
@@ -4938,11 +4892,9 @@ free_frame_tool_bar (struct frame *f)
   if (x->toolbar_widget)
     {
       struct xg_frame_tb_info *tbinfo;
-      GtkWidget *top_widget = TOOLBAR_TOP_WIDGET (x);
+      GtkWidget *top_widget = x->toolbar_widget;
 
       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 (x->toolbar_is_packed)
         {
           if (x->toolbar_in_hbox)
@@ -4956,7 +4908,7 @@ free_frame_tool_bar (struct frame *f)
         gtk_widget_destroy (x->toolbar_widget);
 
       x->toolbar_widget = 0;
-      TOOLBAR_TOP_WIDGET (x) = 0;
+      x->toolbar_widget = 0;
       x->toolbar_is_packed = false;
       FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
       FRAME_TOOLBAR_LEFT_WIDTH (f) = FRAME_TOOLBAR_RIGHT_WIDTH (f) = 0;
@@ -4971,7 +4923,8 @@ free_frame_tool_bar (struct frame *f)
                              NULL);
         }
 
-      xg_height_or_width_changed (f);
+      frame_size_history_add (f, Qfree_frame_tool_bar, 0, 0, Qnil);
+      adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
 
       unblock_input ();
     }
@@ -4981,7 +4934,7 @@ void
 xg_change_toolbar_position (struct frame *f, Lisp_Object pos)
 {
   struct x_output *x = f->output_data.x;
-  GtkWidget *top_widget = TOOLBAR_TOP_WIDGET (x);
+  GtkWidget *top_widget = x->toolbar_widget;
 
   if (! x->toolbar_widget || ! top_widget)
     return;
@@ -5000,8 +4953,13 @@ xg_change_toolbar_position (struct frame *f, Lisp_Object pos)
 
   xg_pack_tool_bar (f, pos);
   g_object_unref (top_widget);
+
   if (xg_update_tool_bar_sizes (f))
-    xg_height_or_width_changed (f);
+    {
+      frame_size_history_add (f, Qxg_change_toolbar_position, 0, 0, Qnil);
+      adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
+    }
+
 
   unblock_input ();
 }
@@ -5025,9 +4983,6 @@ xg_initialize (void)
 
   gdpy_def = NULL;
   xg_ignore_gtk_scrollbar = 0;
-#ifdef HAVE_GTK_TEAROFF_MENU_ITEM_NEW
-  xg_detached_menus = 0;
-#endif
   xg_menu_cb_list.prev = xg_menu_cb_list.next =
     xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
 
@@ -5038,10 +4993,10 @@ xg_initialize (void)
                                           (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".  */
+     so we set it to "" which in means "no key".  */
   gtk_settings_set_string_property (settings,
                                     "gtk-menu-bar-accel",
-                                    "VoidSymbol",
+                                    "",
                                     EMACS_CLASS);
 
   /* Make GTK text input widgets use Emacs style keybindings.  This is
@@ -5063,6 +5018,7 @@ xg_initialize (void)
   gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
                                 "cancel", 0);
   update_theme_scrollbar_width ();
+  update_theme_scrollbar_height ();
 
 #ifdef HAVE_FREETYPE
   x_last_font_name = NULL;