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