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