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