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