]> Pileus Git - ~andy/gtk/blob - gtk/gtkspinbutton.c
Updated Bulgarian translation by Alexander Shopov <ash@contact.bg>
[~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   /* We don't test whether the entry is editable, since
1247    * this key binding conceptually corresponds to changing
1248    * the value with the buttons using the mouse, which
1249    * we allow for non-editable spin buttons.
1250    */
1251   switch (scroll)
1252     {
1253     case GTK_SCROLL_STEP_BACKWARD:
1254     case GTK_SCROLL_STEP_DOWN:
1255     case GTK_SCROLL_STEP_LEFT:
1256       gtk_spin_button_real_spin (spin, -spin->timer_step);
1257       
1258       if (spin->climb_rate > 0.0 && spin->timer_step
1259           < spin->adjustment->page_increment)
1260         {
1261           if (spin->timer_calls < MAX_TIMER_CALLS)
1262             spin->timer_calls++;
1263           else 
1264             {
1265               spin->timer_calls = 0;
1266               spin->timer_step += spin->climb_rate;
1267             }
1268         }
1269       break;
1270       
1271     case GTK_SCROLL_STEP_FORWARD:
1272     case GTK_SCROLL_STEP_UP:
1273     case GTK_SCROLL_STEP_RIGHT:
1274       gtk_spin_button_real_spin (spin, spin->timer_step);
1275       
1276       if (spin->climb_rate > 0.0 && spin->timer_step
1277           < spin->adjustment->page_increment)
1278         {
1279           if (spin->timer_calls < MAX_TIMER_CALLS)
1280             spin->timer_calls++;
1281           else 
1282             {
1283               spin->timer_calls = 0;
1284               spin->timer_step += spin->climb_rate;
1285             }
1286         }
1287       break;
1288       
1289     case GTK_SCROLL_PAGE_BACKWARD:
1290     case GTK_SCROLL_PAGE_DOWN:
1291     case GTK_SCROLL_PAGE_LEFT:
1292       gtk_spin_button_real_spin (spin, -spin->adjustment->page_increment);
1293       break;
1294       
1295     case GTK_SCROLL_PAGE_FORWARD:
1296     case GTK_SCROLL_PAGE_UP:
1297     case GTK_SCROLL_PAGE_RIGHT:
1298       gtk_spin_button_real_spin (spin, spin->adjustment->page_increment);
1299       break;
1300       
1301     case GTK_SCROLL_START:
1302       {
1303         gdouble diff = spin->adjustment->value - spin->adjustment->lower;
1304         if (diff > EPSILON)
1305           gtk_spin_button_real_spin (spin, -diff);
1306         break;
1307       }
1308       
1309     case GTK_SCROLL_END:
1310       {
1311         gdouble diff = spin->adjustment->upper - spin->adjustment->value;
1312         if (diff > EPSILON)
1313           gtk_spin_button_real_spin (spin, diff);
1314         break;
1315       }
1316       
1317     default:
1318       g_warning ("Invalid scroll type %d for GtkSpinButton::change-value", scroll);
1319       break;
1320     }
1321   
1322   gtk_spin_button_update (spin);
1323 }
1324
1325 static gint
1326 gtk_spin_button_key_release (GtkWidget   *widget,
1327                              GdkEventKey *event)
1328 {
1329   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
1330
1331   /* We only get a release at the end of a key repeat run, so reset the timer_step */
1332   spin->timer_step = spin->adjustment->step_increment;
1333   spin->timer_calls = 0;
1334   
1335   return TRUE;
1336 }
1337
1338 static void
1339 gtk_spin_button_snap (GtkSpinButton *spin_button,
1340                       gdouble        val)
1341 {
1342   gdouble inc;
1343   gdouble tmp;
1344
1345   inc = spin_button->adjustment->step_increment;
1346   if (inc == 0)
1347     return;
1348   
1349   tmp = (val - spin_button->adjustment->lower) / inc;
1350   if (tmp - floor (tmp) < ceil (tmp) - tmp)
1351     val = spin_button->adjustment->lower + floor (tmp) * inc;
1352   else
1353     val = spin_button->adjustment->lower + ceil (tmp) * inc;
1354
1355   if (fabs (val - spin_button->adjustment->value) > EPSILON)
1356     gtk_adjustment_set_value (spin_button->adjustment, val);
1357   else
1358     {
1359       gint return_val = FALSE;
1360       g_signal_emit (spin_button, spinbutton_signals[OUTPUT], 0, &return_val);
1361       if (return_val == FALSE)
1362         gtk_spin_button_default_output (spin_button);
1363     }
1364 }
1365
1366 static void
1367 gtk_spin_button_activate (GtkEntry *entry)
1368 {
1369   if (entry->editable)
1370     gtk_spin_button_update (GTK_SPIN_BUTTON (entry));
1371
1372   /* Chain up so that entry->activates_default is honored */
1373   GTK_ENTRY_CLASS (gtk_spin_button_parent_class)->activate (entry);
1374 }
1375
1376 static void
1377 gtk_spin_button_insert_text (GtkEditable *editable,
1378                              const gchar *new_text,
1379                              gint         new_text_length,
1380                              gint        *position)
1381 {
1382   GtkEntry *entry = GTK_ENTRY (editable);
1383   GtkSpinButton *spin = GTK_SPIN_BUTTON (editable);
1384   GtkEditableClass *parent_editable_iface = g_type_interface_peek (gtk_spin_button_parent_class, GTK_TYPE_EDITABLE);
1385  
1386   if (spin->numeric)
1387     {
1388       struct lconv *lc;
1389       gboolean sign;
1390       gint dotpos = -1;
1391       gint i;
1392       GdkWChar pos_sign;
1393       GdkWChar neg_sign;
1394       gint entry_length;
1395
1396       entry_length = entry->text_length;
1397
1398       lc = localeconv ();
1399
1400       if (*(lc->negative_sign))
1401         neg_sign = *(lc->negative_sign);
1402       else 
1403         neg_sign = '-';
1404
1405       if (*(lc->positive_sign))
1406         pos_sign = *(lc->positive_sign);
1407       else 
1408         pos_sign = '+';
1409
1410       for (sign=0, i=0; i<entry_length; i++)
1411         if ((entry->text[i] == neg_sign) ||
1412             (entry->text[i] == pos_sign))
1413           {
1414             sign = 1;
1415             break;
1416           }
1417
1418       if (sign && !(*position))
1419         return;
1420
1421       for (dotpos=-1, i=0; i<entry_length; i++)
1422         if (entry->text[i] == *(lc->decimal_point))
1423           {
1424             dotpos = i;
1425             break;
1426           }
1427
1428       if (dotpos > -1 && *position > dotpos &&
1429           (gint)spin->digits - entry_length
1430             + dotpos - new_text_length + 1 < 0)
1431         return;
1432
1433       for (i = 0; i < new_text_length; i++)
1434         {
1435           if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1436             {
1437               if (sign || (*position) || i)
1438                 return;
1439               sign = TRUE;
1440             }
1441           else if (new_text[i] == *(lc->decimal_point))
1442             {
1443               if (!spin->digits || dotpos > -1 || 
1444                   (new_text_length - 1 - i + entry_length
1445                     - *position > (gint)spin->digits)) 
1446                 return;
1447               dotpos = *position + i;
1448             }
1449           else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1450             return;
1451         }
1452     }
1453
1454   parent_editable_iface->insert_text (editable, new_text,
1455                                       new_text_length, position);
1456 }
1457
1458 static void
1459 gtk_spin_button_real_spin (GtkSpinButton *spin_button,
1460                            gdouble        increment)
1461 {
1462   GtkAdjustment *adj;
1463   gdouble new_value = 0.0;
1464   gboolean wrapped = FALSE;
1465   
1466   adj = spin_button->adjustment;
1467
1468   new_value = adj->value + increment;
1469
1470   if (increment > 0)
1471     {
1472       if (spin_button->wrap)
1473         {
1474           if (fabs (adj->value - adj->upper) < EPSILON)
1475             {
1476               new_value = adj->lower;
1477               wrapped = TRUE;
1478             }
1479           else if (new_value > adj->upper)
1480             new_value = adj->upper;
1481         }
1482       else
1483         new_value = MIN (new_value, adj->upper);
1484     }
1485   else if (increment < 0) 
1486     {
1487       if (spin_button->wrap)
1488         {
1489           if (fabs (adj->value - adj->lower) < EPSILON)
1490             {
1491               new_value = adj->upper;
1492               wrapped = TRUE;
1493             }
1494           else if (new_value < adj->lower)
1495             new_value = adj->lower;
1496         }
1497       else
1498         new_value = MAX (new_value, adj->lower);
1499     }
1500
1501   if (fabs (new_value - adj->value) > EPSILON)
1502     gtk_adjustment_set_value (adj, new_value);
1503
1504   if (wrapped)
1505     g_signal_emit (spin_button, spinbutton_signals[WRAPPED], 0);
1506
1507   spin_button_redraw (spin_button);
1508 }
1509
1510 static gint
1511 gtk_spin_button_default_input (GtkSpinButton *spin_button,
1512                                gdouble       *new_val)
1513 {
1514   gchar *err = NULL;
1515
1516   *new_val = g_strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &err);
1517   if (*err)
1518     return GTK_INPUT_ERROR;
1519   else
1520     return FALSE;
1521 }
1522
1523 static gint
1524 gtk_spin_button_default_output (GtkSpinButton *spin_button)
1525 {
1526   gchar *buf = g_strdup_printf ("%0.*f", spin_button->digits, spin_button->adjustment->value);
1527
1528   if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1529     gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1530   g_free (buf);
1531   return FALSE;
1532 }
1533
1534
1535 /***********************************************************
1536  ***********************************************************
1537  ***                  Public interface                   ***
1538  ***********************************************************
1539  ***********************************************************/
1540
1541
1542 void
1543 gtk_spin_button_configure (GtkSpinButton  *spin_button,
1544                            GtkAdjustment  *adjustment,
1545                            gdouble         climb_rate,
1546                            guint           digits)
1547 {
1548   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1549
1550   if (adjustment)
1551     gtk_spin_button_set_adjustment (spin_button, adjustment);
1552   else
1553     adjustment = spin_button->adjustment;
1554
1555   g_object_freeze_notify (G_OBJECT (spin_button));
1556   if (spin_button->digits != digits) 
1557     {
1558       spin_button->digits = digits;
1559       g_object_notify (G_OBJECT (spin_button), "digits");
1560     }
1561
1562   if (spin_button->climb_rate != climb_rate)
1563     {
1564       spin_button->climb_rate = climb_rate;
1565       g_object_notify (G_OBJECT (spin_button), "climb-rate");
1566     }
1567   g_object_thaw_notify (G_OBJECT (spin_button));
1568
1569   gtk_adjustment_value_changed (adjustment);
1570 }
1571
1572 GtkWidget *
1573 gtk_spin_button_new (GtkAdjustment *adjustment,
1574                      gdouble        climb_rate,
1575                      guint          digits)
1576 {
1577   GtkSpinButton *spin;
1578
1579   if (adjustment)
1580     g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
1581
1582   spin = g_object_new (GTK_TYPE_SPIN_BUTTON, NULL);
1583
1584   gtk_spin_button_configure (spin, adjustment, climb_rate, digits);
1585
1586   return GTK_WIDGET (spin);
1587 }
1588
1589 /**
1590  * gtk_spin_button_new_with_range:
1591  * @min: Minimum allowable value
1592  * @max: Maximum allowable value
1593  * @step: Increment added or subtracted by spinning the widget
1594  * 
1595  * This is a convenience constructor that allows creation of a numeric 
1596  * #GtkSpinButton without manually creating an adjustment. The value is 
1597  * initially set to the minimum value and a page increment of 10 * @step
1598  * is the default. The precision of the spin button is equivalent to the 
1599  * precision of @step. 
1600  * 
1601  * Note that the way in which the precision is derived works best if @step 
1602  * is a power of ten. If the resulting precision is not suitable for your 
1603  * needs, use gtk_spin_button_set_digits() to correct it.
1604  * 
1605  * Return value: The new spin button as a #GtkWidget.
1606  **/
1607 GtkWidget *
1608 gtk_spin_button_new_with_range (gdouble min,
1609                                 gdouble max,
1610                                 gdouble step)
1611 {
1612   GtkObject *adj;
1613   GtkSpinButton *spin;
1614   gint digits;
1615
1616   g_return_val_if_fail (min <= max, NULL);
1617   g_return_val_if_fail (step != 0.0, NULL);
1618
1619   spin = g_object_new (GTK_TYPE_SPIN_BUTTON, NULL);
1620
1621   adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
1622
1623   if (fabs (step) >= 1.0 || step == 0.0)
1624     digits = 0;
1625   else {
1626     digits = abs ((gint) floor (log10 (fabs (step))));
1627     if (digits > MAX_DIGITS)
1628       digits = MAX_DIGITS;
1629   }
1630
1631   gtk_spin_button_configure (spin, GTK_ADJUSTMENT (adj), step, digits);
1632
1633   gtk_spin_button_set_numeric (spin, TRUE);
1634
1635   return GTK_WIDGET (spin);
1636 }
1637
1638 /* Callback used when the spin button's adjustment changes.  We need to redraw
1639  * the arrows when the adjustment's range changes, and reevaluate our size request.
1640  */
1641 static void
1642 adjustment_changed_cb (GtkAdjustment *adjustment, gpointer data)
1643 {
1644   GtkSpinButton *spin_button;
1645
1646   spin_button = GTK_SPIN_BUTTON (data);
1647
1648   spin_button->timer_step = spin_button->adjustment->step_increment;
1649   gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1650 }
1651
1652 /**
1653  * gtk_spin_button_set_adjustment:
1654  * @spin_button: a #GtkSpinButton
1655  * @adjustment: a #GtkAdjustment to replace the existing adjustment
1656  * 
1657  * Replaces the #GtkAdjustment associated with @spin_button.
1658  **/
1659 void
1660 gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
1661                                 GtkAdjustment *adjustment)
1662 {
1663   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1664
1665   if (spin_button->adjustment != adjustment)
1666     {
1667       if (spin_button->adjustment)
1668         {
1669           g_signal_handlers_disconnect_by_func (spin_button->adjustment,
1670                                                 gtk_spin_button_value_changed,
1671                                                 spin_button);
1672           g_signal_handlers_disconnect_by_func (spin_button->adjustment,
1673                                                 adjustment_changed_cb,
1674                                                 spin_button);
1675           g_object_unref (spin_button->adjustment);
1676         }
1677       spin_button->adjustment = adjustment;
1678       if (adjustment)
1679         {
1680           g_object_ref_sink (adjustment);
1681           g_signal_connect (adjustment, "value_changed",
1682                             G_CALLBACK (gtk_spin_button_value_changed),
1683                             spin_button);
1684           g_signal_connect (adjustment, "changed",
1685                             G_CALLBACK (adjustment_changed_cb),
1686                             spin_button);
1687           spin_button->timer_step = spin_button->adjustment->step_increment;
1688         }
1689
1690       gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1691     }
1692
1693   g_object_notify (G_OBJECT (spin_button), "adjustment");
1694 }
1695
1696 /**
1697  * gtk_spin_button_get_adjustment:
1698  * @spin_button: a #GtkSpinButton
1699  * 
1700  * Get the adjustment associated with a #GtkSpinButton
1701  * 
1702  * Return value: the #GtkAdjustment of @spin_button
1703  **/
1704 GtkAdjustment *
1705 gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
1706 {
1707   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
1708
1709   return spin_button->adjustment;
1710 }
1711
1712 /**
1713  * gtk_spin_button_set_digits:
1714  * @spin_button: a #GtkSpinButton
1715  * @digits: the number of digits after the decimal point to be displayed for the spin button's value
1716  * 
1717  * Set the precision to be displayed by @spin_button. Up to 20 digit precision
1718  * is allowed.
1719  **/
1720 void
1721 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
1722                             guint          digits)
1723 {
1724   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1725
1726   if (spin_button->digits != digits)
1727     {
1728       spin_button->digits = digits;
1729       gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1730       g_object_notify (G_OBJECT (spin_button), "digits");
1731       
1732       /* since lower/upper may have changed */
1733       gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1734     }
1735 }
1736
1737 /**
1738  * gtk_spin_button_get_digits:
1739  * @spin_button: a #GtkSpinButton
1740  *
1741  * Fetches the precision of @spin_button. See gtk_spin_button_set_digits().
1742  *
1743  * Returns: the current precision
1744  **/
1745 guint
1746 gtk_spin_button_get_digits (GtkSpinButton *spin_button)
1747 {
1748   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1749
1750   return spin_button->digits;
1751 }
1752
1753 /**
1754  * gtk_spin_button_set_increments:
1755  * @spin_button: a #GtkSpinButton
1756  * @step: increment applied for a button 1 press.
1757  * @page: increment applied for a button 2 press.
1758  * 
1759  * Sets the step and page increments for spin_button.  This affects how 
1760  * quickly the value changes when the spin button's arrows are activated.
1761  **/
1762 void
1763 gtk_spin_button_set_increments (GtkSpinButton *spin_button,
1764                                 gdouble        step,
1765                                 gdouble        page)
1766 {
1767   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1768
1769   spin_button->adjustment->step_increment = step;
1770   spin_button->adjustment->page_increment = page;
1771 }
1772
1773 /**
1774  * gtk_spin_button_get_increments:
1775  * @spin_button: a #GtkSpinButton
1776  * @step: location to store step increment, or %NULL
1777  * @page: location to store page increment, or %NULL
1778  *
1779  * Gets the current step and page the increments used by @spin_button. See
1780  * gtk_spin_button_set_increments().
1781  **/
1782 void
1783 gtk_spin_button_get_increments (GtkSpinButton *spin_button,
1784                                 gdouble       *step,
1785                                 gdouble       *page)
1786 {
1787   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1788
1789   if (step)
1790     *step = spin_button->adjustment->step_increment;
1791   if (page)
1792     *page = spin_button->adjustment->page_increment;
1793 }
1794
1795 /**
1796  * gtk_spin_button_set_range:
1797  * @spin_button: a #GtkSpinButton
1798  * @min: minimum allowable value
1799  * @max: maximum allowable value
1800  * 
1801  * Sets the minimum and maximum allowable values for @spin_button
1802  **/
1803 void
1804 gtk_spin_button_set_range (GtkSpinButton *spin_button,
1805                            gdouble        min,
1806                            gdouble        max)
1807 {
1808   gdouble value;
1809   
1810   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1811
1812   spin_button->adjustment->lower = min;
1813   spin_button->adjustment->upper = max;
1814
1815   value = CLAMP (spin_button->adjustment->value,
1816                  spin_button->adjustment->lower,
1817                  (spin_button->adjustment->upper - spin_button->adjustment->page_size));
1818
1819   if (value != spin_button->adjustment->value)
1820     gtk_spin_button_set_value (spin_button, value);
1821
1822   gtk_adjustment_changed (spin_button->adjustment);
1823 }
1824
1825 /**
1826  * gtk_spin_button_get_range:
1827  * @spin_button: a #GtkSpinButton
1828  * @min: location to store minimum allowed value, or %NULL
1829  * @max: location to store maximum allowed value, or %NULL
1830  *
1831  * Gets the range allowed for @spin_button. See
1832  * gtk_spin_button_set_range().
1833  **/
1834 void
1835 gtk_spin_button_get_range (GtkSpinButton *spin_button,
1836                            gdouble       *min,
1837                            gdouble       *max)
1838 {
1839   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1840
1841   if (min)
1842     *min = spin_button->adjustment->lower;
1843   if (max)
1844     *max = spin_button->adjustment->upper;
1845 }
1846
1847 /**
1848  * gtk_spin_button_get_value:
1849  * @spin_button: a #GtkSpinButton
1850  * 
1851  * Get the value in the @spin_button.
1852  * 
1853  * Return value: the value of @spin_button
1854  **/
1855 gdouble
1856 gtk_spin_button_get_value (GtkSpinButton *spin_button)
1857 {
1858   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0.0);
1859
1860   return spin_button->adjustment->value;
1861 }
1862
1863 /**
1864  * gtk_spin_button_get_value_as_int:
1865  * @spin_button: a #GtkSpinButton
1866  * 
1867  * Get the value @spin_button represented as an integer.
1868  * 
1869  * Return value: the value of @spin_button
1870  **/
1871 gint
1872 gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
1873 {
1874   gdouble val;
1875
1876   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1877
1878   val = spin_button->adjustment->value;
1879   if (val - floor (val) < ceil (val) - val)
1880     return floor (val);
1881   else
1882     return ceil (val);
1883 }
1884
1885 /**
1886  * gtk_spin_button_set_value:
1887  * @spin_button: a #GtkSpinButton
1888  * @value: the new value
1889  * 
1890  * Set the value of @spin_button.
1891  **/
1892 void 
1893 gtk_spin_button_set_value (GtkSpinButton *spin_button, 
1894                            gdouble        value)
1895 {
1896   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1897
1898   if (fabs (value - spin_button->adjustment->value) > EPSILON)
1899     gtk_adjustment_set_value (spin_button->adjustment, value);
1900   else
1901     {
1902       gint return_val = FALSE;
1903       g_signal_emit (spin_button, spinbutton_signals[OUTPUT], 0, &return_val);
1904       if (return_val == FALSE)
1905         gtk_spin_button_default_output (spin_button);
1906     }
1907 }
1908
1909 /**
1910  * gtk_spin_button_set_update_policy:
1911  * @spin_button: a #GtkSpinButton 
1912  * @policy: a #GtkSpinButtonUpdatePolicy value
1913  * 
1914  * Sets the update behavior of a spin button. This determines whether the
1915  * spin button is always updated or only when a valid value is set.
1916  **/
1917 void
1918 gtk_spin_button_set_update_policy (GtkSpinButton             *spin_button,
1919                                    GtkSpinButtonUpdatePolicy  policy)
1920 {
1921   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1922
1923   if (spin_button->update_policy != policy)
1924     {
1925       spin_button->update_policy = policy;
1926       g_object_notify (G_OBJECT (spin_button), "update-policy");
1927     }
1928 }
1929
1930 /**
1931  * gtk_spin_button_get_update_policy:
1932  * @spin_button: a #GtkSpinButton
1933  *
1934  * Gets the update behavior of a spin button. See
1935  * gtk_spin_button_set_update_policy().
1936  *
1937  * Return value: the current update policy
1938  **/
1939 GtkSpinButtonUpdatePolicy
1940 gtk_spin_button_get_update_policy (GtkSpinButton *spin_button)
1941 {
1942   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), GTK_UPDATE_ALWAYS);
1943
1944   return spin_button->update_policy;
1945 }
1946
1947 /**
1948  * gtk_spin_button_set_numeric:
1949  * @spin_button: a #GtkSpinButton 
1950  * @numeric: flag indicating if only numeric entry is allowed. 
1951  * 
1952  * Sets the flag that determines if non-numeric text can be typed into
1953  * the spin button.
1954  **/
1955 void
1956 gtk_spin_button_set_numeric (GtkSpinButton  *spin_button,
1957                              gboolean        numeric)
1958 {
1959   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1960
1961   numeric = numeric != FALSE;
1962
1963   if (spin_button->numeric != numeric)
1964     {
1965        spin_button->numeric = numeric;
1966        g_object_notify (G_OBJECT (spin_button), "numeric");
1967     }
1968 }
1969
1970 /**
1971  * gtk_spin_button_get_numeric:
1972  * @spin_button: a #GtkSpinButton
1973  *
1974  * Returns whether non-numeric text can be typed into the spin button.
1975  * See gtk_spin_button_set_numeric().
1976  *
1977  * Return value: %TRUE if only numeric text can be entered
1978  **/
1979 gboolean
1980 gtk_spin_button_get_numeric (GtkSpinButton *spin_button)
1981 {
1982   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
1983
1984   return spin_button->numeric;
1985 }
1986
1987 /**
1988  * gtk_spin_button_set_wrap:
1989  * @spin_button: a #GtkSpinButton 
1990  * @wrap: a flag indicating if wrapping behavior is performed.
1991  * 
1992  * Sets the flag that determines if a spin button value wraps around to the
1993  * opposite limit when the upper or lower limit of the range is exceeded.
1994  **/
1995 void
1996 gtk_spin_button_set_wrap (GtkSpinButton  *spin_button,
1997                           gboolean        wrap)
1998 {
1999   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2000
2001   wrap = wrap != FALSE; 
2002
2003   if (spin_button->wrap != wrap)
2004     {
2005        spin_button->wrap = (wrap != 0);
2006   
2007        g_object_notify (G_OBJECT (spin_button), "wrap");
2008     }
2009 }
2010
2011 /**
2012  * gtk_spin_button_get_wrap:
2013  * @spin_button: a #GtkSpinButton
2014  *
2015  * Returns whether the spin button's value wraps around to the
2016  * opposite limit when the upper or lower limit of the range is
2017  * exceeded. See gtk_spin_button_set_wrap().
2018  *
2019  * Return value: %TRUE if the spin button wraps around
2020  **/
2021 gboolean
2022 gtk_spin_button_get_wrap (GtkSpinButton *spin_button)
2023 {
2024   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
2025
2026   return spin_button->wrap;
2027 }
2028
2029 static gint
2030 spin_button_get_arrow_size (GtkSpinButton *spin_button)
2031 {
2032   gint size = pango_font_description_get_size (GTK_WIDGET (spin_button)->style->font_desc);
2033   gint arrow_size;
2034
2035   arrow_size = MAX (PANGO_PIXELS (size), MIN_ARROW_WIDTH);
2036
2037   return arrow_size - arrow_size % 2; /* force even */
2038 }
2039
2040 /**
2041  * spin_button_get_shadow_type:
2042  * @spin_button: a #GtkSpinButton 
2043  * 
2044  * Convenience function to Get the shadow type from the underlying widget's
2045  * style.
2046  * 
2047  * Return value: the #GtkShadowType
2048  **/
2049 static gint
2050 spin_button_get_shadow_type (GtkSpinButton *spin_button)
2051 {
2052   GtkShadowType rc_shadow_type;
2053
2054   gtk_widget_style_get (GTK_WIDGET (spin_button), "shadow-type", &rc_shadow_type, NULL);
2055
2056   return rc_shadow_type;
2057 }
2058
2059 /**
2060  * gtk_spin_button_set_snap_to_ticks:
2061  * @spin_button: a #GtkSpinButton 
2062  * @snap_to_ticks: a flag indicating if invalid values should be corrected.
2063  * 
2064  * Sets the policy as to whether values are corrected to the nearest step 
2065  * increment when a spin button is activated after providing an invalid value.
2066  **/
2067 void
2068 gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
2069                                    gboolean       snap_to_ticks)
2070 {
2071   guint new_val;
2072
2073   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2074
2075   new_val = (snap_to_ticks != 0);
2076
2077   if (new_val != spin_button->snap_to_ticks)
2078     {
2079       spin_button->snap_to_ticks = new_val;
2080       if (new_val && GTK_ENTRY (spin_button)->editable)
2081         gtk_spin_button_update (spin_button);
2082       
2083       g_object_notify (G_OBJECT (spin_button), "snap-to-ticks");
2084     }
2085 }
2086
2087 /**
2088  * gtk_spin_button_get_snap_to_ticks:
2089  * @spin_button: a #GtkSpinButton
2090  *
2091  * Returns whether the values are corrected to the nearest step. See
2092  * gtk_spin_button_set_snap_to_ticks().
2093  *
2094  * Return value: %TRUE if values are snapped to the nearest step.
2095  **/
2096 gboolean
2097 gtk_spin_button_get_snap_to_ticks (GtkSpinButton *spin_button)
2098 {
2099   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
2100
2101   return spin_button->snap_to_ticks;
2102 }
2103
2104 /**
2105  * gtk_spin_button_spin:
2106  * @spin_button: a #GtkSpinButton 
2107  * @direction: a #GtkSpinType indicating the direction to spin.
2108  * @increment: step increment to apply in the specified direction.
2109  * 
2110  * Increment or decrement a spin button's value in a specified direction
2111  * by a specified amount. 
2112  **/
2113 void
2114 gtk_spin_button_spin (GtkSpinButton *spin_button,
2115                       GtkSpinType    direction,
2116                       gdouble        increment)
2117 {
2118   GtkAdjustment *adj;
2119   gdouble diff;
2120
2121   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2122   
2123   adj = spin_button->adjustment;
2124
2125   /* for compatibility with the 1.0.x version of this function */
2126   if (increment != 0 && increment != adj->step_increment &&
2127       (direction == GTK_SPIN_STEP_FORWARD ||
2128        direction == GTK_SPIN_STEP_BACKWARD))
2129     {
2130       if (direction == GTK_SPIN_STEP_BACKWARD && increment > 0)
2131         increment = -increment;
2132       direction = GTK_SPIN_USER_DEFINED;
2133     }
2134
2135   switch (direction)
2136     {
2137     case GTK_SPIN_STEP_FORWARD:
2138
2139       gtk_spin_button_real_spin (spin_button, adj->step_increment);
2140       break;
2141
2142     case GTK_SPIN_STEP_BACKWARD:
2143
2144       gtk_spin_button_real_spin (spin_button, -adj->step_increment);
2145       break;
2146
2147     case GTK_SPIN_PAGE_FORWARD:
2148
2149       gtk_spin_button_real_spin (spin_button, adj->page_increment);
2150       break;
2151
2152     case GTK_SPIN_PAGE_BACKWARD:
2153
2154       gtk_spin_button_real_spin (spin_button, -adj->page_increment);
2155       break;
2156
2157     case GTK_SPIN_HOME:
2158
2159       diff = adj->value - adj->lower;
2160       if (diff > EPSILON)
2161         gtk_spin_button_real_spin (spin_button, -diff);
2162       break;
2163
2164     case GTK_SPIN_END:
2165
2166       diff = adj->upper - adj->value;
2167       if (diff > EPSILON)
2168         gtk_spin_button_real_spin (spin_button, diff);
2169       break;
2170
2171     case GTK_SPIN_USER_DEFINED:
2172
2173       if (increment != 0)
2174         gtk_spin_button_real_spin (spin_button, increment);
2175       break;
2176
2177     default:
2178       break;
2179     }
2180 }
2181
2182 /**
2183  * gtk_spin_button_update:
2184  * @spin_button: a #GtkSpinButton 
2185  * 
2186  * Manually force an update of the spin button.
2187  **/
2188 void 
2189 gtk_spin_button_update (GtkSpinButton *spin_button)
2190 {
2191   gdouble val;
2192   gint error = 0;
2193   gint return_val;
2194
2195   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
2196
2197   return_val = FALSE;
2198   g_signal_emit (spin_button, spinbutton_signals[INPUT], 0, &val, &return_val);
2199   if (return_val == FALSE)
2200     {
2201       return_val = gtk_spin_button_default_input (spin_button, &val);
2202       error = (return_val == GTK_INPUT_ERROR);
2203     }
2204   else if (return_val == GTK_INPUT_ERROR)
2205     error = 1;
2206
2207   spin_button_redraw (spin_button);
2208
2209   if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
2210     {
2211       if (val < spin_button->adjustment->lower)
2212         val = spin_button->adjustment->lower;
2213       else if (val > spin_button->adjustment->upper)
2214         val = spin_button->adjustment->upper;
2215     }
2216   else if ((spin_button->update_policy == GTK_UPDATE_IF_VALID) && 
2217            (error ||
2218            val < spin_button->adjustment->lower ||
2219            val > spin_button->adjustment->upper))
2220     {
2221       gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
2222       return;
2223     }
2224
2225   if (spin_button->snap_to_ticks)
2226     gtk_spin_button_snap (spin_button, val);
2227   else
2228     {
2229       if (fabs (val - spin_button->adjustment->value) > EPSILON)
2230         gtk_adjustment_set_value (spin_button->adjustment, val);
2231       else
2232         {
2233           return_val = FALSE;
2234           g_signal_emit (spin_button, spinbutton_signals[OUTPUT], 0,
2235                          &return_val);
2236           if (return_val == FALSE)
2237             gtk_spin_button_default_output (spin_button);
2238         }
2239     }
2240 }
2241
2242 static void
2243 spin_button_redraw (GtkSpinButton *spin_button)
2244 {
2245   GtkWidget *widget;
2246
2247   widget = GTK_WIDGET (spin_button);
2248
2249   if (GTK_WIDGET_DRAWABLE (widget))
2250     {
2251       gtk_widget_queue_draw (widget);
2252
2253       /* We must invalidate the panel window ourselves, because it
2254        * is not a child of widget->window
2255        */
2256       gdk_window_invalidate_rect (spin_button->panel, NULL, TRUE);
2257     }
2258 }        
2259
2260 #define __GTK_SPIN_BUTTON_C__
2261 #include "gtkaliasdef.c"