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