]> Pileus Git - ~andy/gtk/blob - gtk/gtkspinbutton.c
Include "config.h" instead of <config.h> Command used: find -name
[~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;
1270
1271   /* When the key binding is activated, there may be an outstanding
1272    * value, so we first have to commit what is currently written in
1273    * the spin buttons text entry. See #106574
1274    */
1275   gtk_spin_button_update (spin);
1276
1277   old_value = spin->adjustment->value;
1278
1279   /* We don't test whether the entry is editable, since
1280    * this key binding conceptually corresponds to changing
1281    * the value with the buttons using the mouse, which
1282    * we allow for non-editable spin buttons.
1283    */
1284   switch (scroll)
1285     {
1286     case GTK_SCROLL_STEP_BACKWARD:
1287     case GTK_SCROLL_STEP_DOWN:
1288     case GTK_SCROLL_STEP_LEFT:
1289       gtk_spin_button_real_spin (spin, -spin->timer_step);
1290       
1291       if (spin->climb_rate > 0.0 && spin->timer_step
1292           < spin->adjustment->page_increment)
1293         {
1294           if (spin->timer_calls < MAX_TIMER_CALLS)
1295             spin->timer_calls++;
1296           else 
1297             {
1298               spin->timer_calls = 0;
1299               spin->timer_step += spin->climb_rate;
1300             }
1301         }
1302       break;
1303       
1304     case GTK_SCROLL_STEP_FORWARD:
1305     case GTK_SCROLL_STEP_UP:
1306     case GTK_SCROLL_STEP_RIGHT:
1307       gtk_spin_button_real_spin (spin, spin->timer_step);
1308       
1309       if (spin->climb_rate > 0.0 && spin->timer_step
1310           < spin->adjustment->page_increment)
1311         {
1312           if (spin->timer_calls < MAX_TIMER_CALLS)
1313             spin->timer_calls++;
1314           else 
1315             {
1316               spin->timer_calls = 0;
1317               spin->timer_step += spin->climb_rate;
1318             }
1319         }
1320       break;
1321       
1322     case GTK_SCROLL_PAGE_BACKWARD:
1323     case GTK_SCROLL_PAGE_DOWN:
1324     case GTK_SCROLL_PAGE_LEFT:
1325       gtk_spin_button_real_spin (spin, -spin->adjustment->page_increment);
1326       break;
1327       
1328     case GTK_SCROLL_PAGE_FORWARD:
1329     case GTK_SCROLL_PAGE_UP:
1330     case GTK_SCROLL_PAGE_RIGHT:
1331       gtk_spin_button_real_spin (spin, spin->adjustment->page_increment);
1332       break;
1333       
1334     case GTK_SCROLL_START:
1335       {
1336         gdouble diff = spin->adjustment->value - spin->adjustment->lower;
1337         if (diff > EPSILON)
1338           gtk_spin_button_real_spin (spin, -diff);
1339         break;
1340       }
1341       
1342     case GTK_SCROLL_END:
1343       {
1344         gdouble diff = spin->adjustment->upper - spin->adjustment->value;
1345         if (diff > EPSILON)
1346           gtk_spin_button_real_spin (spin, diff);
1347         break;
1348       }
1349       
1350     default:
1351       g_warning ("Invalid scroll type %d for GtkSpinButton::change-value", scroll);
1352       break;
1353     }
1354   
1355   gtk_spin_button_update (spin);
1356
1357   if (spin->adjustment->value == old_value)
1358     gtk_widget_error_bell (GTK_WIDGET (spin));
1359 }
1360
1361 static gint
1362 gtk_spin_button_key_release (GtkWidget   *widget,
1363                              GdkEventKey *event)
1364 {
1365   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1366
1367   /* We only get a release at the end of a key repeat run, so reset the timer_step */
1368   spin->timer_step = spin->adjustment->step_increment;
1369   spin->timer_calls = 0;
1370   
1371   return TRUE;
1372 }
1373
1374 static void
1375 gtk_spin_button_snap (GtkSpinButton *spin_button,
1376                       gdouble        val)
1377 {
1378   gdouble inc;
1379   gdouble tmp;
1380
1381   inc = spin_button->adjustment->step_increment;
1382   if (inc == 0)
1383     return;
1384   
1385   tmp = (val - spin_button->adjustment->lower) / inc;
1386   if (tmp - floor (tmp) < ceil (tmp) - tmp)
1387     val = spin_button->adjustment->lower + floor (tmp) * inc;
1388   else
1389     val = spin_button->adjustment->lower + ceil (tmp) * inc;
1390
1391   gtk_spin_button_set_value (spin_button, val);
1392 }
1393
1394 static void
1395 gtk_spin_button_activate (GtkEntry *entry)
1396 {
1397   if (entry->editable)
1398     gtk_spin_button_update (GTK_SPIN_BUTTON (entry));
1399
1400   /* Chain up so that entry->activates_default is honored */
1401   GTK_ENTRY_CLASS (gtk_spin_button_parent_class)->activate (entry);
1402 }
1403
1404 static void
1405 gtk_spin_button_get_text_area_size (GtkEntry *entry,
1406                                     gint     *x,
1407                                     gint     *y,
1408                                     gint     *width,
1409                                     gint     *height)
1410 {
1411   gint arrow_size;
1412   gint panel_width;
1413
1414   g_return_if_fail (GTK_IS_SPIN_BUTTON (entry));
1415
1416   GTK_ENTRY_CLASS (gtk_spin_button_parent_class)->get_text_area_size (entry, x, y, width, height);
1417
1418   arrow_size = spin_button_get_arrow_size (GTK_SPIN_BUTTON (entry));
1419   panel_width = arrow_size + 2 * GTK_WIDGET (entry)->style->xthickness;
1420
1421   if (width)
1422     *width -= panel_width;
1423
1424   if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_RTL && x)
1425     *x += panel_width;
1426 }
1427
1428 static void
1429 gtk_spin_button_insert_text (GtkEditable *editable,
1430                              const gchar *new_text,
1431                              gint         new_text_length,
1432                              gint        *position)
1433 {
1434   GtkEntry *entry = GTK_ENTRY (editable);
1435   GtkSpinButton *spin = GTK_SPIN_BUTTON (editable);
1436   GtkEditableClass *parent_editable_iface = g_type_interface_peek (gtk_spin_button_parent_class, GTK_TYPE_EDITABLE);
1437  
1438   if (spin->numeric)
1439     {
1440       struct lconv *lc;
1441       gboolean sign;
1442       gint dotpos = -1;
1443       gint i;
1444       GdkWChar pos_sign;
1445       GdkWChar neg_sign;
1446       gint entry_length;
1447
1448       entry_length = entry->text_length;
1449
1450       lc = localeconv ();
1451
1452       if (*(lc->negative_sign))
1453         neg_sign = *(lc->negative_sign);
1454       else 
1455         neg_sign = '-';
1456
1457       if (*(lc->positive_sign))
1458         pos_sign = *(lc->positive_sign);
1459       else 
1460         pos_sign = '+';
1461
1462 #ifdef G_OS_WIN32
1463       /* Workaround for bug caused by some Windows application messing
1464        * up the positive sign of the current locale, more specifically
1465        * HKEY_CURRENT_USER\Control Panel\International\sPositiveSign.
1466        * See bug #330743 and for instance
1467        * http://www.msnewsgroups.net/group/microsoft.public.dotnet.languages.csharp/topic36024.aspx
1468        *
1469        * I don't know if the positive sign always gets bogusly set to
1470        * a digit when the above Registry value is corrupted as
1471        * described. (In my test case, it got set to "8", and in the
1472        * bug report above it presumably was set ot "0".) Probably it
1473        * might get set to almost anything? So how to distinguish a
1474        * bogus value from some correct one for some locale? That is
1475        * probably hard, but at least we should filter out the
1476        * digits...
1477        */
1478       if (pos_sign >= '0' && pos_sign <= '9')
1479         pos_sign = '+';
1480 #endif
1481
1482       for (sign=0, i=0; i<entry_length; i++)
1483         if ((entry->text[i] == neg_sign) ||
1484             (entry->text[i] == pos_sign))
1485           {
1486             sign = 1;
1487             break;
1488           }
1489
1490       if (sign && !(*position))
1491         return;
1492
1493       for (dotpos=-1, i=0; i<entry_length; i++)
1494         if (entry->text[i] == *(lc->decimal_point))
1495           {
1496             dotpos = i;
1497             break;
1498           }
1499
1500       if (dotpos > -1 && *position > dotpos &&
1501           (gint)spin->digits - entry_length
1502             + dotpos - new_text_length + 1 < 0)
1503         return;
1504
1505       for (i = 0; i < new_text_length; i++)
1506         {
1507           if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1508             {
1509               if (sign || (*position) || i)
1510                 return;
1511               sign = TRUE;
1512             }
1513           else if (new_text[i] == *(lc->decimal_point))
1514             {
1515               if (!spin->digits || dotpos > -1 || 
1516                   (new_text_length - 1 - i + entry_length
1517                     - *position > (gint)spin->digits)) 
1518                 return;
1519               dotpos = *position + i;
1520             }
1521           else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1522             return;
1523         }
1524     }
1525
1526   parent_editable_iface->insert_text (editable, new_text,
1527                                       new_text_length, position);
1528 }
1529
1530 static void
1531 gtk_spin_button_real_spin (GtkSpinButton *spin_button,
1532                            gdouble        increment)
1533 {
1534   GtkAdjustment *adj;
1535   gdouble new_value = 0.0;
1536   gboolean wrapped = FALSE;
1537   
1538   adj = spin_button->adjustment;
1539
1540   new_value = adj->value + increment;
1541
1542   if (increment > 0)
1543     {
1544       if (spin_button->wrap)
1545         {
1546           if (fabs (adj->value - adj->upper) < EPSILON)
1547             {
1548               new_value = adj->lower;
1549               wrapped = TRUE;
1550             }
1551           else if (new_value > adj->upper)
1552             new_value = adj->upper;
1553         }
1554       else
1555         new_value = MIN (new_value, adj->upper);
1556     }
1557   else if (increment < 0) 
1558     {
1559       if (spin_button->wrap)
1560         {
1561           if (fabs (adj->value - adj->lower) < EPSILON)
1562             {
1563               new_value = adj->upper;
1564               wrapped = TRUE;
1565             }
1566           else if (new_value < adj->lower)
1567             new_value = adj->lower;
1568         }
1569       else
1570         new_value = MAX (new_value, adj->lower);
1571     }
1572
1573   if (fabs (new_value - adj->value) > EPSILON)
1574     gtk_adjustment_set_value (adj, new_value);
1575
1576   if (wrapped)
1577     g_signal_emit (spin_button, spinbutton_signals[WRAPPED], 0);
1578
1579   gtk_widget_queue_draw (GTK_WIDGET (spin_button));
1580 }
1581
1582 static gint
1583 gtk_spin_button_default_input (GtkSpinButton *spin_button,
1584                                gdouble       *new_val)
1585 {
1586   gchar *err = NULL;
1587
1588   *new_val = g_strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &err);
1589   if (*err)
1590     return GTK_INPUT_ERROR;
1591   else
1592     return FALSE;
1593 }
1594
1595 static gint
1596 gtk_spin_button_default_output (GtkSpinButton *spin_button)
1597 {
1598   gchar *buf = g_strdup_printf ("%0.*f", spin_button->digits, spin_button->adjustment->value);
1599
1600   if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1601     gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1602   g_free (buf);
1603   return FALSE;
1604 }
1605
1606
1607 /***********************************************************
1608  ***********************************************************
1609  ***                  Public interface                   ***
1610  ***********************************************************
1611  ***********************************************************/
1612
1613
1614 void
1615 gtk_spin_button_configure (GtkSpinButton  *spin_button,
1616                            GtkAdjustment  *adjustment,
1617                            gdouble         climb_rate,
1618                            guint           digits)
1619 {
1620   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1621
1622   if (adjustment)
1623     gtk_spin_button_set_adjustment (spin_button, adjustment);
1624   else
1625     adjustment = spin_button->adjustment;
1626
1627   g_object_freeze_notify (G_OBJECT (spin_button));
1628   if (spin_button->digits != digits) 
1629     {
1630       spin_button->digits = digits;
1631       g_object_notify (G_OBJECT (spin_button), "digits");
1632     }
1633
1634   if (spin_button->climb_rate != climb_rate)
1635     {
1636       spin_button->climb_rate = climb_rate;
1637       g_object_notify (G_OBJECT (spin_button), "climb-rate");
1638     }
1639   g_object_thaw_notify (G_OBJECT (spin_button));
1640
1641   gtk_adjustment_value_changed (adjustment);
1642 }
1643
1644 GtkWidget *
1645 gtk_spin_button_new (GtkAdjustment *adjustment,
1646                      gdouble        climb_rate,
1647                      guint          digits)
1648 {
1649   GtkSpinButton *spin;
1650
1651   if (adjustment)
1652     g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
1653
1654   spin = g_object_new (GTK_TYPE_SPIN_BUTTON, NULL);
1655
1656   gtk_spin_button_configure (spin, adjustment, climb_rate, digits);
1657
1658   return GTK_WIDGET (spin);
1659 }
1660
1661 /**
1662  * gtk_spin_button_new_with_range:
1663  * @min: Minimum allowable value
1664  * @max: Maximum allowable value
1665  * @step: Increment added or subtracted by spinning the widget
1666  * 
1667  * This is a convenience constructor that allows creation of a numeric 
1668  * #GtkSpinButton without manually creating an adjustment. The value is 
1669  * initially set to the minimum value and a page increment of 10 * @step
1670  * is the default. The precision of the spin button is equivalent to the 
1671  * precision of @step. 
1672  * 
1673  * Note that the way in which the precision is derived works best if @step 
1674  * is a power of ten. If the resulting precision is not suitable for your 
1675  * needs, use gtk_spin_button_set_digits() to correct it.
1676  * 
1677  * Return value: The new spin button as a #GtkWidget.
1678  **/
1679 GtkWidget *
1680 gtk_spin_button_new_with_range (gdouble min,
1681                                 gdouble max,
1682                                 gdouble step)
1683 {
1684   GtkObject *adj;
1685   GtkSpinButton *spin;
1686   gint digits;
1687
1688   g_return_val_if_fail (min <= max, NULL);
1689   g_return_val_if_fail (step != 0.0, NULL);
1690
1691   spin = g_object_new (GTK_TYPE_SPIN_BUTTON, NULL);
1692
1693   adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
1694
1695   if (fabs (step) >= 1.0 || step == 0.0)
1696     digits = 0;
1697   else {
1698     digits = abs ((gint) floor (log10 (fabs (step))));
1699     if (digits > MAX_DIGITS)
1700       digits = MAX_DIGITS;
1701   }
1702
1703   gtk_spin_button_configure (spin, GTK_ADJUSTMENT (adj), step, digits);
1704
1705   gtk_spin_button_set_numeric (spin, TRUE);
1706
1707   return GTK_WIDGET (spin);
1708 }
1709
1710 /* Callback used when the spin button's adjustment changes.  We need to redraw
1711  * the arrows when the adjustment's range changes, and reevaluate our size request.
1712  */
1713 static void
1714 adjustment_changed_cb (GtkAdjustment *adjustment, gpointer data)
1715 {
1716   GtkSpinButton *spin_button;
1717
1718   spin_button = GTK_SPIN_BUTTON (data);
1719
1720   spin_button->timer_step = spin_button->adjustment->step_increment;
1721   gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1722 }
1723
1724 /**
1725  * gtk_spin_button_set_adjustment:
1726  * @spin_button: a #GtkSpinButton
1727  * @adjustment: a #GtkAdjustment to replace the existing adjustment
1728  * 
1729  * Replaces the #GtkAdjustment associated with @spin_button.
1730  **/
1731 void
1732 gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
1733                                 GtkAdjustment *adjustment)
1734 {
1735   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1736
1737   if (spin_button->adjustment != adjustment)
1738     {
1739       if (spin_button->adjustment)
1740         {
1741           g_signal_handlers_disconnect_by_func (spin_button->adjustment,
1742                                                 gtk_spin_button_value_changed,
1743                                                 spin_button);
1744           g_signal_handlers_disconnect_by_func (spin_button->adjustment,
1745                                                 adjustment_changed_cb,
1746                                                 spin_button);
1747           g_object_unref (spin_button->adjustment);
1748         }
1749       spin_button->adjustment = adjustment;
1750       if (adjustment)
1751         {
1752           g_object_ref_sink (adjustment);
1753           g_signal_connect (adjustment, "value_changed",
1754                             G_CALLBACK (gtk_spin_button_value_changed),
1755                             spin_button);
1756           g_signal_connect (adjustment, "changed",
1757                             G_CALLBACK (adjustment_changed_cb),
1758                             spin_button);
1759           spin_button->timer_step = spin_button->adjustment->step_increment;
1760         }
1761
1762       gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1763     }
1764
1765   g_object_notify (G_OBJECT (spin_button), "adjustment");
1766 }
1767
1768 /**
1769  * gtk_spin_button_get_adjustment:
1770  * @spin_button: a #GtkSpinButton
1771  * 
1772  * Get the adjustment associated with a #GtkSpinButton
1773  * 
1774  * Return value: the #GtkAdjustment of @spin_button
1775  **/
1776 GtkAdjustment *
1777 gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
1778 {
1779   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
1780
1781   return spin_button->adjustment;
1782 }
1783
1784 /**
1785  * gtk_spin_button_set_digits:
1786  * @spin_button: a #GtkSpinButton
1787  * @digits: the number of digits after the decimal point to be displayed for the spin button's value
1788  * 
1789  * Set the precision to be displayed by @spin_button. Up to 20 digit precision
1790  * is allowed.
1791  **/
1792 void
1793 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
1794                             guint          digits)
1795 {
1796   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1797
1798   if (spin_button->digits != digits)
1799     {
1800       spin_button->digits = digits;
1801       gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1802       g_object_notify (G_OBJECT (spin_button), "digits");
1803       
1804       /* since lower/upper may have changed */
1805       gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1806     }
1807 }
1808
1809 /**
1810  * gtk_spin_button_get_digits:
1811  * @spin_button: a #GtkSpinButton
1812  *
1813  * Fetches the precision of @spin_button. See gtk_spin_button_set_digits().
1814  *
1815  * Returns: the current precision
1816  **/
1817 guint
1818 gtk_spin_button_get_digits (GtkSpinButton *spin_button)
1819 {
1820   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1821
1822   return spin_button->digits;
1823 }
1824
1825 /**
1826  * gtk_spin_button_set_increments:
1827  * @spin_button: a #GtkSpinButton
1828  * @step: increment applied for a button 1 press.
1829  * @page: increment applied for a button 2 press.
1830  * 
1831  * Sets the step and page increments for spin_button.  This affects how 
1832  * quickly the value changes when the spin button's arrows are activated.
1833  **/
1834 void
1835 gtk_spin_button_set_increments (GtkSpinButton *spin_button,
1836                                 gdouble        step,
1837                                 gdouble        page)
1838 {
1839   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1840
1841   spin_button->adjustment->step_increment = step;
1842   spin_button->adjustment->page_increment = page;
1843 }
1844
1845 /**
1846  * gtk_spin_button_get_increments:
1847  * @spin_button: a #GtkSpinButton
1848  * @step: location to store step increment, or %NULL
1849  * @page: location to store page increment, or %NULL
1850  *
1851  * Gets the current step and page the increments used by @spin_button. See
1852  * gtk_spin_button_set_increments().
1853  **/
1854 void
1855 gtk_spin_button_get_increments (GtkSpinButton *spin_button,
1856                                 gdouble       *step,
1857                                 gdouble       *page)
1858 {
1859   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1860
1861   if (step)
1862     *step = spin_button->adjustment->step_increment;
1863   if (page)
1864     *page = spin_button->adjustment->page_increment;
1865 }
1866
1867 /**
1868  * gtk_spin_button_set_range:
1869  * @spin_button: a #GtkSpinButton
1870  * @min: minimum allowable value
1871  * @max: maximum allowable value
1872  * 
1873  * Sets the minimum and maximum allowable values for @spin_button
1874  **/
1875 void
1876 gtk_spin_button_set_range (GtkSpinButton *spin_button,
1877                            gdouble        min,
1878                            gdouble        max)
1879 {
1880   gdouble value;
1881   
1882   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1883
1884   spin_button->adjustment->lower = min;
1885   spin_button->adjustment->upper = max;
1886
1887   value = CLAMP (spin_button->adjustment->value,
1888                  spin_button->adjustment->lower,
1889                  (spin_button->adjustment->upper - spin_button->adjustment->page_size));
1890
1891   if (value != spin_button->adjustment->value)
1892     gtk_spin_button_set_value (spin_button, value);
1893
1894   gtk_adjustment_changed (spin_button->adjustment);
1895 }
1896
1897 /**
1898  * gtk_spin_button_get_range:
1899  * @spin_button: a #GtkSpinButton
1900  * @min: location to store minimum allowed value, or %NULL
1901  * @max: location to store maximum allowed value, or %NULL
1902  *
1903  * Gets the range allowed for @spin_button. See
1904  * gtk_spin_button_set_range().
1905  **/
1906 void
1907 gtk_spin_button_get_range (GtkSpinButton *spin_button,
1908                            gdouble       *min,
1909                            gdouble       *max)
1910 {
1911   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1912
1913   if (min)
1914     *min = spin_button->adjustment->lower;
1915   if (max)
1916     *max = spin_button->adjustment->upper;
1917 }
1918
1919 /**
1920  * gtk_spin_button_get_value:
1921  * @spin_button: a #GtkSpinButton
1922  * 
1923  * Get the value in the @spin_button.
1924  * 
1925  * Return value: the value of @spin_button
1926  **/
1927 gdouble
1928 gtk_spin_button_get_value (GtkSpinButton *spin_button)
1929 {
1930   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0.0);
1931
1932   return spin_button->adjustment->value;
1933 }
1934
1935 /**
1936  * gtk_spin_button_get_value_as_int:
1937  * @spin_button: a #GtkSpinButton
1938  * 
1939  * Get the value @spin_button represented as an integer.
1940  * 
1941  * Return value: the value of @spin_button
1942  **/
1943 gint
1944 gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
1945 {
1946   gdouble val;
1947
1948   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1949
1950   val = spin_button->adjustment->value;
1951   if (val - floor (val) < ceil (val) - val)
1952     return floor (val);
1953   else
1954     return ceil (val);
1955 }
1956
1957 /**
1958  * gtk_spin_button_set_value:
1959  * @spin_button: a #GtkSpinButton
1960  * @value: the new value
1961  * 
1962  * Set the value of @spin_button.
1963  **/
1964 void 
1965 gtk_spin_button_set_value (GtkSpinButton *spin_button, 
1966                            gdouble        value)
1967 {
1968   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1969
1970   if (fabs (value - spin_button->adjustment->value) > EPSILON)
1971     gtk_adjustment_set_value (spin_button->adjustment, value);
1972   else
1973     {
1974       gint return_val = FALSE;
1975       g_signal_emit (spin_button, spinbutton_signals[OUTPUT], 0, &return_val);
1976       if (return_val == FALSE)
1977         gtk_spin_button_default_output (spin_button);
1978     }
1979 }
1980
1981 /**
1982  * gtk_spin_button_set_update_policy:
1983  * @spin_button: a #GtkSpinButton 
1984  * @policy: a #GtkSpinButtonUpdatePolicy value
1985  * 
1986  * Sets the update behavior of a spin button. This determines whether the
1987  * spin button is always updated or only when a valid value is set.
1988  **/
1989 void
1990 gtk_spin_button_set_update_policy (GtkSpinButton             *spin_button,
1991                                    GtkSpinButtonUpdatePolicy  policy)
1992 {
1993   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1994
1995   if (spin_button->update_policy != policy)
1996     {
1997       spin_button->update_policy = policy;
1998       g_object_notify (G_OBJECT (spin_button), "update-policy");
1999     }
2000 }
2001
2002 /**
2003  * gtk_spin_button_get_update_policy:
2004  * @spin_button: a #GtkSpinButton
2005  *
2006  * Gets the update behavior of a spin button. See
2007  * gtk_spin_button_set_update_policy().
2008  *
2009  * Return value: the current update policy
2010  **/
2011 GtkSpinButtonUpdatePolicy
2012 gtk_spin_button_get_update_policy (GtkSpinButton *spin_button)
2013 {
2014   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), GTK_UPDATE_ALWAYS);
2015
2016   return spin_button->update_policy;
2017 }
2018
2019 /**
2020  * gtk_spin_button_set_numeric:
2021  * @spin_button: a #GtkSpinButton 
2022  * @numeric: flag indicating if only numeric entry is allowed. 
2023  * 
2024  * Sets the flag that determines if non-numeric text can be typed into
2025  * the spin button.
2026  **/
2027 void
2028 gtk_spin_button_set_numeric (GtkSpinButton  *spin_button,
2029                              gboolean        numeric)
2030 {
2031   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2032
2033   numeric = numeric != FALSE;
2034
2035   if (spin_button->numeric != numeric)
2036     {
2037        spin_button->numeric = numeric;
2038        g_object_notify (G_OBJECT (spin_button), "numeric");
2039     }
2040 }
2041
2042 /**
2043  * gtk_spin_button_get_numeric:
2044  * @spin_button: a #GtkSpinButton
2045  *
2046  * Returns whether non-numeric text can be typed into the spin button.
2047  * See gtk_spin_button_set_numeric().
2048  *
2049  * Return value: %TRUE if only numeric text can be entered
2050  **/
2051 gboolean
2052 gtk_spin_button_get_numeric (GtkSpinButton *spin_button)
2053 {
2054   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
2055
2056   return spin_button->numeric;
2057 }
2058
2059 /**
2060  * gtk_spin_button_set_wrap:
2061  * @spin_button: a #GtkSpinButton 
2062  * @wrap: a flag indicating if wrapping behavior is performed.
2063  * 
2064  * Sets the flag that determines if a spin button value wraps around to the
2065  * opposite limit when the upper or lower limit of the range is exceeded.
2066  **/
2067 void
2068 gtk_spin_button_set_wrap (GtkSpinButton  *spin_button,
2069                           gboolean        wrap)
2070 {
2071   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2072
2073   wrap = wrap != FALSE; 
2074
2075   if (spin_button->wrap != wrap)
2076     {
2077        spin_button->wrap = (wrap != 0);
2078   
2079        g_object_notify (G_OBJECT (spin_button), "wrap");
2080     }
2081 }
2082
2083 /**
2084  * gtk_spin_button_get_wrap:
2085  * @spin_button: a #GtkSpinButton
2086  *
2087  * Returns whether the spin button's value wraps around to the
2088  * opposite limit when the upper or lower limit of the range is
2089  * exceeded. See gtk_spin_button_set_wrap().
2090  *
2091  * Return value: %TRUE if the spin button wraps around
2092  **/
2093 gboolean
2094 gtk_spin_button_get_wrap (GtkSpinButton *spin_button)
2095 {
2096   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
2097
2098   return spin_button->wrap;
2099 }
2100
2101 static gint
2102 spin_button_get_arrow_size (GtkSpinButton *spin_button)
2103 {
2104   gint size = pango_font_description_get_size (GTK_WIDGET (spin_button)->style->font_desc);
2105   gint arrow_size;
2106
2107   arrow_size = MAX (PANGO_PIXELS (size), MIN_ARROW_WIDTH);
2108
2109   return arrow_size - arrow_size % 2; /* force even */
2110 }
2111
2112 /**
2113  * spin_button_get_shadow_type:
2114  * @spin_button: a #GtkSpinButton 
2115  * 
2116  * Convenience function to Get the shadow type from the underlying widget's
2117  * style.
2118  * 
2119  * Return value: the #GtkShadowType
2120  **/
2121 static gint
2122 spin_button_get_shadow_type (GtkSpinButton *spin_button)
2123 {
2124   GtkShadowType rc_shadow_type;
2125
2126   gtk_widget_style_get (GTK_WIDGET (spin_button), "shadow-type", &rc_shadow_type, NULL);
2127
2128   return rc_shadow_type;
2129 }
2130
2131 /**
2132  * gtk_spin_button_set_snap_to_ticks:
2133  * @spin_button: a #GtkSpinButton 
2134  * @snap_to_ticks: a flag indicating if invalid values should be corrected.
2135  * 
2136  * Sets the policy as to whether values are corrected to the nearest step 
2137  * increment when a spin button is activated after providing an invalid value.
2138  **/
2139 void
2140 gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
2141                                    gboolean       snap_to_ticks)
2142 {
2143   guint new_val;
2144
2145   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2146
2147   new_val = (snap_to_ticks != 0);
2148
2149   if (new_val != spin_button->snap_to_ticks)
2150     {
2151       spin_button->snap_to_ticks = new_val;
2152       if (new_val && GTK_ENTRY (spin_button)->editable)
2153         gtk_spin_button_update (spin_button);
2154       
2155       g_object_notify (G_OBJECT (spin_button), "snap-to-ticks");
2156     }
2157 }
2158
2159 /**
2160  * gtk_spin_button_get_snap_to_ticks:
2161  * @spin_button: a #GtkSpinButton
2162  *
2163  * Returns whether the values are corrected to the nearest step. See
2164  * gtk_spin_button_set_snap_to_ticks().
2165  *
2166  * Return value: %TRUE if values are snapped to the nearest step.
2167  **/
2168 gboolean
2169 gtk_spin_button_get_snap_to_ticks (GtkSpinButton *spin_button)
2170 {
2171   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
2172
2173   return spin_button->snap_to_ticks;
2174 }
2175
2176 /**
2177  * gtk_spin_button_spin:
2178  * @spin_button: a #GtkSpinButton 
2179  * @direction: a #GtkSpinType indicating the direction to spin.
2180  * @increment: step increment to apply in the specified direction.
2181  * 
2182  * Increment or decrement a spin button's value in a specified direction
2183  * by a specified amount. 
2184  **/
2185 void
2186 gtk_spin_button_spin (GtkSpinButton *spin_button,
2187                       GtkSpinType    direction,
2188                       gdouble        increment)
2189 {
2190   GtkAdjustment *adj;
2191   gdouble diff;
2192
2193   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2194   
2195   adj = spin_button->adjustment;
2196
2197   /* for compatibility with the 1.0.x version of this function */
2198   if (increment != 0 && increment != adj->step_increment &&
2199       (direction == GTK_SPIN_STEP_FORWARD ||
2200        direction == GTK_SPIN_STEP_BACKWARD))
2201     {
2202       if (direction == GTK_SPIN_STEP_BACKWARD && increment > 0)
2203         increment = -increment;
2204       direction = GTK_SPIN_USER_DEFINED;
2205     }
2206
2207   switch (direction)
2208     {
2209     case GTK_SPIN_STEP_FORWARD:
2210
2211       gtk_spin_button_real_spin (spin_button, adj->step_increment);
2212       break;
2213
2214     case GTK_SPIN_STEP_BACKWARD:
2215
2216       gtk_spin_button_real_spin (spin_button, -adj->step_increment);
2217       break;
2218
2219     case GTK_SPIN_PAGE_FORWARD:
2220
2221       gtk_spin_button_real_spin (spin_button, adj->page_increment);
2222       break;
2223
2224     case GTK_SPIN_PAGE_BACKWARD:
2225
2226       gtk_spin_button_real_spin (spin_button, -adj->page_increment);
2227       break;
2228
2229     case GTK_SPIN_HOME:
2230
2231       diff = adj->value - adj->lower;
2232       if (diff > EPSILON)
2233         gtk_spin_button_real_spin (spin_button, -diff);
2234       break;
2235
2236     case GTK_SPIN_END:
2237
2238       diff = adj->upper - adj->value;
2239       if (diff > EPSILON)
2240         gtk_spin_button_real_spin (spin_button, diff);
2241       break;
2242
2243     case GTK_SPIN_USER_DEFINED:
2244
2245       if (increment != 0)
2246         gtk_spin_button_real_spin (spin_button, increment);
2247       break;
2248
2249     default:
2250       break;
2251     }
2252 }
2253
2254 /**
2255  * gtk_spin_button_update:
2256  * @spin_button: a #GtkSpinButton 
2257  * 
2258  * Manually force an update of the spin button.
2259  **/
2260 void 
2261 gtk_spin_button_update (GtkSpinButton *spin_button)
2262 {
2263   gdouble val;
2264   gint error = 0;
2265   gint return_val;
2266
2267   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2268
2269   return_val = FALSE;
2270   g_signal_emit (spin_button, spinbutton_signals[INPUT], 0, &val, &return_val);
2271   if (return_val == FALSE)
2272     {
2273       return_val = gtk_spin_button_default_input (spin_button, &val);
2274       error = (return_val == GTK_INPUT_ERROR);
2275     }
2276   else if (return_val == GTK_INPUT_ERROR)
2277     error = 1;
2278
2279   gtk_widget_queue_draw (GTK_WIDGET (spin_button));
2280
2281   if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
2282     {
2283       if (val < spin_button->adjustment->lower)
2284         val = spin_button->adjustment->lower;
2285       else if (val > spin_button->adjustment->upper)
2286         val = spin_button->adjustment->upper;
2287     }
2288   else if ((spin_button->update_policy == GTK_UPDATE_IF_VALID) && 
2289            (error ||
2290            val < spin_button->adjustment->lower ||
2291            val > spin_button->adjustment->upper))
2292     {
2293       gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
2294       return;
2295     }
2296
2297   if (spin_button->snap_to_ticks)
2298     gtk_spin_button_snap (spin_button, val);
2299   else
2300     gtk_spin_button_set_value (spin_button, val);
2301 }
2302
2303 #define __GTK_SPIN_BUTTON_C__
2304 #include "gtkaliasdef.c"