]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkbutton.c
Apply a cleanup patch by Kjartan Maraas (#341812)
[~andy/gtk] / gtk / gtkbutton.c
index 8190ad8fa2b8cb7459c79373b5df71631dedc01b..4c51a47d68493229109b4033cdc053f5a8f2eb7a 100644 (file)
 #include "gtkmarshalers.h"
 #include "gtkimage.h"
 #include "gtkhbox.h"
+#include "gtkvbox.h"
 #include "gtkstock.h"
 #include "gtkiconfactory.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
 #include "gtkalias.h"
 
-#define CHILD_SPACING     1
-
 static const GtkBorder default_default_border = { 1, 1, 1, 1 };
 static const GtkBorder default_default_outside_border = { 0, 0, 0, 0 };
+static const GtkBorder default_inner_border = { 1, 1, 1, 1 };
 
 /* Time out before giving up on getting a key release when animating
  * the close button.
@@ -69,6 +69,7 @@ enum {
   PROP_FOCUS_ON_CLICK,
   PROP_XALIGN,
   PROP_YALIGN,
+  PROP_IMAGE_POSITION
 };
 
 #define GTK_BUTTON_GET_PRIVATE(o)       (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_BUTTON, GtkButtonPrivate))
@@ -76,55 +77,60 @@ typedef struct _GtkButtonPrivate GtkButtonPrivate;
 
 struct _GtkButtonPrivate
 {
-  gfloat       xalign;
-  gfloat       yalign;
-  GtkWidget   *image;
-  guint        align_set : 1;
-  guint        image_is_stock : 1;
+  gfloat          xalign;
+  gfloat          yalign;
+  GtkWidget      *image;
+  guint           align_set      : 1;
+  guint           image_is_stock : 1;
+  guint           has_grab       : 1;
+  guint32         grab_time;
+  GtkPositionType image_position;
 };
 
-static void gtk_button_class_init     (GtkButtonClass   *klass);
-static void gtk_button_init           (GtkButton        *button);
-static void gtk_button_destroy        (GtkObject        *object);
-static void gtk_button_set_property   (GObject         *object,
-                                       guint            prop_id,
-                                       const GValue    *value,
-                                       GParamSpec      *pspec);
-static void gtk_button_get_property   (GObject         *object,
-                                       guint            prop_id,
-                                       GValue          *value,
-                                       GParamSpec      *pspec);
-static void gtk_button_screen_changed (GtkWidget        *widget,
-                                      GdkScreen        *previous_screen);
-static void gtk_button_realize        (GtkWidget        *widget);
-static void gtk_button_unrealize      (GtkWidget        *widget);
-static void gtk_button_map            (GtkWidget        *widget);
-static void gtk_button_unmap          (GtkWidget        *widget);
-static void gtk_button_size_request   (GtkWidget        *widget,
-                                      GtkRequisition   *requisition);
-static void gtk_button_size_allocate  (GtkWidget        *widget,
-                                      GtkAllocation    *allocation);
-static gint gtk_button_expose         (GtkWidget        *widget,
-                                      GdkEventExpose   *event);
-static gint gtk_button_button_press   (GtkWidget        *widget,
-                                      GdkEventButton   *event);
-static gint gtk_button_button_release (GtkWidget        *widget,
-                                      GdkEventButton   *event);
-static gint gtk_button_key_release    (GtkWidget        *widget,
-                                      GdkEventKey      *event);
-static gint gtk_button_enter_notify   (GtkWidget        *widget,
-                                      GdkEventCrossing *event);
-static gint gtk_button_leave_notify   (GtkWidget        *widget,
-                                      GdkEventCrossing *event);
-static void gtk_real_button_pressed   (GtkButton        *button);
-static void gtk_real_button_released  (GtkButton        *button);
-static void gtk_real_button_activate  (GtkButton         *button);
-static void gtk_button_update_state   (GtkButton        *button);
-static void gtk_button_add            (GtkContainer   *container,
-                                      GtkWidget      *widget);
-static GType gtk_button_child_type    (GtkContainer     *container);
-static void gtk_button_finish_activate (GtkButton *button,
-                                       gboolean   do_it);
+static void gtk_button_destroy        (GtkObject          *object);
+static void gtk_button_set_property   (GObject            *object,
+                                       guint               prop_id,
+                                       const GValue       *value,
+                                       GParamSpec         *pspec);
+static void gtk_button_get_property   (GObject            *object,
+                                       guint               prop_id,
+                                       GValue             *value,
+                                       GParamSpec         *pspec);
+static void gtk_button_screen_changed (GtkWidget          *widget,
+                                      GdkScreen          *previous_screen);
+static void gtk_button_realize        (GtkWidget          *widget);
+static void gtk_button_unrealize      (GtkWidget          *widget);
+static void gtk_button_map            (GtkWidget          *widget);
+static void gtk_button_unmap          (GtkWidget          *widget);
+static void gtk_button_style_set      (GtkWidget          *widget,
+                                      GtkStyle           *prev_style);
+static void gtk_button_size_request   (GtkWidget          *widget,
+                                      GtkRequisition     *requisition);
+static void gtk_button_size_allocate  (GtkWidget          *widget,
+                                      GtkAllocation      *allocation);
+static gint gtk_button_expose         (GtkWidget          *widget,
+                                      GdkEventExpose     *event);
+static gint gtk_button_button_press   (GtkWidget          *widget,
+                                      GdkEventButton     *event);
+static gint gtk_button_button_release (GtkWidget          *widget,
+                                      GdkEventButton     *event);
+static gint gtk_button_grab_broken    (GtkWidget          *widget,
+                                      GdkEventGrabBroken *event);
+static gint gtk_button_key_release    (GtkWidget          *widget,
+                                      GdkEventKey        *event);
+static gint gtk_button_enter_notify   (GtkWidget          *widget,
+                                      GdkEventCrossing   *event);
+static gint gtk_button_leave_notify   (GtkWidget          *widget,
+                                      GdkEventCrossing   *event);
+static void gtk_real_button_pressed   (GtkButton          *button);
+static void gtk_real_button_released  (GtkButton          *button);
+static void gtk_real_button_activate  (GtkButton          *button);
+static void gtk_button_update_state   (GtkButton          *button);
+static void gtk_button_add            (GtkContainer       *container,
+                                      GtkWidget          *widget);
+static GType gtk_button_child_type    (GtkContainer       *container);
+static void gtk_button_finish_activate (GtkButton         *button,
+                                       gboolean           do_it);
 
 static GObject*        gtk_button_constructor (GType                  type,
                                        guint                  n_construct_properties,
@@ -136,37 +142,9 @@ static void gtk_button_grab_notify     (GtkWidget             *widget,
                                        gboolean               was_grabbed);
 
 
-
-static GtkBinClass *parent_class = NULL;
 static guint button_signals[LAST_SIGNAL] = { 0 };
 
-
-GType
-gtk_button_get_type (void)
-{
-  static GType button_type = 0;
-
-  if (!button_type)
-    {
-      static const GTypeInfo button_info =
-      {
-       sizeof (GtkButtonClass),
-       NULL,           /* base_init */
-       NULL,           /* base_finalize */
-       (GClassInitFunc) gtk_button_class_init,
-       NULL,           /* class_finalize */
-       NULL,           /* class_data */
-       sizeof (GtkButton),
-       16,             /* n_preallocs */
-       (GInstanceInitFunc) gtk_button_init,
-      };
-
-      button_type = g_type_register_static (GTK_TYPE_BIN, "GtkButton",
-                                           &button_info, 0);
-    }
-
-  return button_type;
-}
+G_DEFINE_TYPE (GtkButton, gtk_button, GTK_TYPE_BIN)
 
 static void
 gtk_button_class_init (GtkButtonClass *klass)
@@ -181,8 +159,6 @@ gtk_button_class_init (GtkButtonClass *klass)
   widget_class = (GtkWidgetClass*) klass;
   container_class = (GtkContainerClass*) klass;
   
-  parent_class = g_type_class_peek_parent (klass);
-
   gobject_class->constructor = gtk_button_constructor;
   gobject_class->set_property = gtk_button_set_property;
   gobject_class->get_property = gtk_button_get_property;
@@ -194,11 +170,13 @@ gtk_button_class_init (GtkButtonClass *klass)
   widget_class->unrealize = gtk_button_unrealize;
   widget_class->map = gtk_button_map;
   widget_class->unmap = gtk_button_unmap;
+  widget_class->style_set = gtk_button_style_set;
   widget_class->size_request = gtk_button_size_request;
   widget_class->size_allocate = gtk_button_size_allocate;
   widget_class->expose_event = gtk_button_expose;
   widget_class->button_press_event = gtk_button_button_press;
   widget_class->button_release_event = gtk_button_button_release;
+  widget_class->grab_broken_event = gtk_button_grab_broken;
   widget_class->key_release_event = gtk_button_key_release;
   widget_class->enter_notify_event = gtk_button_enter_notify;
   widget_class->leave_notify_event = gtk_button_leave_notify;
@@ -309,6 +287,22 @@ gtk_button_class_init (GtkButtonClass *klass)
                                                         GTK_TYPE_WIDGET,
                                                         GTK_PARAM_READWRITE));
 
+  /**
+   * GtkButton:image-position:
+   *
+   * The position of the image relative to the text inside the button.
+   * 
+   * Since: 2.10
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_IMAGE_POSITION,
+                                   g_param_spec_enum ("image-position",
+                                            P_("Image position"),
+                                                      P_("The position of the image relative to the text"),
+                                                      GTK_TYPE_POSITION_TYPE,
+                                                      GTK_POS_LEFT,
+                                                      GTK_PARAM_READWRITE));
+
   /**
    * GtkButton::pressed:
    * @button: the object that received the signal
@@ -318,7 +312,7 @@ gtk_button_class_init (GtkButtonClass *klass)
    * @Deprecated: Use the GtkWidget::button-press-event signal.
    */ 
   button_signals[PRESSED] =
-    g_signal_new ("pressed",
+    g_signal_new (I_("pressed"),
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkButtonClass, pressed),
@@ -335,7 +329,7 @@ gtk_button_class_init (GtkButtonClass *klass)
    * @Deprecated: Use the GtkWidget::button-release-event signal.
    */ 
   button_signals[RELEASED] =
-    g_signal_new ("released",
+    g_signal_new (I_("released"),
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkButtonClass, released),
@@ -350,7 +344,7 @@ gtk_button_class_init (GtkButtonClass *klass)
    * Emitted when the button has been activated (pressed and released).
    */ 
   button_signals[CLICKED] =
-    g_signal_new ("clicked",
+    g_signal_new (I_("clicked"),
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                  G_STRUCT_OFFSET (GtkButtonClass, clicked),
@@ -367,7 +361,7 @@ gtk_button_class_init (GtkButtonClass *klass)
    * @Deprecated: Use the GtkWidget::enter-notify-event signal.
    */ 
   button_signals[ENTER] =
-    g_signal_new ("enter",
+    g_signal_new (I_("enter"),
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkButtonClass, enter),
@@ -384,7 +378,7 @@ gtk_button_class_init (GtkButtonClass *klass)
    * @Deprecated: Use the GtkWidget::leave-notify-event signal.
    */ 
   button_signals[LEAVE] =
-    g_signal_new ("leave",
+    g_signal_new (I_("leave"),
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkButtonClass, leave),
@@ -402,7 +396,7 @@ gtk_button_class_init (GtkButtonClass *klass)
    * "clicked" signal.
    */
   button_signals[ACTIVATE] =
-    g_signal_new ("activate",
+    g_signal_new (I_("activate"),
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
                  G_STRUCT_OFFSET (GtkButtonClass, activate),
@@ -456,13 +450,44 @@ gtk_button_class_init (GtkButtonClass *klass)
                                                       FALSE,
                                                       GTK_PARAM_READABLE));
 
+  /**
+   * GtkButton:inner-border:
+   *
+   * Sets the border between the button edges and child.
+   *
+   * Since: 2.10
+   */
+  gtk_widget_class_install_style_property (widget_class,
+                                          g_param_spec_boxed ("inner-border",
+                                                               P_("Inner Border"),
+                                                               P_("Border between button edges and child."),
+                                                               GTK_TYPE_BORDER,
+                                                               GTK_PARAM_READABLE));
+
+  /**
+   * GtkButton::image-spacing:
+   * 
+   * Spacing in pixels between the image and label.
+   * 
+   * Since: 2.10
+   */
+  gtk_widget_class_install_style_property (widget_class,
+                                          g_param_spec_int ("image-spacing",
+                                                            P_("Image spacing"),
+                                                            P_("Spacing in pixels between the image and label"),
+                                                            0,
+                                                            G_MAXINT,
+                                                            2,
+                                                            GTK_PARAM_READABLE));
+  
+
   gtk_settings_install_property (g_param_spec_boolean ("gtk-button-images",
                                                       P_("Show button images"),
                                                       P_("Whether stock icons should be shown in buttons"),
                                                       TRUE,
                                                       GTK_PARAM_READWRITE));
-  
-  g_type_class_add_private (gobject_class, sizeof (GtkButtonPrivate));  
+
+  g_type_class_add_private (gobject_class, sizeof (GtkButtonPrivate));
 }
 
 static void
@@ -489,6 +514,7 @@ gtk_button_init (GtkButton *button)
   priv->yalign = 0.5;
   priv->align_set = 0;
   priv->image_is_stock = TRUE;
+  priv->image_position = GTK_POS_LEFT;
 }
 
 static void
@@ -502,7 +528,7 @@ gtk_button_destroy (GtkObject *object)
       button->label_text = NULL;
     }
   
-  (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+  (* GTK_OBJECT_CLASS (gtk_button_parent_class)->destroy) (object);
 }
 
 static GObject*
@@ -513,9 +539,9 @@ gtk_button_constructor (GType                  type,
   GObject *object;
   GtkButton *button;
 
-  object = (* G_OBJECT_CLASS (parent_class)->constructor) (type,
-                                                          n_construct_properties,
-                                                          construct_params);
+  object = (* G_OBJECT_CLASS (gtk_button_parent_class)->constructor) (type,
+                                                                     n_construct_properties,
+                                                                     construct_params);
 
   button = GTK_BUTTON (object);
   button->constructed = TRUE;
@@ -565,7 +591,7 @@ gtk_button_add (GtkContainer *container,
 {
   maybe_set_alignment (GTK_BUTTON (container), widget);
 
-  GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
+  GTK_CONTAINER_CLASS (gtk_button_parent_class)->add (container, widget);
 }
 
 static void
@@ -603,6 +629,9 @@ gtk_button_set_property (GObject         *object,
     case PROP_YALIGN:
       gtk_button_set_alignment (button, priv->xalign, g_value_get_float (value));
       break;
+    case PROP_IMAGE_POSITION:
+      gtk_button_set_image_position (button, g_value_get_enum (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -644,6 +673,9 @@ gtk_button_get_property (GObject         *object,
     case PROP_YALIGN:
       g_value_set_float (value, priv->yalign);
       break;
+    case PROP_IMAGE_POSITION:
+      g_value_set_enum (value, priv->image_position);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -659,10 +691,17 @@ gtk_button_new (void)
 static gboolean
 show_image (GtkButton *button)
 {
-  GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (button));  
   gboolean show;
+  
+  if (button->label_text)
+    {
+      GtkSettings *settings;
 
-  g_object_get (settings, "gtk-button-images", &show, NULL);
+      settings = gtk_widget_get_settings (GTK_WIDGET (button));        
+      g_object_get (settings, "gtk-button-images", &show, NULL);
+    }
+  else
+    show = TRUE;
 
   return show;
 }
@@ -673,33 +712,37 @@ gtk_button_construct_child (GtkButton *button)
   GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
   GtkStockItem item;
   GtkWidget *label;
-  GtkWidget *hbox;
+  GtkWidget *box;
   GtkWidget *align;
   GtkWidget *image = NULL;
   gchar *label_text = NULL;
+  gint image_spacing;
   
   if (!button->constructed)
     return;
-  
-  if (button->label_text == NULL)
+  if (!button->label_text && !priv->image)
     return;
+  
+  gtk_widget_style_get (GTK_WIDGET (button), 
+                       "image-spacing", &image_spacing, 
+                       NULL);
 
-  if (GTK_BIN (button)->child)
+  if (priv->image && !priv->image_is_stock)
     {
-      if (priv->image && !priv->image_is_stock)
-       {
-         image = g_object_ref (priv->image);
-         if (image->parent)
-           gtk_container_remove (GTK_CONTAINER (image->parent), image);
-       }
-
-      gtk_container_remove (GTK_CONTAINER (button),
-                           GTK_BIN (button)->child);
-  
+      image = g_object_ref (priv->image);
+      if (image->parent)
+       gtk_container_remove (GTK_CONTAINER (image->parent), image);
+      
       priv->image = NULL;
     }
   
+  if (GTK_BIN (button)->child)
+    gtk_container_remove (GTK_CONTAINER (button),
+                         GTK_BIN (button)->child);
+  
   if (button->use_stock &&
+      button->label_text &&
       gtk_stock_lookup (button->label_text, &item))
     {
       if (!image)
@@ -712,27 +755,44 @@ gtk_button_construct_child (GtkButton *button)
 
   if (image)
     {
-      label = gtk_label_new_with_mnemonic (label_text);
-      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
-      
       priv->image = image;
-
       g_object_set (priv->image, 
                    "visible", show_image (button),
                    "no-show-all", TRUE,
                    NULL);
-      hbox = gtk_hbox_new (FALSE, 2);
+
+      if (priv->image_position == GTK_POS_LEFT ||
+         priv->image_position == GTK_POS_RIGHT)
+       box = gtk_hbox_new (FALSE, image_spacing);
+      else
+       box = gtk_vbox_new (FALSE, image_spacing);
 
       if (priv->align_set)
        align = gtk_alignment_new (priv->xalign, priv->yalign, 0.0, 0.0);
       else
        align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
-       
-      gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0);
-      gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+      if (priv->image_position == GTK_POS_LEFT ||
+         priv->image_position == GTK_POS_TOP)
+       gtk_box_pack_start (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
+      else
+       gtk_box_pack_end (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
+
+      if (label_text)
+       {
+         label = gtk_label_new_with_mnemonic (label_text);
+         gtk_label_set_mnemonic_widget (GTK_LABEL (label), 
+                                        GTK_WIDGET (button));
+
+         if (priv->image_position == GTK_POS_RIGHT ||
+             priv->image_position == GTK_POS_BOTTOM)
+           gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+         else
+           gtk_box_pack_end (GTK_BOX (box), label, FALSE, FALSE, 0);
+       }
       
       gtk_container_add (GTK_CONTAINER (button), align);
-      gtk_container_add (GTK_CONTAINER (align), hbox);
+      gtk_container_add (GTK_CONTAINER (align), box);
       gtk_widget_show_all (align);
 
       g_object_unref (image);
@@ -740,7 +800,7 @@ gtk_button_construct_child (GtkButton *button)
       return;
     }
   
- if (button->use_underline)
 if (button->use_underline)
     {
       label = gtk_label_new_with_mnemonic (button->label_text);
       gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
@@ -750,7 +810,7 @@ gtk_button_construct_child (GtkButton *button)
   
   if (priv->align_set)
     gtk_misc_set_alignment (GTK_MISC (label), priv->xalign, priv->yalign);
-
+  
   gtk_widget_show (label);
   gtk_container_add (GTK_CONTAINER (button), label);
 }
@@ -919,7 +979,7 @@ gtk_button_unrealize (GtkWidget *widget)
       button->event_window = NULL;
     }
   
-  GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
+  GTK_WIDGET_CLASS (gtk_button_parent_class)->unrealize (widget);
 }
 
 static void
@@ -927,7 +987,7 @@ gtk_button_map (GtkWidget *widget)
 {
   GtkButton *button = GTK_BUTTON (widget);
   
-  GTK_WIDGET_CLASS (parent_class)->map (widget);
+  GTK_WIDGET_CLASS (gtk_button_parent_class)->map (widget);
 
   if (button->event_window)
     gdk_window_show (button->event_window);
@@ -941,13 +1001,50 @@ gtk_button_unmap (GtkWidget *widget)
   if (button->event_window)
     gdk_window_hide (button->event_window);
 
-  GTK_WIDGET_CLASS (parent_class)->unmap (widget);
+  GTK_WIDGET_CLASS (gtk_button_parent_class)->unmap (widget);
+}
+
+static void
+gtk_button_update_image_spacing (GtkButton *button)
+{
+  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
+  GtkWidget *child; 
+  gint spacing;
+
+  /* Keep in sync with gtk_button_construct_child,
+   * we only want to update the spacing if the box 
+   * was constructed there.
+   */
+  if (!button->constructed || !priv->image)
+    return;
+
+  child = GTK_BIN (button)->child;
+  if (GTK_IS_ALIGNMENT (child))
+    {
+      child = GTK_BIN (child)->child;
+      if (GTK_IS_BOX (child))
+        {
+          gtk_widget_style_get (GTK_WIDGET (button),
+                                "image-spacing", &spacing,
+                                NULL);
+
+          gtk_box_set_spacing (GTK_BOX (child), spacing);
+        }
+    }   
+}
+
+static void
+gtk_button_style_set (GtkWidget *widget,
+                     GtkStyle  *prev_style)
+{
+  gtk_button_update_image_spacing (GTK_BUTTON (widget));
 }
 
 static void
 gtk_button_get_props (GtkButton *button,
                      GtkBorder *default_border,
                      GtkBorder *default_outside_border,
+                      GtkBorder *inner_border,
                      gboolean  *interior_focus)
 {
   GtkWidget *widget =  GTK_WIDGET (button);
@@ -979,6 +1076,19 @@ gtk_button_get_props (GtkButton *button,
        *default_outside_border = default_default_outside_border;
     }
 
+  if (inner_border)
+    {
+      gtk_widget_style_get (widget, "inner-border", &tmp_border, NULL);
+
+      if (tmp_border)
+       {
+         *inner_border = *tmp_border;
+         g_free (tmp_border);
+       }
+      else
+       *inner_border = default_inner_border;
+    }
+
   if (interior_focus)
     gtk_widget_style_get (widget, "interior-focus", interior_focus, NULL);
 }
@@ -989,19 +1099,22 @@ gtk_button_size_request (GtkWidget      *widget,
 {
   GtkButton *button = GTK_BUTTON (widget);
   GtkBorder default_border;
+  GtkBorder inner_border;
   gint focus_width;
   gint focus_pad;
 
-  gtk_button_get_props (button, &default_border, NULL, NULL);
+  gtk_button_get_props (button, &default_border, NULL, &inner_border, NULL);
   gtk_widget_style_get (GTK_WIDGET (widget),
                        "focus-line-width", &focus_width,
                        "focus-padding", &focus_pad,
                        NULL);
  
-  requisition->width = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
-                       GTK_WIDGET (widget)->style->xthickness) * 2;
-  requisition->height = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
-                        GTK_WIDGET (widget)->style->ythickness) * 2;
+  requisition->width = ((GTK_CONTAINER (widget)->border_width +
+                         GTK_WIDGET (widget)->style->xthickness) * 2 +
+                        inner_border.left + inner_border.right);
+  requisition->height = ((GTK_CONTAINER (widget)->border_width +
+                          GTK_WIDGET (widget)->style->ythickness) * 2 +
+                         inner_border.top + inner_border.bottom);
 
   if (GTK_WIDGET_CAN_DEFAULT (widget))
     {
@@ -1034,10 +1147,11 @@ gtk_button_size_allocate (GtkWidget     *widget,
   gint xthickness = GTK_WIDGET (widget)->style->xthickness;
   gint ythickness = GTK_WIDGET (widget)->style->ythickness;
   GtkBorder default_border;
+  GtkBorder inner_border;
   gint focus_width;
   gint focus_pad;
 
-  gtk_button_get_props (button, &default_border, NULL, NULL);
+  gtk_button_get_props (button, &default_border, NULL, &inner_border, NULL);
   gtk_widget_style_get (GTK_WIDGET (widget),
                        "focus-line-width", &focus_width,
                        "focus-padding", &focus_pad,
@@ -1055,12 +1169,18 @@ gtk_button_size_allocate (GtkWidget     *widget,
 
   if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
     {
-      child_allocation.x = widget->allocation.x + border_width + CHILD_SPACING + xthickness;
-      child_allocation.y = widget->allocation.y + border_width + CHILD_SPACING + ythickness;
+      child_allocation.x = widget->allocation.x + border_width + inner_border.left + xthickness;
+      child_allocation.y = widget->allocation.y + border_width + inner_border.top + ythickness;
       
-      child_allocation.width = MAX (1, widget->allocation.width - (CHILD_SPACING + xthickness) * 2 -
+      child_allocation.width = MAX (1, widget->allocation.width -
+                                    xthickness * 2 -
+                                    inner_border.left -
+                                    inner_border.right -
                                    border_width * 2);
-      child_allocation.height = MAX (1, widget->allocation.height - (CHILD_SPACING + ythickness) * 2 -
+      child_allocation.height = MAX (1, widget->allocation.height -
+                                     ythickness * 2 -
+                                     inner_border.top -
+                                     inner_border.bottom -
                                     border_width * 2);
 
       if (GTK_WIDGET_CAN_DEFAULT (button))
@@ -1119,7 +1239,7 @@ _gtk_button_paint (GtkButton    *button,
       widget = GTK_WIDGET (button);
       border_width = GTK_CONTAINER (widget)->border_width;
 
-      gtk_button_get_props (button, &default_border, &default_outside_border, &interior_focus);
+      gtk_button_get_props (button, &default_border, &default_outside_border, NULL, &interior_focus);
       gtk_widget_style_get (GTK_WIDGET (widget),
                            "focus-line-width", &focus_width,
                            "focus-padding", &focus_pad,
@@ -1219,7 +1339,7 @@ gtk_button_expose (GtkWidget      *widget,
                         button->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
                         "button", "buttondefault");
       
-      (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
+      (* GTK_WIDGET_CLASS (gtk_button_parent_class)->expose_event) (widget, event);
     }
   
   return FALSE;
@@ -1260,6 +1380,29 @@ gtk_button_button_release (GtkWidget      *widget,
   return TRUE;
 }
 
+static gboolean
+gtk_button_grab_broken (GtkWidget          *widget,
+                       GdkEventGrabBroken *event)
+{
+  GtkButton *button = GTK_BUTTON (widget);
+  gboolean save_in;
+  
+  /* Simulate a button release without the pointer in the button */
+  if (button->button_down)
+    {
+      save_in = button->in_button;
+      button->in_button = FALSE;
+      gtk_button_released (button);
+      if (save_in != button->in_button)
+       {
+         button->in_button = save_in;
+         gtk_button_update_state (button);
+       }
+    }
+
+  return TRUE;
+}
+
 static gboolean
 gtk_button_key_release (GtkWidget   *widget,
                        GdkEventKey *event)
@@ -1271,8 +1414,8 @@ gtk_button_key_release (GtkWidget   *widget,
       gtk_button_finish_activate (button, TRUE);
       return TRUE;
     }
-  else if (GTK_WIDGET_CLASS (parent_class)->key_release_event)
-    return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event);
+  else if (GTK_WIDGET_CLASS (gtk_button_parent_class)->key_release_event)
+    return GTK_WIDGET_CLASS (gtk_button_parent_class)->key_release_event (widget, event);
   else
     return FALSE;
 }
@@ -1360,21 +1503,29 @@ static void
 gtk_real_button_activate (GtkButton *button)
 {
   GtkWidget *widget = GTK_WIDGET (button);
-  
+  GtkButtonPrivate *priv;
+  guint32 time;
+
+  priv = GTK_BUTTON_GET_PRIVATE (button);
+
   if (GTK_WIDGET_REALIZED (button) && !button->activate_timeout)
     {
-      if (gdk_keyboard_grab (button->event_window, TRUE,
-                            gtk_get_current_event_time ()) == 0)
+      time = gtk_get_current_event_time ();
+      if (gdk_keyboard_grab (button->event_window, TRUE, time) == 
+         GDK_GRAB_SUCCESS)
        {
-         gtk_grab_add (widget);
-         
-         button->activate_timeout = g_timeout_add (ACTIVATE_TIMEOUT,
-                                                   button_activate_timeout,
-                                                   button);
-         button->button_down = TRUE;
-         gtk_button_update_state (button);
-         gtk_widget_queue_draw (GTK_WIDGET (button));
+         priv->has_grab = TRUE;
+         priv->grab_time = time;
        }
+
+      gtk_grab_add (widget);
+      
+      button->activate_timeout = g_timeout_add (ACTIVATE_TIMEOUT,
+                                               button_activate_timeout,
+                                               button);
+      button->button_down = TRUE;
+      gtk_button_update_state (button);
+      gtk_widget_queue_draw (GTK_WIDGET (button));
     }
 }
 
@@ -1383,12 +1534,18 @@ gtk_button_finish_activate (GtkButton *button,
                            gboolean   do_it)
 {
   GtkWidget *widget = GTK_WIDGET (button);
+  GtkButtonPrivate *priv;
   
+  priv = GTK_BUTTON_GET_PRIVATE (button);
+
   g_source_remove (button->activate_timeout);
   button->activate_timeout = 0;
 
-  gdk_display_keyboard_ungrab (gtk_widget_get_display (widget),
-                              gtk_get_current_event_time ());
+  if (priv->has_grab)
+    {
+      gdk_display_keyboard_ungrab (gtk_widget_get_display (widget),
+                                  priv->grab_time);
+    }
   gtk_grab_remove (widget);
 
   button->button_down = FALSE;
@@ -1753,9 +1910,9 @@ gtk_button_screen_changed (GtkWidget *widget,
 
   show_image_connection =
     g_signal_connect (settings, "notify::gtk-button-images",
-                     G_CALLBACK (gtk_button_setting_changed), 0);
+                     G_CALLBACK (gtk_button_setting_changed), NULL);
   g_object_set_data (G_OBJECT (settings), 
-                    "gtk-button-connection",
+                    I_("gtk-button-connection"),
                     GUINT_TO_POINTER (show_image_connection));
 
   show_image_change_notify (GTK_BUTTON (widget));
@@ -1803,7 +1960,12 @@ void
 gtk_button_set_image (GtkButton *button,
                      GtkWidget *image)
 {
-  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
+  GtkButtonPrivate *priv;
+
+  g_return_if_fail (GTK_IS_BUTTON (button));
+  g_return_if_fail (image == NULL || GTK_IS_WIDGET (image));
+
+  priv = GTK_BUTTON_GET_PRIVATE (button);
 
   priv->image = image;
   priv->image_is_stock = (image == NULL);
@@ -1836,6 +1998,62 @@ gtk_button_get_image (GtkButton *button)
   
   return priv->image;
 }
+
+/**
+ * gtk_button_set_image_position:
+ * @button: a #GtkButton
+ * @position: the position
+ *
+ * Sets the position of the image relative to the text 
+ * inside the button.
+ *
+ * Since: 2.10
+ */ 
+void
+gtk_button_set_image_position (GtkButton       *button,
+                              GtkPositionType  position)
+{
+
+  GtkButtonPrivate *priv;
+
+  g_return_if_fail (GTK_IS_BUTTON (button));
+  g_return_if_fail (position >= GTK_POS_LEFT && position <= GTK_POS_BOTTOM);
   
+  priv = GTK_BUTTON_GET_PRIVATE (button);
+
+  if (priv->image_position != position)
+    {
+      priv->image_position = position;
+
+      gtk_button_construct_child (button);
+
+      g_object_notify (G_OBJECT (button), "image-position");
+    }
+}
+
+/**
+ * gtk_button_get_image_position:
+ * @button: a #GtkButton
+ *
+ * Gets the position of the image relative to the text 
+ * inside the button.
+ *
+ * Return value: the position
+ *
+ * Since: 2.10
+ */
+GtkPositionType
+gtk_button_get_image_position (GtkButton *button)
+{
+  GtkButtonPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_BUTTON (button), GTK_POS_LEFT);
+
+  priv = GTK_BUTTON_GET_PRIVATE (button);
+  
+  return priv->image_position;
+}
+
+
 #define __GTK_BUTTON_C__
 #include "gtkaliasdef.c"