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