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