Simplify redefinition of 'abort' (Bug#12316).
[bpt/emacs.git] / src / gtkutil.c
index af697c7..3bce5be 100644 (file)
@@ -1,6 +1,6 @@
 /* Functions for creating and updating GTK widgets.
 
-Copyright (C) 2003-2011  Free Software Foundation, Inc.
+Copyright (C) 2003-2012  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -24,6 +24,9 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <signal.h>
 #include <stdio.h>
 #include <setjmp.h>
+
+#include <c-ctype.h>
+
 #include "lisp.h"
 #include "xterm.h"
 #include "blockinput.h"
@@ -75,6 +78,18 @@ 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_MAJOR_VERSION > 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 2)
+#define USE_NEW_GTK_FONT_CHOOSER 1
+#else
+#define USE_NEW_GTK_FONT_CHOOSER 0
+#define gtk_font_chooser_dialog_new(x, y) \
+  gtk_font_selection_dialog_new (x)
+#undef GTK_FONT_CHOOSER
+#define GTK_FONT_CHOOSER(x) GTK_FONT_SELECTION_DIALOG (x)
+#define  gtk_font_chooser_set_font(x, y) \
+  gtk_font_selection_dialog_set_font_name (x, y)
+#endif
+
 #ifndef HAVE_GTK3
 #ifdef USE_GTK_TOOLTIP
 #define gdk_window_get_screen(w) gdk_drawable_get_screen (w)
@@ -83,15 +98,31 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
   gdk_window_get_geometry (w, a, b, c, d, 0)
 #define gdk_x11_window_lookup_for_display(d, w) \
   gdk_xid_table_lookup_for_display (d, w)
+#define gtk_box_new(ori, spacing)                                       \
+  ((ori) == GTK_ORIENTATION_HORIZONTAL                                  \
+   ? gtk_hbox_new (FALSE, (spacing)) : gtk_vbox_new (FALSE, (spacing)))
+#define gtk_scrollbar_new(ori, spacing)                                 \
+  ((ori) == GTK_ORIENTATION_HORIZONTAL                                  \
+   ? gtk_hscrollbar_new ((spacing)) : gtk_vscrollbar_new ((spacing)))
 #ifndef GDK_KEY_g
 #define GDK_KEY_g GDK_g
 #endif
-#endif
+#endif /* HAVE_GTK3 */
 
 #define XG_BIN_CHILD(x) gtk_bin_get_child (GTK_BIN (x))
 
 static void update_theme_scrollbar_width (void);
 
+#define TB_INFO_KEY "xg_frame_tb_info"
+struct xg_frame_tb_info
+{
+  Lisp_Object last_tool_bar;
+  Lisp_Object style;
+  int n_last_items;
+  int hmargin, vmargin;
+  GtkTextDirection dir;
+};
+
 \f
 /***********************************************************************
                       Display handling functions
@@ -127,7 +158,7 @@ xg_set_screen (GtkWidget *w, FRAME_PTR f)
 
    Returns non-zero if display could be opened, zero if display could not
    be opened, and less than zero if the GTK version doesn't support
-   multipe displays.  */
+   multiple displays.  */
 
 void
 xg_display_open (char *display_name, Display **dpy)
@@ -209,7 +240,7 @@ malloc_widget_value (void)
     }
   else
     {
-      wv = (widget_value *) xmalloc (sizeof (widget_value));
+      wv = xmalloc (sizeof *wv);
       malloc_cpt++;
     }
   memset (wv, 0, sizeof (widget_value));
@@ -223,7 +254,7 @@ void
 free_widget_value (widget_value *wv)
 {
   if (wv->free_list)
-    abort ();
+    emacs_abort ();
 
   if (malloc_cpt > 25)
     {
@@ -348,7 +379,7 @@ file_for_image (Lisp_Object image)
 
 /* For the image defined in IMG, make and return a GtkImage.  For displays with
    8 planes or less we must make a GdkPixbuf and apply the mask manually.
-   Otherwise the highlightning and dimming the tool bar code in GTK does
+   Otherwise the highlighting and dimming the tool bar code in GTK does
    will look bad.  For display with more than 8 planes we just use the
    pixmap and mask directly.  For monochrome displays, GTK doesn't seem
    able to use external pixmaps, it looks bad whatever we do.
@@ -523,9 +554,8 @@ get_utf8_string (const char *str)
                                        &bytes_written, &err))
              && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
         {
-          strncpy (up, (char *)p, bytes_written);
+          memcpy (up, p, bytes_written);
           sprintf (up + bytes_written, "\\%03o", p[bytes_written]);
-          up[bytes_written+4] = '\0';
           up += bytes_written+4;
           p += bytes_written+1;
           g_error_free (err);
@@ -891,7 +921,7 @@ xg_frame_resized (FRAME_PTR f, int pixelwidth, int pixelheight)
     }
 }
 
-/* Resize the outer window of frame F after chainging the height.
+/* Resize the outer window of frame F after changing the height.
    COLUMNS/ROWS is the size the edit area shall have after the resize.  */
 
 void
@@ -1090,20 +1120,35 @@ xg_create_frame_widgets (FRAME_PTR f)
   GtkWidget *wtop;
   GtkWidget *wvbox, *whbox;
   GtkWidget *wfixed;
+#ifndef HAVE_GTK3
   GtkRcStyle *style;
+#endif
   char *title = 0;
 
   BLOCK_INPUT;
 
   if (FRAME_X_EMBEDDED_P (f))
-    wtop = gtk_plug_new (f->output_data.x->parent_desc);
+    {
+      GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
+      wtop = gtk_plug_new_for_display (gdpy, f->output_data.x->parent_desc);
+    }
   else
     wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 
+  /* gtk_window_set_has_resize_grip is a Gtk+ 3.0 function but Ubuntu
+     has backported it to Gtk+ 2.0 and they add the resize grip for
+     Gtk+ 2.0 applications also.  But it has a bug that makes Emacs loop
+     forever, so disable the grip.  */
+#if GTK_MAJOR_VERSION < 3 && defined (HAVE_GTK_WINDOW_SET_HAS_RESIZE_GRIP)
+  gtk_window_set_has_resize_grip (GTK_WINDOW (wtop), FALSE);
+#endif
+
   xg_set_screen (wtop, f);
 
-  wvbox = gtk_vbox_new (FALSE, 0);
-  whbox = gtk_hbox_new (FALSE, 0);
+  wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  whbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_box_set_homogeneous (GTK_BOX (wvbox), FALSE);
+  gtk_box_set_homogeneous (GTK_BOX (whbox), FALSE);
 
 #ifdef HAVE_GTK3
   wfixed = emacs_fixed_new (f);
@@ -1128,8 +1173,10 @@ xg_create_frame_widgets (FRAME_PTR f)
   gtk_widget_set_name (wfixed, SSDATA (Vx_resource_name));
 
   /* If this frame has a title or name, set it in the title bar.  */
-  if (! NILP (f->title)) title = SSDATA (ENCODE_UTF_8 (f->title));
-  else if (! NILP (f->name)) title = SSDATA (ENCODE_UTF_8 (f->name));
+  if (! NILP (f->title))
+    title = SSDATA (ENCODE_UTF_8 (f->title));
+  else if (! NILP (f->name))
+    title = SSDATA (ENCODE_UTF_8 (f->name));
 
   if (title) gtk_window_set_title (GTK_WINDOW (wtop), title);
 
@@ -1243,6 +1290,12 @@ xg_free_frame_widgets (FRAME_PTR f)
 #ifdef USE_GTK_TOOLTIP
       struct x_output *x = f->output_data.x;
 #endif
+      struct xg_frame_tb_info *tbinfo
+        = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                             TB_INFO_KEY);
+      if (tbinfo)
+        xfree (tbinfo);
+
       gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
       FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
       FRAME_GTK_OUTER_WIDGET (f) = 0;
@@ -1295,11 +1348,15 @@ x_wm_set_size_hint (FRAME_PTR f, long int flags, int user_position)
   size_hints.height_inc = FRAME_LINE_HEIGHT (f);
 
   hint_flags |= GDK_HINT_BASE_SIZE;
-  base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0) + FRAME_TOOLBAR_WIDTH (f);
-  base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0)
+  /* Use one row/col here so base_height/width does not become zero.
+     Gtk+ and/or Unity on Ubuntu 12.04 can't handle it.  */
+  base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TOOLBAR_WIDTH (f);
+  base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
     + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
 
   check_frame_size (f, &min_rows, &min_cols);
+  if (min_cols > 0) --min_cols; /* We used one col in base_width = ... 1); */
+  if (min_rows > 0) --min_rows; /* We used one row in base_height = ... 1); */
 
   size_hints.base_width = base_width;
   size_hints.base_height = base_height;
@@ -1423,7 +1480,7 @@ get_dialog_title (char key)
 /* Callback for dialogs that get WM_DELETE_WINDOW.  We pop down
    the dialog, but return TRUE so the event does not propagate further
    in GTK.  This prevents GTK from destroying the dialog widget automatically
-   and we can always destrou the widget manually, regardles of how
+   and we can always destroy the widget manually, regardless of how
    it was popped down (button press or WM_DELETE_WINDOW).
    W is the dialog widget.
    EVENT is the GdkEvent that represents WM_DELETE_WINDOW (not used).
@@ -1475,9 +1532,12 @@ create_dialog (widget_value *wv,
 
   if (make_two_rows)
     {
-      GtkWidget *wvbox = gtk_vbox_new (TRUE, button_spacing);
-      GtkWidget *whbox_up = gtk_hbox_new (FALSE, 0);
-      whbox_down = gtk_hbox_new (FALSE, 0);
+      GtkWidget *wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, button_spacing);
+      GtkWidget *whbox_up = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+      gtk_box_set_homogeneous (GTK_BOX (wvbox), TRUE);
+      gtk_box_set_homogeneous (GTK_BOX (whbox_up), FALSE);
+      whbox_down = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+      gtk_box_set_homogeneous (GTK_BOX (whbox_down), FALSE);
 
       gtk_box_pack_start (cur_box, wvbox, FALSE, FALSE, 0);
       gtk_box_pack_start (GTK_BOX (wvbox), whbox_up, FALSE, FALSE, 0);
@@ -1599,16 +1659,16 @@ xg_maybe_add_timer (gpointer data)
 {
   struct xg_dialog_data *dd = (struct xg_dialog_data *) data;
   EMACS_TIME next_time = timer_check ();
-  long secs = EMACS_SECS (next_time);
-  long usecs = EMACS_USECS (next_time);
 
   dd->timerid = 0;
 
-  if (secs >= 0 && usecs >= 0 && secs < ((guint)-1)/1000)
+  if (EMACS_TIME_VALID_P (next_time))
     {
-      dd->timerid = g_timeout_add (secs * 1000 + usecs/1000,
-                                   xg_maybe_add_timer,
-                                   dd);
+      time_t s = EMACS_SECS (next_time);
+      int per_ms = EMACS_TIME_RESOLUTION / 1000;
+      int ms = (EMACS_NSECS (next_time) + per_ms - 1) / per_ms;
+      if (s <= ((guint) -1 - ms) / 1000)
+       dd->timerid = g_timeout_add (s * 1000 + ms, xg_maybe_add_timer, dd);
     }
   return FALSE;
 }
@@ -1621,7 +1681,7 @@ xg_maybe_add_timer (gpointer data)
 static int
 xg_dialog_run (FRAME_PTR f, GtkWidget *w)
 {
-  int count = SPECPDL_INDEX ();
+  ptrdiff_t count = SPECPDL_INDEX ();
   struct xg_dialog_data dd;
 
   xg_set_screen (w, f);
@@ -1733,8 +1793,9 @@ xg_toggle_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data)
    PROMPT is a prompt to show to the user.  May not be NULL.
    DEFAULT_FILENAME is a default selection to be displayed.  May be NULL.
    If MUSTMATCH_P is non-zero, the returned file name must be an existing
-   file.  *FUNC is set to a function that can be used to retrieve the
-   selected file name from the returned widget.
+   file.  (Actually, this only has cosmetic effects, the user can
+   still enter a non-existing file.)  *FUNC is set to a function that
+   can be used to retrieve the selected file name from the returned widget.
 
    Returns the created widget.  */
 
@@ -1764,7 +1825,8 @@ xg_get_file_with_chooser (FRAME_PTR f,
                                          NULL);
   gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
 
-  wbox = gtk_vbox_new (FALSE, 0);
+  wbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
   gtk_widget_show (wbox);
   wtoggle = gtk_check_button_new_with_label ("Show hidden files.");
 
@@ -1949,7 +2011,35 @@ xg_get_file_name (FRAME_PTR f,
   return fn;
 }
 
+/***********************************************************************
+                      GTK font chooser
+ ***********************************************************************/
+
 #ifdef HAVE_FREETYPE
+
+#if USE_NEW_GTK_FONT_CHOOSER
+
+#define XG_WEIGHT_TO_SYMBOL(w)                 \
+  (w <= PANGO_WEIGHT_THIN ? Qextra_light       \
+   : w <= PANGO_WEIGHT_ULTRALIGHT ? Qlight     \
+   : w <= PANGO_WEIGHT_LIGHT ? Qsemi_light     \
+   : w < PANGO_WEIGHT_MEDIUM ? Qnormal         \
+   : w <= PANGO_WEIGHT_SEMIBOLD ? Qsemi_bold   \
+   : w <= PANGO_WEIGHT_BOLD ? Qbold            \
+   : w <= PANGO_WEIGHT_HEAVY ? Qextra_bold     \
+   : Qultra_bold)
+
+#define XG_STYLE_TO_SYMBOL(s)                  \
+  (s == PANGO_STYLE_OBLIQUE ? Qoblique         \
+   : s == PANGO_STYLE_ITALIC ? Qitalic         \
+   : Qnormal)
+
+#endif /* USE_NEW_GTK_FONT_CHOOSER */
+
+
+static char *x_last_font_name;
+extern Lisp_Object Qxft;
+
 /* Pop up a GTK font selector and return the name of the font the user
    selects, as a C string.  The returned font name follows GTK's own
    format:
@@ -1959,25 +2049,40 @@ xg_get_file_name (FRAME_PTR f,
    This can be parsed using font_parse_fcname in font.c.
    DEFAULT_NAME, if non-zero, is the default font name.  */
 
-char *
-xg_get_font_name (FRAME_PTR f, const char *default_name)
+Lisp_Object
+xg_get_font (FRAME_PTR f, const char *default_name)
 {
   GtkWidget *w;
-  char *fontname = NULL;
   int done = 0;
+  Lisp_Object font = Qnil;
 
 #if defined (HAVE_PTHREAD) && defined (__SIGRTMIN)
   sigblock (sigmask (__SIGRTMIN));
 #endif /* HAVE_PTHREAD */
 
-  w = gtk_font_selection_dialog_new ("Pick a font");
-  if (!default_name)
-    default_name = "Monospace 10";
-  gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (w),
-                                           default_name);
+  w = gtk_font_chooser_dialog_new
+    ("Pick a font", GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
 
-  gtk_widget_set_name (w, "emacs-fontdialog");
+  if (default_name)
+    {
+      /* Convert fontconfig names to Gtk names, i.e. remove - before
+        number */
+      char *p = strrchr (default_name, '-');
+      if (p)
+        {
+          char *ep = p+1;
+          while (c_isdigit (*ep))
+            ++ep;
+          if (*ep == '\0') *p = ' ';
+        }
+    }
+  else if (x_last_font_name)
+    default_name = x_last_font_name;
 
+  if (default_name)
+    gtk_font_chooser_set_font (GTK_FONT_CHOOSER (w), default_name);
+
+  gtk_widget_set_name (w, "emacs-fontdialog");
   done = xg_dialog_run (f, w);
 
 #if defined (HAVE_PTHREAD) && defined (__SIGRTMIN)
@@ -1985,11 +2090,58 @@ xg_get_font_name (FRAME_PTR f, const char *default_name)
 #endif
 
   if (done == GTK_RESPONSE_OK)
-    fontname = gtk_font_selection_dialog_get_font_name
-      (GTK_FONT_SELECTION_DIALOG (w));
+    {
+#if USE_NEW_GTK_FONT_CHOOSER
+      /* Use the GTK3 font chooser.  */
+      PangoFontDescription *desc
+       = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (w));
+
+      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);
+
+         pango_font_description_free (desc);
+         xfree (x_last_font_name);
+         x_last_font_name = xstrdup (name);
+       }
+
+#else /* Use old font selector, which just returns the font name.  */
+
+      char *font_name
+       = gtk_font_selection_dialog_get_font_name (GTK_FONT_CHOOSER (w));
+
+      if (font_name)
+       {
+         font = build_string (font_name);
+         g_free (x_last_font_name);
+         x_last_font_name = font_name;
+       }
+#endif /* USE_NEW_GTK_FONT_CHOOSER */
+    }
 
   gtk_widget_destroy (w);
-  return fontname;
+  return font;
 }
 #endif /* HAVE_FREETYPE */
 
@@ -2029,7 +2181,7 @@ make_cl_data (xg_menu_cb_data *cl_data, FRAME_PTR f, GCallback highlight_cb)
 {
   if (! cl_data)
     {
-      cl_data = (xg_menu_cb_data*) xmalloc (sizeof (*cl_data));
+      cl_data = xmalloc (sizeof *cl_data);
       cl_data->f = f;
       cl_data->menu_bar_vector = f->menu_bar_vector;
       cl_data->menu_bar_items_used = f->menu_bar_items_used;
@@ -2092,6 +2244,7 @@ void
 xg_mark_data (void)
 {
   xg_list_node *iter;
+  Lisp_Object rest, frame;
 
   for (iter = xg_menu_cb_list.next; iter; iter = iter->next)
     mark_object (((xg_menu_cb_data *) iter)->menu_bar_vector);
@@ -2103,6 +2256,23 @@ xg_mark_data (void)
       if (! NILP (cb_data->help))
         mark_object (cb_data->help);
     }
+
+  FOR_EACH_FRAME (rest, frame)
+    {
+      FRAME_PTR f = XFRAME (frame);
+
+      if (FRAME_X_P (f) && FRAME_GTK_OUTER_WIDGET (f))
+        {
+          struct xg_frame_tb_info *tbinfo
+            = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                                 TB_INFO_KEY);
+          if (tbinfo)
+            {
+              mark_object (tbinfo->last_tool_bar);
+              mark_object (tbinfo->style);
+            }
+        }
+    }
 }
 
 
@@ -2176,7 +2346,8 @@ make_widget_for_menu_item (const char *utf8_label, const char *utf8_key)
   GtkWidget *wkey;
   GtkWidget *wbox;
 
-  wbox = gtk_hbox_new (FALSE, 0);
+  wbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
   wlbl = gtk_label_new (utf8_label);
   wkey = gtk_label_new (utf8_key);
 
@@ -2294,7 +2465,7 @@ tearoff_activate (GtkWidget *widget, gpointer client_data)
 
 
 /* Create a menu item widget, and connect the callbacks.
-   ITEM decribes the menu item.
+   ITEM describes the menu item.
    F is the frame the created menu belongs to.
    SELECT_CB is the callback to use when a menu item is selected.
    HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
@@ -2328,7 +2499,7 @@ xg_create_one_menuitem (widget_value *item,
   if (utf8_label) g_free (utf8_label);
   if (utf8_key) g_free (utf8_key);
 
-  cb_data = xmalloc (sizeof (xg_menu_item_cb_data));
+  cb_data = xmalloc (sizeof *cb_data);
 
   xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
 
@@ -2363,7 +2534,7 @@ xg_create_one_menuitem (widget_value *item,
    HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
    POP_UP_P is non-zero if we shall create a popup menu.
    MENU_BAR_P is non-zero if we shall create a menu bar.
-   ADD_TEAROFF_P is non-zero if we shall add a teroff menu item.  Ignored
+   ADD_TEAROFF_P is non-zero if we shall add a tearoff menu item.  Ignored
    if MENU_BAR_P is non-zero.
    TOPMENU is the topmost GtkWidget that others shall be placed under.
    It may be NULL, in that case we create the appropriate widget
@@ -2833,7 +3004,7 @@ xg_update_menu_item (widget_value *val,
   utf8_key = get_utf8_string (val->key);
 
   /* See if W is a menu item with a key.  See make_menu_item above.  */
-  if (GTK_IS_HBOX (wchild))
+  if (GTK_IS_BOX (wchild))
     {
       GList *list = gtk_container_get_children (GTK_CONTAINER (wchild));
 
@@ -3044,7 +3215,7 @@ xg_update_submenu (GtkWidget *submenu,
       }
   }
 
-  /* Remove widgets from first structual change.  */
+  /* Remove widgets from first structural change.  */
   if (iter)
     {
       /* If we are adding new menu items below, we must remove from
@@ -3348,7 +3519,7 @@ xg_store_widget_in_map (GtkWidget *w)
     }
 
   /* Should never end up here  */
-  abort ();
+  emacs_abort ();
 }
 
 /* Remove pointer at IDX from id_to_widget.
@@ -3387,7 +3558,7 @@ update_theme_scrollbar_width (void)
   int w = 0, b = 0;
 
   vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX, 0.1, 0.1, 0.1);
-  wscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT (vadj));
+  wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
   g_object_ref_sink (G_OBJECT (wscroll));
   gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
   gtk_widget_destroy (wscroll);
@@ -3464,7 +3635,7 @@ xg_create_scroll_bar (FRAME_PTR f,
   vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
                              0.1, 0.1, 0.1);
 
-  wscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT (vadj));
+  wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
   webox = gtk_event_box_new ();
   gtk_widget_set_name (wscroll, scroll_bar_name);
 #ifndef HAVE_GTK3
@@ -3697,8 +3868,15 @@ xg_event_is_for_scrollbar (FRAME_PTR f, XEvent *event)
     {
       /* Check if press occurred outside the edit widget.  */
       GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
-      retval = gdk_display_get_window_at_pointer (gdpy, NULL, NULL)
-        != gtk_widget_get_window (f->output_data.x->edit_widget);
+      GdkWindow *gwin;
+#ifdef HAVE_GTK3
+      GdkDevice *gdev = gdk_device_manager_get_client_pointer
+        (gdk_display_get_device_manager (gdpy));
+      gwin = gdk_device_get_window_at_position (gdev, NULL, NULL);
+#else
+      gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL);
+#endif
+      retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget);
     }
   else if (f
            && ((event->type == ButtonRelease && event->xbutton.button < 4)
@@ -3909,7 +4087,7 @@ xg_tool_bar_menu_proxy (GtkToolItem *toolitem, gpointer user_data)
           else
             {
               fprintf (stderr, "internal error: GTK_IMAGE_PIXBUF failed\n");
-              abort ();
+              emacs_abort ();
             }
         }
       else if (store_type == GTK_IMAGE_ICON_NAME)
@@ -3924,7 +4102,7 @@ xg_tool_bar_menu_proxy (GtkToolItem *toolitem, gpointer user_data)
       else
         {
           fprintf (stderr, "internal error: store_type is %d\n", store_type);
-          abort ();
+          emacs_abort ();
         }
     }
   if (wmenuimage)
@@ -4167,6 +4345,24 @@ static void
 xg_create_tool_bar (FRAME_PTR f)
 {
   struct x_output *x = f->output_data.x;
+#if GTK_CHECK_VERSION (3, 3, 6)
+  GtkStyleContext *gsty;
+#endif
+  struct xg_frame_tb_info *tbinfo
+    = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                         TB_INFO_KEY);
+  if (! tbinfo)
+    {
+      tbinfo = xmalloc (sizeof (*tbinfo));
+      tbinfo->last_tool_bar = Qnil;
+      tbinfo->style = Qnil;
+      tbinfo->hmargin = tbinfo->vmargin = 0;
+      tbinfo->dir = GTK_TEXT_DIR_NONE;
+      tbinfo->n_last_items = 0;
+      g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                         TB_INFO_KEY,
+                         tbinfo);
+    }
 
   x->toolbar_widget = gtk_toolbar_new ();
   x->toolbar_detached = 0;
@@ -4175,6 +4371,10 @@ xg_create_tool_bar (FRAME_PTR f)
 
   gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
   toolbar_set_orientation (x->toolbar_widget, GTK_ORIENTATION_HORIZONTAL);
+#if GTK_CHECK_VERSION (3, 3, 6)
+  gsty = gtk_widget_get_style_context (x->toolbar_widget);
+  gtk_style_context_add_class (gsty, "primary-toolbar");
+#endif
 }
 
 
@@ -4200,7 +4400,7 @@ find_rtl_image (FRAME_PTR f, Lisp_Object image, Lisp_Object rtl)
         {
           file = call1 (intern ("file-name-sans-extension"),
                        Ffile_name_nondirectory (file));
-          if (EQ (Fequal (file, rtl_name), Qt))
+          if (! NILP (Fequal (file, rtl_name)))
             {
               image = rtl_image;
               break;
@@ -4219,10 +4419,31 @@ xg_make_tool_item (FRAME_PTR f,
                    int i, int horiz, int text_image)
 {
   GtkToolItem *ti = gtk_tool_item_new ();
-  GtkWidget *vb = horiz ? gtk_hbox_new (FALSE, 0) : gtk_vbox_new (FALSE, 0);
+  GtkWidget *vb = gtk_box_new (horiz
+                               ? GTK_ORIENTATION_HORIZONTAL
+                               : GTK_ORIENTATION_VERTICAL,
+                               0);
   GtkWidget *wb = gtk_button_new ();
   /* The eventbox is here so we can have tooltips on disabled items.  */
   GtkWidget *weventbox = gtk_event_box_new ();
+#if GTK_CHECK_VERSION (3, 3, 6)
+  GtkCssProvider *css_prov = gtk_css_provider_new ();
+  GtkStyleContext *gsty;
+
+  gtk_css_provider_load_from_data (css_prov,
+                                  "GtkEventBox {"
+                                  "    background-color: transparent;"
+                                  "}",
+                                  -1, NULL);
+
+  gsty = gtk_widget_get_style_context (weventbox);
+  gtk_style_context_add_provider (gsty,
+                                 GTK_STYLE_PROVIDER (css_prov),
+                                 GTK_STYLE_PROVIDER_PRIORITY_USER);
+  g_object_unref (css_prov);
+#endif
+
+  gtk_box_set_homogeneous (GTK_BOX (vb), FALSE);
 
   if (wimage && !text_image)
     gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
@@ -4262,7 +4483,7 @@ xg_make_tool_item (FRAME_PTR f,
 #endif
       gtk_tool_item_set_homogeneous (ti, FALSE);
 
-      /* Callback to save modifyer mask (Shift/Control, etc).  GTK makes
+      /* Callback to save modifier mask (Shift/Control, etc).  GTK makes
          no distinction based on modifiers in the activate callback,
          so we have to do it ourselves.  */
       g_signal_connect (wb, "button-release-event",
@@ -4275,7 +4496,6 @@ xg_make_tool_item (FRAME_PTR f,
          rather than the GtkButton specific signals "enter" and
          "leave", so we can have only one callback.  The event
          will tell us what kind of event it is.  */
-      /* The EMACS_INT cast avoids a warning. */
       g_signal_connect (G_OBJECT (weventbox),
                         "enter-notify-event",
                         G_CALLBACK (xg_tool_bar_help_callback),
@@ -4291,6 +4511,24 @@ xg_make_tool_item (FRAME_PTR f,
   return ti;
 }
 
+static int
+is_box_type (GtkWidget *vb, int is_horizontal)
+{
+#ifdef HAVE_GTK3
+  int ret = 0;
+  if (GTK_IS_BOX (vb))
+    {
+      GtkOrientation ori = gtk_orientable_get_orientation (GTK_ORIENTABLE (vb));
+      ret = (ori == GTK_ORIENTATION_HORIZONTAL && is_horizontal)
+        || (ori == GTK_ORIENTATION_VERTICAL && ! is_horizontal);
+    }
+  return ret;
+#else
+  return is_horizontal ? GTK_IS_VBOX (vb) : GTK_IS_HBOX (vb);
+#endif
+}
+
+
 static int
 xg_tool_item_stale_p (GtkWidget *wbutton, const char *stock_name,
                      const char *icon_name, const struct image *img,
@@ -4319,14 +4557,14 @@ xg_tool_item_stale_p (GtkWidget *wbutton, const char *stock_name,
   else if (wimage)
     {
       gpointer gold_img = g_object_get_data (G_OBJECT (wimage),
-                                           XG_TOOL_BAR_IMAGE_DATA);
+                                             XG_TOOL_BAR_IMAGE_DATA);
       Pixmap old_img = (Pixmap) gold_img;
       if (old_img != img->pixmap)
        return 1;
     }
 
   /* Check button configuration and label.  */
-  if ((horiz ? GTK_IS_VBOX (vb) : GTK_IS_HBOX (vb))
+  if (is_box_type (vb, horiz)
       || (label ? (wlbl == NULL) : (wlbl != NULL)))
     return 1;
 
@@ -4395,26 +4633,24 @@ update_frame_tool_bar (FRAME_PTR f)
   int pack_tool_bar = x->handlebox_widget == NULL;
   Lisp_Object style;
   int text_image, horiz;
+  struct xg_frame_tb_info *tbinfo;
 
   if (! FRAME_GTK_WIDGET (f))
     return;
 
   BLOCK_INPUT;
 
-  if (INTEGERP (Vtool_bar_button_margin)
-      && XINT (Vtool_bar_button_margin) > 0)
+  if (RANGED_INTEGERP (1, Vtool_bar_button_margin, INT_MAX))
     {
       hmargin = XFASTINT (Vtool_bar_button_margin);
       vmargin = XFASTINT (Vtool_bar_button_margin);
     }
   else if (CONSP (Vtool_bar_button_margin))
     {
-      if (INTEGERP (XCAR (Vtool_bar_button_margin))
-          && XINT (XCAR (Vtool_bar_button_margin)) > 0)
+      if (RANGED_INTEGERP (1, XCAR (Vtool_bar_button_margin), INT_MAX))
         hmargin = XFASTINT (XCAR (Vtool_bar_button_margin));
 
-      if (INTEGERP (XCDR (Vtool_bar_button_margin))
-          && XINT (XCDR (Vtool_bar_button_margin)) > 0)
+      if (RANGED_INTEGERP (1, XCDR (Vtool_bar_button_margin), INT_MAX))
         vmargin = XFASTINT (XCDR (Vtool_bar_button_margin));
     }
 
@@ -4432,6 +4668,29 @@ update_frame_tool_bar (FRAME_PTR f)
   dir = gtk_widget_get_direction (GTK_WIDGET (wtoolbar));
 
   style = Ftool_bar_get_system_style ();
+
+  /* Are we up to date? */
+  tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                              TB_INFO_KEY);
+
+  if (! NILP (tbinfo->last_tool_bar) && ! NILP (f->tool_bar_items)
+      && tbinfo->n_last_items == f->n_tool_bar_items
+      && tbinfo->hmargin == hmargin && tbinfo->vmargin == vmargin
+      && tbinfo->dir == dir
+      && ! NILP (Fequal (tbinfo->style, style))
+      && ! NILP (Fequal (tbinfo->last_tool_bar, f->tool_bar_items)))
+    {
+      UNBLOCK_INPUT;
+      return;
+    }
+
+  tbinfo->last_tool_bar = f->tool_bar_items;
+  tbinfo->n_last_items = f->n_tool_bar_items;
+  tbinfo->style = style;
+  tbinfo->hmargin = hmargin;
+  tbinfo->vmargin = vmargin;
+  tbinfo->dir = dir;
+
   text_image = EQ (style, Qtext_image_horiz);
   horiz = EQ (style, Qboth_horiz) || text_image;
 
@@ -4546,7 +4805,7 @@ update_frame_tool_bar (FRAME_PTR f)
                        ? TOOL_BAR_IMAGE_DISABLED_SELECTED
                        : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
 
-              xassert (ASIZE (image) >= idx);
+              eassert (ASIZE (image) >= idx);
               image = AREF (image, idx);
             }
           else
@@ -4645,6 +4904,7 @@ free_frame_tool_bar (FRAME_PTR f)
 
   if (x->toolbar_widget)
     {
+      struct xg_frame_tb_info *tbinfo;
       int is_packed = x->handlebox_widget != 0;
       BLOCK_INPUT;
       /* We may have created the toolbar_widget in xg_create_tool_bar, but
@@ -4666,6 +4926,16 @@ free_frame_tool_bar (FRAME_PTR f)
       FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
       FRAME_TOOLBAR_LEFT_WIDTH (f) = FRAME_TOOLBAR_RIGHT_WIDTH (f) = 0;
 
+      tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                                  TB_INFO_KEY);
+      if (tbinfo)
+        {
+          xfree (tbinfo);
+          g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
+                             TB_INFO_KEY,
+                             NULL);
+        }
+
       xg_height_or_width_changed (f);
 
       UNBLOCK_INPUT;
@@ -4706,6 +4976,7 @@ void
 xg_initialize (void)
 {
   GtkBindingSet *binding_set;
+  GtkSettings *settings;
 
 #if HAVE_XFT
   /* Work around a bug with corrupted data if libXft gets unloaded.  This way
@@ -4722,17 +4993,19 @@ xg_initialize (void)
   id_to_widget.max_size = id_to_widget.used = 0;
   id_to_widget.widgets = 0;
 
+  settings = gtk_settings_get_for_screen (gdk_display_get_default_screen
+                                          (gdk_display_get_default ()));
   /* Remove F10 as a menu accelerator, it does not mix well with Emacs key
      bindings.  It doesn't seem to be any way to remove properties,
      so we set it to VoidSymbol which in X means "no key".  */
-  gtk_settings_set_string_property (gtk_settings_get_default (),
+  gtk_settings_set_string_property (settings,
                                     "gtk-menu-bar-accel",
                                     "VoidSymbol",
                                     EMACS_CLASS);
 
   /* Make GTK text input widgets use Emacs style keybindings.  This is
      Emacs after all.  */
-  gtk_settings_set_string_property (gtk_settings_get_default (),
+  gtk_settings_set_string_property (settings,
                                     "gtk-key-theme-name",
                                     "Emacs",
                                     EMACS_CLASS);
@@ -4749,6 +5022,8 @@ xg_initialize (void)
   gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
                                 "cancel", 0);
   update_theme_scrollbar_width ();
+
+  x_last_font_name = NULL;
 }
 
 #endif /* USE_GTK */