]> Pileus Git - ~andy/gtk/blob - gtk/gtkspinbutton.c
172120234b70cc6d0fd793a0e046436ec619ff35
[~andy/gtk] / gtk / gtkspinbutton.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GtkSpinButton widget for GTK+
5  * Copyright (C) 1998 Lars Hamann and Stefan Jeske
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /*
24  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
25  * file for a list of people on the GTK+ Team.  See the ChangeLog
26  * files for a list of changes.  These files are distributed with
27  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <math.h>
33 #include <string.h>
34 #include <locale.h>
35 #include "gdk/gdkkeysyms.h"
36 #include "gtkspinbutton.h"
37 #include "gtkmain.h"
38 #include "gtkmarshalers.h"
39 #include "gtksignal.h"
40 #include "gtksettings.h"
41 #include "gtkintl.h"
42
43 #define MIN_SPIN_BUTTON_WIDTH              30
44 #define SPIN_BUTTON_INITIAL_TIMER_DELAY    200
45 #define SPIN_BUTTON_TIMER_DELAY            20
46 #define MAX_TIMER_CALLS                    5
47 #define EPSILON                            1e-5
48 #define MAX_DIGITS                         20
49
50 enum {
51   PROP_0,
52   PROP_ADJUSTMENT,
53   PROP_CLIMB_RATE,
54   PROP_DIGITS,
55   PROP_SNAP_TO_TICKS,
56   PROP_NUMERIC,
57   PROP_WRAP,
58   PROP_UPDATE_POLICY,
59   PROP_VALUE
60 };
61
62 /* Signals */
63 enum
64 {
65   INPUT,
66   OUTPUT,
67   VALUE_CHANGED,
68   LAST_SIGNAL
69 };
70
71 static void gtk_spin_button_class_init     (GtkSpinButtonClass *klass);
72 static void gtk_spin_button_editable_init  (GtkEditableClass   *iface);
73 static void gtk_spin_button_init           (GtkSpinButton      *spin_button);
74 static void gtk_spin_button_finalize       (GObject            *object);
75 static void gtk_spin_button_set_property   (GObject         *object,
76                                             guint            prop_id,
77                                             const GValue    *value,
78                                             GParamSpec      *pspec);
79 static void gtk_spin_button_get_property   (GObject         *object,
80                                             guint            prop_id,
81                                             GValue          *value,
82                                             GParamSpec      *pspec);
83 static void gtk_spin_button_map            (GtkWidget          *widget);
84 static void gtk_spin_button_unmap          (GtkWidget          *widget);
85 static void gtk_spin_button_realize        (GtkWidget          *widget);
86 static void gtk_spin_button_unrealize      (GtkWidget          *widget);
87 static void gtk_spin_button_size_request   (GtkWidget          *widget,
88                                             GtkRequisition     *requisition);
89 static void gtk_spin_button_size_allocate  (GtkWidget          *widget,
90                                             GtkAllocation      *allocation);
91 static gint gtk_spin_button_expose         (GtkWidget          *widget,
92                                             GdkEventExpose     *event);
93 static gint gtk_spin_button_button_press   (GtkWidget          *widget,
94                                             GdkEventButton     *event);
95 static gint gtk_spin_button_button_release (GtkWidget          *widget,
96                                             GdkEventButton     *event);
97 static gint gtk_spin_button_motion_notify  (GtkWidget          *widget,
98                                             GdkEventMotion     *event);
99 static gint gtk_spin_button_enter_notify   (GtkWidget          *widget,
100                                             GdkEventCrossing   *event);
101 static gint gtk_spin_button_leave_notify   (GtkWidget          *widget,
102                                             GdkEventCrossing   *event);
103 static gint gtk_spin_button_focus_out      (GtkWidget          *widget,
104                                             GdkEventFocus      *event);
105 static void gtk_spin_button_draw_arrow     (GtkSpinButton      *spin_button, 
106                                             guint               arrow);
107 static gint gtk_spin_button_timer          (GtkSpinButton      *spin_button);
108 static void gtk_spin_button_value_changed  (GtkAdjustment      *adjustment,
109                                             GtkSpinButton      *spin_button); 
110 static gint gtk_spin_button_key_press      (GtkWidget          *widget,
111                                             GdkEventKey        *event);
112 static gint gtk_spin_button_key_release    (GtkWidget          *widget,
113                                             GdkEventKey        *event);
114 static gint gtk_spin_button_scroll         (GtkWidget          *widget,
115                                             GdkEventScroll     *event);
116 static void gtk_spin_button_activate       (GtkEntry           *entry);
117 static void gtk_spin_button_snap           (GtkSpinButton      *spin_button,
118                                             gdouble             val);
119 static void gtk_spin_button_insert_text    (GtkEditable        *editable,
120                                             const gchar        *new_text,
121                                             gint                new_text_length,
122                                             gint               *position);
123 static void gtk_spin_button_real_spin      (GtkSpinButton      *spin_button,
124                                             gdouble             step);
125 static gint gtk_spin_button_default_input  (GtkSpinButton      *spin_button,
126                                             gdouble            *new_val);
127 static gint gtk_spin_button_default_output (GtkSpinButton      *spin_button);
128 static gint spin_button_get_arrow_size     (GtkSpinButton      *spin_button);
129 static gint spin_button_get_shadow_type    (GtkSpinButton      *spin_button);
130
131
132 static GtkEntryClass *parent_class = NULL;
133 static guint spinbutton_signals[LAST_SIGNAL] = {0};
134
135
136 GtkType
137 gtk_spin_button_get_type (void)
138 {
139   static GtkType spin_button_type = 0;
140
141   if (!spin_button_type)
142     {
143       static const GtkTypeInfo spin_button_info =
144       {
145         "GtkSpinButton",
146         sizeof (GtkSpinButton),
147         sizeof (GtkSpinButtonClass),
148         (GtkClassInitFunc) gtk_spin_button_class_init,
149         (GtkObjectInitFunc) gtk_spin_button_init,
150         /* reserved_1 */ NULL,
151         /* reserved_2 */ NULL,
152         (GtkClassInitFunc) NULL,
153       };
154
155       static const GInterfaceInfo editable_info =
156       {
157         (GInterfaceInitFunc) gtk_spin_button_editable_init, /* interface_init */
158         NULL, /* interface_finalize */
159         NULL  /* interface_data */
160       };
161
162       spin_button_type = gtk_type_unique (GTK_TYPE_ENTRY, &spin_button_info);
163       g_type_add_interface_static (spin_button_type,
164                                    GTK_TYPE_EDITABLE,
165                                    &editable_info);
166     }
167   return spin_button_type;
168 }
169
170 static void
171 gtk_spin_button_class_init (GtkSpinButtonClass *class)
172 {
173   GObjectClass     *gobject_class = G_OBJECT_CLASS (class);
174   GtkObjectClass   *object_class;
175   GtkWidgetClass   *widget_class;
176   GtkEntryClass    *entry_class;
177
178   object_class   = (GtkObjectClass*)   class;
179   widget_class   = (GtkWidgetClass*)   class;
180   entry_class    = (GtkEntryClass*)    class;
181
182   parent_class = gtk_type_class (GTK_TYPE_ENTRY);
183
184   gobject_class->finalize = gtk_spin_button_finalize;
185
186   gobject_class->set_property = gtk_spin_button_set_property;
187   gobject_class->get_property = gtk_spin_button_get_property;
188
189   widget_class->map = gtk_spin_button_map;
190   widget_class->unmap = gtk_spin_button_unmap;
191   widget_class->realize = gtk_spin_button_realize;
192   widget_class->unrealize = gtk_spin_button_unrealize;
193   widget_class->size_request = gtk_spin_button_size_request;
194   widget_class->size_allocate = gtk_spin_button_size_allocate;
195   widget_class->expose_event = gtk_spin_button_expose;
196   widget_class->scroll_event = gtk_spin_button_scroll;
197   widget_class->button_press_event = gtk_spin_button_button_press;
198   widget_class->button_release_event = gtk_spin_button_button_release;
199   widget_class->motion_notify_event = gtk_spin_button_motion_notify;
200   widget_class->key_press_event = gtk_spin_button_key_press;
201   widget_class->key_release_event = gtk_spin_button_key_release;
202   widget_class->enter_notify_event = gtk_spin_button_enter_notify;
203   widget_class->leave_notify_event = gtk_spin_button_leave_notify;
204   widget_class->focus_out_event = gtk_spin_button_focus_out;
205
206   entry_class->activate = gtk_spin_button_activate;
207
208   class->input = NULL;
209   class->output = NULL;
210
211   g_object_class_install_property (gobject_class,
212                                    PROP_ADJUSTMENT,
213                                    g_param_spec_object ("adjustment",
214                                                         _("Adjustment"),
215                                                         _("The adjustment that holds the value of the spinbutton"),
216                                                         GTK_TYPE_ADJUSTMENT,
217                                                         G_PARAM_READWRITE));
218   
219   g_object_class_install_property (gobject_class,
220                                    PROP_CLIMB_RATE,
221                                    g_param_spec_double ("climb_rate",
222                                                         _("Climb Rate"),
223                                                         _("The acceleration rate when you hold down a button"),
224                                                         0.0,
225                                                         G_MAXDOUBLE,
226                                                         0.0,
227                                                         G_PARAM_READWRITE));  
228   
229   g_object_class_install_property (gobject_class,
230                                    PROP_DIGITS,
231                                    g_param_spec_uint ("digits",
232                                                       _("Digits"),
233                                                       _("The number of decimal places to display"),
234                                                       0,
235                                                       MAX_DIGITS,
236                                                       0,
237                                                       G_PARAM_READWRITE));
238   
239   g_object_class_install_property (gobject_class,
240                                    PROP_SNAP_TO_TICKS,
241                                    g_param_spec_boolean ("snap_to_ticks",
242                                                          _("Snap to Ticks"),
243                                                          _("Whether erroneous values are automatically changed to a spin button's nearest step increment"),
244                                                          FALSE,
245                                                          G_PARAM_READWRITE));
246   
247   g_object_class_install_property (gobject_class,
248                                    PROP_NUMERIC,
249                                    g_param_spec_boolean ("numeric",
250                                                          _("Numeric"),
251                                                          _("Whether non-numeric characters should be ignored"),
252                                                          FALSE,
253                                                          G_PARAM_READWRITE));
254   
255   g_object_class_install_property (gobject_class,
256                                    PROP_WRAP,
257                                    g_param_spec_boolean ("wrap",
258                                                          _("Wrap"),
259                                                          _("Whether a spin button should wrap upon reaching its limits"),
260                                                          FALSE,
261                                                          G_PARAM_READWRITE));
262   
263   g_object_class_install_property (gobject_class,
264                                    PROP_UPDATE_POLICY,
265                                    g_param_spec_enum ("update_policy",
266                                                       _("Update Policy"),
267                                                       _("Whether the spin button should update always, or only when the value is legal"),
268                                                       GTK_TYPE_SPIN_BUTTON_UPDATE_POLICY,
269                                                       GTK_UPDATE_ALWAYS,
270                                                       G_PARAM_READWRITE));
271   
272   g_object_class_install_property (gobject_class,
273                                    PROP_VALUE,
274                                    g_param_spec_double ("value",
275                                                         _("Value"),
276                                                         _("Reads the current value, or sets a new value"),
277                                                         -G_MAXDOUBLE,
278                                                         G_MAXDOUBLE,
279                                                         0.0,
280                                                         G_PARAM_READWRITE));  
281   
282   gtk_widget_class_install_style_property_parser (widget_class,
283                                                   g_param_spec_enum ("shadow_type", "Shadow Type", NULL,
284                                                                      GTK_TYPE_SHADOW_TYPE,
285                                                                      GTK_SHADOW_IN,
286                                                                      G_PARAM_READABLE),
287                                                   gtk_rc_property_parse_enum);
288   spinbutton_signals[INPUT] =
289     gtk_signal_new ("input",
290                     GTK_RUN_LAST,
291                     GTK_CLASS_TYPE (object_class),
292                     GTK_SIGNAL_OFFSET (GtkSpinButtonClass, input),
293                     _gtk_marshal_INT__POINTER,
294                     GTK_TYPE_INT, 1, GTK_TYPE_POINTER);
295
296   spinbutton_signals[OUTPUT] =
297     g_signal_new ("output",
298                   G_TYPE_FROM_CLASS(object_class),
299                   G_SIGNAL_RUN_LAST,
300                   G_STRUCT_OFFSET(GtkSpinButtonClass, output),
301                   _gtk_boolean_handled_accumulator, NULL,
302                   _gtk_marshal_BOOLEAN__VOID,
303                   G_TYPE_BOOLEAN, 0);
304
305   spinbutton_signals[VALUE_CHANGED] =
306     gtk_signal_new ("value_changed",
307                     GTK_RUN_LAST,
308                     GTK_CLASS_TYPE (object_class),
309                     GTK_SIGNAL_OFFSET (GtkSpinButtonClass, value_changed),
310                     _gtk_marshal_VOID__VOID,
311                     GTK_TYPE_NONE, 0);
312 }
313
314 static void
315 gtk_spin_button_editable_init (GtkEditableClass *iface)
316 {
317   iface->insert_text = gtk_spin_button_insert_text;
318 }
319
320 static void
321 gtk_spin_button_set_property (GObject      *object,
322                               guint         prop_id,
323                               const GValue *value,
324                               GParamSpec   *pspec)
325 {
326   GtkSpinButton *spin_button;
327
328   spin_button = GTK_SPIN_BUTTON (object);
329   
330   switch (prop_id)
331     {
332       GtkAdjustment *adjustment;
333
334     case PROP_ADJUSTMENT:
335       adjustment = GTK_ADJUSTMENT (g_value_get_object (value));
336       if (!adjustment)
337         adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
338       gtk_spin_button_set_adjustment (spin_button, adjustment);
339       break;
340     case PROP_CLIMB_RATE:
341       gtk_spin_button_configure (spin_button,
342                                  spin_button->adjustment,
343                                  g_value_get_double (value),
344                                  spin_button->digits);
345       break;
346     case PROP_DIGITS:
347       gtk_spin_button_configure (spin_button,
348                                  spin_button->adjustment,
349                                  spin_button->climb_rate,
350                                  g_value_get_uint (value));
351       break;
352     case PROP_SNAP_TO_TICKS:
353       gtk_spin_button_set_snap_to_ticks (spin_button, g_value_get_boolean (value));
354       break;
355     case PROP_NUMERIC:
356       gtk_spin_button_set_numeric (spin_button, g_value_get_boolean (value));
357       break;
358     case PROP_WRAP:
359       gtk_spin_button_set_wrap (spin_button, g_value_get_boolean (value));
360       break;
361     case PROP_UPDATE_POLICY:
362       gtk_spin_button_set_update_policy (spin_button, g_value_get_enum (value));
363       break;
364     case PROP_VALUE:
365       gtk_spin_button_set_value (spin_button, g_value_get_double (value));
366       break;
367     default:
368       break;
369     }
370 }
371
372 static void
373 gtk_spin_button_get_property (GObject      *object,
374                               guint         prop_id,
375                               GValue       *value,
376                               GParamSpec   *pspec)
377 {
378   GtkSpinButton *spin_button;
379
380   spin_button = GTK_SPIN_BUTTON (object);
381   
382   switch (prop_id)
383     {
384     case PROP_ADJUSTMENT:
385       g_value_set_object (value, G_OBJECT (spin_button->adjustment));
386       break;
387     case PROP_CLIMB_RATE:
388       g_value_set_double (value, spin_button->climb_rate);
389       break;
390     case PROP_DIGITS:
391       g_value_set_uint (value, spin_button->digits);
392       break;
393     case PROP_SNAP_TO_TICKS:
394       g_value_set_boolean (value, spin_button->snap_to_ticks);
395       break;
396     case PROP_NUMERIC:
397       g_value_set_boolean (value, spin_button->numeric);
398       break;
399     case PROP_WRAP:
400       g_value_set_boolean (value, spin_button->wrap);
401       break;
402     case PROP_UPDATE_POLICY:
403       g_value_set_enum (value, spin_button->update_policy);
404       break;
405      case PROP_VALUE:
406        g_value_set_double (value, spin_button->adjustment->value);
407       break;
408     default:
409       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
410       break;
411     }
412 }
413
414 static void
415 gtk_spin_button_init (GtkSpinButton *spin_button)
416 {
417   spin_button->adjustment = NULL;
418   spin_button->panel = NULL;
419   spin_button->timer = 0;
420   spin_button->ev_time = 0;
421   spin_button->climb_rate = 0.0;
422   spin_button->timer_step = 0.0;
423   spin_button->update_policy = GTK_UPDATE_ALWAYS;
424   spin_button->in_child = 2;
425   spin_button->click_child = 2;
426   spin_button->button = 0;
427   spin_button->need_timer = FALSE;
428   spin_button->timer_calls = 0;
429   spin_button->digits = 0;
430   spin_button->numeric = FALSE;
431   spin_button->wrap = FALSE;
432   spin_button->snap_to_ticks = FALSE;
433   gtk_spin_button_set_adjustment (spin_button,
434           (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0));
435 }
436
437 static void
438 gtk_spin_button_finalize (GObject *object)
439 {
440   g_return_if_fail (GTK_IS_SPIN_BUTTON (object));
441
442   gtk_object_unref (GTK_OBJECT (GTK_SPIN_BUTTON (object)->adjustment));
443   
444   G_OBJECT_CLASS (parent_class)->finalize (object);
445 }
446
447 static void
448 gtk_spin_button_map (GtkWidget *widget)
449 {
450   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
451
452   if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
453     {
454       GTK_WIDGET_CLASS (parent_class)->map (widget);
455       gdk_window_show (GTK_SPIN_BUTTON (widget)->panel);
456     }
457 }
458
459 static void
460 gtk_spin_button_unmap (GtkWidget *widget)
461 {
462   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
463
464   if (GTK_WIDGET_MAPPED (widget))
465     {
466       gdk_window_hide (GTK_SPIN_BUTTON (widget)->panel);
467       GTK_WIDGET_CLASS (parent_class)->unmap (widget);
468     }
469 }
470
471 static void
472 gtk_spin_button_realize (GtkWidget *widget)
473 {
474   GtkSpinButton *spin_button;
475   GdkWindowAttr attributes;
476   gint attributes_mask;
477   guint real_width;
478   gint return_val;
479   gint arrow_size;
480
481   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
482   
483   spin_button = GTK_SPIN_BUTTON (widget);
484   arrow_size = spin_button_get_arrow_size (spin_button);
485
486   real_width = widget->allocation.width;
487   widget->allocation.width -= arrow_size + 2 * widget->style->xthickness;
488   gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
489                          GDK_KEY_RELEASE_MASK);
490   GTK_WIDGET_CLASS (parent_class)->realize (widget);
491
492   widget->allocation.width = real_width;
493   
494   attributes.window_type = GDK_WINDOW_CHILD;
495   attributes.wclass = GDK_INPUT_OUTPUT;
496   attributes.visual = gtk_widget_get_visual (widget);
497   attributes.colormap = gtk_widget_get_colormap (widget);
498   attributes.event_mask = gtk_widget_get_events (widget);
499   attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK 
500     | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK 
501     | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
502
503   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
504
505   attributes.x = (widget->allocation.x +
506                   widget->allocation.width - arrow_size -
507                   2 * widget->style->xthickness);
508   attributes.y = widget->allocation.y + (widget->allocation.height -
509                                          widget->requisition.height) / 2;
510   attributes.width = arrow_size + 2 * widget->style->xthickness;
511   attributes.height = widget->requisition.height;
512   
513   spin_button->panel = gdk_window_new (gtk_widget_get_parent_window (widget), 
514                                        &attributes, attributes_mask);
515   gdk_window_set_user_data (spin_button->panel, widget);
516
517   gtk_style_set_background (widget->style, spin_button->panel, GTK_STATE_NORMAL);
518
519   return_val = FALSE;
520   gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
521                    &return_val);
522   if (return_val == FALSE)
523     gtk_spin_button_default_output (spin_button);
524 }
525
526 static void
527 gtk_spin_button_unrealize (GtkWidget *widget)
528 {
529   GtkSpinButton *spin;
530
531   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
532
533   spin = GTK_SPIN_BUTTON (widget);
534
535   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
536
537   if (spin->panel)
538     {
539       gdk_window_set_user_data (spin->panel, NULL);
540       gdk_window_destroy (spin->panel);
541       spin->panel = NULL;
542     }
543 }
544
545 static int
546 compute_double_length (double val, int digits)
547 {
548   int a;
549   int extra;
550
551   a = 1;
552   if (fabs (val) > 1.0)
553     a = floor (log10 (fabs (val))) + 1;  
554
555   extra = 0;
556   
557   /* The dot: */
558   if (digits > 0)
559     extra++;
560
561   /* The sign: */
562   if (val < 0)
563     extra++;
564
565   return a + digits + extra;
566 }
567
568 static void
569 gtk_spin_button_size_request (GtkWidget      *widget,
570                               GtkRequisition *requisition)
571 {
572   GtkEntry *entry;
573   GtkSpinButton *spin_button;
574   gint arrow_size;
575   
576   g_return_if_fail (requisition != NULL);
577   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
578
579   entry = GTK_ENTRY (widget);
580   spin_button = GTK_SPIN_BUTTON (widget);
581   arrow_size = spin_button_get_arrow_size (spin_button);
582   
583   GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
584
585   if (entry->width_chars < 0)
586     {
587       PangoContext *context;
588       PangoFontMetrics *metrics;
589       gint width;
590       gint w;
591       gint string_len;
592       gint max_string_len;
593       gint digit_width;
594
595       context = gtk_widget_get_pango_context (widget);
596       metrics = pango_context_get_metrics (context,
597                                            widget->style->font_desc,
598                                            pango_context_get_language (context));
599
600       digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
601       digit_width = PANGO_PIXELS (digit_width);
602
603       pango_font_metrics_unref (metrics);
604       
605       /* Get max of MIN_SPIN_BUTTON_WIDTH, size of upper, size of lower */
606       
607       width = MIN_SPIN_BUTTON_WIDTH;
608       max_string_len = MAX (10, compute_double_length (1e9 * spin_button->adjustment->step_increment,
609                                                        spin_button->digits));
610
611       string_len = compute_double_length (spin_button->adjustment->upper,
612                                           spin_button->digits);
613       w = MIN (string_len, max_string_len) * digit_width;
614       width = MAX (width, w);
615       string_len = compute_double_length (spin_button->adjustment->lower,
616                                           spin_button->digits);
617       w = MIN (string_len, max_string_len) * digit_width;
618       width = MAX (width, w);
619       
620       requisition->width = (width + arrow_size +
621                             2 * widget->style->xthickness);
622     }
623   else
624     requisition->width += arrow_size + 2 * widget->style->xthickness;
625 }
626
627 static void
628 gtk_spin_button_size_allocate (GtkWidget     *widget,
629                                GtkAllocation *allocation)
630 {
631   GtkSpinButton *spin;
632   GtkAllocation child_allocation;
633   gint arrow_size;
634
635   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
636   g_return_if_fail (allocation != NULL);
637
638   spin = GTK_SPIN_BUTTON (widget);
639   arrow_size = spin_button_get_arrow_size (spin);
640
641   child_allocation = *allocation;
642   if (child_allocation.width > arrow_size + 2 * widget->style->xthickness)
643     child_allocation.width -= arrow_size + 2 * widget->style->xthickness;
644
645   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
646     child_allocation.x += arrow_size + 2 * widget->style->xthickness;
647
648   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &child_allocation);
649
650   widget->allocation = *allocation;
651
652   if (GTK_WIDGET_REALIZED (widget))
653     {
654       child_allocation.width = arrow_size + 2 * widget->style->xthickness;
655       child_allocation.height = widget->requisition.height;
656
657       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
658         child_allocation.x = (allocation->x + allocation->width -
659                               arrow_size - 2 * widget->style->xthickness);
660       else
661         child_allocation.x = allocation->x;      
662
663       child_allocation.y = allocation->y + (allocation->height - widget->requisition.height) / 2;
664
665       gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel, 
666                               child_allocation.x,
667                               child_allocation.y,
668                               child_allocation.width,
669                               child_allocation.height); 
670     }
671 }
672
673 static gint
674 gtk_spin_button_expose (GtkWidget      *widget,
675                         GdkEventExpose *event)
676 {
677   GtkSpinButton *spin;
678   gint arrow_size;
679
680   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
681   g_return_val_if_fail (event != NULL, FALSE);
682
683   spin = GTK_SPIN_BUTTON (widget);
684   arrow_size = spin_button_get_arrow_size (spin);
685
686   if (GTK_WIDGET_DRAWABLE (widget))
687     {
688       GtkShadowType shadow_type;
689
690       /* FIXME this seems like really broken code -
691        * why aren't we looking at event->window
692        * and acting accordingly?
693        */
694
695       shadow_type = spin_button_get_shadow_type (spin);
696       if (shadow_type != GTK_SHADOW_NONE)
697         gtk_paint_box (widget->style, spin->panel,
698                        GTK_STATE_NORMAL, shadow_type,
699                        &event->area, widget, "spinbutton",
700                        0, 0, 
701                        arrow_size + 2 * widget->style->xthickness,
702                        widget->requisition.height); 
703       else
704          {
705             gdk_window_set_back_pixmap (spin->panel, NULL, TRUE);
706             gdk_window_clear_area (spin->panel,
707                                    event->area.x, event->area.y,
708                                    event->area.width, event->area.height);
709          }
710        gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
711        gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
712
713        GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
714     }
715
716   return FALSE;
717 }
718
719 static void
720 gtk_spin_button_draw_arrow (GtkSpinButton *spin_button, 
721                             guint          arrow)
722 {
723   GtkShadowType spin_shadow_type;
724   GtkStateType state_type;
725   GtkShadowType shadow_type;
726   GtkWidget *widget;
727   gint x;
728   gint y;
729   gint arrow_size;
730
731   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
732   
733   widget = GTK_WIDGET (spin_button);
734   spin_shadow_type = spin_button_get_shadow_type (spin_button);
735   arrow_size = spin_button_get_arrow_size (spin_button);
736
737   if (GTK_WIDGET_DRAWABLE (spin_button))
738     {
739       if (!spin_button->wrap &&
740           (((arrow == GTK_ARROW_UP &&
741           (spin_button->adjustment->upper - spin_button->adjustment->value
742            <= EPSILON))) ||
743           ((arrow == GTK_ARROW_DOWN &&
744           (spin_button->adjustment->value - spin_button->adjustment->lower
745            <= EPSILON)))))
746         {
747           shadow_type = GTK_SHADOW_ETCHED_IN;
748           state_type = GTK_STATE_NORMAL;
749         }
750       else
751         {
752           if (spin_button->in_child == arrow)
753             {
754               if (spin_button->click_child == arrow)
755                 state_type = GTK_STATE_ACTIVE;
756               else
757                 state_type = GTK_STATE_PRELIGHT;
758             }
759           else
760             state_type = GTK_STATE_NORMAL;
761           
762           if (spin_button->click_child == arrow)
763             shadow_type = GTK_SHADOW_IN;
764           else
765             shadow_type = GTK_SHADOW_OUT;
766         }
767       if (arrow == GTK_ARROW_UP)
768         {
769           if (spin_shadow_type != GTK_SHADOW_NONE)
770             {
771               x = widget->style->xthickness;
772               y = widget->style->ythickness;
773             }
774           else
775             {
776               x = widget->style->xthickness - 1;
777               y = widget->style->ythickness - 1;
778             }
779           gtk_paint_arrow (widget->style, spin_button->panel,
780                            state_type, shadow_type, 
781                            NULL, widget, "spinbutton",
782                            arrow, TRUE, 
783                            x, y, arrow_size,
784                            widget->requisition.height / 2 
785                            - widget->style->ythickness);
786         }
787       else
788         {
789           if (spin_shadow_type != GTK_SHADOW_NONE)
790             {
791               x = widget->style->xthickness;
792               y = widget->requisition.height / 2;
793             }
794           else
795             {
796               x = widget->style->xthickness - 1;
797               y = widget->requisition.height / 2 + 1;
798             }
799           gtk_paint_arrow (widget->style, spin_button->panel,
800                            state_type, shadow_type, 
801                            NULL, widget, "spinbutton",
802                            arrow, TRUE, 
803                            x, y, arrow_size,
804                            widget->requisition.height / 2 
805                            - widget->style->ythickness);
806         }
807     }
808 }
809
810 static gint
811 gtk_spin_button_enter_notify (GtkWidget        *widget,
812                               GdkEventCrossing *event)
813 {
814   GtkSpinButton *spin;
815
816   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
817   g_return_val_if_fail (event != NULL, FALSE);
818
819   spin = GTK_SPIN_BUTTON (widget);
820
821   if (event->window == spin->panel)
822     {
823       gint x;
824       gint y;
825
826       gdk_window_get_pointer (spin->panel, &x, &y, NULL);
827
828       if (y <= widget->requisition.height / 2)
829         {
830           spin->in_child = GTK_ARROW_UP;
831           if (spin->click_child == 2) 
832             gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
833         }
834       else
835         {
836           spin->in_child = GTK_ARROW_DOWN;
837           if (spin->click_child == 2) 
838             gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
839         }
840     }
841   return FALSE;
842 }
843
844 static gint
845 gtk_spin_button_leave_notify (GtkWidget        *widget,
846                               GdkEventCrossing *event)
847 {
848   GtkSpinButton *spin;
849
850   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
851   g_return_val_if_fail (event != NULL, FALSE);
852
853   spin = GTK_SPIN_BUTTON (widget);
854
855   if (event->window == spin->panel && spin->click_child == 2)
856     {
857       if (spin->in_child == GTK_ARROW_UP) 
858         {
859           spin->in_child = 2;
860           gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
861         }
862       else
863         {
864           spin->in_child = 2;
865           gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
866         }
867     }
868   return FALSE;
869 }
870
871 static gint
872 gtk_spin_button_focus_out (GtkWidget     *widget,
873                            GdkEventFocus *event)
874 {
875   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
876   g_return_val_if_fail (event != NULL, FALSE);
877
878   if (GTK_ENTRY (widget)->editable)
879     gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
880
881   return GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
882 }
883
884 static gint
885 gtk_spin_button_scroll (GtkWidget      *widget,
886                         GdkEventScroll *event)
887 {
888   GtkSpinButton *spin;
889
890   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
891   g_return_val_if_fail (event != NULL, FALSE);
892
893   spin = GTK_SPIN_BUTTON (widget);
894
895   if (event->direction == GDK_SCROLL_UP)
896     {
897       if (!GTK_WIDGET_HAS_FOCUS (widget))
898         gtk_widget_grab_focus (widget);
899       gtk_spin_button_real_spin (spin, spin->adjustment->step_increment);
900     }
901   else if (event->direction == GDK_SCROLL_DOWN)
902     {
903       if (!GTK_WIDGET_HAS_FOCUS (widget))
904         gtk_widget_grab_focus (widget);
905       gtk_spin_button_real_spin (spin, -spin->adjustment->step_increment); 
906     }
907   else
908     return FALSE;
909
910   return TRUE;
911 }
912
913 static gint
914 gtk_spin_button_button_press (GtkWidget      *widget,
915                               GdkEventButton *event)
916 {
917   GtkSpinButton *spin;
918
919   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
920   g_return_val_if_fail (event != NULL, FALSE);
921
922   spin = GTK_SPIN_BUTTON (widget);
923
924   if (!spin->button)
925     {
926       if (event->window == spin->panel)
927         {
928           if (!GTK_WIDGET_HAS_FOCUS (widget))
929             gtk_widget_grab_focus (widget);
930           gtk_grab_add (widget);
931           spin->button = event->button;
932           
933           if (GTK_ENTRY (widget)->editable)
934             gtk_spin_button_update (spin);
935           
936           if (event->y <= widget->requisition.height / 2)
937             {
938               spin->click_child = GTK_ARROW_UP;
939               if (event->button == 1)
940                 {
941                  gtk_spin_button_real_spin (spin, 
942                                             spin->adjustment->step_increment);
943                   if (!spin->timer)
944                     {
945                       spin->timer_step = spin->adjustment->step_increment;
946                       spin->need_timer = TRUE;
947                       spin->timer = gtk_timeout_add 
948                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
949                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
950                     }
951                 }
952               else if (event->button == 2)
953                 {
954                  gtk_spin_button_real_spin (spin, 
955                                             spin->adjustment->page_increment);
956                   if (!spin->timer) 
957                     {
958                       spin->timer_step = spin->adjustment->page_increment;
959                       spin->need_timer = TRUE;
960                       spin->timer = gtk_timeout_add 
961                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
962                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
963                     }
964                 }
965               gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
966             }
967           else 
968             {
969               spin->click_child = GTK_ARROW_DOWN;
970               if (event->button == 1)
971                 {
972                   gtk_spin_button_real_spin (spin,
973                                              -spin->adjustment->step_increment);
974                   if (!spin->timer)
975                     {
976                       spin->timer_step = spin->adjustment->step_increment;
977                       spin->need_timer = TRUE;
978                       spin->timer = gtk_timeout_add 
979                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
980                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
981                     }
982                 }      
983               else if (event->button == 2)
984                 {
985                   gtk_spin_button_real_spin (spin,
986                                              -spin->adjustment->page_increment);
987                   if (!spin->timer) 
988                     {
989                       spin->timer_step = spin->adjustment->page_increment;
990                       spin->need_timer = TRUE;
991                       spin->timer = gtk_timeout_add 
992                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
993                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
994                     }
995                 }
996               gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
997             }
998           return TRUE;
999         }
1000       else
1001         return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
1002     }
1003   return FALSE;
1004 }
1005
1006 static gint
1007 gtk_spin_button_button_release (GtkWidget      *widget,
1008                                 GdkEventButton *event)
1009 {
1010   GtkSpinButton *spin;
1011   gint arrow_size;
1012
1013   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1014   g_return_val_if_fail (event != NULL, FALSE);
1015
1016   spin = GTK_SPIN_BUTTON (widget);
1017   arrow_size = spin_button_get_arrow_size (spin);
1018
1019   if (event->button == spin->button)
1020     {
1021       guint click_child;
1022
1023       if (spin->timer)
1024         {
1025           gtk_timeout_remove (spin->timer);
1026           spin->timer = 0;
1027           spin->timer_calls = 0;
1028           spin->need_timer = FALSE;
1029         }
1030
1031       if (event->button == 3)
1032         {
1033           if (event->y >= 0 && event->x >= 0 && 
1034               event->y <= widget->requisition.height &&
1035               event->x <= arrow_size + 2 * widget->style->xthickness)
1036             {
1037               if (spin->click_child == GTK_ARROW_UP &&
1038                   event->y <= widget->requisition.height / 2)
1039                 {
1040                   gdouble diff;
1041
1042                   diff = spin->adjustment->upper - spin->adjustment->value;
1043                   if (diff > EPSILON)
1044                     gtk_spin_button_real_spin (spin, diff);
1045                 }
1046               else if (spin->click_child == GTK_ARROW_DOWN &&
1047                        event->y > widget->requisition.height / 2)
1048                 {
1049                   gdouble diff;
1050
1051                   diff = spin->adjustment->value - spin->adjustment->lower;
1052                   if (diff > EPSILON)
1053                     gtk_spin_button_real_spin (spin, -diff);
1054                 }
1055             }
1056         }                 
1057       gtk_grab_remove (widget);
1058       click_child = spin->click_child;
1059       spin->click_child = 2;
1060       spin->button = 0;
1061       gtk_spin_button_draw_arrow (spin, click_child);
1062       return TRUE;
1063     }
1064   else
1065     return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
1066
1067   return FALSE;
1068 }
1069
1070 static gint
1071 gtk_spin_button_motion_notify (GtkWidget      *widget,
1072                                GdkEventMotion *event)
1073 {
1074   GtkSpinButton *spin;
1075
1076   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1077   g_return_val_if_fail (event != NULL, FALSE);
1078
1079   spin = GTK_SPIN_BUTTON (widget);
1080   
1081   if (spin->button)
1082     return FALSE;
1083
1084   if (event->window == spin->panel)
1085     {
1086       gint y;
1087
1088       y = event->y;
1089       if (event->is_hint)
1090         gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
1091
1092       if (y <= widget->requisition.height / 2 && 
1093           spin->in_child == GTK_ARROW_DOWN)
1094         {
1095           spin->in_child = GTK_ARROW_UP;
1096           gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
1097           gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
1098         }
1099       else if (y > widget->requisition.height / 2 && 
1100           spin->in_child == GTK_ARROW_UP)
1101         {
1102           spin->in_child = GTK_ARROW_DOWN;
1103           gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
1104           gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
1105         }
1106       return FALSE;
1107     }
1108           
1109   return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
1110 }
1111
1112 static gint
1113 gtk_spin_button_timer (GtkSpinButton *spin_button)
1114 {
1115   gboolean retval = FALSE;
1116   
1117   GDK_THREADS_ENTER ();
1118
1119   if (spin_button->timer)
1120     {
1121       if (spin_button->click_child == GTK_ARROW_UP)
1122         gtk_spin_button_real_spin (spin_button, spin_button->timer_step);
1123       else
1124         gtk_spin_button_real_spin (spin_button, -spin_button->timer_step);
1125
1126       if (spin_button->need_timer)
1127         {
1128           spin_button->need_timer = FALSE;
1129           spin_button->timer = gtk_timeout_add 
1130             (SPIN_BUTTON_TIMER_DELAY, (GtkFunction) gtk_spin_button_timer, 
1131              (gpointer) spin_button);
1132         }
1133       else 
1134         {
1135           if (spin_button->climb_rate > 0.0 && spin_button->timer_step 
1136               < spin_button->adjustment->page_increment)
1137             {
1138               if (spin_button->timer_calls < MAX_TIMER_CALLS)
1139                 spin_button->timer_calls++;
1140               else 
1141                 {
1142                   spin_button->timer_calls = 0;
1143                   spin_button->timer_step += spin_button->climb_rate;
1144                 }
1145             }
1146           retval = TRUE;
1147         }
1148     }
1149
1150   GDK_THREADS_LEAVE ();
1151
1152   return retval;
1153 }
1154
1155 static void
1156 gtk_spin_button_value_changed (GtkAdjustment *adjustment,
1157                                GtkSpinButton *spin_button)
1158 {
1159   gint return_val;
1160
1161   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1162
1163   return_val = FALSE;
1164   gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1165                    &return_val);
1166   if (return_val == FALSE)
1167     gtk_spin_button_default_output (spin_button);
1168
1169   gtk_signal_emit (GTK_OBJECT (spin_button), 
1170                    spinbutton_signals[VALUE_CHANGED]);
1171
1172   gtk_spin_button_draw_arrow (spin_button, GTK_ARROW_UP);
1173   gtk_spin_button_draw_arrow (spin_button, GTK_ARROW_DOWN);
1174   
1175   g_object_notify (G_OBJECT (spin_button), "value");
1176 }
1177
1178 static gint
1179 gtk_spin_button_key_press (GtkWidget     *widget,
1180                            GdkEventKey   *event)
1181 {
1182   GtkSpinButton *spin;
1183   gint key;
1184   gboolean key_repeat = FALSE;
1185   gboolean retval = FALSE;
1186   
1187   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1188   g_return_val_if_fail (event != NULL, FALSE);
1189   
1190   spin = GTK_SPIN_BUTTON (widget);
1191   key = event->keyval;
1192
1193   key_repeat = (event->time == spin->ev_time);
1194
1195   if (GTK_ENTRY (widget)->editable)
1196     {
1197       switch (key)
1198         {
1199         case GDK_KP_Up:
1200         case GDK_Up:
1201
1202           if (GTK_WIDGET_HAS_FOCUS (widget))
1203             {
1204               gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), 
1205                                             "key_press_event");
1206               if (!key_repeat)
1207                 spin->timer_step = spin->adjustment->step_increment;
1208
1209               gtk_spin_button_real_spin (spin, spin->timer_step);
1210
1211               if (key_repeat)
1212                 {
1213                   if (spin->climb_rate > 0.0 && spin->timer_step
1214                       < spin->adjustment->page_increment)
1215                     {
1216                       if (spin->timer_calls < MAX_TIMER_CALLS)
1217                         spin->timer_calls++;
1218                       else 
1219                         {
1220                           spin->timer_calls = 0;
1221                           spin->timer_step += spin->climb_rate;
1222                         }
1223                     }
1224                 }
1225               retval = TRUE;
1226             }
1227           break;
1228
1229         case GDK_KP_Down:
1230         case GDK_Down:
1231
1232           if (GTK_WIDGET_HAS_FOCUS (widget))
1233             {
1234               gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), 
1235                                             "key_press_event");
1236               if (!key_repeat)
1237                 spin->timer_step = spin->adjustment->step_increment;
1238
1239               gtk_spin_button_real_spin (spin, -spin->timer_step);
1240
1241               if (key_repeat)
1242                 {
1243                   if (spin->climb_rate > 0.0 && spin->timer_step
1244                       < spin->adjustment->page_increment)
1245                     {
1246                       if (spin->timer_calls < MAX_TIMER_CALLS)
1247                         spin->timer_calls++;
1248                       else 
1249                         {
1250                           spin->timer_calls = 0;
1251                           spin->timer_step += spin->climb_rate;
1252                         }
1253                     }
1254                 }
1255               retval = TRUE;
1256             }
1257           break;
1258
1259         case GDK_KP_Page_Up:
1260         case GDK_Page_Up:
1261
1262           if (event->state & GDK_CONTROL_MASK)
1263             {
1264               gdouble diff = spin->adjustment->upper - spin->adjustment->value;
1265               if (diff > EPSILON)
1266                 gtk_spin_button_real_spin (spin, diff);
1267             }
1268           else
1269             gtk_spin_button_real_spin (spin, spin->adjustment->page_increment);
1270
1271           retval = TRUE;
1272           break;
1273           
1274         case GDK_KP_Page_Down:
1275         case GDK_Page_Down:
1276
1277           if (event->state & GDK_CONTROL_MASK)
1278             {
1279               gdouble diff = spin->adjustment->value - spin->adjustment->lower;
1280               if (diff > EPSILON)
1281                 gtk_spin_button_real_spin (spin, -diff);
1282             }
1283           else
1284             gtk_spin_button_real_spin (spin, -spin->adjustment->page_increment);
1285
1286           retval = TRUE;
1287           break;
1288
1289         default:
1290           break;
1291         }
1292     }
1293
1294   if (retval)
1295     {
1296       gtk_spin_button_update (spin);
1297       return TRUE;
1298     }
1299   else
1300     return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
1301 }
1302
1303 static gint
1304 gtk_spin_button_key_release (GtkWidget   *widget,
1305                              GdkEventKey *event)
1306 {
1307   GtkSpinButton *spin;
1308
1309   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1310   
1311   spin = GTK_SPIN_BUTTON (widget);
1312   
1313   spin->ev_time = event->time;
1314   return TRUE;
1315 }
1316
1317 static void
1318 gtk_spin_button_snap (GtkSpinButton *spin_button,
1319                       gdouble        val)
1320 {
1321   gdouble inc;
1322   gdouble tmp;
1323   
1324   inc = spin_button->adjustment->step_increment;
1325   tmp = (val - spin_button->adjustment->lower) / inc;
1326   if (tmp - floor (tmp) < ceil (tmp) - tmp)
1327     val = spin_button->adjustment->lower + floor (tmp) * inc;
1328   else
1329     val = spin_button->adjustment->lower + ceil (tmp) * inc;
1330
1331   if (fabs (val - spin_button->adjustment->value) > EPSILON)
1332     gtk_adjustment_set_value (spin_button->adjustment, val);
1333   else
1334     {
1335       gint return_val = FALSE;
1336       gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1337                        &return_val);
1338       if (return_val == FALSE)
1339         gtk_spin_button_default_output (spin_button);
1340     }
1341 }
1342
1343 static void
1344 gtk_spin_button_activate (GtkEntry *entry)
1345 {
1346   if (entry->editable)
1347     gtk_spin_button_update (GTK_SPIN_BUTTON (entry));
1348 }
1349
1350 static void
1351 gtk_spin_button_insert_text (GtkEditable *editable,
1352                              const gchar *new_text,
1353                              gint         new_text_length,
1354                              gint        *position)
1355 {
1356   GtkEntry *entry = GTK_ENTRY (editable);
1357   GtkSpinButton *spin = GTK_SPIN_BUTTON (editable);
1358   GtkEditableClass *parent_editable_iface = g_type_interface_peek (parent_class, GTK_TYPE_EDITABLE);
1359  
1360   if (spin->numeric)
1361     {
1362       struct lconv *lc;
1363       gboolean sign;
1364       gint dotpos = -1;
1365       gint i;
1366       GdkWChar pos_sign;
1367       GdkWChar neg_sign;
1368       gint entry_length;
1369
1370       entry_length = entry->text_length;
1371
1372       lc = localeconv ();
1373
1374       if (*(lc->negative_sign))
1375         neg_sign = *(lc->negative_sign);
1376       else 
1377         neg_sign = '-';
1378
1379       if (*(lc->positive_sign))
1380         pos_sign = *(lc->positive_sign);
1381       else 
1382         pos_sign = '+';
1383
1384       for (sign=0, i=0; i<entry_length; i++)
1385         if ((entry->text[i] == neg_sign) ||
1386             (entry->text[i] == pos_sign))
1387           {
1388             sign = 1;
1389             break;
1390           }
1391
1392       if (sign && !(*position))
1393         return;
1394
1395       for (dotpos=-1, i=0; i<entry_length; i++)
1396         if (entry->text[i] == *(lc->decimal_point))
1397           {
1398             dotpos = i;
1399             break;
1400           }
1401
1402       if (dotpos > -1 && *position > dotpos &&
1403           (gint)spin->digits - entry_length
1404             + dotpos - new_text_length + 1 < 0)
1405         return;
1406
1407       for (i = 0; i < new_text_length; i++)
1408         {
1409           if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1410             {
1411               if (sign || (*position) || i)
1412                 return;
1413               sign = TRUE;
1414             }
1415           else if (new_text[i] == *(lc->decimal_point))
1416             {
1417               if (!spin->digits || dotpos > -1 || 
1418                   (new_text_length - 1 - i + entry_length
1419                     - *position > (gint)spin->digits)) 
1420                 return;
1421               dotpos = *position + i;
1422             }
1423           else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1424             return;
1425         }
1426     }
1427
1428   parent_editable_iface->insert_text (editable, new_text,
1429                                       new_text_length, position);
1430 }
1431
1432 static void
1433 gtk_spin_button_real_spin (GtkSpinButton *spin_button,
1434                            gdouble        increment)
1435 {
1436   GtkAdjustment *adj;
1437   gdouble new_value = 0.0;
1438
1439   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1440   
1441   adj = spin_button->adjustment;
1442
1443   new_value = adj->value + increment;
1444
1445   if (increment > 0)
1446     {
1447       if (spin_button->wrap)
1448         {
1449           if (fabs (adj->value - adj->upper) < EPSILON)
1450             new_value = adj->lower;
1451           else if (new_value > adj->upper)
1452             new_value = adj->upper;
1453         }
1454       else
1455         new_value = MIN (new_value, adj->upper);
1456     }
1457   else if (increment < 0) 
1458     {
1459       if (spin_button->wrap)
1460         {
1461           if (fabs (adj->value - adj->lower) < EPSILON)
1462             new_value = adj->upper;
1463           else if (new_value < adj->lower)
1464             new_value = adj->lower;
1465         }
1466       else
1467         new_value = MAX (new_value, adj->lower);
1468     }
1469
1470   if (fabs (new_value - adj->value) > EPSILON)
1471     gtk_adjustment_set_value (adj, new_value);
1472 }
1473
1474 static gint
1475 gtk_spin_button_default_input (GtkSpinButton *spin_button,
1476                                gdouble       *new_val)
1477 {
1478   gchar *err = NULL;
1479
1480   *new_val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &err);
1481   if (*err)
1482     return GTK_INPUT_ERROR;
1483   else
1484     return FALSE;
1485 }
1486
1487 static gint
1488 gtk_spin_button_default_output (GtkSpinButton *spin_button)
1489 {
1490   gchar *buf = g_strdup_printf ("%0.*f", spin_button->digits, spin_button->adjustment->value);
1491
1492   if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1493     gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1494   g_free (buf);
1495   return FALSE;
1496 }
1497
1498
1499 /***********************************************************
1500  ***********************************************************
1501  ***                  Public interface                   ***
1502  ***********************************************************
1503  ***********************************************************/
1504
1505
1506 void
1507 gtk_spin_button_configure (GtkSpinButton  *spin_button,
1508                            GtkAdjustment  *adjustment,
1509                            gdouble         climb_rate,
1510                            guint           digits)
1511 {
1512   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1513
1514   if (adjustment)
1515     gtk_spin_button_set_adjustment (spin_button, adjustment);
1516   else
1517     adjustment = spin_button->adjustment;
1518
1519   g_object_freeze_notify (G_OBJECT (spin_button));
1520   if (spin_button->digits != digits) 
1521     {
1522       spin_button->digits = digits;
1523       g_object_notify (G_OBJECT (spin_button), "digits");
1524     }
1525
1526   if (spin_button->climb_rate != climb_rate)
1527     {
1528       spin_button->climb_rate = climb_rate;
1529       g_object_notify (G_OBJECT (spin_button), "climb_rate");
1530     }
1531   g_object_thaw_notify (G_OBJECT (spin_button));
1532
1533   gtk_adjustment_value_changed (adjustment);
1534 }
1535
1536 GtkWidget *
1537 gtk_spin_button_new (GtkAdjustment *adjustment,
1538                      gdouble        climb_rate,
1539                      guint          digits)
1540 {
1541   GtkSpinButton *spin;
1542
1543   if (adjustment)
1544     g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
1545
1546   spin = gtk_type_new (GTK_TYPE_SPIN_BUTTON);
1547
1548   gtk_spin_button_configure (spin, adjustment, climb_rate, digits);
1549
1550   return GTK_WIDGET (spin);
1551 }
1552
1553 /**
1554  * gtk_spin_button_new_with_range:
1555  * @min: Minimum allowable value
1556  * @max: Maximum allowable value
1557  * @step: Increment added or subtracted by spinning the widget
1558  * 
1559  * This is a convenience constructor that allows creation of a numeric 
1560  * #GtkSpinButton without manually creating an adjustment. The value is 
1561  * initially set to the minimum value and a page increment of 10 * @step
1562  * is the default. The precision of the spin button is equivalent to the 
1563  * precision of @step.
1564  * 
1565  * Return value: The new spin button as a #GtkWidget.
1566  **/
1567 GtkWidget *
1568 gtk_spin_button_new_with_range (gdouble min,
1569                                 gdouble max,
1570                                 gdouble step)
1571 {
1572   GtkObject *adj;
1573   GtkSpinButton *spin;
1574   gint digits;
1575
1576   g_return_val_if_fail (min < max, NULL);
1577   g_return_val_if_fail (step != 0.0, NULL);
1578
1579   spin = gtk_type_new (GTK_TYPE_SPIN_BUTTON);
1580
1581   adj = gtk_adjustment_new (min, min, max, step, 10 * step, step);
1582
1583   if (fabs (step) >= 1.0 || step == 0.0)
1584     digits = 0;
1585   else {
1586     digits = abs ((gint) floor (log10 (fabs (step))));
1587     if (digits > MAX_DIGITS)
1588       digits = MAX_DIGITS;
1589   }
1590
1591   gtk_spin_button_configure (spin, GTK_ADJUSTMENT (adj), step, digits);
1592
1593   gtk_spin_button_set_numeric (spin, TRUE);
1594
1595   return GTK_WIDGET (spin);
1596 }
1597
1598 /* Callback used when the spin button's adjustment changes.  We need to redraw
1599  * the arrows when the adjustment's range changes, and reevaluate our size request.
1600  */
1601 static void
1602 adjustment_changed_cb (GtkAdjustment *adjustment, gpointer data)
1603 {
1604   GtkSpinButton *spin_button;
1605
1606   spin_button = GTK_SPIN_BUTTON (data);
1607
1608   gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1609 }
1610
1611 /**
1612  * gtk_spin_button_set_adjustment:
1613  * @spin_button: a #GtkSpinButton
1614  * @adjustment: a #GtkAdjustment to replace the existing adjustment
1615  * 
1616  * Replaces the #GtkAdjustment associated with @spin_button.
1617  **/
1618 void
1619 gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
1620                                 GtkAdjustment *adjustment)
1621 {
1622   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1623
1624   if (spin_button->adjustment != adjustment)
1625     {
1626       if (spin_button->adjustment)
1627         {
1628           gtk_signal_disconnect_by_data (GTK_OBJECT (spin_button->adjustment),
1629                                          (gpointer) spin_button);
1630           gtk_object_unref (GTK_OBJECT (spin_button->adjustment));
1631         }
1632       spin_button->adjustment = adjustment;
1633       if (adjustment)
1634         {
1635           gtk_object_ref (GTK_OBJECT (adjustment));
1636           gtk_object_sink (GTK_OBJECT (adjustment));
1637           gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
1638                               (GtkSignalFunc) gtk_spin_button_value_changed,
1639                               (gpointer) spin_button);
1640           gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
1641                               (GtkSignalFunc) adjustment_changed_cb,
1642                               (gpointer) spin_button);
1643         }
1644
1645       gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1646     }
1647
1648   g_object_notify (G_OBJECT (spin_button), "adjustment");
1649 }
1650
1651 /**
1652  * gtk_spin_button_get_adjustment:
1653  * @spin_button: 
1654  * 
1655  * Get the adjustment associated with a #GtkSpinButton
1656  * 
1657  * Return value: the #GtkAdjustment of @spin_button
1658  **/
1659 GtkAdjustment *
1660 gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
1661 {
1662   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
1663
1664   return spin_button->adjustment;
1665 }
1666
1667 /**
1668  * gtk_spin_button_set_digits:
1669  * @spin_button: a #GtkSpinButton
1670  * @digits: the number of digits to be displayed for the spin button's value
1671  * 
1672  * Set the precision to be displayed by @spin_button. Up to 20 digit precision
1673  * is allowed.
1674  **/
1675 void
1676 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
1677                             guint          digits)
1678 {
1679   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1680
1681   if (spin_button->digits != digits)
1682     {
1683       spin_button->digits = digits;
1684       gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1685       g_object_notify (G_OBJECT (spin_button), "digits");
1686       
1687       /* since lower/upper may have changed */
1688       gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1689     }
1690 }
1691
1692 /**
1693  * gtk_spin_button_get_digits:
1694  * @spin_button: a #GtkSpinButton
1695  *
1696  * Fetches the precision of @spin_button. See gtk_spin_button_set_digits().
1697  *
1698  * Returns: the current precision
1699  **/
1700 guint
1701 gtk_spin_button_get_digits (GtkSpinButton *spin_button)
1702 {
1703   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1704
1705   return spin_button->digits;
1706 }
1707
1708 /**
1709  * gtk_spin_button_set_increments:
1710  * @spin_button: a #GtkSpinButton
1711  * @step: increment applied for a button 1 press.
1712  * @page: increment applied for a button 2 press.
1713  * 
1714  * Sets the step and page increments for spin_button.  This affects how 
1715  * quickly the value changes when the spin button's arrows are activated.
1716  **/
1717 void
1718 gtk_spin_button_set_increments (GtkSpinButton *spin_button,
1719                                 gdouble        step,
1720                                 gdouble        page)
1721 {
1722   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1723
1724   spin_button->adjustment->step_increment = step;
1725   spin_button->adjustment->page_increment = page;
1726 }
1727
1728 /**
1729  * gtk_spin_button_get_increments:
1730  * @spin_button: a #GtkSpinButton
1731  * @step: location to store step increment, or %NULL
1732  * @page: location to store page increment, or %NULL
1733  *
1734  * Gets the current step and page the increments used by @spin_button. See
1735  * gtk_spin_button_set_increments().
1736  **/
1737 void
1738 gtk_spin_button_get_increments (GtkSpinButton *spin_button,
1739                                 gdouble       *step,
1740                                 gdouble       *page)
1741 {
1742   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1743
1744   if (step)
1745     *step = spin_button->adjustment->step_increment;
1746   if (page)
1747     *page = spin_button->adjustment->page_increment;
1748 }
1749
1750 /**
1751  * gtk_spin_button_set_range:
1752  * @spin_button: a #GtkSpinButton
1753  * @min: minimum allowable value
1754  * @max: maximum allowable value
1755  * 
1756  * Sets the minimum and maximum allowable values for @spin_button
1757  **/
1758 void
1759 gtk_spin_button_set_range (GtkSpinButton *spin_button,
1760                            gdouble        min,
1761                            gdouble        max)
1762 {
1763   gdouble value;
1764   
1765   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1766
1767   spin_button->adjustment->lower = min;
1768   spin_button->adjustment->upper = max;
1769
1770   value = CLAMP (spin_button->adjustment->value,
1771                  spin_button->adjustment->lower,
1772                  (spin_button->adjustment->upper - spin_button->adjustment->page_size));
1773
1774   if (value != spin_button->adjustment->value)
1775     gtk_spin_button_set_value (spin_button, value);
1776
1777   gtk_adjustment_changed (spin_button->adjustment);
1778 }
1779
1780 /**
1781  * gtk_spin_button_get_range:
1782  * @spin_button: a #GtkSpinButton
1783  * @min: location to store minimum allowed value, or %NULL
1784  * @max: location to store maximum allowed value, or %NULL
1785  *
1786  * Gets the range allowed for @spin_button. See
1787  * gtk_spin_button_set_range().
1788  **/
1789 void
1790 gtk_spin_button_get_range (GtkSpinButton *spin_button,
1791                            gdouble       *min,
1792                            gdouble       *max)
1793 {
1794   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1795
1796   if (min)
1797     *min = spin_button->adjustment->lower;
1798   if (max)
1799     *max = spin_button->adjustment->upper;
1800 }
1801
1802 /**
1803  * gtk_spin_button_get_value:
1804  * @spin_button: a #GtkSpinButton
1805  * 
1806  * Get the value in the @spin_button.
1807  * 
1808  * Return value: the value of @spin_button
1809  **/
1810 gdouble
1811 gtk_spin_button_get_value (GtkSpinButton *spin_button)
1812 {
1813   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0.0);
1814
1815   return spin_button->adjustment->value;
1816 }
1817
1818 /**
1819  * gtk_spin_button_get_value_as_int:
1820  * @spin_button: a #GtkSpinButton
1821  * 
1822  * Get the value @spin_button represented as an integer.
1823  * 
1824  * Return value: the value of @spin_button
1825  **/
1826 gint
1827 gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
1828 {
1829   gdouble val;
1830
1831   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1832
1833   val = spin_button->adjustment->value;
1834   if (val - floor (val) < ceil (val) - val)
1835     return floor (val);
1836   else
1837     return ceil (val);
1838 }
1839
1840 /**
1841  * gtk_spin_button_set_value:
1842  * @spin_button: a #GtkSpinButton
1843  * @value: the new value
1844  * 
1845  * Set the value of @spin_button.
1846  **/
1847 void 
1848 gtk_spin_button_set_value (GtkSpinButton *spin_button, 
1849                            gdouble        value)
1850 {
1851   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1852
1853   if (fabs (value - spin_button->adjustment->value) > EPSILON)
1854     gtk_adjustment_set_value (spin_button->adjustment, value);
1855   else
1856     {
1857       gint return_val = FALSE;
1858       gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1859                        &return_val);
1860       if (return_val == FALSE)
1861         gtk_spin_button_default_output (spin_button);
1862     }
1863 }
1864
1865 /**
1866  * gtk_spin_button_set_update_policy:
1867  * @spin_button: a #GtkSpinButton 
1868  * @policy: a #GtkSpinButtonUpdatePolicy value
1869  * 
1870  * Sets the update behavior of a spin button. This determines whether the
1871  * spin button is always updated or only when a valid value is set.
1872  **/
1873 void
1874 gtk_spin_button_set_update_policy (GtkSpinButton             *spin_button,
1875                                    GtkSpinButtonUpdatePolicy  policy)
1876 {
1877   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1878
1879   if (spin_button->update_policy != policy)
1880     {
1881       spin_button->update_policy = policy;
1882       g_object_notify (G_OBJECT (spin_button), "update_policy");
1883     }
1884 }
1885
1886 /**
1887  * gtk_spin_button_get_update_policy:
1888  * @spin_button: a #GtkSpinButton
1889  *
1890  * Gets the update behavior of a spin button. See
1891  * gtk_spin_button_set_update_policy().
1892  *
1893  * Return value: the current update policy
1894  **/
1895 GtkSpinButtonUpdatePolicy
1896 gtk_spin_button_get_update_policy (GtkSpinButton *spin_button)
1897 {
1898   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), GTK_UPDATE_ALWAYS);
1899
1900   return spin_button->update_policy;
1901 }
1902
1903 /**
1904  * gtk_spin_button_set_numeric:
1905  * @spin_button: a #GtkSpinButton 
1906  * @numeric: flag indicating if only numeric entry is allowed. 
1907  * 
1908  * Sets the flag that determines if non-numeric text can be typed into
1909  * the spin button.
1910  **/
1911 void
1912 gtk_spin_button_set_numeric (GtkSpinButton  *spin_button,
1913                              gboolean        numeric)
1914 {
1915   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1916
1917   spin_button->numeric = (numeric != 0);
1918
1919   g_object_notify (G_OBJECT (spin_button), "numeric");
1920 }
1921
1922 /**
1923  * gtk_spin_button_get_numeric:
1924  * @spin_button: a #GtkSpinButton
1925  *
1926  * Returns whether non-numeric text can be typed into the spin button.
1927  * See gtk_spin_button_set_numeric().
1928  *
1929  * Return value: %TRUE if only numeric text can be entered
1930  **/
1931 gboolean
1932 gtk_spin_button_get_numeric (GtkSpinButton *spin_button)
1933 {
1934   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
1935
1936   return spin_button->numeric;
1937 }
1938
1939 /**
1940  * gtk_spin_button_set_wrap:
1941  * @spin_button: a #GtkSpinButton 
1942  * @wrap: a flag indicating if wrapping behavior is performed.
1943  * 
1944  * Sets the flag that determines if a spin button value wraps around to the
1945  * opposite limit when the upper or lower limit of the range is exceeded.
1946  **/
1947 void
1948 gtk_spin_button_set_wrap (GtkSpinButton  *spin_button,
1949                           gboolean        wrap)
1950 {
1951   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1952
1953   spin_button->wrap = (wrap != 0);
1954   
1955   g_object_notify (G_OBJECT (spin_button), "wrap");
1956 }
1957
1958 /**
1959  * gtk_spin_button_get_wrap:
1960  * @spin_button: a #GtkSpinButton
1961  *
1962  * Returns whether the spin button's value wraps around to the
1963  * opposite limit when the upper or lower limit of the range is
1964  * exceeded. See gtk_spin_button_set_wrap().
1965  *
1966  * Return value: %TRUE if the spin button wraps around
1967  **/
1968 gboolean
1969 gtk_spin_button_get_wrap (GtkSpinButton *spin_button)
1970 {
1971   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
1972
1973   return spin_button->wrap;
1974 }
1975
1976 static gint
1977 spin_button_get_arrow_size (GtkSpinButton *spin_button)
1978 {
1979   gint size = pango_font_description_get_size (GTK_WIDGET (spin_button)->style->font_desc);
1980   
1981   return PANGO_PIXELS (size);
1982 }
1983
1984 /**
1985  * spin_button_get_shadow_type:
1986  * @spin_button: a #GtkSpinButton 
1987  * 
1988  * Convenience function to Get the shadow type from the underlying widget's
1989  * style.
1990  * 
1991  * Return value: the #GtkShadowType
1992  **/
1993 static gint
1994 spin_button_get_shadow_type (GtkSpinButton *spin_button)
1995 {
1996   GtkShadowType rc_shadow_type;
1997
1998   gtk_widget_style_get (GTK_WIDGET (spin_button), "shadow_type", &rc_shadow_type, NULL);
1999
2000   return rc_shadow_type;
2001 }
2002
2003 /**
2004  * gtk_spin_button_set_snap_to_ticks:
2005  * @spin_button: a #GtkSpinButton 
2006  * @snap_to_ticks: a flag indicating if invalid values should be corrected.
2007  * 
2008  * Sets the policy as to whether values are corrected to the nearest step 
2009  * increment when a spin button is activated after providing an invalid value.
2010  **/
2011 void
2012 gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
2013                                    gboolean       snap_to_ticks)
2014 {
2015   guint new_val;
2016
2017   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2018
2019   new_val = (snap_to_ticks != 0);
2020
2021   if (new_val != spin_button->snap_to_ticks)
2022     {
2023       spin_button->snap_to_ticks = new_val;
2024       if (new_val && GTK_ENTRY (spin_button)->editable)
2025         gtk_spin_button_update (spin_button);
2026       
2027       g_object_notify (G_OBJECT (spin_button), "snap_to_ticks");
2028     }
2029 }
2030
2031 /**
2032  * gtk_spin_button_get_snap_to_ticks:
2033  * @spin_button: a #GtkSpinButton
2034  *
2035  * Returns whether the values are corrected to the nearest step. See
2036  * gtk_spin_button_set_snap_to_ticks().
2037  *
2038  * Return value: %TRUE if values are snapped to the nearest step.
2039  **/
2040 gboolean
2041 gtk_spin_button_get_snap_to_ticks (GtkSpinButton *spin_button)
2042 {
2043   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
2044
2045   return spin_button->snap_to_ticks;
2046 }
2047
2048 /**
2049  * gtk_spin_button_spin:
2050  * @spin_button: a #GtkSpinButton 
2051  * @direction: a #GtkSpinType indicating the direction to spin.
2052  * @increment: step increment to apply in the specified direction.
2053  * 
2054  * Increment or decrement a spin button's value in a specified direction
2055  * by a specified amount. 
2056  **/
2057 void
2058 gtk_spin_button_spin (GtkSpinButton *spin_button,
2059                       GtkSpinType    direction,
2060                       gdouble        increment)
2061 {
2062   GtkAdjustment *adj;
2063   gdouble diff;
2064
2065   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2066   
2067   adj = spin_button->adjustment;
2068
2069   /* for compatibility with the 1.0.x version of this function */
2070   if (increment != 0 && increment != adj->step_increment &&
2071       (direction == GTK_SPIN_STEP_FORWARD ||
2072        direction == GTK_SPIN_STEP_BACKWARD))
2073     {
2074       if (direction == GTK_SPIN_STEP_BACKWARD && increment > 0)
2075         increment = -increment;
2076       direction = GTK_SPIN_USER_DEFINED;
2077     }
2078
2079   switch (direction)
2080     {
2081     case GTK_SPIN_STEP_FORWARD:
2082
2083       gtk_spin_button_real_spin (spin_button, adj->step_increment);
2084       break;
2085
2086     case GTK_SPIN_STEP_BACKWARD:
2087
2088       gtk_spin_button_real_spin (spin_button, -adj->step_increment);
2089       break;
2090
2091     case GTK_SPIN_PAGE_FORWARD:
2092
2093       gtk_spin_button_real_spin (spin_button, adj->page_increment);
2094       break;
2095
2096     case GTK_SPIN_PAGE_BACKWARD:
2097
2098       gtk_spin_button_real_spin (spin_button, -adj->page_increment);
2099       break;
2100
2101     case GTK_SPIN_HOME:
2102
2103       diff = adj->value - adj->lower;
2104       if (diff > EPSILON)
2105         gtk_spin_button_real_spin (spin_button, -diff);
2106       break;
2107
2108     case GTK_SPIN_END:
2109
2110       diff = adj->upper - adj->value;
2111       if (diff > EPSILON)
2112         gtk_spin_button_real_spin (spin_button, diff);
2113       break;
2114
2115     case GTK_SPIN_USER_DEFINED:
2116
2117       if (increment != 0)
2118         gtk_spin_button_real_spin (spin_button, increment);
2119       break;
2120
2121     default:
2122       break;
2123     }
2124 }
2125
2126 /**
2127  * gtk_spin_button_update:
2128  * @spin_button: a #GtkSpinButton 
2129  * 
2130  * Manually force an update of the spin button.
2131  **/
2132 void 
2133 gtk_spin_button_update (GtkSpinButton *spin_button)
2134 {
2135   gdouble val;
2136   gint error = 0;
2137   gint return_val;
2138
2139   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2140
2141   return_val = FALSE;
2142   gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[INPUT],
2143                    &val, &return_val);
2144   if (return_val == FALSE)
2145     {
2146       return_val = gtk_spin_button_default_input (spin_button, &val);
2147       error = (return_val == GTK_INPUT_ERROR);
2148     }
2149   else if (return_val == GTK_INPUT_ERROR)
2150     error = 1;
2151
2152   if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
2153     {
2154       if (val < spin_button->adjustment->lower)
2155         val = spin_button->adjustment->lower;
2156       else if (val > spin_button->adjustment->upper)
2157         val = spin_button->adjustment->upper;
2158     }
2159   else if ((spin_button->update_policy == GTK_UPDATE_IF_VALID) && 
2160            (error ||
2161            val < spin_button->adjustment->lower ||
2162            val > spin_button->adjustment->upper))
2163     {
2164       gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
2165       return;
2166     }
2167
2168   if (spin_button->snap_to_ticks)
2169     gtk_spin_button_snap (spin_button, val);
2170   else
2171     {
2172       if (fabs (val - spin_button->adjustment->value) > EPSILON)
2173         gtk_adjustment_set_value (spin_button->adjustment, val);
2174       else
2175         {
2176           return_val = FALSE;
2177           gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
2178                            &return_val);
2179           if (return_val == FALSE)
2180             gtk_spin_button_default_output (spin_button);
2181         }
2182     }
2183 }
2184