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