]> code.delx.au - gnu-emacs/blobdiff - src/gtkutil.c
(url-current-object, url-package-name, url-package-version): Add defvars.
[gnu-emacs] / src / gtkutil.c
index f5f05709e48fce005ccd0d12eeeea79ce1dc5ec9..3b590e0faed92518dd6dee47507401a8433acf3c 100644 (file)
@@ -1,6 +1,5 @@
 /* Functions for creating and updating GTK widgets.
-   Copyright (C) 2003
-   Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -16,17 +15,19 @@ 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., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
 #include "config.h"
 
 #ifdef USE_GTK
 #include <string.h>
+#include <signal.h>
 #include <stdio.h>
 #include "lisp.h"
 #include "xterm.h"
 #include "blockinput.h"
+#include "syssignal.h"
 #include "window.h"
 #include "atimer.h"
 #include "gtkutil.h"
@@ -238,6 +239,64 @@ xg_create_default_cursor (dpy)
   return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
 }
 
+/* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel.  */
+
+static GdkPixbuf *
+xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap)
+     GdkPixmap *gpix;
+     GdkPixmap *gmask;
+     GdkColormap *cmap;
+{
+  int x, y, width, height, rowstride, mask_rowstride;
+  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,
+                                          0, 0, 0, 0, width, height);
+  icon_buf = gdk_pixbuf_add_alpha (tmp_buf, FALSE, 0, 0, 0);
+  g_object_unref (G_OBJECT (tmp_buf));
+
+  if (gmask)
+    {
+      GdkPixbuf *mask_buf = gdk_pixbuf_get_from_drawable (NULL,
+                                                          gmask,
+                                                          NULL,
+                                                          0, 0, 0, 0,
+                                                          width, height);
+      guchar *pixels = gdk_pixbuf_get_pixels (icon_buf);
+      guchar *mask_pixels = gdk_pixbuf_get_pixels (mask_buf);
+      int rowstride = gdk_pixbuf_get_rowstride (icon_buf);
+      int mask_rowstride = gdk_pixbuf_get_rowstride (mask_buf);
+      int y;
+
+      for (y = 0; y < height; ++y)
+        {
+          guchar *iconptr, *maskptr;
+          int x;
+
+          iconptr = pixels + y * rowstride;
+          maskptr = mask_pixels + y * mask_rowstride;
+
+          for (x = 0; x < width; ++x)
+            {
+              /* In a bitmap, RGB is either 255/255/255 or 0/0/0.  Checking
+                 just R is sufficient.  */
+              if (maskptr[0] == 0)
+                iconptr[3] = 0; /* 0, 1, 2 is R, G, B.  3 is alpha.  */
+
+              iconptr += rowstride/width;
+              maskptr += mask_rowstride/width;
+            }
+        }
+
+      g_object_unref (G_OBJECT (mask_buf));
+    }
+
+  return icon_buf;
+}
+
 /* 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
@@ -310,60 +369,15 @@ xg_get_image_for_pixmap (f, img, widget, old_widget)
     }
   else
     {
+
       /* This is a workaround to make icons look good on pseudo color
          displays.  Apparently GTK expects the images to have an alpha
          channel.  If they don't, insensitive and activated icons will
          look bad.  This workaround does not work on monochrome displays,
          and is not needed on true color/static color displays (i.e.
          16 bits and higher).  */
-      int x, y, width, height, rowstride, mask_rowstride;
-      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,
-                                             gtk_widget_get_colormap (widget),
-                                              0, 0, 0, 0, width, height);
-      icon_buf = gdk_pixbuf_add_alpha (tmp_buf, FALSE, 0, 0, 0);
-      g_object_unref (G_OBJECT (tmp_buf));
-
-      if (gmask)
-        {
-          GdkPixbuf *mask_buf = gdk_pixbuf_get_from_drawable (NULL,
-                                                              gmask,
-                                                              NULL,
-                                                              0, 0, 0, 0,
-                                                              width, height);
-          guchar *pixels = gdk_pixbuf_get_pixels (icon_buf);
-          guchar *mask_pixels = gdk_pixbuf_get_pixels (mask_buf);
-          int rowstride = gdk_pixbuf_get_rowstride (icon_buf);
-          int mask_rowstride = gdk_pixbuf_get_rowstride (mask_buf);
-          int y;
-
-          for (y = 0; y < height; ++y)
-            {
-              guchar *iconptr, *maskptr;
-              int x;
-
-              iconptr = pixels + y * rowstride;
-              maskptr = mask_pixels + y * mask_rowstride;
-
-              for (x = 0; x < width; ++x)
-                {
-                  /* In a bitmap, RGB is either 255/255/255 or 0/0/0.  Checking
-                     just R is sufficient.  */
-                  if (maskptr[0] == 0)
-                    iconptr[3] = 0; /* 0, 1, 2 is R, G, B.  3 is alpha.  */
-
-                  iconptr += rowstride/width;
-                  maskptr += mask_rowstride/width;
-                }
-            }
-
-          g_object_unref (G_OBJECT (mask_buf));
-        }
+      GdkColormap *cmap = gtk_widget_get_colormap (widget);
+      GdkPixbuf *icon_buf = xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap);
 
       if (! old_widget)
         old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf));
@@ -554,12 +568,6 @@ xg_resize_outer_widget (f, columns, rows)
      int columns;
      int rows;
 {
-  gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                     FRAME_PIXEL_WIDTH (f), FRAME_TOTAL_PIXEL_HEIGHT (f));
-
-  /* base_height is now changed.  */
-  x_wm_set_size_hint (f, 0, 0);
-
   /* 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)))
@@ -582,14 +590,14 @@ xg_resize_widgets (f, pixelwidth, pixelheight)
 {
   int mbheight = FRAME_MENUBAR_HEIGHT (f);
   int tbheight = FRAME_TOOLBAR_HEIGHT (f);
-  int rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, (pixelheight 
+  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) 
+      && (columns != FRAME_COLS (f)
          || rows != FRAME_LINES (f)
-          || pixelwidth != FRAME_PIXEL_WIDTH (f) 
+          || pixelwidth != FRAME_PIXEL_WIDTH (f)
          || pixelheight != FRAME_PIXEL_HEIGHT (f)))
     {
       struct x_output *x = f->output_data.x;
@@ -717,6 +725,7 @@ xg_create_frame_widgets (f)
       if (wvbox) gtk_widget_destroy (wvbox);
       if (wfixed) gtk_widget_destroy (wfixed);
 
+      UNBLOCK_INPUT;
       return 0;
     }
 
@@ -754,7 +763,7 @@ xg_create_frame_widgets (f)
      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) = 34;
+    FRAME_TOOLBAR_HEIGHT (f) = 38;
 
 
   /* We don't want this widget double buffered, because we draw on it
@@ -943,6 +952,24 @@ xg_set_background_color (f, bg)
 }
 
 
+/* Set the frame icon to ICON_PIXMAP/MASK.  This must be done with GTK
+   functions so GTK does not overwrite the icon.  */
+
+void
+xg_set_frame_icon (f, icon_pixmap, icon_mask)
+     FRAME_PTR f;
+     Pixmap icon_pixmap;
+     Pixmap icon_mask;
+{
+    GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
+    GdkPixmap *gpix = gdk_pixmap_foreign_new_for_display (gdpy, icon_pixmap);
+    GdkPixmap *gmask = gdk_pixmap_foreign_new_for_display (gdpy, icon_mask);
+    GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (gpix, gmask, NULL);
+
+    gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp);
+}
+
+
 \f
 /***********************************************************************
                       Dialog functions
@@ -1122,41 +1149,71 @@ create_dialog (wv, select_cb, deactivate_cb)
 /***********************************************************************
                       File dialog functions
  ***********************************************************************/
-enum
+/* 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;
 {
-  XG_FILE_NOT_DONE,
-  XG_FILE_OK,
-  XG_FILE_CANCEL,
-  XG_FILE_DESTROYED,
-};
+  int *ptr = (int *) user_data;
+  *ptr = response;
+}
 
-#ifdef HAVE_GTK_FILE_BOTH
-int use_old_gtk_file_dialog;
-#endif
 
+/*  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
+
+/* Return the selected file for file chooser dialog W.
+   The returned string must be free:d.  */
+
+static char *
+xg_get_file_name_from_chooser (w)
+     GtkWidget *w;
+{
+  return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
+}
+
 /* Read a file name from the user using a file chooser dialog.
    F is the current frame.
    PROMPT is a prompt to show to the user.  May not be NULL.
    DEFAULT_FILENAME is a default selection to be displayed.  May be NULL.
    If MUSTMATCH_P is non-zero, the returned file name must be an existing
-   file.
+   file.  *FUNC is set to a function that can be used to retrieve the
+   selected file name from the returned widget.
 
-   Returns a file name or NULL if no file was selected.
-   The returned string must be freed by the caller.  */
+   Returns the created widget.  */
 
-static char *
-xg_get_file_with_chooser (f, prompt, default_filename, mustmatch_p, only_dir_p)
+static GtkWidget *
+xg_get_file_with_chooser (f, prompt, default_filename,
+                          mustmatch_p, only_dir_p, func)
      FRAME_PTR f;
      char *prompt;
      char *default_filename;
      int mustmatch_p, only_dir_p;
+     xg_get_file_func *func;
 {
   GtkWidget *filewin;
   GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
-
-  char *fn = 0;
   GtkFileChooserAction action = (mustmatch_p ?
                                  GTK_FILE_CHOOSER_ACTION_OPEN :
                                  GTK_FILE_CHOOSER_ACTION_SAVE);
@@ -1170,12 +1227,7 @@ xg_get_file_with_chooser (f, prompt, default_filename, mustmatch_p, only_dir_p)
                                           GTK_STOCK_OPEN : GTK_STOCK_OK),
                                          GTK_RESPONSE_OK,
                                          NULL);
-
-  xg_set_screen (filewin, f);
-  gtk_widget_set_name (filewin, "emacs-filedialog");
-  gtk_window_set_transient_for (GTK_WINDOW (filewin), gwin);
-  gtk_window_set_destroy_with_parent (GTK_WINDOW (filewin), TRUE);
-
+  gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
 
   if (default_filename)
     {
@@ -1183,118 +1235,67 @@ xg_get_file_with_chooser (f, prompt, default_filename, mustmatch_p, only_dir_p)
       struct gcpro gcpro1;
       GCPRO1 (file);
 
+      file = build_string (default_filename);
+
       /* File chooser does not understand ~/... in the file name.  It must be
          an absolute name starting with /.  */
       if (default_filename[0] != '/')
-        {
-          file = Fexpand_file_name (build_string (default_filename), Qnil);
-          default_filename = SDATA (file);
-        }
+        file = Fexpand_file_name (file, Qnil);
 
-      gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
-                                     default_filename);
+      default_filename = SDATA (file);
+      if (Ffile_directory_p (file))
+        gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filewin),
+                                             default_filename);
+      else
+        gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
+                                       default_filename);
 
       UNGCPRO;
     }
 
-  gtk_widget_show (filewin);
-
-  if (gtk_dialog_run (GTK_DIALOG (filewin)) == GTK_RESPONSE_OK)
-    fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filewin));
-
-  gtk_widget_destroy (filewin);
-
-  return fn;
+  *func = xg_get_file_name_from_chooser;
+  return filewin;
 }
 #endif /* HAVE_GTK_FILE_CHOOSER_DIALOG_NEW */
 
 #ifdef HAVE_GTK_FILE_SELECTION_NEW
-/* Callback function invoked when the Ok button is pressed in
-   a file dialog.
-   W is the file dialog widget,
-   ARG points to an integer where we record what has happend.  */
 
-static void
-xg_file_sel_ok (w, arg)
-     GtkWidget *w;
-     gpointer arg;
-{
-  *(int*)arg = XG_FILE_OK;
-}
-
-/* Callback function invoked when the Cancel button is pressed in
-   a file dialog.
-   W is the file dialog widget,
-   ARG points to an integer where we record what has happend.  */
-
-static void
-xg_file_sel_cancel (w, arg)
-     GtkWidget *w;
-     gpointer arg;
-{
-  *(int*)arg = XG_FILE_CANCEL;
-}
-
-/* Callback function invoked when the file dialog is destroyed (i.e.
-   popped down).  We must keep track of this, because if this
-   happens, GTK destroys the widget.  But if for example, Ok is pressed,
-   the dialog is popped down, but the dialog widget is not destroyed.
-   W is the file dialog widget,
-   ARG points to an integer where we record what has happend.  */
+/* Return the selected file for file selector dialog W.
+   The returned string must be free:d.  */
 
-static void
-xg_file_sel_destroy (w, arg)
+static char *
+xg_get_file_name_from_selector (w)
      GtkWidget *w;
-     gpointer arg;
 {
-  *(int*)arg = XG_FILE_DESTROYED;
+  GtkFileSelection *filesel = GTK_FILE_SELECTION (w);
+  return xstrdup ((char*) gtk_file_selection_get_filename (filesel));
 }
 
-/* Read a file name from the user using a file selection dialog.
+/* Create a file selection dialog.
    F is the current frame.
    PROMPT is a prompt to show to the user.  May not be NULL.
    DEFAULT_FILENAME is a default selection to be displayed.  May be NULL.
    If MUSTMATCH_P is non-zero, the returned file name must be an existing
-   file.
+   file.  *FUNC is set to a function that can be used to retrieve the
+   selected file name from the returned widget.
 
-   Returns a file name or NULL if no file was selected.
-   The returned string must be freed by the caller.  */
+   Returns the created widget.  */
 
-static char *
+static GtkWidget *
 xg_get_file_with_selection (f, prompt, default_filename,
-                            mustmatch_p, only_dir_p)
+                            mustmatch_p, only_dir_p, func)
      FRAME_PTR f;
      char *prompt;
      char *default_filename;
      int mustmatch_p, only_dir_p;
+     xg_get_file_func *func;
 {
   GtkWidget *filewin;
   GtkFileSelection *filesel;
-  int filesel_done = XG_FILE_NOT_DONE;
-  char *fn = 0;
 
   filewin = gtk_file_selection_new (prompt);
   filesel = GTK_FILE_SELECTION (filewin);
 
-  xg_set_screen (filewin, f);
-  gtk_widget_set_name (filewin, "emacs-filedialog");
-  gtk_window_set_transient_for (GTK_WINDOW (filewin),
-                                GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
-  gtk_window_set_destroy_with_parent (GTK_WINDOW (filewin), TRUE);
-
-  g_signal_connect (G_OBJECT (filesel->ok_button),
-                    "clicked",
-                    G_CALLBACK (xg_file_sel_ok),
-                    &filesel_done);
-  g_signal_connect (G_OBJECT (filesel->cancel_button),
-                    "clicked",
-                    G_CALLBACK (xg_file_sel_cancel),
-                    &filesel_done);
-  g_signal_connect (G_OBJECT (filesel),
-                    "destroy",
-                    G_CALLBACK (xg_file_sel_destroy),
-                    &filesel_done);
-
   if (default_filename)
     gtk_file_selection_set_filename (filesel, default_filename);
 
@@ -1305,19 +1306,9 @@ xg_get_file_with_selection (f, prompt, default_filename,
       gtk_file_selection_hide_fileop_buttons (filesel);
     }
 
+  *func = xg_get_file_name_from_selector;
 
-  gtk_widget_show_all (filewin);
-
-  while (filesel_done == XG_FILE_NOT_DONE)
-    gtk_main_iteration ();
-
-  if (filesel_done == XG_FILE_OK)
-    fn = xstrdup ((char*) gtk_file_selection_get_filename (filesel));
-
-  if (filesel_done != XG_FILE_DESTROYED)
-    gtk_widget_destroy (filewin);
-
-  return fn;
+  return filewin;
 }
 #endif /* HAVE_GTK_FILE_SELECTION_NEW */
 
@@ -1341,26 +1332,76 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p)
      char *default_filename;
      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;
+  extern int x_use_old_gtk_file_dialog;
+
+#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
+  /* I really don't know why this is needed, but without this the GLIBC add on
+     library linuxthreads hangs when the Gnome file chooser backend creates
+     threads.  */
+  sigblock (sigmask (__SIGRTMIN));
+#endif /* HAVE_GTK_AND_PTHREAD */
+
 #ifdef HAVE_GTK_FILE_BOTH
-  if (use_old_gtk_file_dialog)
-    return xg_get_file_with_selection (f, prompt, default_filename,
-                                       mustmatch_p, only_dir_p);
-  return xg_get_file_with_chooser (f, prompt, default_filename,
-                                   mustmatch_p, only_dir_p);
+
+  if (x_use_old_gtk_file_dialog)
+    w = xg_get_file_with_selection (f, prompt, default_filename,
+                                    mustmatch_p, only_dir_p, &func);
+  else
+    w = xg_get_file_with_chooser (f, prompt, default_filename,
+                                  mustmatch_p, only_dir_p, &func);
 
 #else /* not HAVE_GTK_FILE_BOTH */
 
-#ifdef HAVE_GTK_FILE_SELECTION_DIALOG_NEW
-  return xg_get_file_with_selection (f, prompt, default_filename,
-                                     mustmatch_p, only_dir_p);
+#ifdef HAVE_GTK_FILE_SELECTION_NEW
+  w = xg_get_file_with_selection (f, prompt, default_filename,
+                                  mustmatch_p, only_dir_p, &func);
 #endif
 #ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW
-  return xg_get_file_with_chooser (f, prompt, default_filename,
-                                   mustmatch_p, only_dir_p);
+  w = xg_get_file_with_chooser (f, prompt, default_filename,
+                                mustmatch_p, only_dir_p, &func);
 #endif
 
 #endif /* HAVE_GTK_FILE_BOTH */
-  return 0;
+
+  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 ();
+    }
+
+#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
+  sigunblock (sigmask (__SIGRTMIN));
+#endif
+
+  if (filesel_done == GTK_RESPONSE_OK)
+    fn = (*func) (w);
+
+  unbind_to (count, Qnil);
+
+  return fn;
 }
 
 \f
@@ -1889,7 +1930,7 @@ create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
 
       if (deactivate_cb)
         g_signal_connect (G_OBJECT (wmenu),
-                          "deactivate", deactivate_cb, 0);
+                          "selection-done", deactivate_cb, 0);
 
       g_signal_connect (G_OBJECT (wmenu),
                         "grab-notify", G_CALLBACK (menu_grab_callback), 0);
@@ -1999,6 +2040,7 @@ xg_create_widget (type, name, f, val,
                                     GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
       gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
       gtk_widget_set_name (w, "emacs-dialog");
+      gtk_window_set_modal (GTK_WINDOW (w), TRUE);
     }
   else if (menu_bar_p || pop_up_p)
     {
@@ -2595,7 +2637,7 @@ xg_modify_menubar_widgets (menubar, f, val, deep_p,
   xg_update_menubar (menubar, f, &list, list, 0, val->contents,
                      select_cb, highlight_cb, cl_data);
 
-  if (deep_p);
+  if (deep_p)
     {
       widget_value *cur;
 
@@ -2833,7 +2875,7 @@ xg_gtk_scroll_destroy (widget, data)
      gpointer data;
 {
   gpointer p;
-  int id = (int)data;
+  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);
@@ -2864,7 +2906,7 @@ scroll_bar_button_cb (widget, event, user_data)
       if (xg_timer) xg_stop_timer ();
       bar->dragging = Qnil;
     }
-  
+
   return FALSE;
 }
 
@@ -2903,10 +2945,11 @@ xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
                     "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) scroll_id);
+                    (gpointer) (EMACS_INT) scroll_id);
 
   /* Connect to button press and button release to detect if any scroll bar
      has the pointer.  */
@@ -2927,7 +2970,7 @@ xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
      event box window.  */
   gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
   gtk_container_add (GTK_CONTAINER (webox), wscroll);
-  
+
 
   /* Set the cursor to an arrow.  */
   xg_set_cursor (webox, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
@@ -2986,9 +3029,14 @@ xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height)
       GtkWidget *wparent = gtk_widget_get_parent (wscroll);
 
       /* Move and resize to new values.  */
-      gtk_widget_set_size_request (wscroll, width, height);
       gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
-
+      gtk_widget_set_size_request (wscroll, width, height);
+      gtk_widget_queue_draw (wparent);
+      gdk_window_process_all_updates ();
+      /* 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);
     }
@@ -3095,7 +3143,8 @@ xg_tool_bar_callback (w, client_data)
      GtkWidget *w;
      gpointer client_data;
 {
-  int idx = (int)client_data;
+  /* The EMACS_INT cast avoids a warning. */
+  int idx = (int) (EMACS_INT) client_data;
   FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
   Lisp_Object key, frame;
   struct input_event event;
@@ -3137,10 +3186,11 @@ xg_tool_bar_detach_callback (wbox, w, client_data)
 
   if (f)
     {
+      FRAME_X_OUTPUT (f)->toolbar_detached = 1;
+
       /* When detaching a tool bar, not everything dissapear.  There are
          a few pixels left that are used to drop the tool bar back into
          place.  */
-      int bw = gtk_container_get_border_width (GTK_CONTAINER (wbox));
       FRAME_TOOLBAR_HEIGHT (f) = 2;
 
       /* The height has changed, resize outer widget and set columns
@@ -3168,11 +3218,13 @@ xg_tool_bar_attach_callback (wbox, w, client_data)
     {
       GtkRequisition req;
 
+      FRAME_X_OUTPUT (f)->toolbar_detached = 0;
+
       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 detaching the tool bar.  */
+         rows to what we had before attaching the tool bar.  */
       xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
     }
 }
@@ -3192,7 +3244,8 @@ xg_tool_bar_help_callback (w, event, client_data)
      GdkEventCrossing *event;
      gpointer client_data;
 {
-  int idx = (int)client_data;
+  /* The EMACS_INT cast avoids a warning. */
+  int idx = (int) (EMACS_INT) client_data;
   FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
   Lisp_Object help, frame;
 
@@ -3247,10 +3300,10 @@ xg_tool_bar_item_expose_callback (w, event, client_data)
 
   event->area.x = max (0, event->area.x);
   event->area.y = max (0, event->area.y);
-  
+
   event->area.width = max (width, event->area.width);
   event->area.height = max (height, event->area.height);
-  
+
   return FALSE;
 }
 
@@ -3285,6 +3338,8 @@ xg_create_tool_bar (f)
 
   x->toolbar_widget = gtk_toolbar_new ();
   x->handlebox_widget = gtk_handle_box_new ();
+  x->toolbar_detached = 0;
+
   gtk_container_add (GTK_CONTAINER (x->handlebox_widget),
                      x->toolbar_widget);
 
@@ -3339,12 +3394,37 @@ update_frame_tool_bar (f)
   GList *icon_list;
   GList *iter;
   struct x_output *x = f->output_data.x;
+  int hmargin, vmargin;
 
   if (! FRAME_GTK_WIDGET (f))
     return;
 
   BLOCK_INPUT;
 
+  if (INTEGERP (Vtool_bar_button_margin)
+      && XINT (Vtool_bar_button_margin) > 0)
+    {
+      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)
+        hmargin = XFASTINT (XCAR (Vtool_bar_button_margin));
+
+      if (INTEGERP (XCDR (Vtool_bar_button_margin))
+          && XINT (XCDR (Vtool_bar_button_margin)) > 0)
+        vmargin = XFASTINT (XCDR (Vtool_bar_button_margin));
+    }
+
+  /* The natural size (i.e. when GTK uses 0 as margin) looks best,
+     so take DEFAULT_TOOL_BAR_BUTTON_MARGIN to mean "default for GTK",
+     i.e. zero.  This means that margins less than
+     DEFAULT_TOOL_BAR_BUTTON_MARGIN has no effect.  */
+  hmargin = max (0, hmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
+  vmargin = max (0, vmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
+
   if (! x->toolbar_widget)
     xg_create_tool_bar (f);
 
@@ -3408,11 +3488,14 @@ update_frame_tool_bar (f)
         {
           GtkWidget *w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
 
+          gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
+
+          /* The EMACS_INT cast avoids a warning. */
           gtk_toolbar_append_item (GTK_TOOLBAR (x->toolbar_widget),
                                    0, 0, 0,
                                    w,
                                    GTK_SIGNAL_FUNC (xg_tool_bar_callback),
-                                   (gpointer)i);
+                                   (gpointer) (EMACS_INT) i);
 
           /* Save the image so we can see if an update is needed when
              this function is called again.  */
@@ -3442,14 +3525,15 @@ update_frame_tool_bar (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 (w),
                                 "enter-notify-event",
                                 G_CALLBACK (xg_tool_bar_help_callback),
-                                (gpointer)i);
+                                (gpointer) (EMACS_INT) i);
               g_signal_connect (G_OBJECT (w),
                                 "leave-notify-event",
                                 G_CALLBACK (xg_tool_bar_help_callback),
-                                (gpointer)i);
+                                (gpointer) (EMACS_INT) i);
             }
         }
       else
@@ -3463,6 +3547,8 @@ update_frame_tool_bar (f)
                                                       XG_TOOL_BAR_IMAGE_DATA);
           g_list_free (chlist);
 
+          gtk_misc_set_padding (GTK_MISC (wimage), hmargin, vmargin);
+
           if (old_img != img->pixmap)
             (void) xg_get_image_for_pixmap (f, img, x->widget, wimage);
 
@@ -3486,7 +3572,8 @@ update_frame_tool_bar (f)
     }
 
   gtk_widget_size_request (x->toolbar_widget, &new_req);
-  if (old_req.height != new_req.height)
+  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));
@@ -3532,6 +3619,8 @@ free_frame_tool_bar (f)
 void
 xg_initialize ()
 {
+  GtkBindingSet *binding_set;
+
   xg_ignore_gtk_scrollbar = 0;
   xg_detached_menus = 0;
   xg_menu_cb_list.prev = xg_menu_cb_list.next =
@@ -3554,6 +3643,17 @@ xg_initialize ()
                                     "gtk-key-theme-name",
                                     "Emacs",
                                     EMACS_CLASS);
+
+  /* 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));
+  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));
+  gtk_binding_entry_add_signal (binding_set, GDK_g, GDK_CONTROL_MASK,
+                                "cancel", 0);
 }
 
 #endif /* USE_GTK */