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