(syms_of_ntproc) <w32-get-true-file-attributes>: Doc fix.
[bpt/emacs.git] / src / gtkutil.c
index f52e68b..fa84830 100644 (file)
@@ -1,5 +1,6 @@
 /* Functions for creating and updating GTK widgets.
-   Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008
+     Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -51,6 +52,13 @@ 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.  */
+
+static GdkDisplay *gdpy_def;
+
+
 /* Return the GdkDisplay that corresponds to the X display DPY.  */
 
 static GdkDisplay *
@@ -147,9 +155,15 @@ xg_display_close (Display *dpy)
             break;
           }
 
-      if (! new_dpy) return; /* Emacs will exit anyway.  */
+      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;
+        }
 
-      gdpy_new = gdk_x11_lookup_xdisplay (new_dpy);
       gdk_display_manager_set_default_display (gdk_display_manager_get (),
                                                gdpy_new);
     }
@@ -298,6 +312,23 @@ xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap)
   return icon_buf;
 }
 
+static Lisp_Object
+file_for_image(image)
+     Lisp_Object image;
+{
+  Lisp_Object specified_file = Qnil;
+  Lisp_Object tail;
+  extern Lisp_Object QCfile;
+
+  for (tail = XCDR (image);
+       NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
+       tail = XCDR (XCDR (tail)))
+    if (EQ (XCAR (tail), QCfile))
+      specified_file = XCAR (XCDR (tail));
+
+  return specified_file;
+}
+
 /* 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
@@ -319,20 +350,14 @@ xg_get_image_for_pixmap (f, img, widget, old_widget)
   GdkPixmap *gpix;
   GdkPixmap *gmask;
   GdkDisplay *gdpy;
+  GdkColormap *cmap;
+  GdkPixbuf *icon_buf;
 
   /* If we have a file, let GTK do all the image handling.
      This seems to be the only way to make insensitive and activated icons
      look good in all cases.  */
-  Lisp_Object specified_file = Qnil;
-  Lisp_Object tail;
+  Lisp_Object specified_file = file_for_image (img->spec);
   Lisp_Object file;
-  extern Lisp_Object QCfile;
-
-  for (tail = XCDR (img->spec);
-       NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
-       tail = XCDR (XCDR (tail)))
-    if (EQ (XCAR (tail), QCfile))
-      specified_file = XCAR (XCDR (tail));
 
   /* We already loaded the image once before calling this
      function, so this only fails if the image file has been removed.
@@ -357,32 +382,24 @@ xg_get_image_for_pixmap (f, img, widget, old_widget)
   gpix = gdk_pixmap_foreign_new_for_display (gdpy, img->pixmap);
   gmask = img->mask ? gdk_pixmap_foreign_new_for_display (gdpy, img->mask) : 0;
 
-  if (x_screen_planes (f) > 8 || x_screen_planes (f) == 1)
-    {
-      if (! old_widget)
-        old_widget = GTK_IMAGE (gtk_image_new_from_pixmap (gpix, gmask));
-      else
-        gtk_image_set_from_pixmap (old_widget, gpix, gmask);
-    }
+  /* 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 strictly not needed on true color/static color displays (i.e.
+     16 bits and higher).  But we do it anyway so we get a pixbuf that is
+     not associated with the img->pixmap.  The img->pixmap may be removed
+     by clearing the image cache and then the tool bar redraw fails, since
+     Gtk+ assumes the pixmap is always there.  */
+  cmap = gtk_widget_get_colormap (widget);
+  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));
   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).  */
-      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));
-      else
-        gtk_image_set_from_pixbuf (old_widget, icon_buf);
+    gtk_image_set_from_pixbuf (old_widget, icon_buf);
 
-      g_object_unref (G_OBJECT (icon_buf));
-    }
+  g_object_unref (G_OBJECT (icon_buf));
 
   g_object_unref (G_OBJECT (gpix));
   if (gmask) g_object_unref (G_OBJECT (gmask));
@@ -586,68 +603,46 @@ 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.  */
-
-static void
-xg_resize_outer_widget (f, columns, rows)
-     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 ();
-}
-
-/* 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 rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelheight);
   int columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
 
   if (FRAME_GTK_WIDGET (f)
@@ -656,25 +651,65 @@ xg_resize_widgets (f, pixelwidth, pixelheight)
           || pixelwidth != FRAME_PIXEL_WIDTH (f)
          || pixelheight != FRAME_PIXEL_HEIGHT (f)))
     {
-      struct x_output *x = f->output_data.x;
-      GtkAllocation all;
+      FRAME_PIXEL_WIDTH (f) = pixelwidth;
+      FRAME_PIXEL_HEIGHT (f) = pixelheight;
 
-      all.y = mbheight + tbheight;
-      all.x = 0;
-
-      all.width = pixelwidth;
-      all.height = pixelheight - mbheight - tbheight;
+      if (rows != FRAME_LINES (f) || columns != FRAME_COLS (f)
+          || (f->new_text_lines != 0 && f->new_text_lines != rows)
+          || (f->new_text_cols != 0 && f->new_text_cols != columns))
+        {
+          change_frame_size (f, rows, columns, 0, 1, 0);
+          SET_FRAME_GARBAGED (f);
+          cancel_mouse_face (f);
+        }
+    }
+}
 
-      gtk_widget_size_allocate (x->edit_widget, &all);
+/* Process all pending events on the display for frame F.  */
 
-      change_frame_size (f, rows, columns, 0, 1, 0);
-      SET_FRAME_GARBAGED (f);
-      cancel_mouse_face (f);
+static void
+flush_and_sync (f)
+     FRAME_PTR f;
+{
+  gdk_window_process_all_updates ();
+  x_sync (f);
+  while (gtk_events_pending ())
+    {
+      gtk_main_iteration ();
+      gdk_window_process_all_updates ();
+      x_sync (f);
     }
 }
 
+/* Turn wm hints for resize off on frame F */
+
+static void
+x_wm_size_hint_off (f)
+     FRAME_PTR f;
+{
+  GdkGeometry size_hints;
+  gint hint_flags = 0;
+  memset (&size_hints, 0, sizeof (size_hints));
+  hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
+  size_hints.width_inc = 1;
+  size_hints.height_inc = 1;
+  hint_flags |= GDK_HINT_BASE_SIZE;
+  size_hints.base_width = 1;
+  size_hints.base_height = 1;
+  size_hints.min_width  = 1;
+  size_hints.min_height = 1;
+  gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                                 NULL,
+                                 &size_hints,
+                                 hint_flags);
+  /* Make sure these get set again in next call to x_wm_set_size_hint. */
+  f->output_data.x->hint_flags &= ~hint_flags;
+  flush_and_sync (f);
+}
 
-/* Update our widget size to be COLS/ROWS characters for frame F.  */
+/* 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.  */
 
 void
 xg_frame_set_char_size (f, cols, rows)
@@ -686,6 +721,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
@@ -701,13 +739,15 @@ xg_frame_set_char_size (f, cols, rows)
   pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols);
 
   /* Must resize our top level widget.  Font size may have changed,
-     but not rows/cols.  */
+     but not rows/cols.
+     Turn wm hints (min/max size and size increments) of temporarly.
+     It interferes too much, when for example adding or removing the
+     menu/tool bar.  */
+  x_wm_size_hint_off (f);
   gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
                      pixelwidth, pixelheight);
-  xg_resize_widgets (f, pixelwidth, pixelheight);
+  flush_and_sync (f);
   x_wm_set_size_hint (f, 0, 0);
-  SET_FRAME_GARBAGED (f);
-  cancel_mouse_face (f);
 }
 
 /* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
@@ -769,7 +809,11 @@ xg_create_frame_widgets (f)
 
   BLOCK_INPUT;
 
-  wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  if (FRAME_X_EMBEDDED_P (f))
+    wtop = gtk_plug_new (f->output_data.x->parent_desc);
+  else
+    wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
   xg_set_screen (wtop, f);
 
   wvbox = gtk_vbox_new (FALSE, 0);
@@ -811,17 +855,6 @@ xg_create_frame_widgets (f)
   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
@@ -865,7 +898,7 @@ xg_create_frame_widgets (f)
 
   /* Since GTK clears its window by filling with the background color,
      we must keep X and GTK background in sync.  */
-  xg_pix_to_gcolor (wfixed, f->output_data.x->background_pixel, &bg);
+  xg_pix_to_gcolor (wfixed, FRAME_BACKGROUND_PIXEL (f), &bg);
   gtk_widget_modify_bg (wfixed, GTK_STATE_NORMAL, &bg);
 
   /* Also, do not let any background pixmap to be set, this looks very
@@ -972,16 +1005,22 @@ x_wm_set_size_hint (f, flags, user_position)
         hint_flags |= GDK_HINT_USER_POS;
       }
 
-    BLOCK_INPUT;
+    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)),
-                                   FRAME_GTK_OUTER_WIDGET (f),
-                                   &size_hints,
-                                   hint_flags);
+        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;
+        f->output_data.x->size_hints = size_hints;
+        f->output_data.x->hint_flags = hint_flags;
+        UNBLOCK_INPUT;
+      }
   }
 }
 
@@ -1403,8 +1442,8 @@ xg_get_file_with_chooser (f, prompt, default_filename,
     {
       Lisp_Object file;
       struct gcpro gcpro1;
-      GCPRO1 (file);
       char *utf8_filename;
+      GCPRO1 (file);
 
       file = build_string (default_filename);
 
@@ -1771,19 +1810,19 @@ menu_destroy_callback (w, client_data)
    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)
 {
-  /* Keep track of total number of grabs.  */
-  static int cnt;
+  if (ungrab_p) menu_grab_callback_cnt--;
+  else menu_grab_callback_cnt++;
 
-  if (ungrab_p) cnt--;
-  else cnt++;
-
-  if (cnt > 0 && ! xg_timer) xg_start_timer ();
-  else if (cnt == 0 && xg_timer) xg_stop_timer ();
+  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
@@ -1881,6 +1920,24 @@ 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[] = {
+  "space",
+  "no-line",
+  "single-line",
+  "double-line",
+  "single-dashed-line",
+  "double-dashed-line",
+  "shadow-etched-in",
+  "shadow-etched-out",
+  "shadow-etched-in-dash",
+  "shadow-etched-out-dash",
+  "shadow-double-etched-in",
+  "shadow-double-etched-out",
+  "shadow-double-etched-in-dash",
+  "shadow-double-etched-out-dash",
+  0,
+};
+
 static int
 xg_separator_p (char *label)
 {
@@ -1889,24 +1946,6 @@ xg_separator_p (char *label)
           && strncmp (label, "--", 2) == 0
           && label[2] != '-')
     {
-      static char* separator_names[] = {
-        "space",
-       "no-line",
-       "single-line",
-       "double-line",
-       "single-dashed-line",
-       "double-dashed-line",
-       "shadow-etched-in",
-       "shadow-etched-out",
-       "shadow-etched-in-dash",
-       "shadow-etched-out-dash",
-       "shadow-double-etched-in",
-       "shadow-double-etched-out",
-       "shadow-double-etched-in-dash",
-       "shadow-double-etched-out-dash",
-        0,
-      };
-
       int i;
 
       label += 2;
@@ -2034,7 +2073,7 @@ xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group)
   return w;
 }
 
-/* Callback called when keyboard traversal (started by menu-bar-open) ends.
+/* 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.  */
@@ -2906,7 +2945,7 @@ xg_update_frame_menubar (f)
 
   /* 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));
+  xg_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (f));
 
   SET_FRAME_GARBAGED (f);
   UNBLOCK_INPUT;
@@ -2935,7 +2974,7 @@ free_frame_menubar (f)
 
       /* 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));
+      xg_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (f));
 
       SET_FRAME_GARBAGED (f);
       UNBLOCK_INPUT;
@@ -2954,10 +2993,9 @@ free_frame_menubar (f)
 
 int xg_ignore_gtk_scrollbar;
 
-/* SET_SCROLL_BAR_X_WINDOW assumes the second argument fits in
-   32 bits.  But we want to store pointers, and they may be larger
-   than 32 bits.  Keep a mapping from integer index to widget pointers
-   to get around the 32 bit limitation.  */
+/* Xlib's `Window' fits in 32 bits.  But we want to store pointers, and they
+   may be larger than 32 bits.  Keep a mapping from integer index to widget
+   pointers to get around the 32 bit limitation.  */
 
 static struct
 {
@@ -3167,7 +3205,7 @@ xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
   /* Set the cursor to an arrow.  */
   xg_set_cursor (webox, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
 
-  SET_SCROLL_BAR_X_WINDOW (bar, scroll_id);
+  bar->x_window = scroll_id;
 }
 
 /* Make the scroll bar represented by SCROLLBAR_ID visible.  */
@@ -3242,7 +3280,7 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
      struct scroll_bar *bar;
      int portion, position, whole;
 {
-  GtkWidget *wscroll = xg_get_widget_from_map (SCROLL_BAR_X_WINDOW (bar));
+  GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
 
   FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
 
@@ -3332,6 +3370,20 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
 /* The key for storing the button widget in its proxy menu item. */
 #define XG_TOOL_BAR_PROXY_BUTTON "emacs-tool-bar-proxy-button"
 
+/* The key for the data we put in the GtkImage widgets.  The data is
+   the stock name used by Emacs.  We use this to see if we need to update
+   the GtkImage with a new image.  */
+#define XG_TOOL_BAR_STOCK_NAME "emacs-tool-bar-stock-name"
+
+/* As above, but this is used for named theme widgets, as opposed to
+   stock items.  */
+#define XG_TOOL_BAR_ICON_NAME "emacs-tool-bar-icon-name"
+
+/* Callback function invoked when a tool bar item is pressed.
+   W is the button widget in the tool bar that got pressed,
+   CLIENT_DATA is an integer that is the index of the button in the
+   tool bar.  0 is the first button.  */
+
 static gboolean
 xg_tool_bar_button_cb (widget, event, user_data)
     GtkWidget      *widget;
@@ -3372,8 +3424,16 @@ xg_tool_bar_callback (w, client_data)
 
   key = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_KEY);
   XSETFRAME (frame, f);
+
+  /* We generate two events here.  The first one is to set the prefix
+     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.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
@@ -3422,6 +3482,7 @@ xg_tool_bar_menu_proxy (toolitem, user_data)
       GtkImage *wimage = GTK_IMAGE (gtk_bin_get_child (GTK_BIN (wbutton)));
       GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (wbutton));
       GtkImageType store_type = gtk_image_get_storage_type (wimage);
+
       if (store_type == GTK_IMAGE_STOCK)
         {
           gchar *stock_id;
@@ -3451,6 +3512,25 @@ xg_tool_bar_menu_proxy (toolitem, user_data)
 
               wmenuimage = gtk_image_new_from_pixbuf (dest_pixbuf);
             }
+          else
+            {
+              fprintf (stderr, "internal error: GTK_IMAGE_PIXBUF failed\n");
+              abort ();
+            }
+        }
+      else if (store_type == GTK_IMAGE_ICON_NAME) 
+        {
+          const gchar *icon_name;
+          GtkIconSize icon_size;
+
+          gtk_image_get_icon_name (wimage, &icon_name, &icon_size);
+          wmenuimage = gtk_image_new_from_icon_name (icon_name,
+                                                     GTK_ICON_SIZE_MENU);
+        }
+      else
+        {
+          fprintf (stderr, "internal error: store_type is %d\n", store_type);
+          abort ();
         }
     }
   if (wmenuimage)
@@ -3498,7 +3578,7 @@ xg_tool_bar_detach_callback (wbox, w, client_data)
 
       /* 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));
+      xg_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (f));
     }
 }
 
@@ -3529,7 +3609,7 @@ xg_tool_bar_attach_callback (wbox, w, client_data)
 
       /* 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_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (f));
     }
 }
 
@@ -3606,38 +3686,20 @@ xg_tool_bar_item_expose_callback (w, event, client_data)
   return FALSE;
 }
 
-/* This callback is called when a tool bar shall be redrawn.
-   We need to update the tool bar from here in case the image cache
-   has deleted the pixmaps used in the tool bar.
-   W is the GtkToolbar to be redrawn.
-   EVENT is the expose event for W.
-   CLIENT_DATA is pointing to the frame for this tool bar.
-
-   Returns FALSE to tell GTK to keep processing this event.  */
-
-static gboolean
-xg_tool_bar_expose_callback (w, event, client_data)
-     GtkWidget *w;
-     GdkEventExpose *event;
-     gpointer client_data;
-{
-  update_frame_tool_bar ((FRAME_PTR) client_data);
-  return FALSE;
-}
-
-/* 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);
@@ -3647,6 +3709,20 @@ 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;
+  GtkRequisition req;
+
+  x->toolbar_widget = gtk_toolbar_new ();
+  x->toolbar_detached = 0;
 
   gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
 
@@ -3660,26 +3736,43 @@ 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);
-  g_signal_connect (G_OBJECT (x->toolbar_widget),
-                    "expose-event",
-                    G_CALLBACK (xg_tool_bar_expose_callback),
-                    f);
 
-  gtk_widget_show_all (x->handlebox_widget);
+#define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
 
-  gtk_widget_size_request (x->toolbar_widget, &req);
-  FRAME_TOOLBAR_HEIGHT (f) = req.height;
+/* Find the right-to-left image named by RTL in the tool bar images for F.
+   Returns IMAGE if RTL is not found.  */
 
-  /* 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));
+static Lisp_Object
+find_rtl_image (f, image, rtl)
+     FRAME_PTR f;
+     Lisp_Object image;
+     Lisp_Object rtl;
+{
+  int i;
+  Lisp_Object file, rtl_name;
+  struct gcpro gcpro1, gcpro2;
+  GCPRO2 (file, rtl_name);
 
-  SET_FRAME_GARBAGED (f);
+  rtl_name = Ffile_name_nondirectory (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))) 
+        {
+          file = call1 (intern ("file-name-sans-extension"),
+                       Ffile_name_nondirectory (file));
+          if (EQ (Fequal (file, rtl_name), Qt))
+            {
+              image = rtl_image;
+              break;
+            }
+        }
+    }
+
+  return image;
 }
 
 /* Update the tool bar for frame F.  Add new buttons and remove old.  */
@@ -3692,7 +3785,10 @@ update_frame_tool_bar (f)
   GtkRequisition old_req, new_req;
   struct x_output *x = f->output_data.x;
   int hmargin = 0, vmargin = 0;
+  GtkToolbar *wtoolbar;
   GtkToolItem *ti;
+  GtkTextDirection dir;
+  int pack_tool_bar = x->handlebox_widget == NULL;
 
   if (! FRAME_GTK_WIDGET (f))
     return;
@@ -3726,20 +3822,29 @@ update_frame_tool_bar (f)
   if (! x->toolbar_widget)
     xg_create_tool_bar (f);
 
-  gtk_widget_size_request (x->toolbar_widget, &old_req);
+  wtoolbar = GTK_TOOLBAR (x->toolbar_widget);
+  gtk_widget_size_request (GTK_WIDGET (wtoolbar), &old_req);
+  dir = gtk_widget_get_direction (x->toolbar_widget);
 
   for (i = 0; i < f->n_tool_bar_items; ++i)
     {
-#define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
 
       int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
       int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
       int idx;
       int img_id;
-      struct image *img;
+      int icon_size = 0;
+      struct image *img = NULL;
       Lisp_Object image;
-      GtkWidget *wbutton;
+      Lisp_Object stock;
+      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);
 
@@ -3749,59 +3854,126 @@ update_frame_tool_bar (f)
           wbutton = gtk_bin_get_child (GTK_BIN (weventbox));
         }
 
-      /* If image is a vector, choose the image according to the
-        button state.  */
       image = PROP (TOOL_BAR_ITEM_IMAGES);
-      if (VECTORP (image))
-       {
-         if (enabled_p)
-           idx = (selected_p
-                  ? TOOL_BAR_IMAGE_ENABLED_SELECTED
-                  : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
-         else
-           idx = (selected_p
-                  ? TOOL_BAR_IMAGE_DISABLED_SELECTED
-                  : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
-
-         xassert (ASIZE (image) >= idx);
-         image = AREF (image, idx);
-       }
-      else
-       idx = -1;
 
       /* Ignore invalid image specifications.  */
       if (!valid_image_p (image))
         {
-          if (ti) gtk_widget_hide_all (GTK_WIDGET (ti));
+          if (wbutton) gtk_widget_hide (wbutton);
           continue;
         }
 
-      img_id = lookup_image (f, image);
-      img = IMAGE_FROM_ID (f, img_id);
-      prepare_image_for_display (f, img);
+      specified_file = file_for_image (image);
+      if (!NILP (specified_file) && EQ (Qt, Ffboundp (func)))
+        stock = call1 (func, specified_file);
 
-      if (img->load_failed_p || img->pixmap == None)
+      if (! NILP (stock) && STRINGP (stock))
         {
-          if (ti)
-           gtk_widget_hide_all (GTK_WIDGET (ti));
-         else
+          stock_name = SSDATA (stock);
+          if (stock_name[0] == 'n' && stock_name[1] == ':')
             {
-              /* Insert an empty (non-image) button */
-              weventbox = gtk_event_box_new ();
-              wbutton = gtk_button_new ();
-              gtk_button_set_focus_on_click (GTK_BUTTON (wbutton), FALSE);
-              gtk_button_set_relief (GTK_BUTTON (wbutton), GTK_RELIEF_NONE);
-              gtk_container_add (GTK_CONTAINER (weventbox), wbutton);
-              ti = gtk_tool_item_new ();
-              gtk_container_add (GTK_CONTAINER (ti), weventbox);
-              gtk_toolbar_insert (GTK_TOOLBAR (x->toolbar_widget), ti, i);
+              GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (wtoolbar));
+              GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (screen);
+
+              icon_name = stock_name + 2;
+              stock_name = NULL;
+              stock = Qnil;
+
+              if (! gtk_icon_theme_has_icon (icon_theme, icon_name))
+                icon_name = NULL;
+              else
+                icon_size = gtk_toolbar_get_icon_size (wtoolbar);
+            }
+          else if (gtk_stock_lookup (SSDATA (stock), &stock_item))
+              icon_size = gtk_toolbar_get_icon_size (wtoolbar);
+          else 
+            {
+              stock = Qnil;
+              stock_name = NULL;
+            }
+        }
+
+      if (stock_name == NULL && icon_name == NULL)
+        {
+          /* No stock image, or stock item not known.  Try regular image.  */
+
+          /* If image is a vector, choose the image according to the
+             button state.  */
+          if (dir == GTK_TEXT_DIR_RTL
+              && !NILP (rtl = PROP (TOOL_BAR_ITEM_RTL_IMAGE))
+              && STRINGP (rtl))
+            {
+              image = find_rtl_image (f, image, rtl);
+            }
+
+          if (VECTORP (image))
+            {
+              if (enabled_p)
+                idx = (selected_p
+                       ? TOOL_BAR_IMAGE_ENABLED_SELECTED
+                       : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
+              else
+                idx = (selected_p
+                       ? TOOL_BAR_IMAGE_DISABLED_SELECTED
+                       : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
+
+              xassert (ASIZE (image) >= idx);
+              image = AREF (image, idx);
+            }
+          else
+            idx = -1;
+
+          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)
+                    gtk_widget_hide_all (GTK_WIDGET (ti));
+                else
+                {
+                    /* Insert an empty (non-image) button */
+                    weventbox = gtk_event_box_new ();
+                    wbutton = gtk_button_new ();
+                    gtk_button_set_focus_on_click (GTK_BUTTON (wbutton), FALSE);
+                    gtk_button_set_relief (GTK_BUTTON (wbutton),
+                                           GTK_RELIEF_NONE);
+                    gtk_container_add (GTK_CONTAINER (weventbox), wbutton);
+                    ti = gtk_tool_item_new ();
+                    gtk_container_add (GTK_CONTAINER (ti), weventbox);
+                    gtk_toolbar_insert (GTK_TOOLBAR (x->toolbar_widget), ti, -1);
+                }
+                continue;
             }
-          continue;
         }
 
       if (ti == NULL)
         {
-          GtkWidget *w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
+          GtkWidget *w;
+          if (stock_name)
+            {
+              w = gtk_image_new_from_stock (stock_name, icon_size);
+              g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_STOCK_NAME,
+                                      (gpointer) xstrdup (stock_name),
+                                      (GDestroyNotify) xfree);
+            }
+          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,
+                                      (gpointer) xstrdup (icon_name),
+                                      (GDestroyNotify) xfree);
+            }
+          else
+            {
+              w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
+              /* Save the image so we can see if an update is needed when
+                 this function is called again.  */
+              g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
+                                 (gpointer)img->pixmap);
+            }
+
           gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
           wbutton = gtk_button_new ();
           gtk_button_set_focus_on_click (GTK_BUTTON (wbutton), FALSE);
@@ -3811,7 +3983,7 @@ update_frame_tool_bar (f)
           gtk_container_add (GTK_CONTAINER (weventbox), wbutton);
           ti = gtk_tool_item_new ();
           gtk_container_add (GTK_CONTAINER (ti), weventbox);
-          gtk_toolbar_insert (GTK_TOOLBAR (x->toolbar_widget), ti, i);
+          gtk_toolbar_insert (GTK_TOOLBAR (x->toolbar_widget), ti, -1);
 
 
           /* The EMACS_INT cast avoids a warning. */
@@ -3825,10 +3997,6 @@ update_frame_tool_bar (f)
 
           gtk_widget_show_all (GTK_WIDGET (ti));
 
-          /* Save the image so we can see if an update is needed when
-             this function is called again.  */
-          g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
-                             (gpointer)img->pixmap);
 
           g_object_set_data (G_OBJECT (weventbox), XG_FRAME_DATA, (gpointer)f);
 
@@ -3870,13 +4038,47 @@ update_frame_tool_bar (f)
           GtkWidget *wimage = gtk_bin_get_child (GTK_BIN (wbutton));
           Pixmap old_img = (Pixmap)g_object_get_data (G_OBJECT (wimage),
                                                       XG_TOOL_BAR_IMAGE_DATA);
-          gtk_misc_set_padding (GTK_MISC (wimage), hmargin, vmargin);
+          gpointer old_stock_name = g_object_get_data (G_OBJECT (wimage),
+                                                       XG_TOOL_BAR_STOCK_NAME);
+          gpointer old_icon_name = g_object_get_data (G_OBJECT (wimage),
+                                                      XG_TOOL_BAR_ICON_NAME);
+          if (stock_name &&
+              (! old_stock_name || strcmp (old_stock_name, stock_name) != 0))
+            {
+              gtk_image_set_from_stock (GTK_IMAGE (wimage),
+                                        stock_name, icon_size);
+              g_object_set_data_full (G_OBJECT (wimage), XG_TOOL_BAR_STOCK_NAME,
+                                      (gpointer) xstrdup (stock_name),
+                                      (GDestroyNotify) xfree);
+              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
+                                 NULL);
+              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_ICON_NAME, NULL);
+            }
+          else if (icon_name &&
+                   (! old_icon_name || strcmp (old_icon_name, icon_name) != 0))
+            {
+              gtk_image_set_from_icon_name (GTK_IMAGE (wimage),
+                                            icon_name, icon_size);
+              g_object_set_data_full (G_OBJECT (wimage), XG_TOOL_BAR_ICON_NAME,
+                                      (gpointer) xstrdup (icon_name),
+                                      (GDestroyNotify) xfree);
+              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
+                                 NULL);
+              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_STOCK_NAME,
+                                 NULL);
+            }
+          else if (img && old_img != img->pixmap)
+            {
+              (void) xg_get_image_for_pixmap (f, img, x->widget, wimage);
+              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
+                                 (gpointer)img->pixmap);
 
-          if (old_img != img->pixmap)
-            (void) xg_get_image_for_pixmap (f, img, x->widget, wimage);
+              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_STOCK_NAME,
+                                 NULL);
+              g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_ICON_NAME, NULL);
+            }
 
-          g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
-                             (gpointer)img->pixmap);
+          gtk_misc_set_padding (GTK_MISC (wimage), hmargin, vmargin);
 
           gtk_widget_set_sensitive (wbutton, enabled_p);
           gtk_widget_show_all (GTK_WIDGET (ti));
@@ -3893,12 +4095,19 @@ update_frame_tool_bar (f)
       if (ti) gtk_widget_hide_all (GTK_WIDGET (ti));
     } while (ti != NULL);
 
-  gtk_widget_size_request (x->toolbar_widget, &new_req);
-  if (old_req.height != new_req.height
+  new_req.height = 0;
+  gtk_widget_size_request (GTK_WIDGET (wtoolbar), &new_req);
+
+  if (pack_tool_bar && f->n_tool_bar_items != 0)
+    xg_pack_tool_bar (f);
+
+  if (new_req.height != 0
+      && f->n_tool_bar_items != 0
+      && 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_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (f));
     }
 
   UNBLOCK_INPUT;
@@ -3915,18 +4124,23 @@ 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;
 
       /* 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);
+      xg_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (f));
       UNBLOCK_INPUT;
     }
 }
@@ -3946,6 +4160,8 @@ xg_initialize ()
      we keep it permanently linked in.  */
   XftInit (0);
 #endif
+
+  gdpy_def = NULL;
   xg_ignore_gtk_scrollbar = 0;
   xg_detached_menus = 0;
   xg_menu_cb_list.prev = xg_menu_cb_list.next =