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