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