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