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