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