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