]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkspinbutton.c
Use IPC_RMID _after_ doing XShmAttach. This should work everywhere, and
[~andy/gtk] / gtk / gtkspinbutton.c
index 6f8cd8a1940a4902f184fa6928a1a38f03f1e706..1307119235b330d0ed5bd0d8045ccace1532daee 100644 (file)
@@ -1,6 +1,9 @@
 /* GTK - The GIMP Toolkit
  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
  *
+ * GtkSpinButton widget for GTK+
+ * Copyright (C) 1998 Lars Hamann and Stefan Jeske
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * License as published by the Free Software Foundation; either
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
  */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <math.h>
+#include <string.h>
+#include <locale.h>
 #include "gdk/gdkkeysyms.h"
 #include "gtkspinbutton.h"
 #include "gtkmain.h"
 #include "gtksignal.h"
 
 
-#define MIN_SPIN_BUTTON_WIDTH           30
-#define ARROW_SIZE                      11
-#define SPIN_BUTTON_INITIAL_TIMER_DELAY 200
-#define SPIN_BUTTON_TIMER_DELAY         20
-#define MAX_TEXT_LENGTH                 256
-#define MAX_TIMER_CALLS                 5
+#define MIN_SPIN_BUTTON_WIDTH              30
+#define ARROW_SIZE                         11
+#define SPIN_BUTTON_INITIAL_TIMER_DELAY    200
+#define SPIN_BUTTON_TIMER_DELAY            20
+#define MAX_TEXT_LENGTH                    256
+#define MAX_TIMER_CALLS                    5
+#define EPSILON                            1e-5
+
+enum {
+  ARG_0,
+  ARG_ADJUSTMENT,
+  ARG_CLIMB_RATE,
+  ARG_DIGITS,
+  ARG_SNAP_TO_TICKS,
+  ARG_NUMERIC,
+  ARG_WRAP,
+  ARG_UPDATE_POLICY,
+  ARG_SHADOW_TYPE,
+  ARG_VALUE
+};
 
 
 static void gtk_spin_button_class_init     (GtkSpinButtonClass *klass);
 static void gtk_spin_button_init           (GtkSpinButton      *spin_button);
 static void gtk_spin_button_finalize       (GtkObject          *object);
+static void gtk_spin_button_set_arg        (GtkObject          *object,
+                                           GtkArg             *arg,
+                                           guint               arg_id);
+static void gtk_spin_button_get_arg        (GtkObject          *object,
+                                           GtkArg             *arg,
+                                           guint               arg_id);
 static void gtk_spin_button_map            (GtkWidget          *widget);
 static void gtk_spin_button_unmap          (GtkWidget          *widget);
 static void gtk_spin_button_realize        (GtkWidget          *widget);
@@ -65,41 +91,47 @@ static gint gtk_spin_button_focus_out      (GtkWidget          *widget,
 static void gtk_spin_button_draw_arrow     (GtkSpinButton      *spin_button, 
                                            guint               arrow);
 static gint gtk_spin_button_timer          (GtkSpinButton      *spin_button);
-static void gtk_spin_button_spin           (GtkSpinButton      *spin_button,
-                                           guint               direction,
-                                           gfloat              step);
-static void gtk_spin_button_value_changed  (GtkWidget          *widget,
+static void gtk_spin_button_value_changed  (GtkAdjustment      *adjustment,
                                            GtkSpinButton      *spin_button); 
 static gint gtk_spin_button_key_press      (GtkWidget          *widget,
                                            GdkEventKey        *event);
+static gint gtk_spin_button_key_release    (GtkWidget          *widget,
+                                           GdkEventKey        *event);
 static void gtk_spin_button_update         (GtkSpinButton      *spin_button);
-static void gtk_spin_button_changed        (GtkEditable        *editable);
-static void gtk_spin_button_activate       (GtkEntry           *entry);
+static void gtk_spin_button_activate       (GtkEditable        *editable);
+static void gtk_spin_button_snap           (GtkSpinButton      *spin_button,
+                                           gfloat              val);
+static void gtk_spin_button_insert_text    (GtkEditable        *editable,
+                                           const gchar        *new_text,
+                                           gint                new_text_length,
+                                           gint               *position);
+static void gtk_spin_button_real_spin      (GtkSpinButton      *spin_button,
+                                           gfloat              step);
 
 
-static GtkWidgetClass *parent_class = NULL;
+static GtkEntryClass *parent_class = NULL;
 
 
-guint
-gtk_spin_button_get_type ()
+GtkType
+gtk_spin_button_get_type (void)
 {
   static guint spin_button_type = 0;
 
   if (!spin_button_type)
     {
-      GtkTypeInfo spin_button_info =
+      static const GtkTypeInfo spin_button_info =
       {
        "GtkSpinButton",
        sizeof (GtkSpinButton),
        sizeof (GtkSpinButtonClass),
        (GtkClassInitFunc) gtk_spin_button_class_init,
        (GtkObjectInitFunc) gtk_spin_button_init,
-       (GtkArgSetFunc) NULL,
-        (GtkArgGetFunc) NULL,
+       /* reserved_1 */ NULL,
+        /* reserved_2 */ NULL,
+        (GtkClassInitFunc) NULL,
       };
 
-      spin_button_type = gtk_type_unique (gtk_entry_get_type (), 
-                                         &spin_button_info);
+      spin_button_type = gtk_type_unique (GTK_TYPE_ENTRY, &spin_button_info);
     }
   return spin_button_type;
 }
@@ -107,18 +139,56 @@ gtk_spin_button_get_type ()
 static void
 gtk_spin_button_class_init (GtkSpinButtonClass *class)
 {
-  GtkObjectClass *object_class;
-  GtkWidgetClass *widget_class;
+  GtkObjectClass   *object_class;
+  GtkWidgetClass   *widget_class;
   GtkEditableClass *editable_class;
-  GtkEntryClass  *entry_class;
-
-  object_class = (GtkObjectClass*) class;
-  widget_class = (GtkWidgetClass*) class;
-  entry_class  = (GtkEntryClass*)  class; 
-  editable_class  = (GtkEditableClass*)  class; 
 
-  parent_class = gtk_type_class (gtk_entry_get_type ());
+  object_class   = (GtkObjectClass*)   class;
+  widget_class   = (GtkWidgetClass*)   class;
+  editable_class = (GtkEditableClass*) class; 
+
+  parent_class = gtk_type_class (GTK_TYPE_ENTRY);
+
+  gtk_object_add_arg_type ("GtkSpinButton::adjustment",
+                          GTK_TYPE_ADJUSTMENT,
+                          GTK_ARG_READWRITE,
+                          ARG_ADJUSTMENT);
+  gtk_object_add_arg_type ("GtkSpinButton::climb_rate",
+                          GTK_TYPE_FLOAT,
+                          GTK_ARG_READWRITE,
+                          ARG_CLIMB_RATE);
+  gtk_object_add_arg_type ("GtkSpinButton::digits",
+                          GTK_TYPE_UINT,
+                          GTK_ARG_READWRITE,
+                          ARG_DIGITS);
+  gtk_object_add_arg_type ("GtkSpinButton::snap_to_ticks",
+                          GTK_TYPE_BOOL,
+                          GTK_ARG_READWRITE,
+                          ARG_SNAP_TO_TICKS);
+  gtk_object_add_arg_type ("GtkSpinButton::numeric",
+                          GTK_TYPE_BOOL,
+                          GTK_ARG_READWRITE,
+                          ARG_NUMERIC);
+  gtk_object_add_arg_type ("GtkSpinButton::wrap",
+                          GTK_TYPE_BOOL,
+                          GTK_ARG_READWRITE,
+                          ARG_WRAP);
+  gtk_object_add_arg_type ("GtkSpinButton::update_policy",
+                          GTK_TYPE_SPIN_BUTTON_UPDATE_POLICY,
+                          GTK_ARG_READWRITE,
+                          ARG_UPDATE_POLICY);
+  gtk_object_add_arg_type ("GtkSpinButton::shadow_type",
+                          GTK_TYPE_SHADOW_TYPE,
+                          GTK_ARG_READWRITE,
+                          ARG_SHADOW_TYPE);
+  gtk_object_add_arg_type ("GtkSpinButton::value",
+                          GTK_TYPE_FLOAT,
+                          GTK_ARG_READWRITE,
+                          ARG_VALUE);
+  
 
+  object_class->set_arg = gtk_spin_button_set_arg;
+  object_class->get_arg = gtk_spin_button_get_arg;
   object_class->finalize = gtk_spin_button_finalize;
 
   widget_class->map = gtk_spin_button_map;
@@ -133,57 +203,136 @@ gtk_spin_button_class_init (GtkSpinButtonClass *class)
   widget_class->button_release_event = gtk_spin_button_button_release;
   widget_class->motion_notify_event = gtk_spin_button_motion_notify;
   widget_class->key_press_event = gtk_spin_button_key_press;
+  widget_class->key_release_event = gtk_spin_button_key_release;
   widget_class->enter_notify_event = gtk_spin_button_enter_notify;
   widget_class->leave_notify_event = gtk_spin_button_leave_notify;
   widget_class->focus_out_event = gtk_spin_button_focus_out;
 
-  editable_class->changed = gtk_spin_button_changed;
-  entry_class->activate = gtk_spin_button_activate;
+  editable_class->insert_text = gtk_spin_button_insert_text;
+  editable_class->activate = gtk_spin_button_activate;
+}
+
+static void
+gtk_spin_button_set_arg (GtkObject        *object,
+                        GtkArg           *arg,
+                        guint             arg_id)
+{
+  GtkSpinButton *spin_button;
+
+  spin_button = GTK_SPIN_BUTTON (object);
+  
+  switch (arg_id)
+    {
+      GtkAdjustment *adjustment;
+
+    case ARG_ADJUSTMENT:
+      adjustment = GTK_VALUE_POINTER (*arg);
+      if (!adjustment)
+       adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+      gtk_spin_button_set_adjustment (spin_button, adjustment);
+      break;
+    case ARG_CLIMB_RATE:
+      gtk_spin_button_configure (spin_button,
+                                spin_button->adjustment,
+                                GTK_VALUE_FLOAT (*arg),
+                                spin_button->digits);
+      break;
+    case ARG_DIGITS:
+      gtk_spin_button_configure (spin_button,
+                                spin_button->adjustment,
+                                spin_button->climb_rate,
+                                GTK_VALUE_UINT (*arg));
+      break;
+    case ARG_SNAP_TO_TICKS:
+      gtk_spin_button_set_snap_to_ticks (spin_button, GTK_VALUE_BOOL (*arg));
+      break;
+    case ARG_NUMERIC:
+      gtk_spin_button_set_numeric (spin_button, GTK_VALUE_BOOL (*arg));
+      break;
+    case ARG_WRAP:
+      gtk_spin_button_set_wrap (spin_button, GTK_VALUE_BOOL (*arg));
+      break;
+    case ARG_UPDATE_POLICY:
+      gtk_spin_button_set_update_policy (spin_button, GTK_VALUE_ENUM (*arg));
+      break;
+    case ARG_SHADOW_TYPE:
+      gtk_spin_button_set_shadow_type (spin_button, GTK_VALUE_ENUM (*arg));
+      break;
+    case ARG_VALUE:
+      gtk_spin_button_set_value (spin_button, GTK_VALUE_FLOAT (*arg));
+      break;
+    default:
+      break;
+    }
+}
+
+static void
+gtk_spin_button_get_arg (GtkObject        *object,
+                        GtkArg           *arg,
+                        guint             arg_id)
+{
+  GtkSpinButton *spin_button;
+
+  spin_button = GTK_SPIN_BUTTON (object);
+  
+  switch (arg_id)
+    {
+    case ARG_ADJUSTMENT:
+      GTK_VALUE_POINTER (*arg) = spin_button->adjustment;
+      break;
+    case ARG_CLIMB_RATE:
+      GTK_VALUE_FLOAT (*arg) = spin_button->climb_rate;
+      break;
+    case ARG_DIGITS:
+      GTK_VALUE_UINT (*arg) = spin_button->digits;
+      break;
+    case ARG_SNAP_TO_TICKS:
+      GTK_VALUE_BOOL (*arg) = spin_button->snap_to_ticks;
+      break;
+    case ARG_NUMERIC:
+      GTK_VALUE_BOOL (*arg) = spin_button->numeric;
+      break;
+    case ARG_WRAP:
+      GTK_VALUE_BOOL (*arg) = spin_button->wrap;
+      break;
+    case ARG_UPDATE_POLICY:
+      GTK_VALUE_ENUM (*arg) = spin_button->update_policy;
+      break;
+    case ARG_SHADOW_TYPE:
+      GTK_VALUE_ENUM (*arg) = spin_button->shadow_type;
+      break;
+    case ARG_VALUE:
+      GTK_VALUE_FLOAT (*arg) = spin_button->adjustment->value;
+      break;
+    default:
+      arg->type = GTK_TYPE_INVALID;
+      break;
+    }
 }
 
 static void
 gtk_spin_button_init (GtkSpinButton *spin_button)
 {
   spin_button->adjustment = NULL;
-  spin_button->panel      = NULL;
-
+  spin_button->panel = NULL;
+  spin_button->shadow_type = GTK_SHADOW_NONE;
   spin_button->timer = 0;
+  spin_button->ev_time = 0;
   spin_button->climb_rate = 0.0;
   spin_button->timer_step = 0.0;
-  
-  spin_button->update_policy = GTK_UPDATE_ALWAYS | GTK_UPDATE_SNAP_TO_TICKS;
-
-  spin_button->snapped = 0;
+  spin_button->update_policy = GTK_UPDATE_ALWAYS;
   spin_button->in_child = 2;
   spin_button->click_child = 2;
   spin_button->button = 0;
-  spin_button->need_timer = 0;
+  spin_button->need_timer = FALSE;
   spin_button->timer_calls = 0;
   spin_button->digits = 0;
-}
-
-GtkWidget*
-gtk_spin_button_new (GtkAdjustment *adjustment,
-                    gfloat         climb_rate,
-                    gint           digits)
-{
-  GtkSpinButton *spin;
-  char buf[MAX_TEXT_LENGTH];
-
-  g_return_val_if_fail (digits >= 0 && digits < 128, NULL);
-
-  spin = gtk_type_new (gtk_spin_button_get_type ());
+  spin_button->numeric = FALSE;
+  spin_button->wrap = FALSE;
+  spin_button->snap_to_ticks = FALSE;
 
-  if (!adjustment)
-    adjustment = (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0);
-
-  gtk_spin_button_set_adjustment (GTK_SPIN_BUTTON (spin), adjustment);
-  spin->digits = digits;
-  sprintf (buf, "%0.*f", digits, adjustment->value);
-  gtk_entry_set_text (GTK_ENTRY (spin), buf);
-  spin->climb_rate = climb_rate;
-
-  return GTK_WIDGET (spin);
+  gtk_spin_button_set_adjustment (spin_button,
+                                 (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0));
 }
 
 static void
@@ -194,7 +343,7 @@ gtk_spin_button_finalize (GtkObject *object)
 
   gtk_object_unref (GTK_OBJECT (GTK_SPIN_BUTTON (object)->adjustment));
   
-  (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
+  GTK_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 static void
@@ -203,10 +352,10 @@ gtk_spin_button_map (GtkWidget *widget)
   g_return_if_fail (widget != NULL);
   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
 
-  if (!GTK_WIDGET_MAPPED (widget))
+  if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
     {
-      gdk_window_show (GTK_SPIN_BUTTON (widget)->panel);
       GTK_WIDGET_CLASS (parent_class)->map (widget);
+      gdk_window_show (GTK_SPIN_BUTTON (widget)->panel);
     }
 }
 
@@ -229,14 +378,21 @@ gtk_spin_button_realize (GtkWidget *widget)
   GtkSpinButton *spin;
   GdkWindowAttr attributes;
   gint attributes_mask;
+  guint real_width;
 
   g_return_if_fail (widget != NULL);
   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
-
+  
   spin = GTK_SPIN_BUTTON (widget);
 
+  real_width = widget->allocation.width;
+  widget->allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
+  gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
+                        GDK_KEY_RELEASE_MASK);
   GTK_WIDGET_CLASS (parent_class)->realize (widget);
 
+  widget->allocation.width = real_width;
+  
   attributes.window_type = GDK_WINDOW_CHILD;
   attributes.wclass = GDK_INPUT_OUTPUT;
   attributes.visual = gtk_widget_get_visual (widget);
@@ -248,19 +404,18 @@ gtk_spin_button_realize (GtkWidget *widget)
 
   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
 
-  attributes.x = widget->allocation.x + widget->allocation.width
-    - ARROW_SIZE - 2 * widget->style->klass->xthickness;
-  attributes.y = widget->allocation.y
-
-  attributes.width = ARROW_SIZE;
-  attributes.height = widget->allocation.height;
+  attributes.x = (widget->allocation.x + widget->allocation.width - ARROW_SIZE -
+                 2 * widget->style->klass->xthickness);
+  attributes.y = widget->allocation.y + (widget->allocation.height -
+                                        widget->requisition.height) / 2;
+  attributes.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
+  attributes.height = widget->requisition.height;
   
   spin->panel = gdk_window_new (gtk_widget_get_parent_window (widget), 
                                &attributes, attributes_mask);
+  gdk_window_set_user_data (spin->panel, widget);
 
   gtk_style_set_background (widget->style, spin->panel, GTK_STATE_NORMAL);
-
-  gdk_window_set_user_data (spin->panel, widget);
 }
 
 static void
@@ -277,6 +432,7 @@ gtk_spin_button_unrealize (GtkWidget *widget)
 
   if (spin->panel)
     {
+      gdk_window_set_user_data (spin->panel, NULL);
       gdk_window_destroy (spin->panel);
       spin->panel = NULL;
     }
@@ -307,26 +463,26 @@ gtk_spin_button_size_allocate (GtkWidget     *widget,
   g_return_if_fail (allocation != NULL);
 
   child_allocation = *allocation;
-  child_allocation.width = allocation->width - ARROW_SIZE  
-    - 2 * widget->style->klass->xthickness;
-  child_allocation.height = allocation->height;
+  child_allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
 
   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &child_allocation);
 
   widget->allocation = *allocation;
-  
-  child_allocation.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
-  child_allocation.height = widget->requisition.height;  
-  child_allocation.x = allocation->x + allocation->width - ARROW_SIZE 
-    - 2 * widget->style->klass->xthickness;
-  child_allocation.y = allocation->y + 
-    (allocation->height - widget->requisition.height) / 2;
-
-  gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel, 
-                         child_allocation.x,
-                         child_allocation.y,
-                         child_allocation.width,
-                         child_allocation.height); 
+
+  if (GTK_WIDGET_REALIZED (widget))
+    {
+      child_allocation.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
+      child_allocation.height = widget->requisition.height;  
+      child_allocation.x = (allocation->x + allocation->width - ARROW_SIZE - 
+                           2 * widget->style->klass->xthickness);
+      child_allocation.y = allocation->y + (allocation->height - widget->requisition.height) / 2;
+
+      gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel, 
+                             child_allocation.x,
+                             child_allocation.y,
+                             child_allocation.width,
+                             child_allocation.height); 
+    }
 }
 
 static void
@@ -342,15 +498,22 @@ gtk_spin_button_paint (GtkWidget    *widget,
 
   if (GTK_WIDGET_DRAWABLE (widget))
     {
-      gtk_draw_shadow (widget->style, spin->panel,
-                      GTK_STATE_NORMAL, GTK_SHADOW_OUT,
-                      0, 0, ARROW_SIZE + 2 * widget->style->klass->xthickness,
+      if (spin->shadow_type != GTK_SHADOW_NONE)
+       gtk_paint_box (widget->style, spin->panel,
+                      GTK_STATE_NORMAL, spin->shadow_type,
+                      area, widget, "spinbutton",
+                      0, 0, 
+                      ARROW_SIZE + 2 * widget->style->klass->xthickness,
                       widget->requisition.height); 
-
-      gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
-      gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
-
-      GTK_WIDGET_CLASS (parent_class)->draw (widget, area);
+      else
+        {
+           gdk_window_set_back_pixmap (spin->panel, NULL, TRUE);
+           gdk_window_clear_area (spin->panel, area->x, area->y, area->width, area->height);
+        }
+       gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
+       gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
+
+       GTK_WIDGET_CLASS (parent_class)->draw (widget, area);
     }
 }
 
@@ -362,7 +525,6 @@ gtk_spin_button_draw (GtkWidget    *widget,
   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
   g_return_if_fail (area != NULL);
 
-
   if (GTK_WIDGET_DRAWABLE (widget))
     gtk_spin_button_paint (widget, area);
 }
@@ -388,6 +550,8 @@ gtk_spin_button_draw_arrow (GtkSpinButton *spin_button,
   GtkStateType state_type;
   GtkShadowType shadow_type;
   GtkWidget *widget;
+  gint x;
+  gint y;
 
   g_return_if_fail (spin_button != NULL);
   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
@@ -396,40 +560,71 @@ gtk_spin_button_draw_arrow (GtkSpinButton *spin_button,
 
   if (GTK_WIDGET_DRAWABLE (spin_button))
     {
-      if (spin_button->in_child == arrow)
-        {
-          if (spin_button->click_child == arrow)
-            state_type = GTK_STATE_ACTIVE;
-          else
-            state_type = GTK_STATE_PRELIGHT;
-        }
-      else
-        state_type = GTK_STATE_NORMAL;
-
-      if (spin_button->click_child == arrow)
-        shadow_type = GTK_SHADOW_IN;
+      if (!spin_button->wrap &&
+         (((arrow == GTK_ARROW_UP &&
+         (spin_button->adjustment->upper - spin_button->adjustment->value
+          <= EPSILON))) ||
+         ((arrow == GTK_ARROW_DOWN &&
+         (spin_button->adjustment->value - spin_button->adjustment->lower
+          <= EPSILON)))))
+       {
+         shadow_type = GTK_SHADOW_ETCHED_IN;
+         state_type = GTK_STATE_NORMAL;
+       }
       else
-        shadow_type = GTK_SHADOW_OUT;
-
+       {
+         if (spin_button->in_child == arrow)
+           {
+             if (spin_button->click_child == arrow)
+               state_type = GTK_STATE_ACTIVE;
+             else
+               state_type = GTK_STATE_PRELIGHT;
+           }
+         else
+           state_type = GTK_STATE_NORMAL;
+         
+         if (spin_button->click_child == arrow)
+           shadow_type = GTK_SHADOW_IN;
+         else
+           shadow_type = GTK_SHADOW_OUT;
+       }
       if (arrow == GTK_ARROW_UP)
        {
-         gtk_draw_arrow (widget->style, spin_button->panel,
-                         state_type, shadow_type, arrow, TRUE, 
-                         widget->style->klass->xthickness, 
-                         widget->style->klass->ythickness, 
-                         ARROW_SIZE, 
-                         widget->requisition.height / 2 
-                         - widget->style->klass->ythickness);
+         if (spin_button->shadow_type != GTK_SHADOW_NONE)
+           {
+             x = widget->style->klass->xthickness;
+             y = widget->style->klass->ythickness;
+           }
+         else
+           {
+             x = widget->style->klass->xthickness - 1;
+             y = widget->style->klass->ythickness - 1;
+           }
+         gtk_paint_arrow (widget->style, spin_button->panel,
+                          state_type, shadow_type, 
+                          NULL, widget, "spinbutton",
+                          arrow, TRUE, 
+                          x, y, ARROW_SIZE, widget->requisition.height / 2 
+                          - widget->style->klass->ythickness);
        }
       else
        {
-         gtk_draw_arrow (widget->style, spin_button->panel,
-                         state_type, shadow_type, arrow, TRUE, 
-                         widget->style->klass->xthickness, 
-                         widget->requisition.height / 2,
-                         ARROW_SIZE, 
-                         widget->requisition.height / 2 
-                         - widget->style->klass->ythickness);
+         if (spin_button->shadow_type != GTK_SHADOW_NONE)
+           {
+             x = widget->style->klass->xthickness;
+             y = widget->requisition.height / 2;
+           }
+         else
+           {
+             x = widget->style->klass->xthickness - 1;
+             y = widget->requisition.height / 2 + 1;
+           }
+         gtk_paint_arrow (widget->style, spin_button->panel,
+                          state_type, shadow_type, 
+                          NULL, widget, "spinbutton",
+                          arrow, TRUE, 
+                          x, y, ARROW_SIZE, widget->requisition.height / 2 
+                          - widget->style->klass->ythickness);
        }
     }
 }
@@ -501,19 +696,13 @@ static gint
 gtk_spin_button_focus_out (GtkWidget     *widget,
                           GdkEventFocus *event)
 {
-  GtkSpinButton *spin;
-
   g_return_val_if_fail (widget != NULL, FALSE);
   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);
 
-  spin = GTK_SPIN_BUTTON (widget);
-  if ((spin->update_policy & GTK_UPDATE_SNAP_TO_TICKS) && !spin->snapped)
-    gtk_spin_button_update (spin);
+  gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
 
-  GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
-  
-  return FALSE;
+  return GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
 }
 
 static gint
@@ -536,14 +725,16 @@ gtk_spin_button_button_press (GtkWidget      *widget,
            gtk_widget_grab_focus (widget);
          gtk_grab_add (widget);
          spin->button = event->button;
-
+         
+         gtk_spin_button_update (spin);
+         
          if (event->y <= widget->requisition.height / 2)
            {
              spin->click_child = GTK_ARROW_UP;
              if (event->button == 1)
                {
-                 gtk_spin_button_spin (spin, spin->click_child,
-                                       spin->adjustment->step_increment);
+                gtk_spin_button_real_spin (spin, 
+                                           spin->adjustment->step_increment);
                  if (!spin->timer)
                    {
                      spin->timer_step = spin->adjustment->step_increment;
@@ -555,8 +746,8 @@ gtk_spin_button_button_press (GtkWidget      *widget,
                }
              else if (event->button == 2)
                {
-                 gtk_spin_button_spin (spin, spin->click_child,
-                                       spin->adjustment->page_increment);
+                gtk_spin_button_real_spin (spin, 
+                                           spin->adjustment->page_increment);
                  if (!spin->timer) 
                    {
                      spin->timer_step = spin->adjustment->page_increment;
@@ -573,8 +764,8 @@ gtk_spin_button_button_press (GtkWidget      *widget,
              spin->click_child = GTK_ARROW_DOWN;
              if (event->button == 1)
                {
-                 gtk_spin_button_spin (spin, spin->click_child,
-                                       spin->adjustment->step_increment);
+                 gtk_spin_button_real_spin (spin,
+                                            -spin->adjustment->step_increment);
                  if (!spin->timer)
                    {
                      spin->timer_step = spin->adjustment->step_increment;
@@ -586,8 +777,8 @@ gtk_spin_button_button_press (GtkWidget      *widget,
                }      
              else if (event->button == 2)
                {
-                 gtk_spin_button_spin (spin, spin->click_child,
-                                       spin->adjustment->page_increment);
+                 gtk_spin_button_real_spin (spin,
+                                            -spin->adjustment->page_increment);
                  if (!spin->timer) 
                    {
                      spin->timer_step = spin->adjustment->page_increment;
@@ -600,7 +791,7 @@ gtk_spin_button_button_press (GtkWidget      *widget,
              gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
            }
        }
-      else 
+      else
        GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
     }
   return FALSE;
@@ -616,9 +807,6 @@ gtk_spin_button_button_release (GtkWidget      *widget,
   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);
 
-  if (event->type != GDK_BUTTON_RELEASE)
-    return FALSE;
-
   spin = GTK_SPIN_BUTTON (widget);
 
   if (event->button == spin->button)
@@ -640,20 +828,22 @@ gtk_spin_button_button_release (GtkWidget      *widget,
              event->x <= ARROW_SIZE + 2 * widget->style->klass->xthickness)
            {
              if (spin->click_child == GTK_ARROW_UP &&
-                 spin->adjustment->value < spin->adjustment->upper &&
                  event->y <= widget->requisition.height / 2)
                {
-                 spin->adjustment->value = spin->adjustment->upper;
-                 gtk_signal_emit_by_name (GTK_OBJECT (spin->adjustment), 
-                                          "value_changed");
+                 gfloat diff;
+
+                 diff = spin->adjustment->upper - spin->adjustment->value;
+                 if (diff > EPSILON)
+                   gtk_spin_button_real_spin (spin, diff);
                }
              else if (spin->click_child == GTK_ARROW_DOWN &&
-                      spin->adjustment->value > spin->adjustment->lower &&
                       event->y > widget->requisition.height / 2)
                {
-                 spin->adjustment->value = spin->adjustment->lower;
-                 gtk_signal_emit_by_name (GTK_OBJECT (spin->adjustment), 
-                                          "value_changed");
+                 gfloat diff;
+
+                 diff = spin->adjustment->value - spin->adjustment->lower;
+                 if (diff > EPSILON)
+                   gtk_spin_button_real_spin (spin, -diff);
                }
            }
        }                 
@@ -709,9 +899,7 @@ gtk_spin_button_motion_notify (GtkWidget      *widget,
       return FALSE;
     }
          
-  GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
-
-  return FALSE;
+  return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
 }
 
 static gint
@@ -722,18 +910,21 @@ gtk_spin_button_timer (GtkSpinButton *spin_button)
 
   if (spin_button->timer)
     {
-      gtk_spin_button_spin (spin_button, spin_button->click_child, 
-                           spin_button->timer_step);
-
-      if (spin_button->need_timer) {
-       spin_button->need_timer = FALSE;
-       spin_button->timer = gtk_timeout_add 
-         (SPIN_BUTTON_TIMER_DELAY, (GtkFunction) gtk_spin_button_timer, 
-          (gpointer) spin_button);
-       return FALSE;
-      }
-      else if (spin_button->climb_rate > 0.0 && 
-          spin_button->timer_step < spin_button->adjustment->page_increment)
+      if (spin_button->click_child == GTK_ARROW_UP)
+       gtk_spin_button_real_spin (spin_button, spin_button->timer_step);
+      else
+       gtk_spin_button_real_spin (spin_button, -spin_button->timer_step);
+
+      if (spin_button->need_timer)
+       {
+         spin_button->need_timer = FALSE;
+         spin_button->timer = gtk_timeout_add 
+           (SPIN_BUTTON_TIMER_DELAY, (GtkFunction) gtk_spin_button_timer, 
+            (gpointer) spin_button);
+         return FALSE;
+       }
+      else if (spin_button->climb_rate > 0.0 && spin_button->timer_step 
+              < spin_button->adjustment->page_increment)
        {
          if (spin_button->timer_calls < MAX_TIMER_CALLS)
            spin_button->timer_calls++;
@@ -742,195 +933,405 @@ gtk_spin_button_timer (GtkSpinButton *spin_button)
              spin_button->timer_calls = 0;
              spin_button->timer_step += spin_button->climb_rate;
            }
-      }
+       }
       return TRUE;
     }
   return FALSE;
 }
 
 static void
-gtk_spin_button_spin (GtkSpinButton *spin_button,
-                     guint          direction,
-                     gfloat         step)
-{
-  gfloat new_value = 0.0;
-
-  g_return_if_fail (spin_button != NULL);
-  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
-
-  if (direction == GTK_ARROW_UP)
-    {
-      new_value = MIN (spin_button->adjustment->value + step,
-                      spin_button->adjustment->upper);
-    }
-  else if (direction == GTK_ARROW_DOWN) 
-    {
-      new_value = MAX (spin_button->adjustment->value - step,
-                      spin_button->adjustment->lower);
-    }
-
-  if (new_value != spin_button->adjustment->value)
-    {  
-      spin_button->adjustment->value = new_value;
-      gtk_signal_emit_by_name (GTK_OBJECT (spin_button->adjustment),
-                              "value_changed");
-    }
-}
-
-static void
-gtk_spin_button_value_changed (GtkWidget     *widget,
+gtk_spin_button_value_changed (GtkAdjustment *adjustment,
                               GtkSpinButton *spin_button)
 {
   char buf[MAX_TEXT_LENGTH];
 
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_ADJUSTMENT (widget));
+  g_return_if_fail (adjustment != NULL);
+  g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
 
-  sprintf (buf, "%0.*f", spin_button->digits, spin_button->adjustment->value);
+  sprintf (buf, "%0.*f", spin_button->digits, adjustment->value);
   gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
 }
-  
+
 static gint
 gtk_spin_button_key_press (GtkWidget     *widget,
                           GdkEventKey   *event)
 {
   GtkSpinButton *spin;
-  
+  gint key;
+  gboolean key_repeat = FALSE;
+
   g_return_val_if_fail (widget != NULL, FALSE);
   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);
   
   spin = GTK_SPIN_BUTTON (widget);
+  key = event->keyval;
+
+  key_repeat = (event->time == spin->ev_time);
+
+  if (key == GDK_Up || key == GDK_Down || 
+      key == GDK_Page_Up || key == GDK_Page_Down)
+    gtk_spin_button_update (spin);
 
-  switch (event->keyval)
+  switch (key)
     {
     case GDK_Up:
+
       if (GTK_WIDGET_HAS_FOCUS (widget))
        {
          gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), 
                                        "key_press_event");
-         gtk_spin_button_spin (spin, GTK_ARROW_UP,
-                               spin->adjustment->step_increment);
+         if (!key_repeat)
+           spin->timer_step = spin->adjustment->step_increment;
+
+        gtk_spin_button_real_spin (spin, spin->timer_step);
+
+         if (key_repeat)
+           {
+             if (spin->climb_rate > 0.0 && spin->timer_step
+                 < spin->adjustment->page_increment)
+               {
+                 if (spin->timer_calls < MAX_TIMER_CALLS)
+                   spin->timer_calls++;
+                 else 
+                   {
+                     spin->timer_calls = 0;
+                     spin->timer_step += spin->climb_rate;
+                   }
+               }
+           }
          return TRUE;
        }
       return FALSE;
+
     case GDK_Down:
+
       if (GTK_WIDGET_HAS_FOCUS (widget))
        {
          gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), 
                                        "key_press_event");
-         gtk_spin_button_spin (spin, GTK_ARROW_DOWN,
-                               spin->adjustment->step_increment);
+         if (!key_repeat)
+           spin->timer_step = spin->adjustment->step_increment;
+
+        gtk_spin_button_real_spin (spin, -spin->timer_step);
+
+         if (key_repeat)
+           {
+             if (spin->climb_rate > 0.0 && spin->timer_step
+                 < spin->adjustment->page_increment)
+               {
+                 if (spin->timer_calls < MAX_TIMER_CALLS)
+                   spin->timer_calls++;
+                 else 
+                   {
+                     spin->timer_calls = 0;
+                     spin->timer_step += spin->climb_rate;
+                   }
+               }
+           }
          return TRUE;
        }
       return FALSE;
+
     case GDK_Page_Up:
+
       if (event->state & GDK_CONTROL_MASK)
        {
-         spin->adjustment->value = spin->adjustment->upper;
-         gtk_signal_emit_by_name (GTK_OBJECT (spin->adjustment),
-                                  "value_changed");
+         gfloat diff = spin->adjustment->upper - spin->adjustment->value;
+         if (diff > EPSILON)
+           gtk_spin_button_real_spin (spin, diff);
        }
       else
-       gtk_spin_button_spin (spin, GTK_ARROW_UP,
-                             spin->adjustment->page_increment);
+       gtk_spin_button_real_spin (spin, spin->adjustment->page_increment);
       return TRUE;
+
     case GDK_Page_Down:
+
       if (event->state & GDK_CONTROL_MASK)
        {
-         spin->adjustment->value = spin->adjustment->lower;
-         gtk_signal_emit_by_name (GTK_OBJECT (spin->adjustment),
-                                  "value_changed");
+         gfloat diff = spin->adjustment->value - spin->adjustment->lower;
+         if (diff > EPSILON)
+           gtk_spin_button_real_spin (spin, -diff);
        }
       else
-       gtk_spin_button_spin (spin, GTK_ARROW_DOWN,
-                             spin->adjustment->page_increment);
+       gtk_spin_button_real_spin (spin, -spin->adjustment->page_increment);
       return TRUE;
+
     default:
       break;
     }
+
   return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
 }
 
+static gint
+gtk_spin_button_key_release (GtkWidget   *widget,
+                            GdkEventKey *event)
+{
+  GtkSpinButton *spin;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
+  
+  spin = GTK_SPIN_BUTTON (widget);
+  
+  spin->ev_time = event->time;
+  return TRUE;
+}
+
+static void
+gtk_spin_button_snap (GtkSpinButton *spin_button,
+                     gfloat         val)
+{
+  gfloat inc;
+  gfloat tmp;
+  
+  inc = spin_button->adjustment->step_increment;
+  tmp = (val - spin_button->adjustment->lower) / inc;
+  if (tmp - floor (tmp) < ceil (tmp) - tmp)
+    val = spin_button->adjustment->lower + floor (tmp) * inc;
+  else
+    val = spin_button->adjustment->lower + ceil (tmp) * inc;
+
+  if (fabs (val - spin_button->adjustment->value) > EPSILON)
+    gtk_adjustment_set_value (spin_button->adjustment, val);
+  else
+    {
+      char buf[MAX_TEXT_LENGTH];
+
+      sprintf (buf, "%0.*f", spin_button->digits, 
+              spin_button->adjustment->value);
+      if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
+       gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
+    }
+}
+
 static void 
 gtk_spin_button_update (GtkSpinButton *spin_button)
 {
-  gfloat tmp;
   gfloat val;
   gchar *error = NULL;
 
   g_return_if_fail (spin_button != NULL);
   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
 
-  val = strtod (GTK_ENTRY (spin_button)->text, &error);
-  
-  if (spin_button->update_policy & GTK_UPDATE_ALWAYS)
+  val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
+
+  if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
     {
       if (val < spin_button->adjustment->lower)
        val = spin_button->adjustment->lower;
       else if (val > spin_button->adjustment->upper)
        val = spin_button->adjustment->upper;
     }
-  else if ((spin_button->update_policy & GTK_UPDATE_IF_VALID) && 
+  else if ((spin_button->update_policy == GTK_UPDATE_IF_VALID) && 
           (*error ||
           val < spin_button->adjustment->lower ||
           val > spin_button->adjustment->upper))
     {
-      gtk_signal_emit_by_name (GTK_OBJECT (spin_button->adjustment),
-                              "value_changed"); 
+      gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
       return;
     }
 
-  if (spin_button->update_policy & GTK_UPDATE_SNAP_TO_TICKS)
+  if (spin_button->snap_to_ticks)
+    gtk_spin_button_snap (spin_button, val);
+  else
     {
-      gfloat inc;
-
-      inc = spin_button->adjustment->step_increment;
-      tmp = (val - spin_button->adjustment->lower) / inc;
-      if (tmp - floor (tmp) < ceil (tmp) - tmp)
-       val = spin_button->adjustment->lower + floor (tmp) * inc;
+      if (fabs (val - spin_button->adjustment->value) > EPSILON)
+       gtk_adjustment_set_value (spin_button->adjustment, val);
       else
-       val = spin_button->adjustment->lower + ceil (tmp) * inc;
-      spin_button->snapped = 1;
+       {
+         char buf[MAX_TEXT_LENGTH];
+         
+         sprintf (buf, "%0.*f", spin_button->digits, 
+                  spin_button->adjustment->value);
+         if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
+           gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
+       }
     }
-  spin_button->adjustment->value = val;
-  gtk_signal_emit_by_name (GTK_OBJECT (spin_button->adjustment),
-                          "value_changed"); 
 }
 
 static void
-gtk_spin_button_changed (GtkEditable *editable)
+gtk_spin_button_activate (GtkEditable *editable)
 {
   g_return_if_fail (editable != NULL);
   g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
 
-  GTK_EDITABLE_CLASS (parent_class)->changed (editable);
-  if (GTK_WIDGET_VISIBLE (GTK_WIDGET (editable)))
+  if (editable->editable)
+    gtk_spin_button_update (GTK_SPIN_BUTTON (editable));
+}
+
+static void
+gtk_spin_button_insert_text (GtkEditable *editable,
+                            const gchar *new_text,
+                            gint         new_text_length,
+                            gint        *position)
+{
+  GtkEntry *entry;
+  GtkSpinButton *spin;
+  g_return_if_fail (editable != NULL);
+  g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
+
+  entry = GTK_ENTRY (editable);
+  spin  = GTK_SPIN_BUTTON (editable);
+
+  if (spin->numeric)
     {
-      GtkSpinButton *spin;
-      gfloat val;
-      gchar *error = NULL;
-
-      spin = GTK_SPIN_BUTTON (editable);
-      spin->snapped = 0;
-
-      val = strtod (GTK_ENTRY (editable)->text, &error);
-      if (val < spin->adjustment->lower)
-       val = spin->adjustment->lower;
-      else if (val > spin->adjustment->upper)
-       val = spin->adjustment->upper;
-      spin->adjustment->value = val;
+      struct lconv *lc;
+      gboolean sign;
+      gint dotpos = -1;
+      gint i;
+      gchar pos_sign;
+      gchar neg_sign;
+
+      lc = localeconv ();
+
+      if (*(lc->negative_sign))
+       neg_sign = *(lc->negative_sign);
+      else 
+       neg_sign = '-';
+
+      if (*(lc->positive_sign))
+       pos_sign = *(lc->positive_sign);
+      else 
+       pos_sign = '+';
+
+      sign = ((strchr (entry->text, neg_sign) != 0) ||
+             (strchr (entry->text, pos_sign) != 0));
+
+      if (sign && !(*position))
+       return;
+
+      dotpos = strchr (entry->text, *(lc->decimal_point)) - entry->text;
+      
+      if (dotpos > -1 && *position > dotpos &&
+         spin->digits - entry->text_length + dotpos - new_text_length + 1 < 0)
+       return;
+
+      for (i = 0; i < new_text_length; i++)
+       {
+         if (new_text[i] == neg_sign || new_text[i] == pos_sign)
+           {
+             if (sign || (*position) || i)
+               return;
+             sign = TRUE;
+           }
+         else if (new_text[i] == *(lc->decimal_point))
+           {
+             if (!spin->digits || dotpos > -1 || 
+                 (new_text_length - 1 - i + entry->text_length - *position > 
+                  spin->digits)) 
+               return;
+             dotpos = *position + i;
+           }
+         else if (new_text[i] < 0x30 || new_text[i] > 0x39)
+           return;
+       }
     }
+
+  GTK_EDITABLE_CLASS (parent_class)->insert_text (editable, new_text,
+                                                 new_text_length, position);
 }
 
 static void
-gtk_spin_button_activate (GtkEntry *entry)
+gtk_spin_button_real_spin (GtkSpinButton *spin_button,
+                          gfloat         increment)
 {
-  g_return_if_fail (entry != NULL);
-  g_return_if_fail (GTK_IS_SPIN_BUTTON (entry));
+  GtkAdjustment *adj;
+  gfloat new_value = 0.0;
+
+  g_return_if_fail (spin_button != NULL);
+  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
+  
+  adj = spin_button->adjustment;
 
-  if (GTK_EDITABLE(entry)->editable)
-    gtk_spin_button_update (GTK_SPIN_BUTTON (entry));
+  new_value = adj->value + increment;
+
+  if (increment > 0)
+    {
+      if (spin_button->wrap)
+       {
+         if (fabs (adj->value - adj->upper) < EPSILON)
+           new_value = adj->lower;
+         else if (new_value > adj->upper)
+           new_value = adj->upper;
+       }
+      else
+       new_value = MIN (new_value, adj->upper);
+    }
+  else if (increment < 0) 
+    {
+      if (spin_button->wrap)
+       {
+         if (fabs (adj->value - adj->lower) < EPSILON)
+           new_value = adj->upper;
+         else if (new_value < adj->lower)
+           new_value = adj->lower;
+       }
+      else
+       new_value = MAX (new_value, adj->lower);
+    }
+
+  if (fabs (new_value - adj->value) > EPSILON)
+    gtk_adjustment_set_value (adj, new_value);
+}
+
+
+/***********************************************************
+ ***********************************************************
+ ***                  Public interface                   ***
+ ***********************************************************
+ ***********************************************************/
+
+
+void
+gtk_spin_button_construct (GtkSpinButton  *spin_button,
+                          GtkAdjustment  *adjustment,
+                          gfloat           climb_rate,
+                          guint            digits)
+{
+  g_message ("gtk_spin_button_construct() is deprecated");
+
+  gtk_spin_button_configure (spin_button, adjustment, climb_rate, digits);
+}
+
+void
+gtk_spin_button_configure (GtkSpinButton  *spin_button,
+                          GtkAdjustment  *adjustment,
+                          gfloat          climb_rate,
+                          guint           digits)
+{
+  g_return_if_fail (spin_button != NULL);
+  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
+  g_return_if_fail (digits < 6);
+
+  if (adjustment)
+    gtk_spin_button_set_adjustment (spin_button, adjustment);
+  else
+    adjustment = spin_button->adjustment;
+
+  spin_button->digits = digits;
+  spin_button->climb_rate = climb_rate;
+  gtk_adjustment_value_changed (adjustment);
+}
+
+GtkWidget *
+gtk_spin_button_new (GtkAdjustment *adjustment,
+                    gfloat         climb_rate,
+                    guint          digits)
+{
+  GtkSpinButton *spin;
+
+  if (adjustment)
+    g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
+  g_return_val_if_fail (digits < 6, NULL);
+
+  spin = gtk_type_new (GTK_TYPE_SPIN_BUTTON);
+
+  gtk_spin_button_configure (spin, adjustment, climb_rate, digits);
+
+  return GTK_WIDGET (spin);
 }
 
 void
@@ -953,10 +1354,9 @@ gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
         {
           gtk_object_ref (GTK_OBJECT (adjustment));
          gtk_object_sink (GTK_OBJECT (adjustment));
-          gtk_signal_connect 
-           (GTK_OBJECT (adjustment), "value_changed",
-            (GtkSignalFunc) gtk_spin_button_value_changed,
-            (gpointer) spin_button);
+          gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
+                             (GtkSignalFunc) gtk_spin_button_value_changed,
+                             (gpointer) spin_button);
         }
     }
 }
@@ -972,18 +1372,16 @@ gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
 
 void
 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
-                           gint           digits)
+                           guint          digits)
 {
   g_return_if_fail (spin_button != NULL);
   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
-  g_return_if_fail (digits >= 0 || digits < 128);
+  g_return_if_fail (digits < 6);
 
   if (spin_button->digits != digits)
     {
       spin_button->digits = digits;
-      if (GTK_WIDGET_VISIBLE (spin_button) && GTK_WIDGET_MAPPED (spin_button))
-        gtk_signal_emit_by_name (GTK_OBJECT (spin_button->adjustment),
-                                "value_changed"); 
+      gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
     }
 }
 
@@ -1018,12 +1416,16 @@ gtk_spin_button_set_value (GtkSpinButton *spin_button,
   g_return_if_fail (spin_button != NULL);
   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
 
-  if (spin_button->adjustment->value != value)
+  if (fabs (value - spin_button->adjustment->value) > EPSILON)
+    gtk_adjustment_set_value (spin_button->adjustment, value);
+  else
     {
-      spin_button->adjustment->value = value;
-      if (GTK_WIDGET_VISIBLE (spin_button) && GTK_WIDGET_MAPPED (spin_button))
-        gtk_signal_emit_by_name (GTK_OBJECT (spin_button->adjustment),
-                                "value_changed"); 
+      char buf[MAX_TEXT_LENGTH];
+
+      sprintf (buf, "%0.*f", spin_button->digits, 
+               spin_button->adjustment->value);
+      if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
+        gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
     }
 }
 
@@ -1036,3 +1438,133 @@ gtk_spin_button_set_update_policy (GtkSpinButton             *spin_button,
 
   spin_button->update_policy = policy;
 }
+
+void
+gtk_spin_button_set_numeric (GtkSpinButton  *spin_button,
+                            gboolean        numeric)
+{
+  g_return_if_fail (spin_button != NULL);
+  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
+
+  spin_button->numeric = (numeric != 0);
+}
+
+void
+gtk_spin_button_set_wrap (GtkSpinButton  *spin_button,
+                         gboolean        wrap)
+{
+  g_return_if_fail (spin_button != NULL);
+  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
+
+  spin_button->wrap = (wrap != 0);
+}
+
+void
+gtk_spin_button_set_shadow_type (GtkSpinButton *spin_button,
+                                GtkShadowType  shadow_type)
+{
+  g_return_if_fail (spin_button != NULL);
+  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
+
+  if (shadow_type != spin_button->shadow_type)
+    {
+      spin_button->shadow_type = shadow_type;
+      if (GTK_WIDGET_DRAWABLE (spin_button))
+       gtk_widget_queue_draw (GTK_WIDGET (spin_button));
+    }
+}
+
+void
+gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
+                                  gboolean       snap_to_ticks)
+{
+  guint new_val;
+
+  g_return_if_fail (spin_button != NULL);
+  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
+
+  new_val = (snap_to_ticks != 0);
+
+  if (new_val != spin_button->snap_to_ticks)
+    {
+      spin_button->snap_to_ticks = new_val;
+      if (new_val)
+       {
+         gchar *error = NULL;
+         gfloat val;
+
+         val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
+         gtk_spin_button_snap (spin_button, val);
+       }
+    }
+}
+
+void
+gtk_spin_button_spin (GtkSpinButton *spin_button,
+                     GtkSpinType    direction,
+                     gfloat         increment)
+{
+  GtkAdjustment *adj;
+  gfloat diff;
+
+  g_return_if_fail (spin_button != NULL);
+  g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
+  
+  adj = spin_button->adjustment;
+
+  /* for compatibility with the 1.0.x version of this function */
+  if (increment != 0 && increment != adj->step_increment &&
+      (direction == GTK_SPIN_STEP_FORWARD ||
+       direction == GTK_SPIN_STEP_BACKWARD))
+    {
+      if (direction == GTK_SPIN_STEP_BACKWARD && increment > 0)
+       increment = -increment;
+      direction = GTK_SPIN_USER_DEFINED;
+    }
+
+  switch (direction)
+    {
+    case GTK_SPIN_STEP_FORWARD:
+
+      gtk_spin_button_real_spin (spin_button, adj->step_increment);
+      break;
+
+    case GTK_SPIN_STEP_BACKWARD:
+
+      gtk_spin_button_real_spin (spin_button, -adj->step_increment);
+      break;
+
+    case GTK_SPIN_PAGE_FORWARD:
+
+      gtk_spin_button_real_spin (spin_button, adj->page_increment);
+      break;
+
+    case GTK_SPIN_PAGE_BACKWARD:
+
+      gtk_spin_button_real_spin (spin_button, -adj->page_increment);
+      break;
+
+    case GTK_SPIN_HOME:
+
+      diff = adj->value - adj->lower;
+      if (diff > EPSILON)
+       gtk_spin_button_real_spin (spin_button, -diff);
+      break;
+
+    case GTK_SPIN_END:
+
+      diff = adj->upper - adj->value;
+      if (diff > EPSILON)
+       gtk_spin_button_real_spin (spin_button, diff);
+      break;
+
+    case GTK_SPIN_USER_DEFINED:
+
+      if (increment != 0)
+       gtk_spin_button_real_spin (spin_button, increment);
+      break;
+
+    default:
+      break;
+    }
+}