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