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