]> Pileus Git - ~andy/gtk/blob - gtk/gtkspinbutton.c
spinbutton: Set GDK_SCROLL_MASK explicitly
[~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, see <http://www.gnu.org/licenses/>.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
26  */
27
28 #include "config.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <math.h>
33 #include <string.h>
34 #include <locale.h>
35
36 #include "gtkbindings.h"
37 #include "gtkspinbutton.h"
38 #include "gtkentryprivate.h"
39 #include "gtkicontheme.h"
40 #include "gtkmarshalers.h"
41 #include "gtksettings.h"
42 #include "gtkstock.h"
43 #include "gtkprivate.h"
44 #include "gtkintl.h"
45 #include "gtktypebuiltins.h"
46
47 #include "a11y/gtkspinbuttonaccessible.h"
48
49 #define MIN_SPIN_BUTTON_WIDTH 30
50 #define MAX_TIMER_CALLS       5
51 #define EPSILON               1e-10
52 #define MAX_DIGITS            20
53 #define MIN_ARROW_WIDTH       6
54
55
56 /**
57  * SECTION:gtkspinbutton
58  * @Title: GtkSpinButton
59  * @Short_description: Retrieve an integer or floating-point number from
60  *     the user
61  * @See_also: #GtkEntry
62  *
63  * A #GtkSpinButton is an ideal way to allow the user to set the value of
64  * some attribute. Rather than having to directly type a number into a
65  * #GtkEntry, GtkSpinButton allows the user to click on one of two arrows
66  * to increment or decrement the displayed value. A value can still be
67  * typed in, with the bonus that it can be checked to ensure it is in a
68  * given range.
69  *
70  * The main properties of a GtkSpinButton are through an adjustment.
71  * See the #GtkAdjustment section for more details about an adjustment's
72  * properties.
73  *
74  * <example>
75  * <title>Using a GtkSpinButton to get an integer</title>
76  * <programlisting>
77  * /&ast; Provides a function to retrieve an integer value from a
78  *  &ast; GtkSpinButton and creates a spin button to model percentage
79  *  &ast; values.
80  *  &ast;/
81  *
82  * gint
83  * grab_int_value (GtkSpinButton *button,
84  *                 gpointer       user_data)
85  * {
86  *   return gtk_spin_button_get_value_as_int (button);
87  * }
88  *
89  * void
90  * create_integer_spin_button (void)
91  * {
92  *
93  *   GtkWidget *window, *button;
94  *   GtkAdjustment *adjustment;
95  *
96  *   adjustment = gtk_adjustment_new (50.0, 0.0, 100.0, 1.0, 5.0, 0.0);
97  *
98  *   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
99  *   gtk_container_set_border_width (GTK_CONTAINER (window), 5);
100  *
101  *   /&ast; creates the spinbutton, with no decimal places &ast;/
102  *   button = gtk_spin_button_new (adjustment, 1.0, 0);
103  *   gtk_container_add (GTK_CONTAINER (window), button);
104  *
105  *   gtk_widget_show_all (window);
106  * }
107  * </programlisting>
108  * </example>
109  *
110  * <example>
111  * <title>Using a GtkSpinButton to get a floating point value</title>
112  * <programlisting>
113  * /&ast; Provides a function to retrieve a floating point value from a
114  *  &ast; GtkSpinButton, and creates a high precision spin button.
115  *  &ast;/
116  *
117  * gfloat
118  * grab_float_value (GtkSpinButton *button,
119  *                   gpointer       user_data)
120  * {
121  *   return gtk_spin_button_get_value (button);
122  * }
123  *
124  * void
125  * create_floating_spin_button (void)
126  * {
127  *   GtkWidget *window, *button;
128  *   GtkAdjustment *adjustment;
129  *
130  *   adjustment = gtk_adjustment_new (2.500, 0.0, 5.0, 0.001, 0.1, 0.0);
131  *
132  *   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
133  *   gtk_container_set_border_width (GTK_CONTAINER (window), 5);
134  *
135  *   /&ast; creates the spinbutton, with three decimal places &ast;/
136  *   button = gtk_spin_button_new (adjustment, 0.001, 3);
137  *   gtk_container_add (GTK_CONTAINER (window), button);
138  *
139  *   gtk_widget_show_all (window);
140  * }
141  * </programlisting>
142  * </example>
143  */
144
145 struct _GtkSpinButtonPrivate
146 {
147   GtkAdjustment *adjustment;
148
149   GdkWindow     *down_panel;
150   GdkWindow     *up_panel;
151
152   GdkWindow     *click_child;
153   GdkWindow     *in_child;
154
155   guint32        timer;
156
157   GtkSpinButtonUpdatePolicy update_policy;
158
159   gdouble        climb_rate;
160   gdouble        timer_step;
161
162   guint          button        : 2;
163   guint          digits        : 10;
164   guint          need_timer    : 1;
165   guint          numeric       : 1;
166   guint          snap_to_ticks : 1;
167   guint          timer_calls   : 3;
168   guint          wrap          : 1;
169 };
170
171 enum {
172   PROP_0,
173   PROP_ADJUSTMENT,
174   PROP_CLIMB_RATE,
175   PROP_DIGITS,
176   PROP_SNAP_TO_TICKS,
177   PROP_NUMERIC,
178   PROP_WRAP,
179   PROP_UPDATE_POLICY,
180   PROP_VALUE
181 };
182
183 /* Signals */
184 enum
185 {
186   INPUT,
187   OUTPUT,
188   VALUE_CHANGED,
189   CHANGE_VALUE,
190   WRAPPED,
191   LAST_SIGNAL
192 };
193
194 static void gtk_spin_button_editable_init  (GtkEditableInterface *iface);
195 static void gtk_spin_button_finalize       (GObject            *object);
196 static void gtk_spin_button_set_property   (GObject         *object,
197                                             guint            prop_id,
198                                             const GValue    *value,
199                                             GParamSpec      *pspec);
200 static void gtk_spin_button_get_property   (GObject         *object,
201                                             guint            prop_id,
202                                             GValue          *value,
203                                             GParamSpec      *pspec);
204 static void gtk_spin_button_destroy        (GtkWidget          *widget);
205 static void gtk_spin_button_map            (GtkWidget          *widget);
206 static void gtk_spin_button_unmap          (GtkWidget          *widget);
207 static void gtk_spin_button_realize        (GtkWidget          *widget);
208 static void gtk_spin_button_unrealize      (GtkWidget          *widget);
209 static void gtk_spin_button_get_preferred_width  (GtkWidget          *widget,
210                                                   gint               *minimum,
211                                                   gint               *natural);
212
213 static void gtk_spin_button_size_allocate  (GtkWidget          *widget,
214                                             GtkAllocation      *allocation);
215 static gint gtk_spin_button_draw           (GtkWidget          *widget,
216                                             cairo_t            *cr);
217 static gint gtk_spin_button_button_press   (GtkWidget          *widget,
218                                             GdkEventButton     *event);
219 static gint gtk_spin_button_button_release (GtkWidget          *widget,
220                                             GdkEventButton     *event);
221 static gint gtk_spin_button_motion_notify  (GtkWidget          *widget,
222                                             GdkEventMotion     *event);
223 static gint gtk_spin_button_enter_notify   (GtkWidget          *widget,
224                                             GdkEventCrossing   *event);
225 static gint gtk_spin_button_leave_notify   (GtkWidget          *widget,
226                                             GdkEventCrossing   *event);
227 static gint gtk_spin_button_focus_out      (GtkWidget          *widget,
228                                             GdkEventFocus      *event);
229 static void gtk_spin_button_grab_notify    (GtkWidget          *widget,
230                                             gboolean            was_grabbed);
231 static void gtk_spin_button_state_flags_changed  (GtkWidget     *widget,
232                                                   GtkStateFlags  previous_state);
233 static gboolean gtk_spin_button_timer          (GtkSpinButton      *spin_button);
234 static gboolean gtk_spin_button_stop_spinning  (GtkSpinButton      *spin);
235 static void gtk_spin_button_value_changed  (GtkAdjustment      *adjustment,
236                                             GtkSpinButton      *spin_button);
237 static gint gtk_spin_button_key_release    (GtkWidget          *widget,
238                                             GdkEventKey        *event);
239 static gint gtk_spin_button_scroll         (GtkWidget          *widget,
240                                             GdkEventScroll     *event);
241 static void gtk_spin_button_activate       (GtkEntry           *entry);
242 static void gtk_spin_button_get_text_area_size (GtkEntry *entry,
243                                                 gint     *x,
244                                                 gint     *y,
245                                                 gint     *width,
246                                                 gint     *height);
247 static void gtk_spin_button_snap           (GtkSpinButton      *spin_button,
248                                             gdouble             val);
249 static void gtk_spin_button_insert_text    (GtkEditable        *editable,
250                                             const gchar        *new_text,
251                                             gint                new_text_length,
252                                             gint               *position);
253 static void gtk_spin_button_real_spin      (GtkSpinButton      *spin_button,
254                                             gdouble             step);
255 static void gtk_spin_button_real_change_value (GtkSpinButton   *spin,
256                                                GtkScrollType    scroll);
257
258 static gint gtk_spin_button_default_input  (GtkSpinButton      *spin_button,
259                                             gdouble            *new_val);
260 static gint gtk_spin_button_default_output (GtkSpinButton      *spin_button);
261
262 static guint spinbutton_signals[LAST_SIGNAL] = {0};
263
264 G_DEFINE_TYPE_WITH_CODE (GtkSpinButton, gtk_spin_button, GTK_TYPE_ENTRY,
265                          G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
266                                                 gtk_spin_button_editable_init))
267
268 #define add_spin_binding(binding_set, keyval, mask, scroll)            \
269   gtk_binding_entry_add_signal (binding_set, keyval, mask,             \
270                                 "change_value", 1,                     \
271                                 GTK_TYPE_SCROLL_TYPE, scroll)
272
273 static void
274 gtk_spin_button_class_init (GtkSpinButtonClass *class)
275 {
276   GObjectClass     *gobject_class = G_OBJECT_CLASS (class);
277   GtkWidgetClass   *widget_class = GTK_WIDGET_CLASS (class);
278   GtkEntryClass    *entry_class = GTK_ENTRY_CLASS (class);
279   GtkBindingSet    *binding_set;
280
281   gobject_class->finalize = gtk_spin_button_finalize;
282   gobject_class->set_property = gtk_spin_button_set_property;
283   gobject_class->get_property = gtk_spin_button_get_property;
284
285   widget_class->destroy = gtk_spin_button_destroy;
286   widget_class->map = gtk_spin_button_map;
287   widget_class->unmap = gtk_spin_button_unmap;
288   widget_class->realize = gtk_spin_button_realize;
289   widget_class->unrealize = gtk_spin_button_unrealize;
290   widget_class->get_preferred_width = gtk_spin_button_get_preferred_width;
291   widget_class->size_allocate = gtk_spin_button_size_allocate;
292   widget_class->draw = gtk_spin_button_draw;
293   widget_class->scroll_event = gtk_spin_button_scroll;
294   widget_class->button_press_event = gtk_spin_button_button_press;
295   widget_class->button_release_event = gtk_spin_button_button_release;
296   widget_class->motion_notify_event = gtk_spin_button_motion_notify;
297   widget_class->key_release_event = gtk_spin_button_key_release;
298   widget_class->enter_notify_event = gtk_spin_button_enter_notify;
299   widget_class->leave_notify_event = gtk_spin_button_leave_notify;
300   widget_class->focus_out_event = gtk_spin_button_focus_out;
301   widget_class->grab_notify = gtk_spin_button_grab_notify;
302   widget_class->state_flags_changed = gtk_spin_button_state_flags_changed;
303
304   entry_class->activate = gtk_spin_button_activate;
305   entry_class->get_text_area_size = gtk_spin_button_get_text_area_size;
306
307   class->input = NULL;
308   class->output = NULL;
309   class->change_value = gtk_spin_button_real_change_value;
310
311   g_object_class_install_property (gobject_class,
312                                    PROP_ADJUSTMENT,
313                                    g_param_spec_object ("adjustment",
314                                                         P_("Adjustment"),
315                                                         P_("The adjustment that holds the value of the spin button"),
316                                                         GTK_TYPE_ADJUSTMENT,
317                                                         GTK_PARAM_READWRITE));
318
319   g_object_class_install_property (gobject_class,
320                                    PROP_CLIMB_RATE,
321                                    g_param_spec_double ("climb-rate",
322                                                         P_("Climb Rate"),
323                                                         P_("The acceleration rate when you hold down a button"),
324                                                         0.0,
325                                                         G_MAXDOUBLE,
326                                                         0.0,
327                                                         GTK_PARAM_READWRITE));
328
329   g_object_class_install_property (gobject_class,
330                                    PROP_DIGITS,
331                                    g_param_spec_uint ("digits",
332                                                       P_("Digits"),
333                                                       P_("The number of decimal places to display"),
334                                                       0,
335                                                       MAX_DIGITS,
336                                                       0,
337                                                       GTK_PARAM_READWRITE));
338
339   g_object_class_install_property (gobject_class,
340                                    PROP_SNAP_TO_TICKS,
341                                    g_param_spec_boolean ("snap-to-ticks",
342                                                          P_("Snap to Ticks"),
343                                                          P_("Whether erroneous values are automatically changed to a spin button's nearest step increment"),
344                                                          FALSE,
345                                                          GTK_PARAM_READWRITE));
346
347   g_object_class_install_property (gobject_class,
348                                    PROP_NUMERIC,
349                                    g_param_spec_boolean ("numeric",
350                                                          P_("Numeric"),
351                                                          P_("Whether non-numeric characters should be ignored"),
352                                                          FALSE,
353                                                          GTK_PARAM_READWRITE));
354
355   g_object_class_install_property (gobject_class,
356                                    PROP_WRAP,
357                                    g_param_spec_boolean ("wrap",
358                                                          P_("Wrap"),
359                                                          P_("Whether a spin button should wrap upon reaching its limits"),
360                                                          FALSE,
361                                                          GTK_PARAM_READWRITE));
362
363   g_object_class_install_property (gobject_class,
364                                    PROP_UPDATE_POLICY,
365                                    g_param_spec_enum ("update-policy",
366                                                       P_("Update Policy"),
367                                                       P_("Whether the spin button should update always, or only when the value is legal"),
368                                                       GTK_TYPE_SPIN_BUTTON_UPDATE_POLICY,
369                                                       GTK_UPDATE_ALWAYS,
370                                                       GTK_PARAM_READWRITE));
371
372   g_object_class_install_property (gobject_class,
373                                    PROP_VALUE,
374                                    g_param_spec_double ("value",
375                                                         P_("Value"),
376                                                         P_("Reads the current value, or sets a new value"),
377                                                         -G_MAXDOUBLE,
378                                                         G_MAXDOUBLE,
379                                                         0.0,
380                                                         GTK_PARAM_READWRITE));
381
382   gtk_widget_class_install_style_property_parser (widget_class,
383                                                   g_param_spec_enum ("shadow-type",
384                                                                      "Shadow Type",
385                                                                      P_("Style of bevel around the spin button"),
386                                                                      GTK_TYPE_SHADOW_TYPE,
387                                                                      GTK_SHADOW_IN,
388                                                                      GTK_PARAM_READABLE),
389                                                   gtk_rc_property_parse_enum);
390
391   /**
392    * GtkSpinButton::input:
393    * @spin_button: the object on which the signal was emitted
394    * @new_value: (out) (type double): return location for the new value
395    *
396    * The ::input signal can be used to influence the conversion of
397    * the users input into a double value. The signal handler is
398    * expected to use gtk_entry_get_text() to retrieve the text of
399    * the entry and set @new_value to the new value.
400    *
401    * The default conversion uses g_strtod().
402    *
403    * Returns: %TRUE for a successful conversion, %FALSE if the input
404    *     was not handled, and %GTK_INPUT_ERROR if the conversion failed.
405    */
406   spinbutton_signals[INPUT] =
407     g_signal_new (I_("input"),
408                   G_TYPE_FROM_CLASS (gobject_class),
409                   G_SIGNAL_RUN_LAST,
410                   G_STRUCT_OFFSET (GtkSpinButtonClass, input),
411                   NULL, NULL,
412                   _gtk_marshal_INT__POINTER,
413                   G_TYPE_INT, 1,
414                   G_TYPE_POINTER);
415
416   /**
417    * GtkSpinButton::output:
418    * @spin_button: the object which received the signal
419    *
420    * The ::output signal can be used to change to formatting
421    * of the value that is displayed in the spin buttons entry.
422    * |[
423    * /&ast; show leading zeros &ast;/
424    * static gboolean
425    * on_output (GtkSpinButton *spin,
426    *            gpointer       data)
427    * {
428    *    GtkAdjustment *adjustment;
429    *    gchar *text;
430    *    int value;
431    *
432    *    adjustment = gtk_spin_button_get_adjustment (spin);
433    *    value = (int)gtk_adjustment_get_value (adjustment);
434    *    text = g_strdup_printf ("%02d", value);
435    *    gtk_entry_set_text (GTK_ENTRY (spin), text);
436    *    g_free (text);
437    *
438    *    return TRUE;
439    * }
440    * ]|
441    *
442    * Returns: %TRUE if the value has been displayed
443    */
444   spinbutton_signals[OUTPUT] =
445     g_signal_new (I_("output"),
446                   G_TYPE_FROM_CLASS (gobject_class),
447                   G_SIGNAL_RUN_LAST,
448                   G_STRUCT_OFFSET (GtkSpinButtonClass, output),
449                   _gtk_boolean_handled_accumulator, NULL,
450                   _gtk_marshal_BOOLEAN__VOID,
451                   G_TYPE_BOOLEAN, 0);
452
453   spinbutton_signals[VALUE_CHANGED] =
454     g_signal_new (I_("value-changed"),
455                   G_TYPE_FROM_CLASS (gobject_class),
456                   G_SIGNAL_RUN_LAST,
457                   G_STRUCT_OFFSET (GtkSpinButtonClass, value_changed),
458                   NULL, NULL,
459                   _gtk_marshal_VOID__VOID,
460                   G_TYPE_NONE, 0);
461
462   /**
463    * GtkSpinButton::wrapped:
464    * @spinbutton: the object which received the signal
465    *
466    * The wrapped signal is emitted right after the spinbutton wraps
467    * from its maximum to minimum value or vice-versa.
468    *
469    * Since: 2.10
470    */
471   spinbutton_signals[WRAPPED] =
472     g_signal_new (I_("wrapped"),
473                   G_TYPE_FROM_CLASS (gobject_class),
474                   G_SIGNAL_RUN_LAST,
475                   G_STRUCT_OFFSET (GtkSpinButtonClass, wrapped),
476                   NULL, NULL,
477                   _gtk_marshal_VOID__VOID,
478                   G_TYPE_NONE, 0);
479
480   /* Action signals */
481   spinbutton_signals[CHANGE_VALUE] =
482     g_signal_new (I_("change-value"),
483                   G_TYPE_FROM_CLASS (gobject_class),
484                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
485                   G_STRUCT_OFFSET (GtkSpinButtonClass, change_value),
486                   NULL, NULL,
487                   _gtk_marshal_VOID__ENUM,
488                   G_TYPE_NONE, 1,
489                   GTK_TYPE_SCROLL_TYPE);
490
491   binding_set = gtk_binding_set_by_class (class);
492
493   add_spin_binding (binding_set, GDK_KEY_Up, 0, GTK_SCROLL_STEP_UP);
494   add_spin_binding (binding_set, GDK_KEY_KP_Up, 0, GTK_SCROLL_STEP_UP);
495   add_spin_binding (binding_set, GDK_KEY_Down, 0, GTK_SCROLL_STEP_DOWN);
496   add_spin_binding (binding_set, GDK_KEY_KP_Down, 0, GTK_SCROLL_STEP_DOWN);
497   add_spin_binding (binding_set, GDK_KEY_Page_Up, 0, GTK_SCROLL_PAGE_UP);
498   add_spin_binding (binding_set, GDK_KEY_Page_Down, 0, GTK_SCROLL_PAGE_DOWN);
499   add_spin_binding (binding_set, GDK_KEY_Page_Up, GDK_CONTROL_MASK, GTK_SCROLL_END);
500   add_spin_binding (binding_set, GDK_KEY_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_START);
501
502   g_type_class_add_private (class, sizeof (GtkSpinButtonPrivate));
503
504   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_SPIN_BUTTON_ACCESSIBLE);
505 }
506
507 static void
508 gtk_spin_button_editable_init (GtkEditableInterface *iface)
509 {
510   iface->insert_text = gtk_spin_button_insert_text;
511 }
512
513 static void
514 gtk_spin_button_set_property (GObject      *object,
515                               guint         prop_id,
516                               const GValue *value,
517                               GParamSpec   *pspec)
518 {
519   GtkSpinButton *spin_button = GTK_SPIN_BUTTON (object);
520   GtkSpinButtonPrivate *priv = spin_button->priv;
521
522   switch (prop_id)
523     {
524       GtkAdjustment *adjustment;
525
526     case PROP_ADJUSTMENT:
527       adjustment = GTK_ADJUSTMENT (g_value_get_object (value));
528       if (!adjustment)
529         adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
530       gtk_spin_button_set_adjustment (spin_button, adjustment);
531       break;
532     case PROP_CLIMB_RATE:
533       gtk_spin_button_configure (spin_button,
534                                  priv->adjustment,
535                                  g_value_get_double (value),
536                                  priv->digits);
537       break;
538     case PROP_DIGITS:
539       gtk_spin_button_configure (spin_button,
540                                  priv->adjustment,
541                                  priv->climb_rate,
542                                  g_value_get_uint (value));
543       break;
544     case PROP_SNAP_TO_TICKS:
545       gtk_spin_button_set_snap_to_ticks (spin_button, g_value_get_boolean (value));
546       break;
547     case PROP_NUMERIC:
548       gtk_spin_button_set_numeric (spin_button, g_value_get_boolean (value));
549       break;
550     case PROP_WRAP:
551       gtk_spin_button_set_wrap (spin_button, g_value_get_boolean (value));
552       break;
553     case PROP_UPDATE_POLICY:
554       gtk_spin_button_set_update_policy (spin_button, g_value_get_enum (value));
555       break;
556     case PROP_VALUE:
557       gtk_spin_button_set_value (spin_button, g_value_get_double (value));
558       break;
559     default:
560       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
561       break;
562     }
563 }
564
565 static void
566 gtk_spin_button_get_property (GObject      *object,
567                               guint         prop_id,
568                               GValue       *value,
569                               GParamSpec   *pspec)
570 {
571   GtkSpinButton *spin_button = GTK_SPIN_BUTTON (object);
572   GtkSpinButtonPrivate *priv = spin_button->priv;
573
574   switch (prop_id)
575     {
576     case PROP_ADJUSTMENT:
577       g_value_set_object (value, priv->adjustment);
578       break;
579     case PROP_CLIMB_RATE:
580       g_value_set_double (value, priv->climb_rate);
581       break;
582     case PROP_DIGITS:
583       g_value_set_uint (value, priv->digits);
584       break;
585     case PROP_SNAP_TO_TICKS:
586       g_value_set_boolean (value, priv->snap_to_ticks);
587       break;
588     case PROP_NUMERIC:
589       g_value_set_boolean (value, priv->numeric);
590       break;
591     case PROP_WRAP:
592       g_value_set_boolean (value, priv->wrap);
593       break;
594     case PROP_UPDATE_POLICY:
595       g_value_set_enum (value, priv->update_policy);
596       break;
597      case PROP_VALUE:
598        g_value_set_double (value, gtk_adjustment_get_value (priv->adjustment));
599       break;
600     default:
601       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
602       break;
603     }
604 }
605
606 static gint
607 get_icon_size (void)
608 {
609   gint width, height, icon_size;
610
611   gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, &height);
612   icon_size = MAX (width, height);
613
614   return icon_size;
615 }
616
617 static GdkPixbuf *
618 create_one_pixbuf (GtkStyleContext *context,
619                    const gchar *icon_name)
620 {
621   GtkIconInfo *icon_info;
622   GdkPixbuf *pix;
623   gint size = get_icon_size ();
624
625   icon_info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (),
626                                           icon_name, size,
627                                           GTK_ICON_LOOKUP_GENERIC_FALLBACK |
628                                           GTK_ICON_LOOKUP_USE_BUILTIN);
629
630   if (icon_info != NULL)
631     {
632       pix = gtk_icon_info_load_symbolic_for_context (icon_info, context, 
633                                                      NULL, NULL);
634       gtk_icon_info_free (icon_info);
635     }
636   else
637     {
638       GtkIconSet *icon_set;
639
640       icon_set = gtk_style_context_lookup_icon_set (context, GTK_STOCK_MISSING_IMAGE);
641       pix = gtk_icon_set_render_icon_pixbuf (icon_set, context, GTK_ICON_SIZE_MENU);
642
643       g_warning ("Unable to fetch icon %s from the icon theme", icon_name);
644     }
645
646   return pix;
647 }
648
649 static void
650 gtk_spin_button_init (GtkSpinButton *spin_button)
651 {
652   GtkSpinButtonPrivate *priv;
653   GtkStyleContext *context;
654
655   spin_button->priv = G_TYPE_INSTANCE_GET_PRIVATE (spin_button,
656                                                    GTK_TYPE_SPIN_BUTTON,
657                                                    GtkSpinButtonPrivate);
658   priv = spin_button->priv;
659
660   priv->adjustment = NULL;
661   priv->down_panel = NULL;
662   priv->up_panel = NULL;
663   priv->timer = 0;
664   priv->climb_rate = 0.0;
665   priv->timer_step = 0.0;
666   priv->update_policy = GTK_UPDATE_ALWAYS;
667   priv->in_child = NULL;
668   priv->click_child = NULL;
669   priv->button = 0;
670   priv->need_timer = FALSE;
671   priv->timer_calls = 0;
672   priv->digits = 0;
673   priv->numeric = FALSE;
674   priv->wrap = FALSE;
675   priv->snap_to_ticks = FALSE;
676
677   gtk_spin_button_set_adjustment (spin_button,
678                                   gtk_adjustment_new (0, 0, 0, 0, 0, 0));
679
680   context = gtk_widget_get_style_context (GTK_WIDGET (spin_button));
681   gtk_style_context_add_class (context, GTK_STYLE_CLASS_SPINBUTTON);
682
683   gtk_widget_add_events (GTK_WIDGET (spin_button), GDK_SCROLL_MASK);
684 }
685
686 static void
687 gtk_spin_button_finalize (GObject *object)
688 {
689   gtk_spin_button_set_adjustment (GTK_SPIN_BUTTON (object), NULL);
690
691   G_OBJECT_CLASS (gtk_spin_button_parent_class)->finalize (object);
692 }
693
694 static void
695 gtk_spin_button_destroy (GtkWidget *widget)
696 {
697   gtk_spin_button_stop_spinning (GTK_SPIN_BUTTON (widget));
698
699   GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->destroy (widget);
700 }
701
702 static void
703 gtk_spin_button_map (GtkWidget *widget)
704 {
705   GtkSpinButton *spin_button = GTK_SPIN_BUTTON (widget);
706   GtkSpinButtonPrivate *priv = spin_button->priv;
707
708   if (gtk_widget_get_realized (widget) && !gtk_widget_get_mapped (widget))
709     {
710       GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->map (widget);
711       gdk_window_show (priv->down_panel);
712       gdk_window_show (priv->up_panel);
713     }
714 }
715
716 static void
717 gtk_spin_button_unmap (GtkWidget *widget)
718 {
719   GtkSpinButton *spin_button = GTK_SPIN_BUTTON (widget);
720   GtkSpinButtonPrivate *priv = spin_button->priv;
721
722   if (gtk_widget_get_mapped (widget))
723     {
724       gtk_spin_button_stop_spinning (GTK_SPIN_BUTTON (widget));
725
726       gdk_window_hide (priv->down_panel);
727       gdk_window_hide (priv->up_panel);
728       GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->unmap (widget);
729     }
730 }
731
732 static void
733 gtk_spin_button_panel_nthchildize_context (GtkSpinButton *spin_button,
734                                            GtkStyleContext *context,
735                                            GdkWindow *panel)
736 {
737   GtkWidget *widget = GTK_WIDGET (spin_button);
738   GtkWidgetPath *path, *siblings_path;
739   GtkTextDirection direction;
740   gint up_pos, down_pos;
741
742   /* We are a subclass of GtkEntry, which is not a GtkContainer, so we
743    * have to emulate what gtk_container_get_path_for_child() would do
744    * for the button panels
745    */
746   path = gtk_widget_path_copy (gtk_widget_get_path (widget));
747   direction = gtk_widget_get_direction (widget);
748   siblings_path = gtk_widget_path_new ();
749
750   /* flip children order for RTL */
751   if (direction == GTK_TEXT_DIR_RTL)
752     {
753       up_pos = gtk_widget_path_append_type (siblings_path, GTK_TYPE_SPIN_BUTTON);  
754       down_pos = gtk_widget_path_append_type (siblings_path, GTK_TYPE_SPIN_BUTTON);
755       gtk_widget_path_append_type (siblings_path, GTK_TYPE_ENTRY);
756     }
757   else
758     {
759       gtk_widget_path_append_type (siblings_path, GTK_TYPE_ENTRY);
760       down_pos = gtk_widget_path_append_type (siblings_path, GTK_TYPE_SPIN_BUTTON);  
761       up_pos = gtk_widget_path_append_type (siblings_path, GTK_TYPE_SPIN_BUTTON);
762     }
763
764   gtk_widget_path_iter_add_class (siblings_path, up_pos, GTK_STYLE_CLASS_BUTTON);
765   gtk_widget_path_iter_add_class (siblings_path, down_pos, GTK_STYLE_CLASS_BUTTON);
766
767   /* this allows compatibility for themes that use .spinbutton.button */
768   gtk_widget_path_iter_add_class (siblings_path, up_pos, GTK_STYLE_CLASS_SPINBUTTON);
769   gtk_widget_path_iter_add_class (siblings_path, down_pos, GTK_STYLE_CLASS_SPINBUTTON);
770
771   if (panel == spin_button->priv->down_panel)
772     {
773       gtk_widget_path_append_with_siblings (path, siblings_path, up_pos);
774       gtk_widget_path_append_with_siblings (path, siblings_path, down_pos);
775     }
776   else
777     {
778       gtk_widget_path_append_with_siblings (path, siblings_path, down_pos);
779       gtk_widget_path_append_with_siblings (path, siblings_path, up_pos);
780     }
781
782   gtk_style_context_set_path (context, path);
783   gtk_widget_path_unref (path);
784   gtk_widget_path_unref (siblings_path);
785 }
786
787 static gboolean
788 gtk_spin_button_panel_at_limit (GtkSpinButton *spin_button,
789                                 GdkWindow     *panel)
790 {
791   GtkSpinButtonPrivate *priv = spin_button->priv;
792   GdkWindow *effective_panel;
793
794   if (priv->wrap)
795     return FALSE;
796
797   if (gtk_adjustment_get_step_increment (priv->adjustment) > 0)
798     effective_panel = panel;
799   else
800     effective_panel = panel == priv->up_panel ? priv->down_panel : priv->up_panel;
801
802   if (effective_panel == priv->up_panel &&
803       (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_value (priv->adjustment) <= EPSILON))
804     return TRUE;
805
806   if (effective_panel == priv->down_panel &&
807       (gtk_adjustment_get_value (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) <= EPSILON))
808     return TRUE;
809
810   return FALSE;
811 }
812
813 static GtkStateFlags
814 gtk_spin_button_panel_get_state (GtkSpinButton *spin_button,
815                                  GdkWindow *panel)
816 {
817   GtkStateFlags state;
818   GtkSpinButtonPrivate *priv = spin_button->priv;
819
820   state = gtk_widget_get_state_flags (GTK_WIDGET (spin_button));
821
822   state &= ~(GTK_STATE_FLAG_ACTIVE | GTK_STATE_FLAG_PRELIGHT);
823
824   if ((state & GTK_STATE_FLAG_INSENSITIVE) ||
825       gtk_spin_button_panel_at_limit (spin_button, panel) ||
826       !gtk_editable_get_editable (GTK_EDITABLE (spin_button)))
827     {
828       state |= GTK_STATE_FLAG_INSENSITIVE;
829     }
830   else
831     {
832       if (priv->click_child == panel)
833         state |= GTK_STATE_ACTIVE;
834       else if (priv->in_child == panel &&
835                priv->click_child == NULL)
836         state |= GTK_STATE_FLAG_PRELIGHT;
837     }
838
839   return state;
840 }
841
842 static GtkStyleContext *
843 gtk_spin_button_panel_get_context (GtkSpinButton *spin_button,
844                                    GdkWindow *panel)
845 {
846   GtkStyleContext *context;
847
848   context = gtk_style_context_new ();
849   gtk_spin_button_panel_nthchildize_context (spin_button, context, panel);
850
851   return context;
852 }
853
854 static gint
855 gtk_spin_button_panel_get_width (GtkSpinButton *spin_button,
856                                  GdkWindow *panel)
857 {
858   GtkBorder button_padding, button_border;
859   GtkStyleContext *context;
860   GtkStateFlags state;
861
862   context = gtk_spin_button_panel_get_context (spin_button, panel);
863   state = gtk_spin_button_panel_get_state (spin_button, panel);
864
865   gtk_style_context_get_padding (context, state, &button_padding);
866   gtk_style_context_get_border (context, state, &button_border);
867
868   g_object_unref (context);
869
870   return get_icon_size () + button_padding.left + button_padding.right +
871     button_border.left + button_border.right;
872 }
873
874 static void
875 gtk_spin_button_panel_get_allocations (GtkSpinButton *spin_button,
876                                        GtkAllocation *down_allocation_out,
877                                        GtkAllocation *up_allocation_out)
878 {
879   GtkWidget *widget = GTK_WIDGET (spin_button);
880   GtkSpinButtonPrivate *priv = spin_button->priv;
881   GtkAllocation spin_allocation, down_allocation, up_allocation, allocation;
882   GtkRequisition requisition;
883   gint req_height;
884   gint up_panel_width, down_panel_width;
885   GtkStyleContext *context;
886   GtkBorder space;
887
888   gtk_widget_get_allocation (widget, &spin_allocation);
889   gtk_widget_get_preferred_size (widget, &requisition, NULL);
890
891   context = gtk_widget_get_style_context (GTK_WIDGET (spin_button));
892   gtk_style_context_get_border (context, GTK_STATE_NORMAL, &space);
893
894   req_height = requisition.height - gtk_widget_get_margin_top (widget) - gtk_widget_get_margin_bottom (widget);
895   down_panel_width = gtk_spin_button_panel_get_width (spin_button, priv->down_panel);
896   up_panel_width = gtk_spin_button_panel_get_width (spin_button, priv->up_panel);
897
898   /* both panels have the same size, and they're as big as the entry allocation,
899    * excluding margins
900    */
901   allocation.height = MIN (req_height, spin_allocation.height) - space.top - space.bottom;
902   allocation.y = spin_allocation.y + space.top + (spin_allocation.height - req_height) / 2;
903   down_allocation = up_allocation = allocation;
904
905   down_allocation.width = down_panel_width;
906   up_allocation.width = up_panel_width;
907
908   /* invert x axis allocation for RTL */
909   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
910     {
911       up_allocation.x = spin_allocation.x + space.left;
912       down_allocation.x = up_allocation.x + up_panel_width;
913     }
914   else
915     {
916       up_allocation.x = spin_allocation.x + spin_allocation.width - up_panel_width - space.right;
917       down_allocation.x = up_allocation.x - down_panel_width;
918     }
919
920   if (down_allocation_out)
921     *down_allocation_out = down_allocation;
922   if (up_allocation_out)
923     *up_allocation_out = up_allocation;
924 }
925
926 static void
927 gtk_spin_button_panel_draw (GtkSpinButton   *spin_button,
928                             cairo_t         *cr,
929                             GdkWindow       *panel)
930 {
931   GtkSpinButtonPrivate *priv = spin_button->priv;
932   GtkStyleContext *context;
933   GtkStateFlags state;
934   GtkWidget *widget;
935   gdouble width, height, x, y;
936   GdkPixbuf *pix;
937
938   widget = GTK_WIDGET (spin_button);
939
940   cairo_save (cr);
941   gtk_cairo_transform_to_window (cr, widget, panel);
942
943   context = gtk_spin_button_panel_get_context (spin_button, panel);
944   state = gtk_spin_button_panel_get_state (spin_button, panel);
945   gtk_style_context_set_state (context, state);
946
947   height = gdk_window_get_height (panel);
948   width = gdk_window_get_width (panel);
949
950   if (panel == priv->down_panel)
951     pix = create_one_pixbuf (context, "list-remove-symbolic");
952   else
953     pix = create_one_pixbuf (context, "list-add-symbolic");
954
955   gtk_render_background (context, cr,
956                          0, 0, width, height);
957   gtk_render_frame (context, cr,
958                     0, 0, width, height);
959
960   x = floor ((width - gdk_pixbuf_get_width (pix)) / 2.0);
961   y = floor ((height - gdk_pixbuf_get_height (pix)) / 2.0);
962
963   gtk_render_icon (context, cr, pix,
964                    x, y);
965   cairo_restore (cr);
966
967   g_object_unref (pix);
968   g_object_unref (context);
969 }
970
971 static void
972 gtk_spin_button_realize (GtkWidget *widget)
973 {
974   GtkSpinButton *spin_button = GTK_SPIN_BUTTON (widget);
975   GtkSpinButtonPrivate *priv = spin_button->priv;
976   GtkAllocation down_allocation, up_allocation;
977   GdkWindowAttr attributes;
978   gint attributes_mask;
979   gboolean return_val;
980
981   gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
982                          GDK_KEY_RELEASE_MASK);
983   GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->realize (widget);
984
985   attributes.window_type = GDK_WINDOW_CHILD;
986   attributes.wclass = GDK_INPUT_ONLY;
987   attributes.visual = gtk_widget_get_visual (widget);
988   attributes.event_mask = gtk_widget_get_events (widget);
989   attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
990     | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
991     | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
992
993   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
994
995   gtk_spin_button_panel_get_allocations (spin_button, &down_allocation, &up_allocation);
996
997   /* create the left panel window */
998   attributes.x = down_allocation.x;
999   attributes.y = down_allocation.y;
1000   attributes.width = down_allocation.width;
1001   attributes.height = down_allocation.height;
1002
1003   priv->down_panel = gdk_window_new (gtk_widget_get_window (widget),
1004                                      &attributes, attributes_mask);
1005   gdk_window_set_user_data (priv->down_panel, widget);
1006
1007   /* create the right panel window */
1008   attributes.x = up_allocation.x;
1009   attributes.y = up_allocation.y;
1010   attributes.width = up_allocation.width;
1011   attributes.height = up_allocation.height;
1012
1013   priv->up_panel = gdk_window_new (gtk_widget_get_window (widget),
1014                                       &attributes, attributes_mask);
1015   gdk_window_set_user_data (priv->up_panel, widget);
1016
1017   return_val = FALSE;
1018   g_signal_emit (spin_button, spinbutton_signals[OUTPUT], 0, &return_val);
1019   if (return_val == FALSE)
1020     gtk_spin_button_default_output (spin_button);
1021
1022   gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1023 }
1024
1025 static void
1026 gtk_spin_button_unrealize (GtkWidget *widget)
1027 {
1028   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1029   GtkSpinButtonPrivate *priv = spin->priv;
1030
1031   GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->unrealize (widget);
1032
1033   if (priv->down_panel)
1034     {
1035       gdk_window_set_user_data (priv->down_panel, NULL);
1036       gdk_window_destroy (priv->down_panel);
1037       priv->down_panel = NULL;
1038     }
1039
1040   if (priv->up_panel)
1041     {
1042       gdk_window_set_user_data (priv->up_panel, NULL);
1043       gdk_window_destroy (priv->up_panel);
1044       priv->up_panel = NULL;
1045     }
1046 }
1047
1048 static int
1049 compute_double_length (double val, int digits)
1050 {
1051   int a;
1052   int extra;
1053
1054   a = 1;
1055   if (fabs (val) > 1.0)
1056     a = floor (log10 (fabs (val))) + 1;
1057
1058   extra = 0;
1059
1060   /* The dot: */
1061   if (digits > 0)
1062     extra++;
1063
1064   /* The sign: */
1065   if (val < 0)
1066     extra++;
1067
1068   return a + digits + extra;
1069 }
1070
1071 static void
1072 gtk_spin_button_get_preferred_width (GtkWidget *widget,
1073                                      gint      *minimum,
1074                                      gint      *natural)
1075 {
1076   GtkSpinButton *spin_button = GTK_SPIN_BUTTON (widget);
1077   GtkSpinButtonPrivate *priv = spin_button->priv;
1078   GtkEntry *entry = GTK_ENTRY (widget);
1079   GtkStyleContext *style_context;
1080   gint up_panel_width, down_panel_width;
1081
1082   style_context = gtk_widget_get_style_context (widget);
1083   up_panel_width = gtk_spin_button_panel_get_width (spin_button, priv->up_panel);
1084   down_panel_width = gtk_spin_button_panel_get_width (spin_button, priv->down_panel);
1085
1086   GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->get_preferred_width (widget, minimum, natural);
1087
1088   if (gtk_entry_get_width_chars (entry) < 0)
1089     {
1090       PangoContext *context;
1091       const PangoFontDescription *font_desc;
1092       PangoFontMetrics *metrics;
1093       gint width;
1094       gint w;
1095       gint string_len;
1096       gint max_string_len;
1097       gint digit_width;
1098       gboolean interior_focus;
1099       gint focus_width;
1100       GtkBorder borders;
1101
1102       gtk_style_context_get_style (style_context,
1103                                    "interior-focus", &interior_focus,
1104                                    "focus-line-width", &focus_width,
1105                                    NULL);
1106       font_desc = gtk_style_context_get_font (style_context, GTK_STATE_FLAG_NORMAL);
1107
1108       context = gtk_widget_get_pango_context (widget);
1109       metrics = pango_context_get_metrics (context, font_desc,
1110                                            pango_context_get_language (context));
1111
1112       digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
1113       digit_width = PANGO_SCALE *
1114         ((digit_width + PANGO_SCALE - 1) / PANGO_SCALE);
1115
1116       pango_font_metrics_unref (metrics);
1117
1118       /* Get max of MIN_SPIN_BUTTON_WIDTH, size of upper, size of lower */
1119       width = MIN_SPIN_BUTTON_WIDTH;
1120       max_string_len = MAX (10, compute_double_length (1e9 * gtk_adjustment_get_step_increment (priv->adjustment),
1121                                                        priv->digits));
1122
1123       string_len = compute_double_length (gtk_adjustment_get_upper (priv->adjustment),
1124                                           priv->digits);
1125       w = PANGO_PIXELS (MIN (string_len, max_string_len) * digit_width);
1126       width = MAX (width, w);
1127       string_len = compute_double_length (gtk_adjustment_get_lower (priv->adjustment), priv->digits);
1128       w = PANGO_PIXELS (MIN (string_len, max_string_len) * digit_width);
1129       width = MAX (width, w);
1130
1131       _gtk_entry_get_borders (entry, &borders);
1132       width += borders.left + borders.right;
1133
1134       *minimum = width;
1135       *natural = width;
1136     }
1137
1138   *minimum += up_panel_width + down_panel_width;
1139   *natural += up_panel_width + down_panel_width;
1140 }
1141
1142 static void
1143 gtk_spin_button_size_allocate (GtkWidget     *widget,
1144                                GtkAllocation *allocation)
1145 {
1146   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1147   GtkSpinButtonPrivate *priv = spin->priv;
1148   GtkAllocation down_panel_allocation, up_panel_allocation;
1149
1150   gtk_widget_set_allocation (widget, allocation);
1151   GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->size_allocate (widget, allocation);
1152
1153   gtk_spin_button_panel_get_allocations (spin, &down_panel_allocation, &up_panel_allocation);
1154
1155   if (gtk_widget_get_realized (widget))
1156     {
1157       gdk_window_move_resize (priv->down_panel,
1158                               down_panel_allocation.x,
1159                               down_panel_allocation.y,
1160                               down_panel_allocation.width,
1161                               down_panel_allocation.height);
1162
1163       gdk_window_move_resize (priv->up_panel,
1164                               up_panel_allocation.x,
1165                               up_panel_allocation.y,
1166                               up_panel_allocation.width,
1167                               up_panel_allocation.height);
1168     }
1169
1170   gtk_widget_queue_draw (GTK_WIDGET (spin));
1171 }
1172
1173 static gint
1174 gtk_spin_button_draw (GtkWidget      *widget,
1175                       cairo_t        *cr)
1176 {
1177   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1178   GtkSpinButtonPrivate *priv = spin->priv;
1179
1180   GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->draw (widget, cr);
1181
1182   /* Draw the buttons */
1183   gtk_spin_button_panel_draw (spin, cr, priv->down_panel);
1184   gtk_spin_button_panel_draw (spin, cr, priv->up_panel);
1185
1186   return FALSE;
1187 }
1188
1189 static gint
1190 gtk_spin_button_enter_notify (GtkWidget        *widget,
1191                               GdkEventCrossing *event)
1192 {
1193   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1194   GtkSpinButtonPrivate *priv = spin->priv;
1195
1196   if ((event->window == priv->down_panel) ||
1197       (event->window == priv->up_panel))
1198     {
1199       priv->in_child = event->window;
1200       gtk_widget_queue_draw (GTK_WIDGET (spin));
1201     }
1202
1203   return GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->enter_notify_event (widget, event);
1204 }
1205
1206 static gint
1207 gtk_spin_button_leave_notify (GtkWidget        *widget,
1208                               GdkEventCrossing *event)
1209 {
1210   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1211   GtkSpinButtonPrivate *priv = spin->priv;
1212
1213   if (priv->in_child != NULL)
1214     {
1215       priv->in_child = NULL;
1216       gtk_widget_queue_draw (GTK_WIDGET (spin));
1217     }
1218
1219   return GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->leave_notify_event (widget, event);
1220 }
1221
1222 static gint
1223 gtk_spin_button_focus_out (GtkWidget     *widget,
1224                            GdkEventFocus *event)
1225 {
1226   if (gtk_editable_get_editable (GTK_EDITABLE (widget)))
1227     gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
1228
1229   return GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->focus_out_event (widget, event);
1230 }
1231
1232 static void
1233 gtk_spin_button_grab_notify (GtkWidget *widget,
1234                              gboolean   was_grabbed)
1235 {
1236   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1237
1238   if (!was_grabbed)
1239     {
1240       if (gtk_spin_button_stop_spinning (spin))
1241         gtk_widget_queue_draw (GTK_WIDGET (spin));
1242     }
1243 }
1244
1245 static void
1246 gtk_spin_button_state_flags_changed (GtkWidget     *widget,
1247                                      GtkStateFlags  previous_state)
1248 {
1249   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1250
1251   if (!gtk_widget_is_sensitive (widget))
1252     {
1253       if (gtk_spin_button_stop_spinning (spin))
1254         gtk_widget_queue_draw (GTK_WIDGET (spin));
1255     }
1256 }
1257
1258 static gint
1259 gtk_spin_button_scroll (GtkWidget      *widget,
1260                         GdkEventScroll *event)
1261 {
1262   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1263   GtkSpinButtonPrivate *priv = spin->priv;
1264
1265   if (event->direction == GDK_SCROLL_UP)
1266     {
1267       if (!gtk_widget_has_focus (widget))
1268         gtk_widget_grab_focus (widget);
1269       gtk_spin_button_real_spin (spin, gtk_adjustment_get_step_increment (priv->adjustment));
1270     }
1271   else if (event->direction == GDK_SCROLL_DOWN)
1272     {
1273       if (!gtk_widget_has_focus (widget))
1274         gtk_widget_grab_focus (widget);
1275       gtk_spin_button_real_spin (spin, -gtk_adjustment_get_step_increment (priv->adjustment));
1276     }
1277   else
1278     return FALSE;
1279
1280   return TRUE;
1281 }
1282
1283 static gboolean
1284 gtk_spin_button_stop_spinning (GtkSpinButton *spin)
1285 {
1286   GtkSpinButtonPrivate *priv = spin->priv;
1287   gboolean did_spin = FALSE;
1288
1289   if (priv->timer)
1290     {
1291       g_source_remove (priv->timer);
1292       priv->timer = 0;
1293       priv->need_timer = FALSE;
1294
1295       did_spin = TRUE;
1296     }
1297
1298   priv->button = 0;
1299   priv->timer_step = gtk_adjustment_get_step_increment (priv->adjustment);
1300   priv->timer_calls = 0;
1301
1302   priv->click_child = NULL;
1303
1304   return did_spin;
1305 }
1306
1307 static void
1308 start_spinning (GtkSpinButton *spin,
1309                 GdkWindow     *click_child,
1310                 gdouble        step)
1311 {
1312   GtkSpinButtonPrivate *priv;
1313
1314   priv = spin->priv;
1315
1316   priv->click_child = click_child;
1317
1318   if (!priv->timer)
1319     {
1320       GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (spin));
1321       guint        timeout;
1322
1323       g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
1324
1325       priv->timer_step = step;
1326       priv->need_timer = TRUE;
1327       priv->timer = gdk_threads_add_timeout (timeout,
1328                                    (GSourceFunc) gtk_spin_button_timer,
1329                                    (gpointer) spin);
1330     }
1331   gtk_spin_button_real_spin (spin, click_child == priv->up_panel ? step : -step);
1332
1333   gtk_widget_queue_draw (GTK_WIDGET (spin));
1334 }
1335
1336 static gint
1337 gtk_spin_button_button_press (GtkWidget      *widget,
1338                               GdkEventButton *event)
1339 {
1340   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1341   GtkSpinButtonPrivate *priv = spin->priv;
1342
1343   if (!priv->button)
1344     {
1345       if ((event->window == priv->down_panel) ||
1346           (event->window == priv->up_panel))
1347         {
1348           if (!gtk_widget_has_focus (widget))
1349             gtk_widget_grab_focus (widget);
1350           priv->button = event->button;
1351
1352           if (gtk_editable_get_editable (GTK_EDITABLE (widget))) {
1353             gtk_spin_button_update (spin);
1354
1355             if (event->button == GDK_BUTTON_PRIMARY)
1356               start_spinning (spin, event->window, gtk_adjustment_get_step_increment (priv->adjustment));
1357             else if (event->button == GDK_BUTTON_MIDDLE)
1358               start_spinning (spin, event->window, gtk_adjustment_get_page_increment (priv->adjustment));
1359             else
1360               priv->click_child = event->window;
1361           } else
1362             gtk_widget_error_bell (widget);
1363
1364           return TRUE;
1365         }
1366       else
1367         return GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->button_press_event (widget, event);
1368     }
1369   return FALSE;
1370 }
1371
1372 static gint
1373 gtk_spin_button_button_release (GtkWidget      *widget,
1374                                 GdkEventButton *event)
1375 {
1376   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1377   GtkSpinButtonPrivate *priv = spin->priv;
1378
1379   if (event->button == priv->button)
1380     {
1381       GdkWindow *click_child = priv->click_child;
1382
1383       gtk_spin_button_stop_spinning (spin);
1384
1385       if (event->button == GDK_BUTTON_SECONDARY)
1386         {
1387           gdouble diff;
1388
1389           if (event->window == priv->down_panel &&
1390               click_child == event->window)
1391             {
1392               diff = gtk_adjustment_get_value (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment);
1393               if (diff > EPSILON)
1394                 gtk_spin_button_real_spin (spin, -diff);
1395             }
1396           else if (event->window == priv->up_panel &&
1397                    click_child == event->window)
1398             {
1399               diff = gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_value (priv->adjustment);
1400               if (diff > EPSILON)
1401                 gtk_spin_button_real_spin (spin, diff);
1402             }
1403         }
1404       gtk_widget_queue_draw (GTK_WIDGET (spin));
1405
1406       return TRUE;
1407     }
1408   else
1409     return GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->button_release_event (widget, event);
1410 }
1411
1412 static gint
1413 gtk_spin_button_motion_notify (GtkWidget      *widget,
1414                                GdkEventMotion *event)
1415 {
1416   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1417   GtkSpinButtonPrivate *priv = spin->priv;
1418
1419   if (priv->button)
1420     return FALSE;
1421
1422   if (event->window == priv->down_panel ||
1423       event->window == priv->up_panel)
1424     {
1425       gdk_event_request_motions (event);
1426
1427       priv->in_child = event->window;
1428       gtk_widget_queue_draw (widget);
1429
1430       return FALSE;
1431     }
1432
1433   return GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->motion_notify_event (widget, event);
1434 }
1435
1436 static gint
1437 gtk_spin_button_timer (GtkSpinButton *spin_button)
1438 {
1439   GtkSpinButtonPrivate *priv = spin_button->priv;
1440   gboolean retval = FALSE;
1441
1442   if (priv->timer)
1443     {
1444       if (priv->click_child == priv->up_panel)
1445         gtk_spin_button_real_spin (spin_button, priv->timer_step);
1446       else
1447         gtk_spin_button_real_spin (spin_button, -priv->timer_step);
1448
1449       if (priv->need_timer)
1450         {
1451           GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (spin_button));
1452           guint        timeout;
1453
1454           g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
1455
1456           priv->need_timer = FALSE;
1457           priv->timer = gdk_threads_add_timeout (timeout,
1458                                               (GSourceFunc) gtk_spin_button_timer,
1459                                               (gpointer) spin_button);
1460         }
1461       else
1462         {
1463           if (priv->climb_rate > 0.0 && priv->timer_step
1464               < gtk_adjustment_get_page_increment (priv->adjustment))
1465             {
1466               if (priv->timer_calls < MAX_TIMER_CALLS)
1467                 priv->timer_calls++;
1468               else
1469                 {
1470                   priv->timer_calls = 0;
1471                   priv->timer_step += priv->climb_rate;
1472                 }
1473             }
1474           retval = TRUE;
1475         }
1476     }
1477
1478   return retval;
1479 }
1480
1481 static void
1482 gtk_spin_button_value_changed (GtkAdjustment *adjustment,
1483                                GtkSpinButton *spin_button)
1484 {
1485   gboolean return_val;
1486
1487   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1488
1489   return_val = FALSE;
1490   g_signal_emit (spin_button, spinbutton_signals[OUTPUT], 0, &return_val);
1491   if (return_val == FALSE)
1492     gtk_spin_button_default_output (spin_button);
1493
1494   g_signal_emit (spin_button, spinbutton_signals[VALUE_CHANGED], 0);
1495
1496   gtk_widget_queue_draw (GTK_WIDGET (spin_button));
1497
1498   g_object_notify (G_OBJECT (spin_button), "value");
1499 }
1500
1501 static void
1502 gtk_spin_button_real_change_value (GtkSpinButton *spin,
1503                                    GtkScrollType  scroll)
1504 {
1505   GtkSpinButtonPrivate *priv = spin->priv;
1506   gdouble old_value;
1507
1508   if (!gtk_editable_get_editable (GTK_EDITABLE (spin)))
1509     {
1510       gtk_widget_error_bell (GTK_WIDGET (spin));
1511       return;
1512     }
1513
1514   /* When the key binding is activated, there may be an outstanding
1515    * value, so we first have to commit what is currently written in
1516    * the spin buttons text entry. See #106574
1517    */
1518   gtk_spin_button_update (spin);
1519
1520   old_value = gtk_adjustment_get_value (priv->adjustment);
1521
1522   switch (scroll)
1523     {
1524     case GTK_SCROLL_STEP_BACKWARD:
1525     case GTK_SCROLL_STEP_DOWN:
1526     case GTK_SCROLL_STEP_LEFT:
1527       gtk_spin_button_real_spin (spin, -priv->timer_step);
1528
1529       if (priv->climb_rate > 0.0 && priv->timer_step
1530           < gtk_adjustment_get_page_increment (priv->adjustment))
1531         {
1532           if (priv->timer_calls < MAX_TIMER_CALLS)
1533             priv->timer_calls++;
1534           else
1535             {
1536               priv->timer_calls = 0;
1537               priv->timer_step += priv->climb_rate;
1538             }
1539         }
1540       break;
1541
1542     case GTK_SCROLL_STEP_FORWARD:
1543     case GTK_SCROLL_STEP_UP:
1544     case GTK_SCROLL_STEP_RIGHT:
1545       gtk_spin_button_real_spin (spin, priv->timer_step);
1546
1547       if (priv->climb_rate > 0.0 && priv->timer_step
1548           < gtk_adjustment_get_page_increment (priv->adjustment))
1549         {
1550           if (priv->timer_calls < MAX_TIMER_CALLS)
1551             priv->timer_calls++;
1552           else
1553             {
1554               priv->timer_calls = 0;
1555               priv->timer_step += priv->climb_rate;
1556             }
1557         }
1558       break;
1559
1560     case GTK_SCROLL_PAGE_BACKWARD:
1561     case GTK_SCROLL_PAGE_DOWN:
1562     case GTK_SCROLL_PAGE_LEFT:
1563       gtk_spin_button_real_spin (spin, -gtk_adjustment_get_page_increment (priv->adjustment));
1564       break;
1565
1566     case GTK_SCROLL_PAGE_FORWARD:
1567     case GTK_SCROLL_PAGE_UP:
1568     case GTK_SCROLL_PAGE_RIGHT:
1569       gtk_spin_button_real_spin (spin, gtk_adjustment_get_page_increment (priv->adjustment));
1570       break;
1571
1572     case GTK_SCROLL_START:
1573       {
1574         gdouble diff = gtk_adjustment_get_value (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment);
1575         if (diff > EPSILON)
1576           gtk_spin_button_real_spin (spin, -diff);
1577         break;
1578       }
1579
1580     case GTK_SCROLL_END:
1581       {
1582         gdouble diff = gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_value (priv->adjustment);
1583         if (diff > EPSILON)
1584           gtk_spin_button_real_spin (spin, diff);
1585         break;
1586       }
1587
1588     default:
1589       g_warning ("Invalid scroll type %d for GtkSpinButton::change-value", scroll);
1590       break;
1591     }
1592
1593   gtk_spin_button_update (spin);
1594
1595   if (gtk_adjustment_get_value (priv->adjustment) == old_value)
1596     gtk_widget_error_bell (GTK_WIDGET (spin));
1597 }
1598
1599 static gint
1600 gtk_spin_button_key_release (GtkWidget   *widget,
1601                              GdkEventKey *event)
1602 {
1603   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1604   GtkSpinButtonPrivate *priv = spin->priv;
1605
1606   /* We only get a release at the end of a key repeat run, so reset the timer_step */
1607   priv->timer_step = gtk_adjustment_get_step_increment (priv->adjustment);
1608   priv->timer_calls = 0;
1609
1610   return TRUE;
1611 }
1612
1613 static void
1614 gtk_spin_button_snap (GtkSpinButton *spin_button,
1615                       gdouble        val)
1616 {
1617   GtkSpinButtonPrivate *priv = spin_button->priv;
1618   gdouble inc;
1619   gdouble tmp;
1620
1621   inc = gtk_adjustment_get_step_increment (priv->adjustment);
1622   if (inc == 0)
1623     return;
1624
1625   tmp = (val - gtk_adjustment_get_lower (priv->adjustment)) / inc;
1626   if (tmp - floor (tmp) < ceil (tmp) - tmp)
1627     val = gtk_adjustment_get_lower (priv->adjustment) + floor (tmp) * inc;
1628   else
1629     val = gtk_adjustment_get_lower (priv->adjustment) + ceil (tmp) * inc;
1630
1631   gtk_spin_button_set_value (spin_button, val);
1632 }
1633
1634 static void
1635 gtk_spin_button_activate (GtkEntry *entry)
1636 {
1637   if (gtk_editable_get_editable (GTK_EDITABLE (entry)))
1638     gtk_spin_button_update (GTK_SPIN_BUTTON (entry));
1639
1640   /* Chain up so that entry->activates_default is honored */
1641   GTK_ENTRY_CLASS (gtk_spin_button_parent_class)->activate (entry);
1642 }
1643
1644 static void
1645 gtk_spin_button_get_text_area_size (GtkEntry *entry,
1646                                     gint     *x,
1647                                     gint     *y,
1648                                     gint     *width,
1649                                     gint     *height)
1650 {
1651   GtkSpinButtonPrivate *priv = GTK_SPIN_BUTTON (entry)->priv;
1652   gint up_panel_width, down_panel_width;
1653
1654   up_panel_width = gtk_spin_button_panel_get_width (GTK_SPIN_BUTTON (entry), priv->up_panel);
1655   down_panel_width = gtk_spin_button_panel_get_width (GTK_SPIN_BUTTON (entry), priv->down_panel);
1656
1657   GTK_ENTRY_CLASS (gtk_spin_button_parent_class)->get_text_area_size (entry, x, y, width, height);
1658
1659   if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_RTL)
1660     {
1661       if (x)
1662         *x += up_panel_width + down_panel_width;
1663     }
1664
1665   if (width)
1666     *width -= up_panel_width + down_panel_width;
1667 }
1668
1669 static void
1670 gtk_spin_button_insert_text (GtkEditable *editable,
1671                              const gchar *new_text,
1672                              gint         new_text_length,
1673                              gint        *position)
1674 {
1675   GtkEntry *entry = GTK_ENTRY (editable);
1676   GtkSpinButton *spin = GTK_SPIN_BUTTON (editable);
1677   GtkSpinButtonPrivate *priv = spin->priv;
1678   GtkEditableInterface *parent_editable_iface;
1679
1680   parent_editable_iface = g_type_interface_peek (gtk_spin_button_parent_class,
1681                                                  GTK_TYPE_EDITABLE);
1682
1683   if (priv->numeric)
1684     {
1685       struct lconv *lc;
1686       gboolean sign;
1687       gint dotpos = -1;
1688       gint i;
1689       guint32 pos_sign;
1690       guint32 neg_sign;
1691       gint entry_length;
1692       const gchar *entry_text;
1693
1694       entry_length = gtk_entry_get_text_length (entry);
1695       entry_text = gtk_entry_get_text (entry);
1696
1697       lc = localeconv ();
1698
1699       if (*(lc->negative_sign))
1700         neg_sign = *(lc->negative_sign);
1701       else
1702         neg_sign = '-';
1703
1704       if (*(lc->positive_sign))
1705         pos_sign = *(lc->positive_sign);
1706       else
1707         pos_sign = '+';
1708
1709 #ifdef G_OS_WIN32
1710       /* Workaround for bug caused by some Windows application messing
1711        * up the positive sign of the current locale, more specifically
1712        * HKEY_CURRENT_USER\Control Panel\International\sPositiveSign.
1713        * See bug #330743 and for instance
1714        * http://www.msnewsgroups.net/group/microsoft.public.dotnet.languages.csharp/topic36024.aspx
1715        *
1716        * I don't know if the positive sign always gets bogusly set to
1717        * a digit when the above Registry value is corrupted as
1718        * described. (In my test case, it got set to "8", and in the
1719        * bug report above it presumably was set ot "0".) Probably it
1720        * might get set to almost anything? So how to distinguish a
1721        * bogus value from some correct one for some locale? That is
1722        * probably hard, but at least we should filter out the
1723        * digits...
1724        */
1725       if (pos_sign >= '0' && pos_sign <= '9')
1726         pos_sign = '+';
1727 #endif
1728
1729       for (sign=0, i=0; i<entry_length; i++)
1730         if ((entry_text[i] == neg_sign) ||
1731             (entry_text[i] == pos_sign))
1732           {
1733             sign = 1;
1734             break;
1735           }
1736
1737       if (sign && !(*position))
1738         return;
1739
1740       for (dotpos=-1, i=0; i<entry_length; i++)
1741         if (entry_text[i] == *(lc->decimal_point))
1742           {
1743             dotpos = i;
1744             break;
1745           }
1746
1747       if (dotpos > -1 && *position > dotpos &&
1748           (gint)priv->digits - entry_length
1749             + dotpos - new_text_length + 1 < 0)
1750         return;
1751
1752       for (i = 0; i < new_text_length; i++)
1753         {
1754           if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1755             {
1756               if (sign || (*position) || i)
1757                 return;
1758               sign = TRUE;
1759             }
1760           else if (new_text[i] == *(lc->decimal_point))
1761             {
1762               if (!priv->digits || dotpos > -1 ||
1763                   (new_text_length - 1 - i + entry_length
1764                     - *position > (gint)priv->digits))
1765                 return;
1766               dotpos = *position + i;
1767             }
1768           else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1769             return;
1770         }
1771     }
1772
1773   parent_editable_iface->insert_text (editable, new_text,
1774                                       new_text_length, position);
1775 }
1776
1777 static void
1778 gtk_spin_button_real_spin (GtkSpinButton *spin_button,
1779                            gdouble        increment)
1780 {
1781   GtkSpinButtonPrivate *priv = spin_button->priv;
1782   GtkAdjustment *adjustment;
1783   gdouble new_value = 0.0;
1784   gboolean wrapped = FALSE;
1785
1786   adjustment = priv->adjustment;
1787
1788   new_value = gtk_adjustment_get_value (adjustment) + increment;
1789
1790   if (increment > 0)
1791     {
1792       if (priv->wrap)
1793         {
1794           if (fabs (gtk_adjustment_get_value (adjustment) - gtk_adjustment_get_upper (adjustment)) < EPSILON)
1795             {
1796               new_value = gtk_adjustment_get_lower (adjustment);
1797               wrapped = TRUE;
1798             }
1799           else if (new_value > gtk_adjustment_get_upper (adjustment))
1800             new_value = gtk_adjustment_get_upper (adjustment);
1801         }
1802       else
1803         new_value = MIN (new_value, gtk_adjustment_get_upper (adjustment));
1804     }
1805   else if (increment < 0)
1806     {
1807       if (priv->wrap)
1808         {
1809           if (fabs (gtk_adjustment_get_value (adjustment) - gtk_adjustment_get_lower (adjustment)) < EPSILON)
1810             {
1811               new_value = gtk_adjustment_get_upper (adjustment);
1812               wrapped = TRUE;
1813             }
1814           else if (new_value < gtk_adjustment_get_lower (adjustment))
1815             new_value = gtk_adjustment_get_lower (adjustment);
1816         }
1817       else
1818         new_value = MAX (new_value, gtk_adjustment_get_lower (adjustment));
1819     }
1820
1821   if (fabs (new_value - gtk_adjustment_get_value (adjustment)) > EPSILON)
1822     gtk_adjustment_set_value (adjustment, new_value);
1823
1824   if (wrapped)
1825     g_signal_emit (spin_button, spinbutton_signals[WRAPPED], 0);
1826
1827   gtk_widget_queue_draw (GTK_WIDGET (spin_button));
1828 }
1829
1830 static gint
1831 gtk_spin_button_default_input (GtkSpinButton *spin_button,
1832                                gdouble       *new_val)
1833 {
1834   gchar *err = NULL;
1835
1836   *new_val = g_strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &err);
1837   if (*err)
1838     return GTK_INPUT_ERROR;
1839   else
1840     return FALSE;
1841 }
1842
1843 static gint
1844 gtk_spin_button_default_output (GtkSpinButton *spin_button)
1845 {
1846   GtkSpinButtonPrivate *priv = spin_button->priv;
1847
1848   gchar *buf = g_strdup_printf ("%0.*f", priv->digits, gtk_adjustment_get_value (priv->adjustment));
1849
1850   if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1851     gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1852   g_free (buf);
1853   return FALSE;
1854 }
1855
1856
1857 /***********************************************************
1858  ***********************************************************
1859  ***                  Public interface                   ***
1860  ***********************************************************
1861  ***********************************************************/
1862
1863
1864 /**
1865  * gtk_spin_button_configure:
1866  * @spin_button: a #GtkSpinButton
1867  * @adjustment: (allow-none):  a #GtkAdjustment
1868  * @climb_rate: the new climb rate
1869  * @digits: the number of decimal places to display in the spin button
1870  *
1871  * Changes the properties of an existing spin button. The adjustment,
1872  * climb rate, and number of decimal places are all changed accordingly,
1873  * after this function call.
1874  */
1875 void
1876 gtk_spin_button_configure (GtkSpinButton *spin_button,
1877                            GtkAdjustment *adjustment,
1878                            gdouble        climb_rate,
1879                            guint          digits)
1880 {
1881   GtkSpinButtonPrivate *priv;
1882
1883   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1884
1885   priv = spin_button->priv;
1886
1887   if (adjustment)
1888     gtk_spin_button_set_adjustment (spin_button, adjustment);
1889   else
1890     adjustment = priv->adjustment;
1891
1892   g_object_freeze_notify (G_OBJECT (spin_button));
1893   if (priv->digits != digits)
1894     {
1895       priv->digits = digits;
1896       g_object_notify (G_OBJECT (spin_button), "digits");
1897     }
1898
1899   if (priv->climb_rate != climb_rate)
1900     {
1901       priv->climb_rate = climb_rate;
1902       g_object_notify (G_OBJECT (spin_button), "climb-rate");
1903     }
1904   g_object_thaw_notify (G_OBJECT (spin_button));
1905
1906   gtk_adjustment_value_changed (adjustment);
1907 }
1908
1909 /**
1910  * gtk_spin_button_new:
1911  * @adjustment: (allow-none): the #GtkAdjustment object that this spin
1912  *     button should use, or %NULL
1913  * @climb_rate: specifies how much the spin button changes when an arrow
1914  *     is clicked on
1915  * @digits: the number of decimal places to display
1916  *
1917  * Creates a new #GtkSpinButton.
1918  *
1919  * Returns: The new spin button as a #GtkWidget
1920  */
1921 GtkWidget *
1922 gtk_spin_button_new (GtkAdjustment *adjustment,
1923                      gdouble        climb_rate,
1924                      guint          digits)
1925 {
1926   GtkSpinButton *spin;
1927
1928   if (adjustment)
1929     g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
1930
1931   spin = g_object_new (GTK_TYPE_SPIN_BUTTON, NULL);
1932
1933   gtk_spin_button_configure (spin, adjustment, climb_rate, digits);
1934
1935   return GTK_WIDGET (spin);
1936 }
1937
1938 /**
1939  * gtk_spin_button_new_with_range:
1940  * @min: Minimum allowable value
1941  * @max: Maximum allowable value
1942  * @step: Increment added or subtracted by spinning the widget
1943  *
1944  * This is a convenience constructor that allows creation of a numeric
1945  * #GtkSpinButton without manually creating an adjustment. The value is
1946  * initially set to the minimum value and a page increment of 10 * @step
1947  * is the default. The precision of the spin button is equivalent to the
1948  * precision of @step.
1949  *
1950  * Note that the way in which the precision is derived works best if @step
1951  * is a power of ten. If the resulting precision is not suitable for your
1952  * needs, use gtk_spin_button_set_digits() to correct it.
1953  *
1954  * Return value: The new spin button as a #GtkWidget
1955  */
1956 GtkWidget *
1957 gtk_spin_button_new_with_range (gdouble min,
1958                                 gdouble max,
1959                                 gdouble step)
1960 {
1961   GtkAdjustment *adjustment;
1962   GtkSpinButton *spin;
1963   gint digits;
1964
1965   g_return_val_if_fail (min <= max, NULL);
1966   g_return_val_if_fail (step != 0.0, NULL);
1967
1968   spin = g_object_new (GTK_TYPE_SPIN_BUTTON, NULL);
1969
1970   adjustment = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
1971
1972   if (fabs (step) >= 1.0 || step == 0.0)
1973     digits = 0;
1974   else {
1975     digits = abs ((gint) floor (log10 (fabs (step))));
1976     if (digits > MAX_DIGITS)
1977       digits = MAX_DIGITS;
1978   }
1979
1980   gtk_spin_button_configure (spin, adjustment, step, digits);
1981
1982   gtk_spin_button_set_numeric (spin, TRUE);
1983
1984   return GTK_WIDGET (spin);
1985 }
1986
1987 /* Callback used when the spin button's adjustment changes.
1988  * We need to redraw the arrows when the adjustment's range
1989  * changes, and reevaluate our size request.
1990  */
1991 static void
1992 adjustment_changed_cb (GtkAdjustment *adjustment, gpointer data)
1993 {
1994   GtkSpinButton *spin_button = GTK_SPIN_BUTTON (data);
1995   GtkSpinButtonPrivate *priv = spin_button->priv;
1996
1997   priv->timer_step = gtk_adjustment_get_step_increment (priv->adjustment);
1998   gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1999 }
2000
2001 /**
2002  * gtk_spin_button_set_adjustment:
2003  * @spin_button: a #GtkSpinButton
2004  * @adjustment: a #GtkAdjustment to replace the existing adjustment
2005  *
2006  * Replaces the #GtkAdjustment associated with @spin_button.
2007  */
2008 void
2009 gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
2010                                 GtkAdjustment *adjustment)
2011 {
2012   GtkSpinButtonPrivate *priv;
2013
2014   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2015
2016   priv = spin_button->priv;
2017
2018   if (priv->adjustment != adjustment)
2019     {
2020       if (priv->adjustment)
2021         {
2022           g_signal_handlers_disconnect_by_func (priv->adjustment,
2023                                                 gtk_spin_button_value_changed,
2024                                                 spin_button);
2025           g_signal_handlers_disconnect_by_func (priv->adjustment,
2026                                                 adjustment_changed_cb,
2027                                                 spin_button);
2028           g_object_unref (priv->adjustment);
2029         }
2030       priv->adjustment = adjustment;
2031       if (adjustment)
2032         {
2033           g_object_ref_sink (adjustment);
2034           g_signal_connect (adjustment, "value-changed",
2035                             G_CALLBACK (gtk_spin_button_value_changed),
2036                             spin_button);
2037           g_signal_connect (adjustment, "changed",
2038                             G_CALLBACK (adjustment_changed_cb),
2039                             spin_button);
2040           priv->timer_step = gtk_adjustment_get_step_increment (priv->adjustment);
2041         }
2042
2043       gtk_widget_queue_resize (GTK_WIDGET (spin_button));
2044     }
2045
2046   g_object_notify (G_OBJECT (spin_button), "adjustment");
2047 }
2048
2049 /**
2050  * gtk_spin_button_get_adjustment:
2051  * @spin_button: a #GtkSpinButton
2052  *
2053  * Get the adjustment associated with a #GtkSpinButton
2054  *
2055  * Return value: (transfer none): the #GtkAdjustment of @spin_button
2056  **/
2057 GtkAdjustment *
2058 gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
2059 {
2060   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
2061
2062   return spin_button->priv->adjustment;
2063 }
2064
2065 /**
2066  * gtk_spin_button_set_digits:
2067  * @spin_button: a #GtkSpinButton
2068  * @digits: the number of digits after the decimal point to be displayed for the spin button's value
2069  *
2070  * Set the precision to be displayed by @spin_button. Up to 20 digit precision
2071  * is allowed.
2072  **/
2073 void
2074 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
2075                             guint          digits)
2076 {
2077   GtkSpinButtonPrivate *priv;
2078
2079   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2080
2081   priv = spin_button->priv;
2082
2083   if (priv->digits != digits)
2084     {
2085       priv->digits = digits;
2086       gtk_spin_button_value_changed (priv->adjustment, spin_button);
2087       g_object_notify (G_OBJECT (spin_button), "digits");
2088
2089       /* since lower/upper may have changed */
2090       gtk_widget_queue_resize (GTK_WIDGET (spin_button));
2091     }
2092 }
2093
2094 /**
2095  * gtk_spin_button_get_digits:
2096  * @spin_button: a #GtkSpinButton
2097  *
2098  * Fetches the precision of @spin_button. See gtk_spin_button_set_digits().
2099  *
2100  * Returns: the current precision
2101  **/
2102 guint
2103 gtk_spin_button_get_digits (GtkSpinButton *spin_button)
2104 {
2105   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
2106
2107   return spin_button->priv->digits;
2108 }
2109
2110 /**
2111  * gtk_spin_button_set_increments:
2112  * @spin_button: a #GtkSpinButton
2113  * @step: increment applied for a button 1 press.
2114  * @page: increment applied for a button 2 press.
2115  *
2116  * Sets the step and page increments for spin_button.  This affects how
2117  * quickly the value changes when the spin button's arrows are activated.
2118  **/
2119 void
2120 gtk_spin_button_set_increments (GtkSpinButton *spin_button,
2121                                 gdouble        step,
2122                                 gdouble        page)
2123 {
2124   GtkSpinButtonPrivate *priv;
2125
2126   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2127
2128   priv = spin_button->priv;
2129
2130   gtk_adjustment_configure (priv->adjustment,
2131                             gtk_adjustment_get_value (priv->adjustment),
2132                             gtk_adjustment_get_lower (priv->adjustment),
2133                             gtk_adjustment_get_upper (priv->adjustment),
2134                             step,
2135                             page,
2136                             gtk_adjustment_get_page_size (priv->adjustment));
2137 }
2138
2139 /**
2140  * gtk_spin_button_get_increments:
2141  * @spin_button: a #GtkSpinButton
2142  * @step: (out) (allow-none): location to store step increment, or %NULL
2143  * @page: (out) (allow-none): location to store page increment, or %NULL
2144  *
2145  * Gets the current step and page the increments used by @spin_button. See
2146  * gtk_spin_button_set_increments().
2147  **/
2148 void
2149 gtk_spin_button_get_increments (GtkSpinButton *spin_button,
2150                                 gdouble       *step,
2151                                 gdouble       *page)
2152 {
2153   GtkSpinButtonPrivate *priv;
2154
2155   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2156
2157   priv = spin_button->priv;
2158
2159   if (step)
2160     *step = gtk_adjustment_get_step_increment (priv->adjustment);
2161   if (page)
2162     *page = gtk_adjustment_get_page_increment (priv->adjustment);
2163 }
2164
2165 /**
2166  * gtk_spin_button_set_range:
2167  * @spin_button: a #GtkSpinButton
2168  * @min: minimum allowable value
2169  * @max: maximum allowable value
2170  *
2171  * Sets the minimum and maximum allowable values for @spin_button.
2172  *
2173  * If the current value is outside this range, it will be adjusted
2174  * to fit within the range, otherwise it will remain unchanged.
2175  */
2176 void
2177 gtk_spin_button_set_range (GtkSpinButton *spin_button,
2178                            gdouble        min,
2179                            gdouble        max)
2180 {
2181   GtkAdjustment *adjustment;
2182
2183   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2184
2185   adjustment = spin_button->priv->adjustment;
2186
2187   gtk_adjustment_configure (adjustment,
2188                             CLAMP (gtk_adjustment_get_value (adjustment), min, max),
2189                             min,
2190                             max,
2191                             gtk_adjustment_get_step_increment (adjustment),
2192                             gtk_adjustment_get_page_increment (adjustment),
2193                             gtk_adjustment_get_page_size (adjustment));
2194 }
2195
2196 /**
2197  * gtk_spin_button_get_range:
2198  * @spin_button: a #GtkSpinButton
2199  * @min: (out) (allow-none): location to store minimum allowed value, or %NULL
2200  * @max: (out) (allow-none): location to store maximum allowed value, or %NULL
2201  *
2202  * Gets the range allowed for @spin_button.
2203  * See gtk_spin_button_set_range().
2204  */
2205 void
2206 gtk_spin_button_get_range (GtkSpinButton *spin_button,
2207                            gdouble       *min,
2208                            gdouble       *max)
2209 {
2210   GtkSpinButtonPrivate *priv;
2211
2212   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2213
2214   priv = spin_button->priv;
2215
2216   if (min)
2217     *min = gtk_adjustment_get_lower (priv->adjustment);
2218   if (max)
2219     *max = gtk_adjustment_get_upper (priv->adjustment);
2220 }
2221
2222 /**
2223  * gtk_spin_button_get_value:
2224  * @spin_button: a #GtkSpinButton
2225  *
2226  * Get the value in the @spin_button.
2227  *
2228  * Return value: the value of @spin_button
2229  */
2230 gdouble
2231 gtk_spin_button_get_value (GtkSpinButton *spin_button)
2232 {
2233   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0.0);
2234
2235   return gtk_adjustment_get_value (spin_button->priv->adjustment);
2236 }
2237
2238 /**
2239  * gtk_spin_button_get_value_as_int:
2240  * @spin_button: a #GtkSpinButton
2241  *
2242  * Get the value @spin_button represented as an integer.
2243  *
2244  * Return value: the value of @spin_button
2245  */
2246 gint
2247 gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
2248 {
2249   GtkSpinButtonPrivate *priv;
2250   gdouble val;
2251
2252   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
2253
2254   priv = spin_button->priv;
2255
2256   val = gtk_adjustment_get_value (priv->adjustment);
2257   if (val - floor (val) < ceil (val) - val)
2258     return floor (val);
2259   else
2260     return ceil (val);
2261 }
2262
2263 /**
2264  * gtk_spin_button_set_value:
2265  * @spin_button: a #GtkSpinButton
2266  * @value: the new value
2267  *
2268  * Sets the value of @spin_button.
2269  */
2270 void
2271 gtk_spin_button_set_value (GtkSpinButton *spin_button,
2272                            gdouble        value)
2273 {
2274   GtkSpinButtonPrivate *priv;
2275
2276   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2277
2278   priv = spin_button->priv;
2279
2280   if (fabs (value - gtk_adjustment_get_value (priv->adjustment)) > EPSILON)
2281     gtk_adjustment_set_value (priv->adjustment, value);
2282   else
2283     {
2284       gint return_val = FALSE;
2285       g_signal_emit (spin_button, spinbutton_signals[OUTPUT], 0, &return_val);
2286       if (return_val == FALSE)
2287         gtk_spin_button_default_output (spin_button);
2288     }
2289 }
2290
2291 /**
2292  * gtk_spin_button_set_update_policy:
2293  * @spin_button: a #GtkSpinButton
2294  * @policy: a #GtkSpinButtonUpdatePolicy value
2295  *
2296  * Sets the update behavior of a spin button.
2297  * This determines wether the spin button is always updated
2298  * or only when a valid value is set.
2299  */
2300 void
2301 gtk_spin_button_set_update_policy (GtkSpinButton             *spin_button,
2302                                    GtkSpinButtonUpdatePolicy  policy)
2303 {
2304   GtkSpinButtonPrivate *priv;
2305
2306   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2307
2308   priv = spin_button->priv;
2309
2310   if (priv->update_policy != policy)
2311     {
2312       priv->update_policy = policy;
2313       g_object_notify (G_OBJECT (spin_button), "update-policy");
2314     }
2315 }
2316
2317 /**
2318  * gtk_spin_button_get_update_policy:
2319  * @spin_button: a #GtkSpinButton
2320  *
2321  * Gets the update behavior of a spin button.
2322  * See gtk_spin_button_set_update_policy().
2323  *
2324  * Return value: the current update policy
2325  */
2326 GtkSpinButtonUpdatePolicy
2327 gtk_spin_button_get_update_policy (GtkSpinButton *spin_button)
2328 {
2329   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), GTK_UPDATE_ALWAYS);
2330
2331   return spin_button->priv->update_policy;
2332 }
2333
2334 /**
2335  * gtk_spin_button_set_numeric:
2336  * @spin_button: a #GtkSpinButton
2337  * @numeric: flag indicating if only numeric entry is allowed
2338  *
2339  * Sets the flag that determines if non-numeric text can be typed
2340  * into the spin button.
2341  */
2342 void
2343 gtk_spin_button_set_numeric (GtkSpinButton *spin_button,
2344                              gboolean       numeric)
2345 {
2346   GtkSpinButtonPrivate *priv;
2347
2348   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2349
2350   priv = spin_button->priv;
2351
2352   numeric = numeric != FALSE;
2353
2354   if (priv->numeric != numeric)
2355     {
2356        priv->numeric = numeric;
2357        g_object_notify (G_OBJECT (spin_button), "numeric");
2358     }
2359 }
2360
2361 /**
2362  * gtk_spin_button_get_numeric:
2363  * @spin_button: a #GtkSpinButton
2364  *
2365  * Returns whether non-numeric text can be typed into the spin button.
2366  * See gtk_spin_button_set_numeric().
2367  *
2368  * Return value: %TRUE if only numeric text can be entered
2369  */
2370 gboolean
2371 gtk_spin_button_get_numeric (GtkSpinButton *spin_button)
2372 {
2373   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
2374
2375   return spin_button->priv->numeric;
2376 }
2377
2378 /**
2379  * gtk_spin_button_set_wrap:
2380  * @spin_button: a #GtkSpinButton
2381  * @wrap: a flag indicating if wrapping behavior is performed
2382  *
2383  * Sets the flag that determines if a spin button value wraps
2384  * around to the opposite limit when the upper or lower limit
2385  * of the range is exceeded.
2386  */
2387 void
2388 gtk_spin_button_set_wrap (GtkSpinButton  *spin_button,
2389                           gboolean        wrap)
2390 {
2391   GtkSpinButtonPrivate *priv;
2392
2393   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2394
2395   priv = spin_button->priv;
2396
2397   wrap = wrap != FALSE;
2398
2399   if (priv->wrap != wrap)
2400     {
2401        priv->wrap = wrap;
2402
2403        g_object_notify (G_OBJECT (spin_button), "wrap");
2404     }
2405 }
2406
2407 /**
2408  * gtk_spin_button_get_wrap:
2409  * @spin_button: a #GtkSpinButton
2410  *
2411  * Returns whether the spin button's value wraps around to the
2412  * opposite limit when the upper or lower limit of the range is
2413  * exceeded. See gtk_spin_button_set_wrap().
2414  *
2415  * Return value: %TRUE if the spin button wraps around
2416  */
2417 gboolean
2418 gtk_spin_button_get_wrap (GtkSpinButton *spin_button)
2419 {
2420   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
2421
2422   return spin_button->priv->wrap;
2423 }
2424
2425 /**
2426  * gtk_spin_button_set_snap_to_ticks:
2427  * @spin_button: a #GtkSpinButton
2428  * @snap_to_ticks: a flag indicating if invalid values should be corrected
2429  *
2430  * Sets the policy as to whether values are corrected to the
2431  * nearest step increment when a spin button is activated after
2432  * providing an invalid value.
2433  */
2434 void
2435 gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
2436                                    gboolean       snap_to_ticks)
2437 {
2438   GtkSpinButtonPrivate *priv;
2439   guint new_val;
2440
2441   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2442
2443   priv = spin_button->priv;
2444
2445   new_val = (snap_to_ticks != 0);
2446
2447   if (new_val != priv->snap_to_ticks)
2448     {
2449       priv->snap_to_ticks = new_val;
2450       if (new_val && gtk_editable_get_editable (GTK_EDITABLE (spin_button)))
2451         gtk_spin_button_update (spin_button);
2452
2453       g_object_notify (G_OBJECT (spin_button), "snap-to-ticks");
2454     }
2455 }
2456
2457 /**
2458  * gtk_spin_button_get_snap_to_ticks:
2459  * @spin_button: a #GtkSpinButton
2460  *
2461  * Returns whether the values are corrected to the nearest step.
2462  * See gtk_spin_button_set_snap_to_ticks().
2463  *
2464  * Return value: %TRUE if values are snapped to the nearest step
2465  */
2466 gboolean
2467 gtk_spin_button_get_snap_to_ticks (GtkSpinButton *spin_button)
2468 {
2469   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
2470
2471   return spin_button->priv->snap_to_ticks;
2472 }
2473
2474 /**
2475  * gtk_spin_button_spin:
2476  * @spin_button: a #GtkSpinButton
2477  * @direction: a #GtkSpinType indicating the direction to spin
2478  * @increment: step increment to apply in the specified direction
2479  *
2480  * Increment or decrement a spin button's value in a specified
2481  * direction by a specified amount.
2482  */
2483 void
2484 gtk_spin_button_spin (GtkSpinButton *spin_button,
2485                       GtkSpinType    direction,
2486                       gdouble        increment)
2487 {
2488   GtkSpinButtonPrivate *priv;
2489   GtkAdjustment *adjustment;
2490   gdouble diff;
2491
2492   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2493
2494   priv = spin_button->priv;
2495
2496   adjustment = priv->adjustment;
2497
2498   /* for compatibility with the 1.0.x version of this function */
2499   if (increment != 0 && increment != gtk_adjustment_get_step_increment (adjustment) &&
2500       (direction == GTK_SPIN_STEP_FORWARD ||
2501        direction == GTK_SPIN_STEP_BACKWARD))
2502     {
2503       if (direction == GTK_SPIN_STEP_BACKWARD && increment > 0)
2504         increment = -increment;
2505       direction = GTK_SPIN_USER_DEFINED;
2506     }
2507
2508   switch (direction)
2509     {
2510     case GTK_SPIN_STEP_FORWARD:
2511
2512       gtk_spin_button_real_spin (spin_button, gtk_adjustment_get_step_increment (adjustment));
2513       break;
2514
2515     case GTK_SPIN_STEP_BACKWARD:
2516
2517       gtk_spin_button_real_spin (spin_button, -gtk_adjustment_get_step_increment (adjustment));
2518       break;
2519
2520     case GTK_SPIN_PAGE_FORWARD:
2521
2522       gtk_spin_button_real_spin (spin_button, gtk_adjustment_get_page_increment (adjustment));
2523       break;
2524
2525     case GTK_SPIN_PAGE_BACKWARD:
2526
2527       gtk_spin_button_real_spin (spin_button, -gtk_adjustment_get_page_increment (adjustment));
2528       break;
2529
2530     case GTK_SPIN_HOME:
2531
2532       diff = gtk_adjustment_get_value (adjustment) - gtk_adjustment_get_lower (adjustment);
2533       if (diff > EPSILON)
2534         gtk_spin_button_real_spin (spin_button, -diff);
2535       break;
2536
2537     case GTK_SPIN_END:
2538
2539       diff = gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_value (adjustment);
2540       if (diff > EPSILON)
2541         gtk_spin_button_real_spin (spin_button, diff);
2542       break;
2543
2544     case GTK_SPIN_USER_DEFINED:
2545
2546       if (increment != 0)
2547         gtk_spin_button_real_spin (spin_button, increment);
2548       break;
2549
2550     default:
2551       break;
2552     }
2553 }
2554
2555 /**
2556  * gtk_spin_button_update:
2557  * @spin_button: a #GtkSpinButton
2558  *
2559  * Manually force an update of the spin button.
2560  */
2561 void
2562 gtk_spin_button_update (GtkSpinButton *spin_button)
2563 {
2564   GtkSpinButtonPrivate *priv;
2565   gdouble val;
2566   gint error = 0;
2567   gint return_val;
2568
2569   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2570
2571   priv = spin_button->priv;
2572
2573   return_val = FALSE;
2574   g_signal_emit (spin_button, spinbutton_signals[INPUT], 0, &val, &return_val);
2575   if (return_val == FALSE)
2576     {
2577       return_val = gtk_spin_button_default_input (spin_button, &val);
2578       error = (return_val == GTK_INPUT_ERROR);
2579     }
2580   else if (return_val == GTK_INPUT_ERROR)
2581     error = 1;
2582
2583   gtk_widget_queue_draw (GTK_WIDGET (spin_button));
2584
2585   if (priv->update_policy == GTK_UPDATE_ALWAYS)
2586     {
2587       if (val < gtk_adjustment_get_lower (priv->adjustment))
2588         val = gtk_adjustment_get_lower (priv->adjustment);
2589       else if (val > gtk_adjustment_get_upper (priv->adjustment))
2590         val = gtk_adjustment_get_upper (priv->adjustment);
2591     }
2592   else if ((priv->update_policy == GTK_UPDATE_IF_VALID) &&
2593            (error ||
2594             val < gtk_adjustment_get_lower (priv->adjustment) ||
2595             val > gtk_adjustment_get_upper (priv->adjustment)))
2596     {
2597       gtk_spin_button_value_changed (priv->adjustment, spin_button);
2598       return;
2599     }
2600
2601   if (priv->snap_to_ticks)
2602     gtk_spin_button_snap (spin_button, val);
2603   else
2604     gtk_spin_button_set_value (spin_button, val);
2605 }
2606
2607 void
2608 _gtk_spin_button_get_panels (GtkSpinButton *spin_button,
2609                              GdkWindow **down_panel,
2610                              GdkWindow **up_panel)
2611 {
2612   if (down_panel != NULL)
2613     *down_panel = spin_button->priv->down_panel;
2614
2615   if (up_panel != NULL)
2616     *up_panel = spin_button->priv->up_panel;
2617 }