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