]> Pileus Git - ~andy/gtk/blob - gtk/gtkspinbutton.c
Deprecation cleanup
[~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   if (!was_grabbed)
959     gtk_spin_button_stop_spinning (GTK_SPIN_BUTTON (widget));
960 }
961
962 static void
963 gtk_spin_button_state_changed (GtkWidget    *widget,
964                                GtkStateType  previous_state)
965 {
966   if (!GTK_WIDGET_IS_SENSITIVE (widget))
967     gtk_spin_button_stop_spinning (GTK_SPIN_BUTTON (widget));    
968 }
969
970 static gint
971 gtk_spin_button_scroll (GtkWidget      *widget,
972                         GdkEventScroll *event)
973 {
974   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
975
976   if (event->direction == GDK_SCROLL_UP)
977     {
978       if (!GTK_WIDGET_HAS_FOCUS (widget))
979         gtk_widget_grab_focus (widget);
980       gtk_spin_button_real_spin (spin, spin->adjustment->step_increment);
981     }
982   else if (event->direction == GDK_SCROLL_DOWN)
983     {
984       if (!GTK_WIDGET_HAS_FOCUS (widget))
985         gtk_widget_grab_focus (widget);
986       gtk_spin_button_real_spin (spin, -spin->adjustment->step_increment); 
987     }
988   else
989     return FALSE;
990
991   return TRUE;
992 }
993
994 static void
995 gtk_spin_button_stop_spinning (GtkSpinButton *spin)
996 {
997   if (spin->timer)
998     {
999       gtk_timeout_remove (spin->timer);
1000       spin->timer = 0;
1001       spin->timer_calls = 0;
1002       spin->need_timer = FALSE;
1003     }
1004
1005   spin->button = 0;
1006   spin->timer = 0;
1007   spin->timer_step = spin->adjustment->step_increment;
1008   spin->timer_calls = 0;
1009 }
1010
1011 static void
1012 start_spinning (GtkSpinButton *spin,
1013                 GtkArrowType   click_child,
1014                 gfloat         step)
1015 {
1016   g_return_if_fail (click_child == GTK_ARROW_UP || click_child == GTK_ARROW_DOWN);
1017   
1018   spin->click_child = click_child;
1019   gtk_spin_button_real_spin (spin, click_child == GTK_ARROW_UP ? step : -step);
1020   
1021   if (!spin->timer)
1022     {
1023       spin->timer_step = step;
1024       spin->need_timer = TRUE;
1025       spin->timer = gtk_timeout_add (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
1026                                      (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
1027     }
1028
1029   spin_button_redraw (spin);
1030 }
1031
1032 static gint
1033 gtk_spin_button_button_press (GtkWidget      *widget,
1034                               GdkEventButton *event)
1035 {
1036   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1037
1038   if (!spin->button)
1039     {
1040       if (event->window == spin->panel)
1041         {
1042           if (!GTK_WIDGET_HAS_FOCUS (widget))
1043             gtk_widget_grab_focus (widget);
1044           spin->button = event->button;
1045           
1046           if (GTK_ENTRY (widget)->editable)
1047             gtk_spin_button_update (spin);
1048           
1049           if (event->y <= widget->requisition.height / 2)
1050             {
1051               if (event->button == 1)
1052                 start_spinning (spin, GTK_ARROW_UP, spin->adjustment->step_increment);
1053               else if (event->button == 2)
1054                 start_spinning (spin, GTK_ARROW_UP, spin->adjustment->page_increment);
1055               else
1056                 spin->click_child = GTK_ARROW_UP;
1057             }
1058           else 
1059             {
1060               if (event->button == 1)
1061                 start_spinning (spin, GTK_ARROW_DOWN, spin->adjustment->step_increment);
1062               else if (event->button == 2)
1063                 start_spinning (spin, GTK_ARROW_DOWN, spin->adjustment->page_increment);
1064               else
1065                 spin->click_child = GTK_ARROW_DOWN;
1066             }
1067           return TRUE;
1068         }
1069       else
1070         return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
1071     }
1072   return FALSE;
1073 }
1074
1075 static gint
1076 gtk_spin_button_button_release (GtkWidget      *widget,
1077                                 GdkEventButton *event)
1078 {
1079   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1080   gint arrow_size;
1081
1082   arrow_size = spin_button_get_arrow_size (spin);
1083
1084   if (event->button == spin->button)
1085     {
1086       guint click_child;
1087
1088       gtk_spin_button_stop_spinning (spin);
1089
1090       if (event->button == 3)
1091         {
1092           if (event->y >= 0 && event->x >= 0 && 
1093               event->y <= widget->requisition.height &&
1094               event->x <= arrow_size + 2 * widget->style->xthickness)
1095             {
1096               if (spin->click_child == GTK_ARROW_UP &&
1097                   event->y <= widget->requisition.height / 2)
1098                 {
1099                   gdouble diff;
1100
1101                   diff = spin->adjustment->upper - spin->adjustment->value;
1102                   if (diff > EPSILON)
1103                     gtk_spin_button_real_spin (spin, diff);
1104                 }
1105               else if (spin->click_child == GTK_ARROW_DOWN &&
1106                        event->y > widget->requisition.height / 2)
1107                 {
1108                   gdouble diff;
1109
1110                   diff = spin->adjustment->value - spin->adjustment->lower;
1111                   if (diff > EPSILON)
1112                     gtk_spin_button_real_spin (spin, -diff);
1113                 }
1114             }
1115         }                 
1116       click_child = spin->click_child;
1117       spin->click_child = NO_ARROW;
1118       spin->button = 0;
1119       spin_button_redraw (spin);
1120
1121       return TRUE;
1122     }
1123   else
1124     return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
1125 }
1126
1127 static gint
1128 gtk_spin_button_motion_notify (GtkWidget      *widget,
1129                                GdkEventMotion *event)
1130 {
1131   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1132
1133   if (spin->button)
1134     return FALSE;
1135
1136   if (event->window == spin->panel)
1137     {
1138       gint y;
1139       
1140       gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
1141   
1142       if (y <= widget->requisition.height / 2 && 
1143           spin->in_child == GTK_ARROW_DOWN)
1144         {
1145           spin->in_child = GTK_ARROW_UP;
1146           spin_button_redraw (spin);
1147         }
1148       else if (y > widget->requisition.height / 2 && 
1149           spin->in_child == GTK_ARROW_UP)
1150         {
1151           spin->in_child = GTK_ARROW_DOWN;
1152           spin_button_redraw (spin);
1153         }
1154       
1155       return FALSE;
1156     }
1157           
1158   return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
1159 }
1160
1161 static gint
1162 gtk_spin_button_timer (GtkSpinButton *spin_button)
1163 {
1164   gboolean retval = FALSE;
1165   
1166   GDK_THREADS_ENTER ();
1167
1168   if (spin_button->timer)
1169     {
1170       if (spin_button->click_child == GTK_ARROW_UP)
1171         gtk_spin_button_real_spin (spin_button, spin_button->timer_step);
1172       else
1173         gtk_spin_button_real_spin (spin_button, -spin_button->timer_step);
1174
1175       if (spin_button->need_timer)
1176         {
1177           spin_button->need_timer = FALSE;
1178           spin_button->timer = gtk_timeout_add 
1179             (SPIN_BUTTON_TIMER_DELAY, (GtkFunction) gtk_spin_button_timer, 
1180              (gpointer) spin_button);
1181         }
1182       else 
1183         {
1184           if (spin_button->climb_rate > 0.0 && spin_button->timer_step 
1185               < spin_button->adjustment->page_increment)
1186             {
1187               if (spin_button->timer_calls < MAX_TIMER_CALLS)
1188                 spin_button->timer_calls++;
1189               else 
1190                 {
1191                   spin_button->timer_calls = 0;
1192                   spin_button->timer_step += spin_button->climb_rate;
1193                 }
1194             }
1195           retval = TRUE;
1196         }
1197     }
1198
1199   GDK_THREADS_LEAVE ();
1200
1201   return retval;
1202 }
1203
1204 static void
1205 gtk_spin_button_value_changed (GtkAdjustment *adjustment,
1206                                GtkSpinButton *spin_button)
1207 {
1208   gboolean return_val;
1209
1210   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1211
1212   return_val = FALSE;
1213   g_signal_emit (spin_button, spinbutton_signals[OUTPUT], 0, &return_val);
1214   if (return_val == FALSE)
1215     gtk_spin_button_default_output (spin_button);
1216
1217   g_signal_emit (spin_button, spinbutton_signals[VALUE_CHANGED], 0);
1218
1219   spin_button_redraw (spin_button);
1220   
1221   g_object_notify (G_OBJECT (spin_button), "value");
1222 }
1223
1224 static void
1225 gtk_spin_button_real_change_value (GtkSpinButton *spin,
1226                                    GtkScrollType  scroll)
1227 {
1228   /* We don't test whether the entry is editable, since
1229    * this key binding conceptually corresponds to changing
1230    * the value with the buttons using the mouse, which
1231    * we allow for non-editable spin buttons.
1232    */
1233   switch (scroll)
1234     {
1235     case GTK_SCROLL_STEP_BACKWARD:
1236     case GTK_SCROLL_STEP_DOWN:
1237     case GTK_SCROLL_STEP_LEFT:
1238       gtk_spin_button_real_spin (spin, -spin->timer_step);
1239       
1240       if (spin->climb_rate > 0.0 && spin->timer_step
1241           < spin->adjustment->page_increment)
1242         {
1243           if (spin->timer_calls < MAX_TIMER_CALLS)
1244             spin->timer_calls++;
1245           else 
1246             {
1247               spin->timer_calls = 0;
1248               spin->timer_step += spin->climb_rate;
1249             }
1250         }
1251       break;
1252       
1253     case GTK_SCROLL_STEP_FORWARD:
1254     case GTK_SCROLL_STEP_UP:
1255     case GTK_SCROLL_STEP_RIGHT:
1256       gtk_spin_button_real_spin (spin, spin->timer_step);
1257       
1258       if (spin->climb_rate > 0.0 && spin->timer_step
1259           < spin->adjustment->page_increment)
1260         {
1261           if (spin->timer_calls < MAX_TIMER_CALLS)
1262             spin->timer_calls++;
1263           else 
1264             {
1265               spin->timer_calls = 0;
1266               spin->timer_step += spin->climb_rate;
1267             }
1268         }
1269       break;
1270       
1271     case GTK_SCROLL_PAGE_BACKWARD:
1272     case GTK_SCROLL_PAGE_DOWN:
1273     case GTK_SCROLL_PAGE_LEFT:
1274       gtk_spin_button_real_spin (spin, -spin->adjustment->page_increment);
1275       break;
1276       
1277     case GTK_SCROLL_PAGE_FORWARD:
1278     case GTK_SCROLL_PAGE_UP:
1279     case GTK_SCROLL_PAGE_RIGHT:
1280       gtk_spin_button_real_spin (spin, spin->adjustment->page_increment);
1281       break;
1282       
1283     case GTK_SCROLL_START:
1284       {
1285         gdouble diff = spin->adjustment->value - spin->adjustment->lower;
1286         if (diff > EPSILON)
1287           gtk_spin_button_real_spin (spin, -diff);
1288         break;
1289       }
1290       
1291     case GTK_SCROLL_END:
1292       {
1293         gdouble diff = spin->adjustment->upper - spin->adjustment->value;
1294         if (diff > EPSILON)
1295           gtk_spin_button_real_spin (spin, diff);
1296         break;
1297       }
1298       
1299     default:
1300       g_warning ("Invalid scroll type %d for GtkSpinButton::change-value", scroll);
1301       break;
1302     }
1303   
1304   gtk_spin_button_update (spin);
1305 }
1306
1307 static gint
1308 gtk_spin_button_key_release (GtkWidget   *widget,
1309                              GdkEventKey *event)
1310 {
1311   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1312
1313   /* We only get a release at the end of a key repeat run, so reset the timer_step */
1314   spin->timer_step = spin->adjustment->step_increment;
1315   spin->timer_calls = 0;
1316   
1317   return TRUE;
1318 }
1319
1320 static void
1321 gtk_spin_button_snap (GtkSpinButton *spin_button,
1322                       gdouble        val)
1323 {
1324   gdouble inc;
1325   gdouble tmp;
1326
1327   inc = spin_button->adjustment->step_increment;
1328   if (inc == 0)
1329     return;
1330   
1331   tmp = (val - spin_button->adjustment->lower) / inc;
1332   if (tmp - floor (tmp) < ceil (tmp) - tmp)
1333     val = spin_button->adjustment->lower + floor (tmp) * inc;
1334   else
1335     val = spin_button->adjustment->lower + ceil (tmp) * inc;
1336
1337   if (fabs (val - spin_button->adjustment->value) > EPSILON)
1338     gtk_adjustment_set_value (spin_button->adjustment, val);
1339   else
1340     {
1341       gint return_val = FALSE;
1342       g_signal_emit (spin_button, spinbutton_signals[OUTPUT], 0, &return_val);
1343       if (return_val == FALSE)
1344         gtk_spin_button_default_output (spin_button);
1345     }
1346 }
1347
1348 static void
1349 gtk_spin_button_activate (GtkEntry *entry)
1350 {
1351   if (entry->editable)
1352     gtk_spin_button_update (GTK_SPIN_BUTTON (entry));
1353
1354   /* Chain up so that entry->activates_default is honored */
1355   parent_class->activate (entry);
1356 }
1357
1358 static void
1359 gtk_spin_button_insert_text (GtkEditable *editable,
1360                              const gchar *new_text,
1361                              gint         new_text_length,
1362                              gint        *position)
1363 {
1364   GtkEntry *entry = GTK_ENTRY (editable);
1365   GtkSpinButton *spin = GTK_SPIN_BUTTON (editable);
1366   GtkEditableClass *parent_editable_iface = g_type_interface_peek (parent_class, GTK_TYPE_EDITABLE);
1367  
1368   if (spin->numeric)
1369     {
1370       struct lconv *lc;
1371       gboolean sign;
1372       gint dotpos = -1;
1373       gint i;
1374       GdkWChar pos_sign;
1375       GdkWChar neg_sign;
1376       gint entry_length;
1377
1378       entry_length = entry->text_length;
1379
1380       lc = localeconv ();
1381
1382       if (*(lc->negative_sign))
1383         neg_sign = *(lc->negative_sign);
1384       else 
1385         neg_sign = '-';
1386
1387       if (*(lc->positive_sign))
1388         pos_sign = *(lc->positive_sign);
1389       else 
1390         pos_sign = '+';
1391
1392       for (sign=0, i=0; i<entry_length; i++)
1393         if ((entry->text[i] == neg_sign) ||
1394             (entry->text[i] == pos_sign))
1395           {
1396             sign = 1;
1397             break;
1398           }
1399
1400       if (sign && !(*position))
1401         return;
1402
1403       for (dotpos=-1, i=0; i<entry_length; i++)
1404         if (entry->text[i] == *(lc->decimal_point))
1405           {
1406             dotpos = i;
1407             break;
1408           }
1409
1410       if (dotpos > -1 && *position > dotpos &&
1411           (gint)spin->digits - entry_length
1412             + dotpos - new_text_length + 1 < 0)
1413         return;
1414
1415       for (i = 0; i < new_text_length; i++)
1416         {
1417           if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1418             {
1419               if (sign || (*position) || i)
1420                 return;
1421               sign = TRUE;
1422             }
1423           else if (new_text[i] == *(lc->decimal_point))
1424             {
1425               if (!spin->digits || dotpos > -1 || 
1426                   (new_text_length - 1 - i + entry_length
1427                     - *position > (gint)spin->digits)) 
1428                 return;
1429               dotpos = *position + i;
1430             }
1431           else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1432             return;
1433         }
1434     }
1435
1436   parent_editable_iface->insert_text (editable, new_text,
1437                                       new_text_length, position);
1438 }
1439
1440 static void
1441 gtk_spin_button_real_spin (GtkSpinButton *spin_button,
1442                            gdouble        increment)
1443 {
1444   GtkAdjustment *adj;
1445   gdouble new_value = 0.0;
1446   
1447   adj = spin_button->adjustment;
1448
1449   new_value = adj->value + increment;
1450
1451   if (increment > 0)
1452     {
1453       if (spin_button->wrap)
1454         {
1455           if (fabs (adj->value - adj->upper) < EPSILON)
1456             new_value = adj->lower;
1457           else if (new_value > adj->upper)
1458             new_value = adj->upper;
1459         }
1460       else
1461         new_value = MIN (new_value, adj->upper);
1462     }
1463   else if (increment < 0) 
1464     {
1465       if (spin_button->wrap)
1466         {
1467           if (fabs (adj->value - adj->lower) < EPSILON)
1468             new_value = adj->upper;
1469           else if (new_value < adj->lower)
1470             new_value = adj->lower;
1471         }
1472       else
1473         new_value = MAX (new_value, adj->lower);
1474     }
1475
1476   if (fabs (new_value - adj->value) > EPSILON)
1477     gtk_adjustment_set_value (adj, new_value);
1478
1479   spin_button_redraw (spin_button);
1480 }
1481
1482 static gint
1483 gtk_spin_button_default_input (GtkSpinButton *spin_button,
1484                                gdouble       *new_val)
1485 {
1486   gchar *err = NULL;
1487
1488   *new_val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &err);
1489   if (*err)
1490     return GTK_INPUT_ERROR;
1491   else
1492     return FALSE;
1493 }
1494
1495 static gint
1496 gtk_spin_button_default_output (GtkSpinButton *spin_button)
1497 {
1498   gchar *buf = g_strdup_printf ("%0.*f", spin_button->digits, spin_button->adjustment->value);
1499
1500   if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1501     gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1502   g_free (buf);
1503   return FALSE;
1504 }
1505
1506
1507 /***********************************************************
1508  ***********************************************************
1509  ***                  Public interface                   ***
1510  ***********************************************************
1511  ***********************************************************/
1512
1513
1514 void
1515 gtk_spin_button_configure (GtkSpinButton  *spin_button,
1516                            GtkAdjustment  *adjustment,
1517                            gdouble         climb_rate,
1518                            guint           digits)
1519 {
1520   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1521
1522   if (adjustment)
1523     gtk_spin_button_set_adjustment (spin_button, adjustment);
1524   else
1525     adjustment = spin_button->adjustment;
1526
1527   g_object_freeze_notify (G_OBJECT (spin_button));
1528   if (spin_button->digits != digits) 
1529     {
1530       spin_button->digits = digits;
1531       g_object_notify (G_OBJECT (spin_button), "digits");
1532     }
1533
1534   if (spin_button->climb_rate != climb_rate)
1535     {
1536       spin_button->climb_rate = climb_rate;
1537       g_object_notify (G_OBJECT (spin_button), "climb_rate");
1538     }
1539   g_object_thaw_notify (G_OBJECT (spin_button));
1540
1541   gtk_adjustment_value_changed (adjustment);
1542 }
1543
1544 GtkWidget *
1545 gtk_spin_button_new (GtkAdjustment *adjustment,
1546                      gdouble        climb_rate,
1547                      guint          digits)
1548 {
1549   GtkSpinButton *spin;
1550
1551   if (adjustment)
1552     g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
1553
1554   spin = g_object_new (GTK_TYPE_SPIN_BUTTON, NULL);
1555
1556   gtk_spin_button_configure (spin, adjustment, climb_rate, digits);
1557
1558   return GTK_WIDGET (spin);
1559 }
1560
1561 /**
1562  * gtk_spin_button_new_with_range:
1563  * @min: Minimum allowable value
1564  * @max: Maximum allowable value
1565  * @step: Increment added or subtracted by spinning the widget
1566  * 
1567  * This is a convenience constructor that allows creation of a numeric 
1568  * #GtkSpinButton without manually creating an adjustment. The value is 
1569  * initially set to the minimum value and a page increment of 10 * @step
1570  * is the default. The precision of the spin button is equivalent to the 
1571  * precision of @step.
1572  * 
1573  * Return value: The new spin button as a #GtkWidget.
1574  **/
1575 GtkWidget *
1576 gtk_spin_button_new_with_range (gdouble min,
1577                                 gdouble max,
1578                                 gdouble step)
1579 {
1580   GtkObject *adj;
1581   GtkSpinButton *spin;
1582   gint digits;
1583
1584   g_return_val_if_fail (min < max, NULL);
1585   g_return_val_if_fail (step != 0.0, NULL);
1586
1587   spin = g_object_new (GTK_TYPE_SPIN_BUTTON, NULL);
1588
1589   adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
1590
1591   if (fabs (step) >= 1.0 || step == 0.0)
1592     digits = 0;
1593   else {
1594     digits = abs ((gint) floor (log10 (fabs (step))));
1595     if (digits > MAX_DIGITS)
1596       digits = MAX_DIGITS;
1597   }
1598
1599   gtk_spin_button_configure (spin, GTK_ADJUSTMENT (adj), step, digits);
1600
1601   gtk_spin_button_set_numeric (spin, TRUE);
1602
1603   return GTK_WIDGET (spin);
1604 }
1605
1606 /* Callback used when the spin button's adjustment changes.  We need to redraw
1607  * the arrows when the adjustment's range changes, and reevaluate our size request.
1608  */
1609 static void
1610 adjustment_changed_cb (GtkAdjustment *adjustment, gpointer data)
1611 {
1612   GtkSpinButton *spin_button;
1613
1614   spin_button = GTK_SPIN_BUTTON (data);
1615
1616   spin_button->timer_step = spin_button->adjustment->step_increment;
1617   gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1618 }
1619
1620 /**
1621  * gtk_spin_button_set_adjustment:
1622  * @spin_button: a #GtkSpinButton
1623  * @adjustment: a #GtkAdjustment to replace the existing adjustment
1624  * 
1625  * Replaces the #GtkAdjustment associated with @spin_button.
1626  **/
1627 void
1628 gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
1629                                 GtkAdjustment *adjustment)
1630 {
1631   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1632
1633   if (spin_button->adjustment != adjustment)
1634     {
1635       if (spin_button->adjustment)
1636         {
1637           g_signal_handlers_disconnect_by_func (spin_button->adjustment,
1638                                                 gtk_spin_button_value_changed,
1639                                                 spin_button);
1640           g_signal_handlers_disconnect_by_func (spin_button->adjustment,
1641                                                 adjustment_changed_cb,
1642                                                 spin_button);
1643           g_object_unref (spin_button->adjustment);
1644         }
1645       spin_button->adjustment = adjustment;
1646       if (adjustment)
1647         {
1648           g_object_ref (adjustment);
1649           gtk_object_sink (GTK_OBJECT (adjustment));
1650           g_signal_connect (adjustment, "value_changed",
1651                             G_CALLBACK (gtk_spin_button_value_changed),
1652                             spin_button);
1653           g_signal_connect (adjustment, "changed",
1654                             G_CALLBACK (adjustment_changed_cb),
1655                             spin_button);
1656           spin_button->timer_step = spin_button->adjustment->step_increment;
1657         }
1658
1659       gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1660     }
1661
1662   g_object_notify (G_OBJECT (spin_button), "adjustment");
1663 }
1664
1665 /**
1666  * gtk_spin_button_get_adjustment:
1667  * @spin_button: 
1668  * 
1669  * Get the adjustment associated with a #GtkSpinButton
1670  * 
1671  * Return value: the #GtkAdjustment of @spin_button
1672  **/
1673 GtkAdjustment *
1674 gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
1675 {
1676   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
1677
1678   return spin_button->adjustment;
1679 }
1680
1681 /**
1682  * gtk_spin_button_set_digits:
1683  * @spin_button: a #GtkSpinButton
1684  * @digits: the number of digits to be displayed for the spin button's value
1685  * 
1686  * Set the precision to be displayed by @spin_button. Up to 20 digit precision
1687  * is allowed.
1688  **/
1689 void
1690 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
1691                             guint          digits)
1692 {
1693   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1694
1695   if (spin_button->digits != digits)
1696     {
1697       spin_button->digits = digits;
1698       gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1699       g_object_notify (G_OBJECT (spin_button), "digits");
1700       
1701       /* since lower/upper may have changed */
1702       gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1703     }
1704 }
1705
1706 /**
1707  * gtk_spin_button_get_digits:
1708  * @spin_button: a #GtkSpinButton
1709  *
1710  * Fetches the precision of @spin_button. See gtk_spin_button_set_digits().
1711  *
1712  * Returns: the current precision
1713  **/
1714 guint
1715 gtk_spin_button_get_digits (GtkSpinButton *spin_button)
1716 {
1717   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1718
1719   return spin_button->digits;
1720 }
1721
1722 /**
1723  * gtk_spin_button_set_increments:
1724  * @spin_button: a #GtkSpinButton
1725  * @step: increment applied for a button 1 press.
1726  * @page: increment applied for a button 2 press.
1727  * 
1728  * Sets the step and page increments for spin_button.  This affects how 
1729  * quickly the value changes when the spin button's arrows are activated.
1730  **/
1731 void
1732 gtk_spin_button_set_increments (GtkSpinButton *spin_button,
1733                                 gdouble        step,
1734                                 gdouble        page)
1735 {
1736   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1737
1738   spin_button->adjustment->step_increment = step;
1739   spin_button->adjustment->page_increment = page;
1740 }
1741
1742 /**
1743  * gtk_spin_button_get_increments:
1744  * @spin_button: a #GtkSpinButton
1745  * @step: location to store step increment, or %NULL
1746  * @page: location to store page increment, or %NULL
1747  *
1748  * Gets the current step and page the increments used by @spin_button. See
1749  * gtk_spin_button_set_increments().
1750  **/
1751 void
1752 gtk_spin_button_get_increments (GtkSpinButton *spin_button,
1753                                 gdouble       *step,
1754                                 gdouble       *page)
1755 {
1756   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1757
1758   if (step)
1759     *step = spin_button->adjustment->step_increment;
1760   if (page)
1761     *page = spin_button->adjustment->page_increment;
1762 }
1763
1764 /**
1765  * gtk_spin_button_set_range:
1766  * @spin_button: a #GtkSpinButton
1767  * @min: minimum allowable value
1768  * @max: maximum allowable value
1769  * 
1770  * Sets the minimum and maximum allowable values for @spin_button
1771  **/
1772 void
1773 gtk_spin_button_set_range (GtkSpinButton *spin_button,
1774                            gdouble        min,
1775                            gdouble        max)
1776 {
1777   gdouble value;
1778   
1779   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1780
1781   spin_button->adjustment->lower = min;
1782   spin_button->adjustment->upper = max;
1783
1784   value = CLAMP (spin_button->adjustment->value,
1785                  spin_button->adjustment->lower,
1786                  (spin_button->adjustment->upper - spin_button->adjustment->page_size));
1787
1788   if (value != spin_button->adjustment->value)
1789     gtk_spin_button_set_value (spin_button, value);
1790
1791   gtk_adjustment_changed (spin_button->adjustment);
1792 }
1793
1794 /**
1795  * gtk_spin_button_get_range:
1796  * @spin_button: a #GtkSpinButton
1797  * @min: location to store minimum allowed value, or %NULL
1798  * @max: location to store maximum allowed value, or %NULL
1799  *
1800  * Gets the range allowed for @spin_button. See
1801  * gtk_spin_button_set_range().
1802  **/
1803 void
1804 gtk_spin_button_get_range (GtkSpinButton *spin_button,
1805                            gdouble       *min,
1806                            gdouble       *max)
1807 {
1808   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1809
1810   if (min)
1811     *min = spin_button->adjustment->lower;
1812   if (max)
1813     *max = spin_button->adjustment->upper;
1814 }
1815
1816 /**
1817  * gtk_spin_button_get_value:
1818  * @spin_button: a #GtkSpinButton
1819  * 
1820  * Get the value in the @spin_button.
1821  * 
1822  * Return value: the value of @spin_button
1823  **/
1824 gdouble
1825 gtk_spin_button_get_value (GtkSpinButton *spin_button)
1826 {
1827   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0.0);
1828
1829   return spin_button->adjustment->value;
1830 }
1831
1832 /**
1833  * gtk_spin_button_get_value_as_int:
1834  * @spin_button: a #GtkSpinButton
1835  * 
1836  * Get the value @spin_button represented as an integer.
1837  * 
1838  * Return value: the value of @spin_button
1839  **/
1840 gint
1841 gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
1842 {
1843   gdouble val;
1844
1845   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1846
1847   val = spin_button->adjustment->value;
1848   if (val - floor (val) < ceil (val) - val)
1849     return floor (val);
1850   else
1851     return ceil (val);
1852 }
1853
1854 /**
1855  * gtk_spin_button_set_value:
1856  * @spin_button: a #GtkSpinButton
1857  * @value: the new value
1858  * 
1859  * Set the value of @spin_button.
1860  **/
1861 void 
1862 gtk_spin_button_set_value (GtkSpinButton *spin_button, 
1863                            gdouble        value)
1864 {
1865   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1866
1867   if (fabs (value - spin_button->adjustment->value) > EPSILON)
1868     gtk_adjustment_set_value (spin_button->adjustment, value);
1869   else
1870     {
1871       gint return_val = FALSE;
1872       g_signal_emit (spin_button, spinbutton_signals[OUTPUT], 0, &return_val);
1873       if (return_val == FALSE)
1874         gtk_spin_button_default_output (spin_button);
1875     }
1876 }
1877
1878 /**
1879  * gtk_spin_button_set_update_policy:
1880  * @spin_button: a #GtkSpinButton 
1881  * @policy: a #GtkSpinButtonUpdatePolicy value
1882  * 
1883  * Sets the update behavior of a spin button. This determines whether the
1884  * spin button is always updated or only when a valid value is set.
1885  **/
1886 void
1887 gtk_spin_button_set_update_policy (GtkSpinButton             *spin_button,
1888                                    GtkSpinButtonUpdatePolicy  policy)
1889 {
1890   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1891
1892   if (spin_button->update_policy != policy)
1893     {
1894       spin_button->update_policy = policy;
1895       g_object_notify (G_OBJECT (spin_button), "update_policy");
1896     }
1897 }
1898
1899 /**
1900  * gtk_spin_button_get_update_policy:
1901  * @spin_button: a #GtkSpinButton
1902  *
1903  * Gets the update behavior of a spin button. See
1904  * gtk_spin_button_set_update_policy().
1905  *
1906  * Return value: the current update policy
1907  **/
1908 GtkSpinButtonUpdatePolicy
1909 gtk_spin_button_get_update_policy (GtkSpinButton *spin_button)
1910 {
1911   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), GTK_UPDATE_ALWAYS);
1912
1913   return spin_button->update_policy;
1914 }
1915
1916 /**
1917  * gtk_spin_button_set_numeric:
1918  * @spin_button: a #GtkSpinButton 
1919  * @numeric: flag indicating if only numeric entry is allowed. 
1920  * 
1921  * Sets the flag that determines if non-numeric text can be typed into
1922  * the spin button.
1923  **/
1924 void
1925 gtk_spin_button_set_numeric (GtkSpinButton  *spin_button,
1926                              gboolean        numeric)
1927 {
1928   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1929
1930   numeric = numeric != FALSE;
1931
1932   if (spin_button->numeric != numeric)
1933     {
1934        spin_button->numeric = numeric;
1935        g_object_notify (G_OBJECT (spin_button), "numeric");
1936     }
1937 }
1938
1939 /**
1940  * gtk_spin_button_get_numeric:
1941  * @spin_button: a #GtkSpinButton
1942  *
1943  * Returns whether non-numeric text can be typed into the spin button.
1944  * See gtk_spin_button_set_numeric().
1945  *
1946  * Return value: %TRUE if only numeric text can be entered
1947  **/
1948 gboolean
1949 gtk_spin_button_get_numeric (GtkSpinButton *spin_button)
1950 {
1951   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
1952
1953   return spin_button->numeric;
1954 }
1955
1956 /**
1957  * gtk_spin_button_set_wrap:
1958  * @spin_button: a #GtkSpinButton 
1959  * @wrap: a flag indicating if wrapping behavior is performed.
1960  * 
1961  * Sets the flag that determines if a spin button value wraps around to the
1962  * opposite limit when the upper or lower limit of the range is exceeded.
1963  **/
1964 void
1965 gtk_spin_button_set_wrap (GtkSpinButton  *spin_button,
1966                           gboolean        wrap)
1967 {
1968   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1969
1970   wrap = wrap != FALSE; 
1971
1972   if (spin_button->wrap != wrap)
1973     {
1974        spin_button->wrap = (wrap != 0);
1975   
1976        g_object_notify (G_OBJECT (spin_button), "wrap");
1977     }
1978 }
1979
1980 /**
1981  * gtk_spin_button_get_wrap:
1982  * @spin_button: a #GtkSpinButton
1983  *
1984  * Returns whether the spin button's value wraps around to the
1985  * opposite limit when the upper or lower limit of the range is
1986  * exceeded. See gtk_spin_button_set_wrap().
1987  *
1988  * Return value: %TRUE if the spin button wraps around
1989  **/
1990 gboolean
1991 gtk_spin_button_get_wrap (GtkSpinButton *spin_button)
1992 {
1993   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
1994
1995   return spin_button->wrap;
1996 }
1997
1998 static gint
1999 spin_button_get_arrow_size (GtkSpinButton *spin_button)
2000 {
2001   gint size = pango_font_description_get_size (GTK_WIDGET (spin_button)->style->font_desc);
2002   gint arrow_size;
2003
2004   arrow_size = MAX (PANGO_PIXELS (size), MIN_ARROW_WIDTH);
2005
2006   return arrow_size - arrow_size % 2; /* force even */
2007 }
2008
2009 /**
2010  * spin_button_get_shadow_type:
2011  * @spin_button: a #GtkSpinButton 
2012  * 
2013  * Convenience function to Get the shadow type from the underlying widget's
2014  * style.
2015  * 
2016  * Return value: the #GtkShadowType
2017  **/
2018 static gint
2019 spin_button_get_shadow_type (GtkSpinButton *spin_button)
2020 {
2021   GtkShadowType rc_shadow_type;
2022
2023   gtk_widget_style_get (GTK_WIDGET (spin_button), "shadow_type", &rc_shadow_type, NULL);
2024
2025   return rc_shadow_type;
2026 }
2027
2028 /**
2029  * gtk_spin_button_set_snap_to_ticks:
2030  * @spin_button: a #GtkSpinButton 
2031  * @snap_to_ticks: a flag indicating if invalid values should be corrected.
2032  * 
2033  * Sets the policy as to whether values are corrected to the nearest step 
2034  * increment when a spin button is activated after providing an invalid value.
2035  **/
2036 void
2037 gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
2038                                    gboolean       snap_to_ticks)
2039 {
2040   guint new_val;
2041
2042   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2043
2044   new_val = (snap_to_ticks != 0);
2045
2046   if (new_val != spin_button->snap_to_ticks)
2047     {
2048       spin_button->snap_to_ticks = new_val;
2049       if (new_val && GTK_ENTRY (spin_button)->editable)
2050         gtk_spin_button_update (spin_button);
2051       
2052       g_object_notify (G_OBJECT (spin_button), "snap_to_ticks");
2053     }
2054 }
2055
2056 /**
2057  * gtk_spin_button_get_snap_to_ticks:
2058  * @spin_button: a #GtkSpinButton
2059  *
2060  * Returns whether the values are corrected to the nearest step. See
2061  * gtk_spin_button_set_snap_to_ticks().
2062  *
2063  * Return value: %TRUE if values are snapped to the nearest step.
2064  **/
2065 gboolean
2066 gtk_spin_button_get_snap_to_ticks (GtkSpinButton *spin_button)
2067 {
2068   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
2069
2070   return spin_button->snap_to_ticks;
2071 }
2072
2073 /**
2074  * gtk_spin_button_spin:
2075  * @spin_button: a #GtkSpinButton 
2076  * @direction: a #GtkSpinType indicating the direction to spin.
2077  * @increment: step increment to apply in the specified direction.
2078  * 
2079  * Increment or decrement a spin button's value in a specified direction
2080  * by a specified amount. 
2081  **/
2082 void
2083 gtk_spin_button_spin (GtkSpinButton *spin_button,
2084                       GtkSpinType    direction,
2085                       gdouble        increment)
2086 {
2087   GtkAdjustment *adj;
2088   gdouble diff;
2089
2090   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2091   
2092   adj = spin_button->adjustment;
2093
2094   /* for compatibility with the 1.0.x version of this function */
2095   if (increment != 0 && increment != adj->step_increment &&
2096       (direction == GTK_SPIN_STEP_FORWARD ||
2097        direction == GTK_SPIN_STEP_BACKWARD))
2098     {
2099       if (direction == GTK_SPIN_STEP_BACKWARD && increment > 0)
2100         increment = -increment;
2101       direction = GTK_SPIN_USER_DEFINED;
2102     }
2103
2104   switch (direction)
2105     {
2106     case GTK_SPIN_STEP_FORWARD:
2107
2108       gtk_spin_button_real_spin (spin_button, adj->step_increment);
2109       break;
2110
2111     case GTK_SPIN_STEP_BACKWARD:
2112
2113       gtk_spin_button_real_spin (spin_button, -adj->step_increment);
2114       break;
2115
2116     case GTK_SPIN_PAGE_FORWARD:
2117
2118       gtk_spin_button_real_spin (spin_button, adj->page_increment);
2119       break;
2120
2121     case GTK_SPIN_PAGE_BACKWARD:
2122
2123       gtk_spin_button_real_spin (spin_button, -adj->page_increment);
2124       break;
2125
2126     case GTK_SPIN_HOME:
2127
2128       diff = adj->value - adj->lower;
2129       if (diff > EPSILON)
2130         gtk_spin_button_real_spin (spin_button, -diff);
2131       break;
2132
2133     case GTK_SPIN_END:
2134
2135       diff = adj->upper - adj->value;
2136       if (diff > EPSILON)
2137         gtk_spin_button_real_spin (spin_button, diff);
2138       break;
2139
2140     case GTK_SPIN_USER_DEFINED:
2141
2142       if (increment != 0)
2143         gtk_spin_button_real_spin (spin_button, increment);
2144       break;
2145
2146     default:
2147       break;
2148     }
2149 }
2150
2151 /**
2152  * gtk_spin_button_update:
2153  * @spin_button: a #GtkSpinButton 
2154  * 
2155  * Manually force an update of the spin button.
2156  **/
2157 void 
2158 gtk_spin_button_update (GtkSpinButton *spin_button)
2159 {
2160   gdouble val;
2161   gint error = 0;
2162   gint return_val;
2163
2164   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2165
2166   return_val = FALSE;
2167   g_signal_emit (spin_button, spinbutton_signals[INPUT], 0, &val, &return_val);
2168   if (return_val == FALSE)
2169     {
2170       return_val = gtk_spin_button_default_input (spin_button, &val);
2171       error = (return_val == GTK_INPUT_ERROR);
2172     }
2173   else if (return_val == GTK_INPUT_ERROR)
2174     error = 1;
2175
2176   spin_button_redraw (spin_button);
2177
2178   if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
2179     {
2180       if (val < spin_button->adjustment->lower)
2181         val = spin_button->adjustment->lower;
2182       else if (val > spin_button->adjustment->upper)
2183         val = spin_button->adjustment->upper;
2184     }
2185   else if ((spin_button->update_policy == GTK_UPDATE_IF_VALID) && 
2186            (error ||
2187            val < spin_button->adjustment->lower ||
2188            val > spin_button->adjustment->upper))
2189     {
2190       gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
2191       return;
2192     }
2193
2194   if (spin_button->snap_to_ticks)
2195     gtk_spin_button_snap (spin_button, val);
2196   else
2197     {
2198       if (fabs (val - spin_button->adjustment->value) > EPSILON)
2199         gtk_adjustment_set_value (spin_button->adjustment, val);
2200       else
2201         {
2202           return_val = FALSE;
2203           g_signal_emit (spin_button, spinbutton_signals[OUTPUT], 0,
2204                          &return_val);
2205           if (return_val == FALSE)
2206             gtk_spin_button_default_output (spin_button);
2207         }
2208     }
2209 }
2210
2211 static void
2212 spin_button_redraw (GtkSpinButton *spin_button)
2213 {
2214   GtkWidget *widget;
2215
2216   widget = GTK_WIDGET (spin_button);
2217
2218   if (GTK_WIDGET_DRAWABLE (widget))
2219     {
2220       gtk_widget_queue_draw (widget);
2221
2222       /* We must invalidate the panel window ourselves, because it
2223        * is not a child of widget->window
2224        */
2225       gdk_window_invalidate_rect (spin_button->panel, NULL, TRUE);
2226     }
2227 }