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