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