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