]> code.delx.au - gnu-emacs/blobdiff - src/gtkutil.c
Merge from emacs-24
[gnu-emacs] / src / gtkutil.c
index 0f3f5ba58ba41e5a6430cd85511812905346fd73..595e6e0bb6ac00acd130f4cd20a08f19dcde3aaa 100644 (file)
@@ -1,6 +1,6 @@
 /* Functions for creating and updating GTK widgets.
 
-Copyright (C) 2003-2013 Free Software Foundation, Inc.
+Copyright (C) 2003-2014 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"
 
@@ -76,6 +79,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #define remove_submenu(w) gtk_menu_item_remove_submenu ((w))
 #endif
 
+#ifdef HAVE_FREETYPE
 #if GTK_CHECK_VERSION (3, 2, 0)
 #define USE_NEW_GTK_FONT_CHOOSER 1
 #else
@@ -87,6 +91,7 @@ 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 */
 
 #ifndef HAVE_GTK3
 #ifdef USE_GTK_TOOLTIP
@@ -110,6 +115,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
@@ -217,57 +223,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 +508,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 +551,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);
@@ -864,35 +822,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,7 +857,7 @@ 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)
     {
@@ -917,11 +869,11 @@ xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight)
     }
 
 
-  rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelheight);
-  columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
+  width = FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth);
+  height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight);
 
-  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))
     {
@@ -929,9 +881,11 @@ xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight)
       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,38 +893,23 @@ 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);
 
   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);
-
-
   /* 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);
+                     pixelwidth + FRAME_TOOLBAR_WIDTH (f),
+                    pixelheight + FRAME_TOOLBAR_HEIGHT (f)
+                    + FRAME_MENUBAR_HEIGHT (f));
   x_wm_set_size_hint (f, 0, 0);
 
   SET_FRAME_GARBAGED (f);
@@ -991,11 +930,7 @@ xg_frame_set_char_size (struct frame *f, int cols, int rows)
       x_wait_for_event (f, ConfigureNotify);
     }
   else
-    {
-      change_frame_size (f, rows, cols, 0, 1, 0);
-      FRAME_PIXEL_WIDTH (f) = pixelwidth;
-      FRAME_PIXEL_HEIGHT (f) = pixelheight;
-     }
+    adjust_frame_size (f, -1, -1, 5, 0);
 }
 
 /* Handle height/width changes (i.e. add/remove/move menu/toolbar).
@@ -1042,7 +977,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;
@@ -1085,6 +1020,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 +1035,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));
             }
         }
     }
@@ -1372,8 +1309,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.
@@ -1382,14 +1319,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.  */
@@ -1443,7 +1379,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))
     {
@@ -1605,6 +1541,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
         {
@@ -1621,11 +1558,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);
             }
         }
 
@@ -1683,15 +1615,15 @@ static gboolean
 xg_maybe_add_timer (gpointer data)
 {
   struct xg_dialog_data *dd = data;
-  EMACS_TIME next_time = timer_check ();
+  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);
     }
@@ -2050,7 +1982,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
@@ -2125,8 +2056,7 @@ xg_get_font (struct frame *f, const char *default_name)
          font = Ffont_spec (8, args);
 
          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.  */
@@ -2433,9 +2363,12 @@ static int xg_detached_menus;
 /* Return true if there are detached menus.  */
 
 bool
-xg_have_tear_offs (void)
+xg_have_tear_offs (struct frame *f)
 {
-  return xg_detached_menus > 0;
+  /* If the frame's menubar height is zero, the menu bar is probably
+     being redirected outside the window to some kind of global menu;
+     this situation is the moral equivalent of a tear-off.  */
+  return FRAME_MENUBAR_HEIGHT (f) == 0 || xg_detached_menus > 0;
 }
 
 /* Callback invoked when a detached menu window is removed.  Here we
@@ -2468,9 +2401,9 @@ tearoff_activate (GtkWidget *widget, gpointer client_data)
 }
 #else /* ! HAVE_GTK_TEAROFF_MENU_ITEM_NEW */
 bool
-xg_have_tear_offs (void)
+xg_have_tear_offs (struct frame *f)
 {
-  return false;
+  return FRAME_MENUBAR_HEIGHT (f) == 0;
 }
 #endif /* ! HAVE_GTK_TEAROFF_MENU_ITEM_NEW */
 
@@ -2742,7 +2675,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
@@ -2916,7 +2849,13 @@ xg_update_menubar (GtkWidget *menubar,
           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);
+          g_object_notify (G_OBJECT (witem), "label");
 
 #ifdef HAVE_GTK_TEAROFF_MENU_ITEM_NEW
           /* If this item has a submenu that has been detached, change
@@ -2953,6 +2892,7 @@ xg_update_menubar (GtkWidget *menubar,
                                              select_cb, deactivate_cb,
                                              highlight_cb,
                                              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);
@@ -3012,6 +2952,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);
@@ -3056,15 +2997,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);
@@ -3096,6 +3042,9 @@ xg_update_menu_item (widget_value *val,
           cb_data->select_id = 0;
         }
     }
+
+  if (label_changed) /* See comment in xg_update_menubar.  */
+    g_object_notify (G_OBJECT (w), "label");
 }
 
 /* Update the toggle menu item W so it corresponds to VAL.  */
@@ -3378,11 +3327,6 @@ 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;
@@ -3414,7 +3358,7 @@ free_frame_menubar (struct frame *f)
 }
 
 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;
@@ -3430,7 +3374,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;
 
@@ -3475,9 +3419,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
@@ -3580,12 +3524,41 @@ update_theme_scrollbar_width (void)
   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.  */
 
@@ -3681,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.  */
@@ -3750,15 +3791,78 @@ 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
          here to get some events.  */
@@ -3769,6 +3873,7 @@ xg_update_scrollbar_pos (struct frame *f,
     }
 }
 
+
 /* Get the current value of the range, truncated to an integer.  */
 
 static int
@@ -3791,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;
@@ -3863,13 +3968,55 @@ 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));
+#if GTK_CHECK_VERSION (2, 3, 16)
+      gtk_adjustment_configure (adj, (gdouble) value, (gdouble) lower,
+                               (gdouble) upper, (gdouble) step_increment,
+                               (gdouble) page_increment, (gdouble) pagesize);
+#else
+      gtk_adjustment_set_lower (adj, (gdouble) lower);
+      gtk_adjustment_set_upper (adj, (gdouble) upper);
+      gtk_adjustment_set_page_size (adj, (gdouble) pagesize);
+      gtk_adjustment_set_value (adj, (gdouble) value);
+      gtk_adjustment_set_page_increment (adj, (gdouble) page_increment);
+      gtk_adjustment_set_step_increment (adj, (gdouble) step_increment);
+#endif
+      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;
 
@@ -3980,12 +4127,12 @@ 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);
+  /* Return focus to the frame after we have clicked on a detached
+     tool bar button. */
+  x_focus_frame (f);
 }
 
 /* Callback function invoked when a tool bar item is pressed in a detached
@@ -4374,7 +4521,7 @@ tb_size_cb (GtkWidget    *widget,
      size hints if tool bar size changes.  Seen on Fedora 18 at least.  */
   struct frame *f = user_data;
   if (xg_update_tool_bar_sizes (f))
-    x_wm_set_size_hint (f, 0, 0);
+    xg_height_or_width_changed (f);
 }
 
 /* Create a tool bar for frame F.  */
@@ -4924,7 +5071,7 @@ 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);
+        xg_pack_tool_bar (f, FRAME_TOOL_BAR_POSITION (f));
       gtk_widget_show_all (TOOLBAR_TOP_WIDGET (x));
       if (xg_update_tool_bar_sizes (f))
         xg_height_or_width_changed (f);
@@ -5069,6 +5216,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;