Fix resizing for X and non-Gtk+ build. Bug #5848.
[bpt/emacs.git] / src / gtkutil.c
index d3a0c7e..2764382 100644 (file)
@@ -1,13 +1,13 @@
 /* Functions for creating and updating GTK widgets.
-   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
      Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
-GNU Emacs is free software; you can redistribute it and/or modify
+GNU Emacs is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3, or (at your option)
-any later version.
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -15,22 +15,20 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA.  */
+along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
-#include "config.h"
+#include <config.h>
 
 #ifdef USE_GTK
 #include <string.h>
 #include <signal.h>
 #include <stdio.h>
+#include <setjmp.h>
 #include "lisp.h"
 #include "xterm.h"
 #include "blockinput.h"
 #include "syssignal.h"
 #include "window.h"
-#include "atimer.h"
 #include "gtkutil.h"
 #include "termhooks.h"
 #include "keyboard.h"
@@ -38,6 +36,9 @@ Boston, MA 02110-1301, USA.  */
 #include "coding.h"
 #include <gdk/gdkkeysyms.h>
 
+#ifdef HAVE_XFT
+#include <X11/Xft/Xft.h>
+#endif
 
 #define FRAME_TOTAL_PIXEL_HEIGHT(f) \
   (FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
@@ -52,22 +53,11 @@ Boston, MA 02110-1301, USA.  */
 
 #ifdef HAVE_GTK_MULTIDISPLAY
 
-/* Gtk does not work well without any display open.  Emacs may close
-   all its displays.  In that case, keep a display around just for
-   the purpose of having one.  */
+/* Keep track of the default display, or NULL if there is none.  Emacs
+   may close all its displays.  */
 
 static GdkDisplay *gdpy_def;
 
-
-/* Return the GdkDisplay that corresponds to the X display DPY.  */
-
-static GdkDisplay *
-xg_get_gdk_display (dpy)
-     Display *dpy;
-{
-  return gdk_x11_lookup_xdisplay (dpy);
-}
-
 /* When the GTK widget W is to be created on a display for F that
    is not the default display, set the display for W.
    W can be a GtkMenu or a GtkWindow widget.  */
@@ -120,8 +110,14 @@ xg_display_open (display_name, dpy)
   GdkDisplay *gdpy;
 
   gdpy = gdk_display_open (display_name);
-  *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
+  if (!gdpy_def && gdpy)
+    {
+      gdpy_def = gdpy;
+      gdk_display_manager_set_default_display (gdk_display_manager_get (),
+                                              gdpy);
+    }
 
+  *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
   return gdpy != NULL;
 
 #else /* not HAVE_GTK_MULTIDISPLAY */
@@ -139,40 +135,30 @@ xg_display_close (Display *dpy)
 #ifdef HAVE_GTK_MULTIDISPLAY
   GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
 
-  /* If this is the default display, we must change it before calling
-     dispose, otherwise it will crash on some Gtk+ versions.  */
+  /* If this is the default display, try to change it before closing.
+     If there is no other display to use, gdpy_def is set to NULL, and
+     the next call to xg_display_open resets the default display.  */
   if (gdk_display_get_default () == gdpy)
     {
       struct x_display_info *dpyinfo;
-      Display *new_dpy = 0;
-      GdkDisplay *gdpy_new;
+      GdkDisplay *gdpy_new = NULL;
 
       /* Find another display.  */
       for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
         if (dpyinfo->display != dpy)
           {
-            new_dpy = dpyinfo->display;
+           gdpy_new = gdk_x11_lookup_xdisplay (dpyinfo->display);
+           gdk_display_manager_set_default_display (gdk_display_manager_get (),
+                                                    gdpy_new);
             break;
           }
-
-      if (new_dpy)
-        gdpy_new = gdk_x11_lookup_xdisplay (new_dpy);
-      else
-        {
-          if (!gdpy_def)
-            gdpy_def = gdk_display_open (gdk_display_get_name (gdpy));
-          gdpy_new = gdpy_def;
-        }
-
-      gdk_display_manager_set_default_display (gdk_display_manager_get (),
-                                               gdpy_new);
+      gdpy_def = gdpy_new;
     }
 
-  /* GTK 2.2-2.8 has a bug that makes gdk_display_close crash (bug
-     http://bugzilla.gnome.org/show_bug.cgi?id=85715).  This way
-     we can continue running, but there will be memory leaks.  */
-
 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 10
+  /* GTK 2.2-2.8 has a bug that makes gdk_display_close crash (bug
+     http://bugzilla.gnome.org/show_bug.cgi?id=85715).  This way we
+     can continue running, but there will be memory leaks.  */
   g_object_run_dispose (G_OBJECT (gdpy));
 #else
   /* This seems to be fixed in GTK 2.10. */
@@ -185,11 +171,6 @@ xg_display_close (Display *dpy)
 /***********************************************************************
                       Utility functions
  ***********************************************************************/
-/* The timer for scroll bar repetition and menu bar timeouts.
-   NULL if no timer is started.  */
-static struct atimer *xg_timer;
-
-
 /* The next two variables and functions are taken from lwlib.  */
 static widget_value *widget_value_free_list;
 static int malloc_cpt;
@@ -232,7 +213,7 @@ free_widget_value (wv)
     {
       /* When the number of already allocated cells is too big,
         We free it.  */
-      free (wv);
+      xfree (wv);
       malloc_cpt--;
     }
   else
@@ -262,10 +243,8 @@ xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap)
      GdkPixmap *gmask;
      GdkColormap *cmap;
 {
-  int x, y, width, height, rowstride, mask_rowstride;
+  int width, height;
   GdkPixbuf *icon_buf, *tmp_buf;
-  guchar *pixels;
-  guchar *mask_pixels;
 
   gdk_drawable_get_size (gpix, &width, &height);
   tmp_buf = gdk_pixbuf_get_from_drawable (NULL, gpix, cmap,
@@ -313,7 +292,7 @@ xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap)
 }
 
 static Lisp_Object
-file_for_image(image)
+file_for_image (image)
      Lisp_Object image;
 {
   Lisp_Object specified_file = Qnil;
@@ -430,58 +409,6 @@ xg_set_cursor (w, cursor)
     gdk_window_set_cursor (GDK_WINDOW (children->data), cursor);
 }
 
-/*  Timer function called when a timeout occurs for xg_timer.
-    This function processes all GTK events in a recursive event loop.
-    This is done because GTK timer events are not seen by Emacs event
-    detection, Emacs only looks for X events.  When a scroll bar has the
-    pointer (detected by button press/release events below) an Emacs
-    timer is started, and this function can then check if the GTK timer
-    has expired by calling the GTK event loop.
-    Also, when a menu is active, it has a small timeout before it
-    pops down the sub menu under it.  */
-
-static void
-xg_process_timeouts (timer)
-     struct atimer *timer;
-{
-  BLOCK_INPUT;
-  /* Ideally we would like to just handle timer events, like the Xt version
-     of this does in xterm.c, but there is no such feature in GTK.  */
-  while (gtk_events_pending ())
-    gtk_main_iteration ();
-  UNBLOCK_INPUT;
-}
-
-/* Start the xg_timer with an interval of 0.1 seconds, if not already started.
-   xg_process_timeouts is called when the timer expires.  The timer
-   started is continuous, i.e. runs until xg_stop_timer is called.  */
-
-static void
-xg_start_timer ()
-{
-  if (! xg_timer)
-    {
-      EMACS_TIME interval;
-      EMACS_SET_SECS_USECS (interval, 0, 100000);
-      xg_timer = start_atimer (ATIMER_CONTINUOUS,
-                               interval,
-                               xg_process_timeouts,
-                               0);
-    }
-}
-
-/* Stop the xg_timer if started.  */
-
-static void
-xg_stop_timer ()
-{
-  if (xg_timer)
-    {
-      cancel_atimer (xg_timer);
-      xg_timer = 0;
-    }
-}
-
 /* Insert NODE into linked LIST.  */
 
 static void
@@ -530,7 +457,7 @@ get_utf8_string (str)
   if (!g_utf8_validate (str, -1, NULL))
     utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
 
-  if (!utf8_str) 
+  if (!utf8_str)
     {
       /* Probably some control characters in str.  Escape them. */
       size_t nr_bad = 0;
@@ -550,7 +477,7 @@ get_utf8_string (str)
           error = NULL;
         }
 
-      if (error) 
+      if (error)
         {
           g_error_free (error);
           error = NULL;
@@ -573,12 +500,12 @@ get_utf8_string (str)
           error = NULL;
         }
 
-      if (cp) 
+      if (cp)
         {
           strcat (utf8_str, cp);
           g_free (cp);
         }
-      if (error) 
+      if (error)
         {
           g_error_free (error);
           error = NULL;
@@ -603,95 +530,111 @@ xg_set_geometry (f)
      FRAME_PTR f;
 {
   if (f->size_hint_flags & USPosition)
-  {
-    int left = f->left_pos;
-    int xneg = f->size_hint_flags & XNegative;
-    int top = f->top_pos;
-    int yneg = f->size_hint_flags & YNegative;
-    char geom_str[32];
-
-    if (xneg)
-      left = -left;
-    if (yneg)
-      top = -top;
-
-    sprintf (geom_str, "=%dx%d%c%d%c%d",
-             FRAME_PIXEL_WIDTH (f),
-             FRAME_TOTAL_PIXEL_HEIGHT (f),
-             (xneg ? '-' : '+'), left,
-             (yneg ? '-' : '+'), top);
-
-    if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                                    geom_str))
-      fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
-  } else if (f->size_hint_flags & PPosition) {
+    {
+      int left = f->left_pos;
+      int xneg = f->size_hint_flags & XNegative;
+      int top = f->top_pos;
+      int yneg = f->size_hint_flags & YNegative;
+      char geom_str[32];
+
+      if (xneg)
+        left = -left;
+      if (yneg)
+        top = -top;
+
+      sprintf (geom_str, "=%dx%d%c%d%c%d",
+               FRAME_PIXEL_WIDTH (f),
+               FRAME_TOTAL_PIXEL_HEIGHT (f),
+               (xneg ? '-' : '+'), left,
+               (yneg ? '-' : '+'), top);
+
+      if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                                      geom_str))
+        fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
+    }
+  else if (f->size_hint_flags & PPosition)
     gtk_window_move (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
                      f->left_pos, f->top_pos);
-  }
 }
 
-
-/* Resize the outer window of frame F after chainging the height.
-   This happend when the menu bar or the tool bar is added or removed.
-   COLUMNS/ROWS is the size the edit area shall have after the resize.  */
+/* Clear under internal border if any.  As we use a mix of Gtk+ and X calls
+   and use a GtkFixed widget, this doesn't happen automatically.  */
 
 static void
-xg_resize_outer_widget (f, columns, rows)
+xg_clear_under_internal_border (f)
      FRAME_PTR f;
-     int columns;
-     int rows;
 {
-  /* If we are not mapped yet, set geometry once again, as window
-     height now have changed.  */
-  if (! GTK_WIDGET_MAPPED (FRAME_GTK_OUTER_WIDGET (f)))
-    xg_set_geometry (f);
-
-  xg_frame_set_char_size (f, columns, rows);
-  gdk_window_process_all_updates ();
+  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);
+    }
 }
 
-/* Function to handle resize of our widgets.  Since Emacs has some layouts
-   that does not fit well with GTK standard containers, we do most layout
-   manually.
+/* Function to handle resize of our frame.  As we have a Gtk+ tool bar
+   and a Gtk+ menu bar, we get resize events for the edit part of the
+   frame only.  We let Gtk+ deal with the Gtk+ parts.
    F is the frame to resize.
    PIXELWIDTH, PIXELHEIGHT is the new size in pixels.  */
 
 void
-xg_resize_widgets (f, pixelwidth, pixelheight)
+xg_frame_resized (f, pixelwidth, pixelheight)
      FRAME_PTR f;
      int pixelwidth, pixelheight;
 {
-  int mbheight = FRAME_MENUBAR_HEIGHT (f);
-  int tbheight = FRAME_TOOLBAR_HEIGHT (f);
-  int rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, (pixelheight
-                                                  - mbheight - tbheight));
-  int columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
-
-  if (FRAME_GTK_WIDGET (f)
-      && (columns != FRAME_COLS (f)
-         || rows != FRAME_LINES (f)
-          || pixelwidth != FRAME_PIXEL_WIDTH (f)
-         || pixelheight != FRAME_PIXEL_HEIGHT (f)))
-    {
-      struct x_output *x = f->output_data.x;
-      GtkAllocation all;
+  int rows, columns;
 
-      all.y = mbheight + tbheight;
-      all.x = 0;
+  if (pixelwidth == -1 && pixelheight == -1)
+    {
+      if (FRAME_GTK_WIDGET (f) && GTK_WIDGET_MAPPED (FRAME_GTK_WIDGET (f)))
+          gdk_window_get_geometry (FRAME_GTK_WIDGET (f)->window, 0, 0,
+                                   &pixelwidth, &pixelheight, 0);
+      else return;
+    }
+  
 
-      all.width = pixelwidth;
-      all.height = pixelheight - mbheight - tbheight;
+  rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelheight);
+  columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
 
-      gtk_widget_size_allocate (x->edit_widget, &all);
+  if (columns != FRAME_COLS (f)
+      || rows != FRAME_LINES (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);
       SET_FRAME_GARBAGED (f);
       cancel_mouse_face (f);
     }
 }
 
-
-/* Update our widget size to be COLS/ROWS characters for frame F.  */
+/* Resize the outer window of frame F after chainging the height.
+   COLUMNS/ROWS is the size the edit area shall have after the resize.  */
 
 void
 xg_frame_set_char_size (f, cols, rows)
@@ -703,6 +646,9 @@ xg_frame_set_char_size (f, cols, rows)
     + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
   int pixelwidth;
 
+  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
@@ -717,14 +663,52 @@ xg_frame_set_char_size (f, cols, rows)
      after calculating that value.  */
   pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols);
 
+
+  /* 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);
-  xg_resize_widgets (f, pixelwidth, pixelheight);
   x_wm_set_size_hint (f, 0, 0);
+
   SET_FRAME_GARBAGED (f);
   cancel_mouse_face (f);
+
+  /* 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.
+     The best we can do is try to sync, so lisp code sees the updated
+     size as fast as possible.
+     For unmapped windows, we can set rows/cols.  When
+     the frame is mapped again we will (hopefully) get the correct size.  */
+  if (f->async_visible)
+    {
+      /* Must call this to flush out events */
+      (void)gtk_events_pending ();
+      gdk_flush ();
+      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;
+     }
+}
+
+/* Handle height changes (i.e. add/remove menu/toolbar).
+   The policy is to keep the number of editable lines.  */
+
+static void
+xg_height_changed (f)
+     FRAME_PTR f;
+{
+  gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                     FRAME_PIXEL_WIDTH (f), FRAME_TOTAL_PIXEL_HEIGHT (f));
+  f->output_data.x->hint_flags = 0;
+  x_wm_set_size_hint (f, 0, 0);
 }
 
 /* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
@@ -781,7 +765,6 @@ xg_create_frame_widgets (f)
   GtkWidget *wfixed;
   GdkColor bg;
   GtkRcStyle *style;
-  int i;
   char *title = 0;
 
   BLOCK_INPUT;
@@ -823,26 +806,12 @@ xg_create_frame_widgets (f)
 
   gtk_fixed_set_has_window (GTK_FIXED (wfixed), TRUE);
 
-  gtk_widget_set_size_request (wfixed, FRAME_PIXEL_WIDTH (f),
-                               FRAME_PIXEL_HEIGHT (f));
-
   gtk_container_add (GTK_CONTAINER (wtop), wvbox);
   gtk_box_pack_end (GTK_BOX (wvbox), wfixed, TRUE, TRUE, 0);
 
   if (FRAME_EXTERNAL_TOOL_BAR (f))
     update_frame_tool_bar (f);
 
-  /* The tool bar is created but first there are no items in it.
-     This causes it to be zero height.  Later items are added, but then
-     the frame is already mapped, so there is a "jumping" resize.
-     This makes geometry handling difficult, for example -0-0 will end
-     up in the wrong place as tool bar height has not been taken into account.
-     So we cheat a bit by setting a height that is what it will have
-     later on when tool bar items are added.  */
-  if (FRAME_EXTERNAL_TOOL_BAR (f) && f->n_tool_bar_items == 0)
-    FRAME_TOOLBAR_HEIGHT (f) = 38;
-
-
   /* We don't want this widget double buffered, because we draw on it
      with regular X drawing primitives, so from a GTK/GDK point of
      view, the widget is totally blank.  When an expose comes, this
@@ -850,9 +819,6 @@ xg_create_frame_widgets (f)
      a lot, so we turn off double buffering.  */
   gtk_widget_set_double_buffered (wfixed, FALSE);
 
-  /* GTK documents says use gtk_window_set_resizable.  But then a user
-     can't shrink the window from its starting size.  */
-  gtk_window_set_policy (GTK_WINDOW (wtop), TRUE, TRUE, TRUE);
   gtk_window_set_wmclass (GTK_WINDOW (wtop),
                           SSDATA (Vx_resource_name),
                           SSDATA (Vx_resource_class));
@@ -866,6 +832,8 @@ xg_create_frame_widgets (f)
      and specify it.
      GTK will itself handle calculating the real position this way.  */
   xg_set_geometry (f);
+  f->win_gravity
+    = gtk_window_get_gravity (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
 
   gtk_widget_add_events (wfixed,
                          GDK_POINTER_MOTION_MASK
@@ -899,8 +867,10 @@ xg_create_frame_widgets (f)
   gtk_widget_modify_style (wfixed, style);
 
   /* GTK does not set any border, and they look bad with GTK.  */
+  /* That they look bad is no excuse for imposing this here.  --Stef
+     It should be done by providing the proper default in Fx_create_Frame.
   f->border_width = 0;
-  f->internal_border_width = 0;
+  f->internal_border_width = 0; */
 
   UNBLOCK_INPUT;
 
@@ -919,95 +889,99 @@ x_wm_set_size_hint (f, flags, user_position)
      long flags;
      int user_position;
 {
-  if (FRAME_GTK_OUTER_WIDGET (f))
-  {
-    /* Must use GTK routines here, otherwise GTK resets the size hints
-       to its own defaults.  */
-    GdkGeometry size_hints;
-    gint hint_flags = 0;
-    int base_width, base_height;
-    int min_rows = 0, min_cols = 0;
-    int win_gravity = f->win_gravity;
-
-    if (flags)
-      {
-        memset (&size_hints, 0, sizeof (size_hints));
-        f->output_data.x->size_hints = size_hints;
-        f->output_data.x->hint_flags = hint_flags;
-      }
-     else
-       flags = f->size_hint_flags;
-
-    size_hints = f->output_data.x->size_hints;
-    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);
-
-    hint_flags |= GDK_HINT_BASE_SIZE;
-    base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0);
-    base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0)
-      + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
-
-    check_frame_size (f, &min_rows, &min_cols);
-
-    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;
-
-
-    /* These currently have a one to one mapping with the X values, but I
-       don't think we should rely on that.  */
-    hint_flags |= GDK_HINT_WIN_GRAVITY;
-    size_hints.win_gravity = 0;
-    if (win_gravity == NorthWestGravity)
-      size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
-    else if (win_gravity == NorthGravity)
-      size_hints.win_gravity = GDK_GRAVITY_NORTH;
-    else if (win_gravity == NorthEastGravity)
-      size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
-    else if (win_gravity == WestGravity)
-      size_hints.win_gravity = GDK_GRAVITY_WEST;
-    else if (win_gravity == CenterGravity)
-      size_hints.win_gravity = GDK_GRAVITY_CENTER;
-    else if (win_gravity == EastGravity)
-      size_hints.win_gravity = GDK_GRAVITY_EAST;
-    else if (win_gravity == SouthWestGravity)
-      size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
-    else if (win_gravity == SouthGravity)
-      size_hints.win_gravity = GDK_GRAVITY_SOUTH;
-    else if (win_gravity == SouthEastGravity)
-      size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
-    else if (win_gravity == StaticGravity)
-      size_hints.win_gravity = GDK_GRAVITY_STATIC;
-
-    if (flags & PPosition) hint_flags |= GDK_HINT_POS;
-    if (flags & USPosition) hint_flags |= GDK_HINT_USER_POS;
-    if (flags & USSize) hint_flags |= GDK_HINT_USER_SIZE;
-
-    if (user_position)
-      {
-        hint_flags &= ~GDK_HINT_POS;
-        hint_flags |= GDK_HINT_USER_POS;
-      }
+  /* Must use GTK routines here, otherwise GTK resets the size hints
+     to its own defaults.  */
+  GdkGeometry size_hints;
+  gint hint_flags = 0;
+  int base_width, base_height;
+  int min_rows = 0, min_cols = 0;
+  int win_gravity = f->win_gravity;
+
+  /* Don't set size hints during initialization; that apparently leads
+     to a race condition.  See the thread at
+     http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00033.html  */
+  if (NILP (Vafter_init_time) || !FRAME_GTK_OUTER_WIDGET (f))
+    return;
+
+  if (flags)
+    {
+      memset (&size_hints, 0, sizeof (size_hints));
+      f->output_data.x->size_hints = size_hints;
+      f->output_data.x->hint_flags = hint_flags;
+    }
+  else
+    flags = f->size_hint_flags;
 
-    BLOCK_INPUT;
+  size_hints = f->output_data.x->size_hints;
+  hint_flags = f->output_data.x->hint_flags;
 
-    gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                                   FRAME_GTK_OUTER_WIDGET (f),
-                                   &size_hints,
-                                   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);
 
-    f->output_data.x->size_hints = size_hints;
-    f->output_data.x->hint_flags = hint_flags;
-    UNBLOCK_INPUT;
-  }
+  hint_flags |= GDK_HINT_BASE_SIZE;
+  base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0);
+  base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0)
+    + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
+
+  check_frame_size (f, &min_rows, &min_cols);
+
+  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;
+
+  /* These currently have a one to one mapping with the X values, but I
+     don't think we should rely on that.  */
+  hint_flags |= GDK_HINT_WIN_GRAVITY;
+  size_hints.win_gravity = 0;
+  if (win_gravity == NorthWestGravity)
+    size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
+  else if (win_gravity == NorthGravity)
+    size_hints.win_gravity = GDK_GRAVITY_NORTH;
+  else if (win_gravity == NorthEastGravity)
+    size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
+  else if (win_gravity == WestGravity)
+    size_hints.win_gravity = GDK_GRAVITY_WEST;
+  else if (win_gravity == CenterGravity)
+    size_hints.win_gravity = GDK_GRAVITY_CENTER;
+  else if (win_gravity == EastGravity)
+    size_hints.win_gravity = GDK_GRAVITY_EAST;
+  else if (win_gravity == SouthWestGravity)
+    size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
+  else if (win_gravity == SouthGravity)
+    size_hints.win_gravity = GDK_GRAVITY_SOUTH;
+  else if (win_gravity == SouthEastGravity)
+    size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
+  else if (win_gravity == StaticGravity)
+    size_hints.win_gravity = GDK_GRAVITY_STATIC;
+
+  if (flags & PPosition) hint_flags |= GDK_HINT_POS;
+  if (flags & USPosition) hint_flags |= GDK_HINT_USER_POS;
+  if (flags & USSize) hint_flags |= GDK_HINT_USER_SIZE;
+
+  if (user_position)
+    {
+      hint_flags &= ~GDK_HINT_POS;
+      hint_flags |= GDK_HINT_USER_POS;
+    }
+
+  if (hint_flags != f->output_data.x->hint_flags
+      || memcmp (&size_hints,
+                &f->output_data.x->size_hints,
+                sizeof (size_hints)) != 0)
+    {
+      BLOCK_INPUT;
+      gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                                    NULL, &size_hints, hint_flags);
+      f->output_data.x->size_hints = size_hints;
+      f->output_data.x->hint_flags = hint_flags;
+      UNBLOCK_INPUT;
+    }
 }
 
 /* Change background color of a frame.
-   Since GTK uses the background colour to clear the window, we must
+   Since GTK uses the background color to clear the window, we must
    keep the GTK and X colors in sync.
    F is the frame to change,
    BG is the pixel value to change to.  */
@@ -1221,6 +1195,119 @@ create_dialog (wv, select_cb, deactivate_cb)
   return wdialog;
 }
 
+struct xg_dialog_data
+{
+  GMainLoop *loop;
+  int response;
+  GtkWidget *w;
+  guint timerid;
+};
+
+/* Function that is called when the file or font dialogs pop down.
+   W is the dialog widget, RESPONSE is the response code.
+   USER_DATA is what we passed in to g_signal_connect.  */
+
+static void
+xg_dialog_response_cb (w,
+                       response,
+                       user_data)
+     GtkDialog *w;
+     gint response;
+     gpointer user_data;
+{
+  struct xg_dialog_data *dd = (struct xg_dialog_data *)user_data;
+  dd->response = response;
+  g_main_loop_quit (dd->loop);
+}
+
+
+/*  Destroy the dialog.  This makes it pop down.  */
+
+static Lisp_Object
+pop_down_dialog (arg)
+     Lisp_Object arg;
+{
+  struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
+  struct xg_dialog_data *dd = (struct xg_dialog_data *) p->pointer;
+
+  BLOCK_INPUT;
+  if (dd->w) gtk_widget_destroy (dd->w);
+  if (dd->timerid != 0) g_source_remove (dd->timerid);
+
+  g_main_loop_quit (dd->loop);
+  g_main_loop_unref (dd->loop);
+  
+  UNBLOCK_INPUT;
+
+  return Qnil;
+}
+
+/* If there are any emacs timers pending, add a timeout to main loop in DATA.
+    We pass in DATA as gpointer* so we can use this as a callback.  */
+
+static gboolean
+xg_maybe_add_timer (data)
+     gpointer data;
+{
+  struct xg_dialog_data *dd = (struct xg_dialog_data *) data;
+  EMACS_TIME next_time = timer_check (1);
+  long secs = EMACS_SECS (next_time);
+  long usecs = EMACS_USECS (next_time);
+
+  dd->timerid = 0;
+
+  if (secs >= 0 && usecs >= 0 && secs < ((guint)-1)/1000)
+    {
+      dd->timerid = g_timeout_add (secs * 1000 + usecs/1000,
+                                   xg_maybe_add_timer,
+                                   dd);
+    }
+  return FALSE;
+}
+
+     
+/* Pops up a modal dialog W and waits for response.
+   We don't use gtk_dialog_run because we want to process emacs timers.
+   The dialog W is not destroyed when this function returns.  */
+
+static int
+xg_dialog_run (f, w)
+     FRAME_PTR f;
+     GtkWidget *w;
+
+{
+  int count = SPECPDL_INDEX ();
+  struct xg_dialog_data dd;
+
+  xg_set_screen (w, f);
+  gtk_window_set_transient_for (GTK_WINDOW (w),
+                                GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
+  gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
+  gtk_window_set_modal (GTK_WINDOW (w), TRUE);
+
+  dd.loop = g_main_loop_new (NULL, FALSE);
+  dd.response = GTK_RESPONSE_CANCEL;
+  dd.w = w;
+  dd.timerid = 0;
+
+  g_signal_connect (G_OBJECT (w),
+                    "response",
+                    G_CALLBACK (xg_dialog_response_cb),
+                    &dd);
+  /* Don't destroy the widget if closed by the window manager close button.  */
+  g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL);
+  gtk_widget_show (w);
+
+  record_unwind_protect (pop_down_dialog, make_save_value (&dd, 0));
+
+  (void) xg_maybe_add_timer (&dd);
+  g_main_loop_run (dd.loop);
+  
+  dd.w = 0;
+  unbind_to (count, Qnil);
+
+  return dd.response;
+}
 
 \f
 /***********************************************************************
@@ -1247,36 +1334,6 @@ xg_uses_old_file_dialog ()
 }
 
 
-/* Function that is called when the file dialog pops down.
-   W is the dialog widget, RESPONSE is the response code.
-   USER_DATA is what we passed in to g_signal_connect (pointer to int).  */
-
-static void
-xg_file_response_cb (w,
-                     response,
-                     user_data)
-     GtkDialog *w;
-     gint response;
-     gpointer user_data;
-{
-  int *ptr = (int *) user_data;
-  *ptr = response;
-}
-
-
-/*  Destroy the dialog.  This makes it pop down.  */
-
-static Lisp_Object
-pop_down_file_dialog (arg)
-     Lisp_Object arg;
-{
-  struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
-  BLOCK_INPUT;
-  gtk_widget_destroy (GTK_WIDGET (p->pointer));
-  UNBLOCK_INPUT;
-  return Qnil;
-}
-
 typedef char * (*xg_get_file_func) P_ ((GtkWidget *));
 
 #ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW
@@ -1322,7 +1379,6 @@ xg_toggle_notify_cb (gobject, arg1, user_data)
 
   if (strcmp (arg1->name, "show-hidden") == 0)
     {
-      GtkFileChooser *dialog = GTK_FILE_CHOOSER (gobject);
       GtkWidget *wtoggle = GTK_WIDGET (user_data);
       gboolean visible, toggle_on;
 
@@ -1388,8 +1444,8 @@ xg_get_file_with_chooser (f, prompt, default_filename,
   wbox = gtk_vbox_new (FALSE, 0);
   gtk_widget_show (wbox);
   wtoggle = gtk_check_button_new_with_label ("Show hidden files.");
-  
-  if (x_gtk_show_hidden_files) 
+
+  if (x_gtk_show_hidden_files)
     {
       g_object_set (G_OBJECT (filewin), "show-hidden", TRUE, NULL);
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), TRUE);
@@ -1410,7 +1466,7 @@ xg_get_file_with_chooser (f, prompt, default_filename,
       strcat (message, "\nIf you don't like this file selector, use the "
               "corresponding\nkey binding or customize "
               "use-file-dialog to turn it off.");
-    
+
       wmessage = gtk_label_new (message);
       gtk_widget_show (wmessage);
     }
@@ -1433,7 +1489,7 @@ xg_get_file_with_chooser (f, prompt, default_filename,
          an absolute name starting with /.  */
       if (default_filename[0] != '/')
         file = Fexpand_file_name (file, Qnil);
-      
+
       utf8_filename = SSDATA (ENCODE_UTF_8 (file));
       if (! NILP (Ffile_directory_p (file)))
         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filewin),
@@ -1534,7 +1590,6 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p)
      int mustmatch_p, only_dir_p;
 {
   GtkWidget *w = 0;
-  int count = SPECPDL_INDEX ();
   char *fn = 0;
   int filesel_done = 0;
   xg_get_file_func func;
@@ -1568,29 +1623,9 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p)
 
 #endif /* HAVE_GTK_FILE_BOTH */
 
-  xg_set_screen (w, f);
   gtk_widget_set_name (w, "emacs-filedialog");
-  gtk_window_set_transient_for (GTK_WINDOW (w),
-                                GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
-  gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
-  gtk_window_set_modal (GTK_WINDOW (w), TRUE);
-
-  g_signal_connect (G_OBJECT (w),
-                    "response",
-                    G_CALLBACK (xg_file_response_cb),
-                    &filesel_done);
 
-  /* Don't destroy the widget if closed by the window manager close button.  */
-  g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL);
-
-  gtk_widget_show (w);
-
-  record_unwind_protect (pop_down_file_dialog, make_save_value (w, 0));
-  while (! filesel_done)
-    {
-      x_menu_wait_for_event (0);
-      gtk_main_iteration ();
-    }
+  filesel_done = xg_dialog_run (f, w);
 
 #if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
   sigunblock (sigmask (__SIGRTMIN));
@@ -1599,19 +1634,65 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p)
   if (filesel_done == GTK_RESPONSE_OK)
     fn = (*func) (w);
 
-  unbind_to (count, Qnil);
-
+  gtk_widget_destroy (w);
   return fn;
 }
 
+#ifdef HAVE_FREETYPE
+/* 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:
+
+   `FAMILY [VALUE1 VALUE2] SIZE'
+
+   This can be parsed using font_parse_fcname in font.c.
+   DEFAULT_NAME, if non-zero, is the default font name.  */
+
+char *
+xg_get_font_name (f, default_name)
+     FRAME_PTR f;
+     char *default_name;
+{
+  GtkWidget *w;
+  char *fontname = NULL;
+  int done = 0;
+
+#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
+  sigblock (sigmask (__SIGRTMIN));
+#endif /* HAVE_GTK_AND_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);
+
+  gtk_widget_set_name (w, "emacs-fontdialog");
+
+  done = xg_dialog_run (f, w);
+
+#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
+  sigunblock (sigmask (__SIGRTMIN));
+#endif
+
+  if (done == GTK_RESPONSE_OK)
+    fontname = gtk_font_selection_dialog_get_font_name
+      (GTK_FONT_SELECTION_DIALOG (w));
+
+  gtk_widget_destroy (w);
+  return fontname;
+}
+#endif /* HAVE_FREETYPE */
+
+
 \f
 /***********************************************************************
                        Menu functions.
  ***********************************************************************/
 
-/* The name of menu items that can be used for citomization.  Since GTK
+/* The name of menu items that can be used for customization.  Since GTK
    RC files are very crude and primitive, we have to set this on all
-   menu item names so a user can easily cutomize menu items.  */
+   menu item names so a user can easily customize menu items.  */
 
 #define MENU_ITEM_NAME "emacs-menuitem"
 
@@ -1784,29 +1865,6 @@ menu_destroy_callback (w, client_data)
   unref_cl_data ((xg_menu_cb_data*) client_data);
 }
 
-/* Callback called when a menu does a grab or ungrab.  That means the
-   menu has been activated or deactivated.
-   Used to start a timer so the small timeout the menus in GTK uses before
-   popping down a menu is seen by Emacs (see xg_process_timeouts above).
-   W is the widget that does the grab (not used).
-   UNGRAB_P is TRUE if this is an ungrab, FALSE if it is a grab.
-   CLIENT_DATA is NULL (not used).  */
-
-/* Keep track of total number of grabs.  */
-static int menu_grab_callback_cnt;
-
-static void
-menu_grab_callback (GtkWidget *widget,
-                    gboolean ungrab_p,
-                    gpointer client_data)
-{
-  if (ungrab_p) menu_grab_callback_cnt--;
-  else menu_grab_callback_cnt++;
-
-  if (menu_grab_callback_cnt > 0 && ! xg_timer) xg_start_timer ();
-  else if (menu_grab_callback_cnt == 0 && xg_timer) xg_stop_timer ();
-}
-
 /* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
    must be non-NULL) and can be inserted into a menu item.
 
@@ -1902,7 +1960,7 @@ make_menu_item (utf8_label, utf8_key, item, group)
 /* Return non-zero if LABEL specifies a separator (GTK only has one
    separator type)  */
 
-static char* separator_names[] = {
+static const char* separator_names[] = {
   "space",
   "no-line",
   "single-line",
@@ -2055,34 +2113,6 @@ xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group)
   return w;
 }
 
-/* Callback called when keyboard traversal (started by x-menu-bar-open) ends.
-   WMENU is the menu for which traversal has been done.  DATA points to the
-   frame for WMENU.  We must release grabs, some bad interaction between GTK
-   and Emacs makes the menus keep the grabs.  */
-
-static void
-menu_nav_ended (wmenu, data)
-     GtkMenuShell *wmenu;
-     gpointer data;
-{
-  FRAME_PTR f = (FRAME_PTR) data;
-
-  if (FRAME_X_OUTPUT (f)->menubar_widget)
-    {
-      GtkMenuShell *w = GTK_MENU_SHELL (FRAME_X_OUTPUT (f)->menubar_widget);
-      Display *dpy = FRAME_X_DISPLAY (f);
-
-      BLOCK_INPUT;
-      gtk_menu_shell_deactivate (w);
-      gtk_menu_shell_deselect (w);
-
-      XUngrabKeyboard (dpy, CurrentTime);
-      XUngrabPointer (dpy, CurrentTime);
-      UNBLOCK_INPUT;
-    }
-}
-
-
 static GtkWidget *create_menus P_ ((widget_value *, FRAME_PTR, GCallback,
                                    GCallback, GCallback, int, int, int,
                                    GtkWidget *, xg_menu_cb_data *, char *));
@@ -2146,13 +2176,15 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
                           G_CALLBACK (menuitem_highlight_callback),
                           NULL);
       }
-      else wmenu = gtk_menu_bar_new ();
-
-      /* Fix up grabs after keyboard traversal ends.  */
-      g_signal_connect (G_OBJECT (wmenu),
-                        "selection-done",
-                        G_CALLBACK (menu_nav_ended),
-                        f);
+      else
+        {
+          wmenu = gtk_menu_bar_new ();
+          /* Set width of menu bar to a small value so it doesn't enlarge
+             a small initial frame size.  The width will be set to the
+             width of the frame later on when it is added to a container.
+             height -1: Natural height.  */
+          gtk_widget_set_size_request (wmenu, 1, -1);
+        }
 
       /* Put cl_data on the top menu for easier access.  */
       cl_data = make_cl_data (cl_data, f, highlight_cb);
@@ -2166,9 +2198,6 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
       if (deactivate_cb)
         g_signal_connect (G_OBJECT (wmenu),
                           "selection-done", deactivate_cb, 0);
-
-      g_signal_connect (G_OBJECT (wmenu),
-                        "grab-notify", G_CALLBACK (menu_grab_callback), 0);
     }
 
   if (! menu_bar_p && add_tearoff_p)
@@ -2373,7 +2402,7 @@ xg_destroy_widgets (list)
 
 static void
 xg_update_menubar (menubar, f, list, iter, pos, val,
-                   select_cb, highlight_cb, cl_data)
+                   select_cb, deactivate_cb, highlight_cb, cl_data)
      GtkWidget *menubar;
      FRAME_PTR f;
      GList **list;
@@ -2381,6 +2410,7 @@ xg_update_menubar (menubar, f, list, iter, pos, val,
      int pos;
      widget_value *val;
      GCallback select_cb;
+     GCallback deactivate_cb;
      GCallback highlight_cb;
      xg_menu_cb_data *cl_data;
 {
@@ -2391,6 +2421,10 @@ xg_update_menubar (menubar, f, list, iter, pos, val,
       /* Item(s) have been removed.  Remove all remaining items.  */
       xg_destroy_widgets (iter);
 
+      /* Add a blank entry so the menubar doesn't collapse to nothing. */
+      gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
+                             gtk_menu_item_new_with_label (""),
+                             0);
       /* All updated.  */
       val = 0;
       iter = 0;
@@ -2398,7 +2432,7 @@ xg_update_menubar (menubar, f, list, iter, pos, val,
   else if (! iter && val)
     {
       /* Item(s) added.  Add all new items in one call.  */
-      create_menus (val, f, select_cb, 0, highlight_cb,
+      create_menus (val, f, select_cb, deactivate_cb, highlight_cb,
                     0, 1, 0, menubar, cl_data, 0);
 
       /* All updated.  */
@@ -2505,7 +2539,8 @@ xg_update_menubar (menubar, f, list, iter, pos, val,
           /* Create a possibly empty submenu for menu bar items, since some
              themes don't highlight items correctly without it. */
           GtkWidget *submenu = create_menus (NULL, f,
-                                             select_cb, NULL, highlight_cb,
+                                             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);
@@ -2543,13 +2578,13 @@ xg_update_menubar (menubar, f, list, iter, pos, val,
 
   /* Update the rest of the menu bar.  */
   xg_update_menubar (menubar, f, list, iter, pos, val,
-                     select_cb, highlight_cb, cl_data);
+                     select_cb, deactivate_cb, highlight_cb, cl_data);
 }
 
 /* Update the menu item W so it corresponds to VAL.
    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.
-   CL_DATA is the data to set in the widget for menu invokation.  */
+   CL_DATA is the data to set in the widget for menu invocation.  */
 
 static void
 xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data)
@@ -2848,7 +2883,7 @@ xg_modify_menubar_widgets (menubar, f, val, deep_p,
                                                   XG_FRAME_DATA);
 
   xg_update_menubar (menubar, f, &list, list, 0, val->contents,
-                     select_cb, highlight_cb, cl_data);
+                     select_cb, deactivate_cb, highlight_cb, cl_data);
 
   if (deep_p)
     {
@@ -2914,6 +2949,9 @@ xg_update_frame_menubar (f)
   if (!x->menubar_widget || GTK_WIDGET_MAPPED (x->menubar_widget))
     return 0;
 
+  if (x->menubar_widget && gtk_widget_get_parent (x->menubar_widget))
+    return 0; /* Already done this, happens for frames created invisible.  */
+
   BLOCK_INPUT;
 
   gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
@@ -2922,14 +2960,8 @@ xg_update_frame_menubar (f)
 
   gtk_widget_show_all (x->menubar_widget);
   gtk_widget_size_request (x->menubar_widget, &req);
-
   FRAME_MENUBAR_HEIGHT (f) = req.height;
-
-  /* The height has changed, resize outer widget and set columns
-     rows to what we had before adding the menu bar.  */
-  xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
-
-  SET_FRAME_GARBAGED (f);
+  xg_height_changed (f);
   UNBLOCK_INPUT;
 
   return 1;
@@ -2953,12 +2985,7 @@ free_frame_menubar (f)
           the container.  */
       x->menubar_widget = 0;
       FRAME_MENUBAR_HEIGHT (f) = 0;
-
-      /* The height has changed, resize outer widget and set columns
-         rows to what we had before removing the menu bar.  */
-      xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
-
-      SET_FRAME_GARBAGED (f);
+      xg_height_changed (f);
       UNBLOCK_INPUT;
     }
 }
@@ -3086,54 +3113,23 @@ xg_gtk_scroll_destroy (widget, data)
      GtkWidget *widget;
      gpointer data;
 {
-  gpointer p;
   int id = (int) (EMACS_INT) data; /* The EMACS_INT cast avoids a warning. */
-
-  p = g_object_get_data (G_OBJECT (widget), XG_LAST_SB_DATA);
-  if (p) xfree (p);
   xg_remove_widget_from_map (id);
 }
 
-/* Callback for button press/release events.  Used to start timer so that
-   the scroll bar repetition timer in GTK gets handeled.
-   Also, sets bar->dragging to Qnil when dragging (button release) is done.
-   WIDGET is the scroll bar widget the event is for (not used).
-   EVENT contains the event.
-   USER_DATA points to the struct scrollbar structure.
-
-   Returns FALSE to tell GTK that it shall continue propagate the event
-   to widgets.  */
-
-static gboolean
-scroll_bar_button_cb (widget, event, user_data)
-     GtkWidget *widget;
-     GdkEventButton *event;
-     gpointer user_data;
-{
-  if (event->type == GDK_BUTTON_PRESS && ! xg_timer)
-    xg_start_timer ();
-  else if (event->type == GDK_BUTTON_RELEASE)
-    {
-      struct scroll_bar *bar = (struct scroll_bar *) user_data;
-      if (xg_timer) xg_stop_timer ();
-      bar->dragging = Qnil;
-    }
-
-  return FALSE;
-}
-
 /* Create a 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_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
+xg_create_scroll_bar (f, bar, scroll_callback, end_callback, scroll_bar_name)
      FRAME_PTR f;
      struct scroll_bar *bar;
-     GCallback scroll_callback;
+     GCallback scroll_callback, end_callback;
      char *scroll_bar_name;
 {
   GtkWidget *wscroll;
@@ -3150,30 +3146,24 @@ xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
   webox = gtk_event_box_new ();
   gtk_widget_set_name (wscroll, scroll_bar_name);
   gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
+  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),
-                    "value-changed",
-                    scroll_callback,
-                    (gpointer) bar);
   /* The EMACS_INT cast avoids a warning. */
   g_signal_connect (G_OBJECT (wscroll),
                     "destroy",
                     G_CALLBACK (xg_gtk_scroll_destroy),
                     (gpointer) (EMACS_INT) scroll_id);
-
-  /* Connect to button press and button release to detect if any scroll bar
-     has the pointer.  */
   g_signal_connect (G_OBJECT (wscroll),
-                    "button-press-event",
-                    G_CALLBACK (scroll_bar_button_cb),
+                    "change-value",
+                    scroll_callback,
                     (gpointer) bar);
   g_signal_connect (G_OBJECT (wscroll),
                     "button-release-event",
-                    G_CALLBACK (scroll_bar_button_cb),
+                    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
@@ -3239,15 +3229,43 @@ xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height)
     {
       GtkWidget *wfixed = f->output_data.x->edit_widget;
       GtkWidget *wparent = gtk_widget_get_parent (wscroll);
+      GtkFixed *wf = GTK_FIXED (wfixed);
+
+      /* Clear out old position.  */
+      GList *iter;
+      int oldx = -1, oldy = -1, oldw, oldh;
+      for (iter = wf->children; iter; iter = iter->next)
+        if (((GtkFixedChild *)iter->data)->widget == wparent)
+          {
+            GtkFixedChild *ch = (GtkFixedChild *)iter->data;
+            if (ch->x != left || ch->y != top)
+              {
+                oldx = ch->x;
+                oldy = ch->y;
+                gtk_widget_get_size_request (wscroll, &oldw, &oldh);
+              }
+            break;
+          }
 
       /* Move and resize to new values.  */
       gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
       gtk_widget_set_size_request (wscroll, width, height);
-      gtk_widget_queue_draw (wparent);
+      gtk_widget_queue_draw (wfixed);
       gdk_window_process_all_updates ();
+      if (oldx != -1) 
+        {
+          /* 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 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);
@@ -3316,8 +3334,6 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
 
       if (changed || (int) gtk_range_get_value (GTK_RANGE (wscroll)) != value)
       {
-        GtkWidget *wfixed = f->output_data.x->edit_widget;
-
         BLOCK_INPUT;
 
         /* gtk_range_set_value invokes the callback.  Set
@@ -3336,6 +3352,40 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
     }
 }
 
+/* Return non-zero 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.
+
+   Return non-zero if the event is for a scroll bar, zero otherwise.  */
+
+int
+xg_event_is_for_scrollbar (f, event)
+     FRAME_PTR f;
+     XEvent *event;
+{
+  int retval = 0;
+
+  if (f && event->type == ButtonPress && event->xbutton.button < 4)
+    {
+      /* Check if press occurred outside the edit widget.  */
+      GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
+      retval = gdk_display_get_window_at_pointer (gdpy, NULL, NULL)
+        != f->output_data.x->edit_widget->window;
+    }
+  else if (f
+           && ((event->type == ButtonRelease && event->xbutton.button < 4)
+               || event->type == MotionNotify))
+    {
+      /* If we are releasing or moving the scroll bar, it has the grab.  */
+      retval = gtk_grab_get_current () != 0
+        && gtk_grab_get_current () != f->output_data.x->edit_widget;
+    }
+  
+  return retval;
+}
+
+
 \f
 /***********************************************************************
                       Tool bar functions
@@ -3411,17 +3461,21 @@ xg_tool_bar_callback (w, client_data)
      to `(tool_bar)', see keyboard.c.  */
   event.kind = TOOL_BAR_EVENT;
   event.frame_or_window = frame;
-  event.arg = frame;    
-  kbd_buffer_store_event (&event);      
-  
-  event.kind = TOOL_BAR_EVENT;          
+  event.arg = frame;
+  kbd_buffer_store_event (&event);
+
+  event.kind = TOOL_BAR_EVENT;
   event.frame_or_window = frame;
   event.arg = key;
   /* Convert between the modifier bits GDK uses and the modifier bits
-     Emacs uses.  This assumes GDK an X masks are the same, which they are when
+     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);
   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
@@ -3441,6 +3495,28 @@ xg_tool_bar_proxy_callback (w, client_data)
   xg_tool_bar_callback (wbutton, client_data);
 }
 
+
+static gboolean
+xg_tool_bar_help_callback P_ ((GtkWidget *w,
+                               GdkEventCrossing *event,
+                               gpointer client_data));
+
+/* This callback is called when a help is to be shown for an item in
+   the detached tool bar when the detached tool bar it is not expanded.  */
+
+static gboolean
+xg_tool_bar_proxy_help_callback (w, event, client_data)
+     GtkWidget *w;
+     GdkEventCrossing *event;
+     gpointer client_data;
+{
+  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);
+}
+
+
 /* 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
@@ -3453,10 +3529,10 @@ xg_tool_bar_menu_proxy (toolitem, user_data)
 {
   GtkWidget *weventbox = gtk_bin_get_child (GTK_BIN (toolitem));
   GtkButton *wbutton = GTK_BUTTON (gtk_bin_get_child (GTK_BIN (weventbox)));
-  GtkWidget *wmenuitem = gtk_image_menu_item_new ();
+  GtkWidget *wmenuitem = gtk_image_menu_item_new_with_label ("");
   GtkWidget *wmenuimage;
 
-  if (gtk_button_get_use_stock (wbutton)) 
+  if (gtk_button_get_use_stock (wbutton))
     wmenuimage = gtk_image_new_from_stock (gtk_button_get_label (wbutton),
                                            GTK_ICON_SIZE_MENU);
   else
@@ -3481,7 +3557,7 @@ xg_tool_bar_menu_proxy (toolitem, user_data)
       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))
@@ -3500,7 +3576,7 @@ xg_tool_bar_menu_proxy (toolitem, user_data)
               abort ();
             }
         }
-      else if (store_type == GTK_IMAGE_ICON_NAME) 
+      else if (store_type == GTK_IMAGE_ICON_NAME)
         {
           const gchar *icon_name;
           GtkIconSize icon_size;
@@ -3520,12 +3596,27 @@ xg_tool_bar_menu_proxy (toolitem, user_data)
 
   g_signal_connect (G_OBJECT (wmenuitem),
                     "activate",
-                    GTK_SIGNAL_FUNC (xg_tool_bar_proxy_callback),
+                    G_CALLBACK (xg_tool_bar_proxy_callback),
                     user_data);
 
+  
   g_object_set_data (G_OBJECT (wmenuitem), XG_TOOL_BAR_PROXY_BUTTON,
                      (gpointer) wbutton);
   gtk_tool_item_set_proxy_menu_item (toolitem, "Emacs toolbar item", wmenuitem);
+  gtk_widget_set_sensitive (wmenuitem, GTK_WIDGET_SENSITIVE (wbutton));
+
+  /* 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;
 }
@@ -3556,11 +3647,8 @@ xg_tool_bar_detach_callback (wbox, w, client_data)
       /* When detaching a tool bar, not everything dissapear.  There are
          a few pixels left that are used to drop the tool bar back into
          place.  */
-      FRAME_TOOLBAR_HEIGHT (f) = 2;
-
-      /* The height has changed, resize outer widget and set columns
-         rows to what we had before detaching the tool bar.  */
-      xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
+      FRAME_TOOLBAR_HEIGHT (f) = 4;
+      xg_height_changed (f);
     }
 }
 
@@ -3588,10 +3676,7 @@ xg_tool_bar_attach_callback (wbox, w, client_data)
 
       gtk_widget_size_request (w, &req);
       FRAME_TOOLBAR_HEIGHT (f) = req.height;
-
-      /* The height has changed, resize outer widget and set columns
-         rows to what we had before attaching the tool bar.  */
-      xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
+      xg_height_changed (f);
     }
 }
 
@@ -3668,22 +3753,20 @@ xg_tool_bar_item_expose_callback (w, event, client_data)
   return FALSE;
 }
 
-#define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
-
-
-/* Create a tool bar for frame F.  */
+/* Attach a tool bar to frame F.  */
 
 static void
-xg_create_tool_bar (f)
+xg_pack_tool_bar (f)
      FRAME_PTR f;
 {
   struct x_output *x = f->output_data.x;
-  GtkRequisition req;
   int vbox_pos = x->menubar_widget ? 1 : 0;
 
-  x->toolbar_widget = gtk_toolbar_new ();
   x->handlebox_widget = gtk_handle_box_new ();
-  x->toolbar_detached = 0;
+  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);
@@ -3693,6 +3776,19 @@ xg_create_tool_bar (f)
 
   gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->handlebox_widget,
                          vbox_pos);
+  gtk_widget_show_all (x->handlebox_widget);
+}
+
+/* Create a tool bar for frame F.  */
+
+static void
+xg_create_tool_bar (f)
+     FRAME_PTR f;
+{
+  struct x_output *x = f->output_data.x;
+
+  x->toolbar_widget = gtk_toolbar_new ();
+  x->toolbar_detached = 0;
 
   gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
 
@@ -3706,23 +3802,10 @@ xg_create_tool_bar (f)
   gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
   gtk_toolbar_set_orientation (GTK_TOOLBAR (x->toolbar_widget),
                                GTK_ORIENTATION_HORIZONTAL);
+}
 
-  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_widget_show_all (x->handlebox_widget);
-
-  gtk_widget_size_request (x->toolbar_widget, &req);
-  FRAME_TOOLBAR_HEIGHT (f) = req.height;
-
-  /* The height has changed, resize outer widget and set columns
-     rows to what we had before adding the tool bar.  */
-  xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
 
-  SET_FRAME_GARBAGED (f);
-}
+#define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
 
 /* Find the right-to-left image named by RTL in the tool bar images for F.
    Returns IMAGE if RTL is not found.  */
@@ -3743,7 +3826,7 @@ find_rtl_image (f, image, rtl)
   for (i = 0; i < f->n_tool_bar_items; ++i)
     {
       Lisp_Object rtl_image = PROP (TOOL_BAR_ITEM_IMAGES);
-      if (!NILP (file = file_for_image (rtl_image))) 
+      if (!NILP (file = file_for_image (rtl_image)))
         {
           file = call1 (intern ("file-name-sans-extension"),
                        Ffile_name_nondirectory (file));
@@ -3760,6 +3843,8 @@ find_rtl_image (f, image, rtl)
 
 /* Update the tool bar for frame F.  Add new buttons and remove old.  */
 
+extern Lisp_Object Qx_gtk_map_stock;
+
 void
 update_frame_tool_bar (f)
      FRAME_PTR f;
@@ -3771,6 +3856,7 @@ update_frame_tool_bar (f)
   GtkToolbar *wtoolbar;
   GtkToolItem *ti;
   GtkTextDirection dir;
+  int pack_tool_bar = x->handlebox_widget == NULL;
 
   if (! FRAME_GTK_WIDGET (f))
     return;
@@ -3810,7 +3896,6 @@ update_frame_tool_bar (f)
 
   for (i = 0; i < f->n_tool_bar_items; ++i)
     {
-
       int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
       int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
       int idx;
@@ -3818,14 +3903,13 @@ update_frame_tool_bar (f)
       int icon_size = 0;
       struct image *img = NULL;
       Lisp_Object image;
-      Lisp_Object stock;
+      Lisp_Object stock = Qnil;
       GtkStockItem stock_item;
       char *stock_name = NULL;
       char *icon_name = NULL;
       Lisp_Object rtl;
       GtkWidget *wbutton = NULL;
       GtkWidget *weventbox;
-      Lisp_Object func = intern ("x-gtk-map-stock");
       Lisp_Object specified_file;
 
       ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (x->toolbar_widget), i);
@@ -3846,10 +3930,10 @@ update_frame_tool_bar (f)
         }
 
       specified_file = file_for_image (image);
-      if (!NILP (specified_file) && EQ (Qt, Ffboundp (func)))
-        stock = call1 (func, specified_file);
+      if (!NILP (specified_file) && !NILP (Ffboundp (Qx_gtk_map_stock)))
+        stock = call1 (Qx_gtk_map_stock, specified_file);
 
-      if (! NILP (stock) && STRINGP (stock))
+      if (STRINGP (stock))
         {
           stock_name = SSDATA (stock);
           if (stock_name[0] == 'n' && stock_name[1] == ':')
@@ -3868,7 +3952,7 @@ update_frame_tool_bar (f)
             }
           else if (gtk_stock_lookup (SSDATA (stock), &stock_item))
               icon_size = gtk_toolbar_get_icon_size (wtoolbar);
-          else 
+          else
             {
               stock = Qnil;
               stock_name = NULL;
@@ -3908,7 +3992,7 @@ update_frame_tool_bar (f)
           img_id = lookup_image (f, image);
           img = IMAGE_FROM_ID (f, img_id);
           prepare_image_for_display (f, img);
-      
+
           if (img->load_failed_p || img->pixmap == None)
             {
                 if (ti)
@@ -3940,7 +4024,7 @@ update_frame_tool_bar (f)
                                       (gpointer) xstrdup (stock_name),
                                       (GDestroyNotify) xfree);
             }
-          else if (icon_name) 
+          else if (icon_name)
             {
               w = gtk_image_new_from_icon_name (icon_name, icon_size);
               g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_ICON_NAME,
@@ -3970,11 +4054,11 @@ update_frame_tool_bar (f)
 
           /* The EMACS_INT cast avoids a warning. */
           g_signal_connect (G_OBJECT (ti), "create-menu-proxy",
-                            GTK_SIGNAL_FUNC (xg_tool_bar_menu_proxy),
+                            G_CALLBACK (xg_tool_bar_menu_proxy),
                             (gpointer) (EMACS_INT) i);
 
           g_signal_connect (G_OBJECT (wbutton), "clicked",
-                            GTK_SIGNAL_FUNC (xg_tool_bar_callback),
+                            G_CALLBACK (xg_tool_bar_callback),
                             (gpointer) (EMACS_INT) i);
 
           gtk_widget_show_all (GTK_WIDGET (ti));
@@ -3991,16 +4075,16 @@ update_frame_tool_bar (f)
 
           gtk_widget_set_sensitive (wbutton, enabled_p);
           gtk_tool_item_set_homogeneous (ti, FALSE);
-          
+
           /* Callback to save modifyer mask (Shift/Control, etc).  GTK makes
              no distinction based on modifiers in the activate callback,
              so we have to do it ourselves.  */
           g_signal_connect (wbutton, "button-release-event",
-                            GTK_SIGNAL_FUNC (xg_tool_bar_button_cb),
+                            G_CALLBACK (xg_tool_bar_button_cb),
                             NULL);
 
           g_object_set_data (G_OBJECT (wbutton), XG_FRAME_DATA, (gpointer)f);
-
+          
           /* Use enter/leave notify to show help.  We use the events
              rather than the GtkButton specific signals "enter" and
              "leave", so we can have only one callback.  The event
@@ -4077,14 +4161,18 @@ update_frame_tool_bar (f)
       if (ti) gtk_widget_hide_all (GTK_WIDGET (ti));
     } while (ti != NULL);
 
-  gtk_widget_size_request (GTK_WIDGET (wtoolbar), &new_req);
+  new_req.height = 0;
+  if (pack_tool_bar && f->n_tool_bar_items != 0)
+      xg_pack_tool_bar (f);
+  
+
+  gtk_widget_size_request (GTK_WIDGET (x->toolbar_widget), &new_req);
   if (old_req.height != new_req.height
       && ! FRAME_X_OUTPUT (f)->toolbar_detached)
     {
       FRAME_TOOLBAR_HEIGHT (f) = new_req.height;
-      xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
+      xg_height_changed (f);
     }
-
   UNBLOCK_INPUT;
 }
 
@@ -4099,18 +4187,21 @@ free_frame_tool_bar (f)
 
   if (x->toolbar_widget)
     {
+      int is_packed = x->handlebox_widget != 0;
       BLOCK_INPUT;
-      gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
-                            x->handlebox_widget);
+      /* We may have created the toolbar_widget in xg_create_tool_bar, but
+         not the x->handlebox_widget which is created in xg_pack_tool_bar.  */
+      if (is_packed)
+        gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
+                              x->handlebox_widget);
+      else
+        gtk_widget_destroy (x->toolbar_widget);
+
       x->toolbar_widget = 0;
       x->handlebox_widget = 0;
       FRAME_TOOLBAR_HEIGHT (f) = 0;
+      xg_height_changed (f);
 
-      /* The height has changed, resize outer widget and set columns
-         rows to what we had before removing the tool bar.  */
-      xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
-
-      SET_FRAME_GARBAGED (f);
       UNBLOCK_INPUT;
     }
 }
@@ -4157,12 +4248,13 @@ xg_initialize ()
 
   /* Make dialogs close on C-g.  Since file dialog inherits from
      dialog, this works for them also.  */
-  binding_set = gtk_binding_set_by_class (gtk_type_class (GTK_TYPE_DIALOG));
+  binding_set = gtk_binding_set_by_class (g_type_class_ref (GTK_TYPE_DIALOG));
   gtk_binding_entry_add_signal (binding_set, GDK_g, GDK_CONTROL_MASK,
                                 "close", 0);
 
   /* Make menus close on C-g.  */
-  binding_set = gtk_binding_set_by_class (gtk_type_class (GTK_TYPE_MENU_SHELL));
+  binding_set = gtk_binding_set_by_class (g_type_class_ref
+                                          (GTK_TYPE_MENU_SHELL));
   gtk_binding_entry_add_signal (binding_set, GDK_g, GDK_CONTROL_MASK,
                                 "cancel", 0);
 }