]> Pileus Git - ~andy/gtk/blob - gtk/gtkspinbutton.c
1307119235b330d0ed5bd0d8045ccace1532daee
[~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 Library 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  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library 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 #include <stdio.h>
24 #include <stdlib.h>
25 #include <math.h>
26 #include <string.h>
27 #include <locale.h>
28 #include "gdk/gdkkeysyms.h"
29 #include "gtkspinbutton.h"
30 #include "gtkmain.h"
31 #include "gtksignal.h"
32
33
34 #define MIN_SPIN_BUTTON_WIDTH              30
35 #define ARROW_SIZE                         11
36 #define SPIN_BUTTON_INITIAL_TIMER_DELAY    200
37 #define SPIN_BUTTON_TIMER_DELAY            20
38 #define MAX_TEXT_LENGTH                    256
39 #define MAX_TIMER_CALLS                    5
40 #define EPSILON                            1e-5
41
42 enum {
43   ARG_0,
44   ARG_ADJUSTMENT,
45   ARG_CLIMB_RATE,
46   ARG_DIGITS,
47   ARG_SNAP_TO_TICKS,
48   ARG_NUMERIC,
49   ARG_WRAP,
50   ARG_UPDATE_POLICY,
51   ARG_SHADOW_TYPE,
52   ARG_VALUE
53 };
54
55
56 static void gtk_spin_button_class_init     (GtkSpinButtonClass *klass);
57 static void gtk_spin_button_init           (GtkSpinButton      *spin_button);
58 static void gtk_spin_button_finalize       (GtkObject          *object);
59 static void gtk_spin_button_set_arg        (GtkObject          *object,
60                                             GtkArg             *arg,
61                                             guint               arg_id);
62 static void gtk_spin_button_get_arg        (GtkObject          *object,
63                                             GtkArg             *arg,
64                                             guint               arg_id);
65 static void gtk_spin_button_map            (GtkWidget          *widget);
66 static void gtk_spin_button_unmap          (GtkWidget          *widget);
67 static void gtk_spin_button_realize        (GtkWidget          *widget);
68 static void gtk_spin_button_unrealize      (GtkWidget          *widget);
69 static void gtk_spin_button_size_request   (GtkWidget          *widget,
70                                             GtkRequisition     *requisition);
71 static void gtk_spin_button_size_allocate  (GtkWidget          *widget,
72                                             GtkAllocation      *allocation);
73 static void gtk_spin_button_paint          (GtkWidget          *widget,
74                                             GdkRectangle       *area);
75 static void gtk_spin_button_draw           (GtkWidget          *widget,
76                                             GdkRectangle       *area);
77 static gint gtk_spin_button_expose         (GtkWidget          *widget,
78                                             GdkEventExpose     *event);
79 static gint gtk_spin_button_button_press   (GtkWidget          *widget,
80                                             GdkEventButton     *event);
81 static gint gtk_spin_button_button_release (GtkWidget          *widget,
82                                             GdkEventButton     *event);
83 static gint gtk_spin_button_motion_notify  (GtkWidget          *widget,
84                                             GdkEventMotion     *event);
85 static gint gtk_spin_button_enter_notify   (GtkWidget          *widget,
86                                             GdkEventCrossing   *event);
87 static gint gtk_spin_button_leave_notify   (GtkWidget          *widget,
88                                             GdkEventCrossing   *event);
89 static gint gtk_spin_button_focus_out      (GtkWidget          *widget,
90                                             GdkEventFocus      *event);
91 static void gtk_spin_button_draw_arrow     (GtkSpinButton      *spin_button, 
92                                             guint               arrow);
93 static gint gtk_spin_button_timer          (GtkSpinButton      *spin_button);
94 static void gtk_spin_button_value_changed  (GtkAdjustment      *adjustment,
95                                             GtkSpinButton      *spin_button); 
96 static gint gtk_spin_button_key_press      (GtkWidget          *widget,
97                                             GdkEventKey        *event);
98 static gint gtk_spin_button_key_release    (GtkWidget          *widget,
99                                             GdkEventKey        *event);
100 static void gtk_spin_button_update         (GtkSpinButton      *spin_button);
101 static void gtk_spin_button_activate       (GtkEditable        *editable);
102 static void gtk_spin_button_snap           (GtkSpinButton      *spin_button,
103                                             gfloat              val);
104 static void gtk_spin_button_insert_text    (GtkEditable        *editable,
105                                             const gchar        *new_text,
106                                             gint                new_text_length,
107                                             gint               *position);
108 static void gtk_spin_button_real_spin      (GtkSpinButton      *spin_button,
109                                             gfloat              step);
110
111
112 static GtkEntryClass *parent_class = NULL;
113
114
115 GtkType
116 gtk_spin_button_get_type (void)
117 {
118   static guint spin_button_type = 0;
119
120   if (!spin_button_type)
121     {
122       static const GtkTypeInfo spin_button_info =
123       {
124         "GtkSpinButton",
125         sizeof (GtkSpinButton),
126         sizeof (GtkSpinButtonClass),
127         (GtkClassInitFunc) gtk_spin_button_class_init,
128         (GtkObjectInitFunc) gtk_spin_button_init,
129         /* reserved_1 */ NULL,
130         /* reserved_2 */ NULL,
131         (GtkClassInitFunc) NULL,
132       };
133
134       spin_button_type = gtk_type_unique (GTK_TYPE_ENTRY, &spin_button_info);
135     }
136   return spin_button_type;
137 }
138
139 static void
140 gtk_spin_button_class_init (GtkSpinButtonClass *class)
141 {
142   GtkObjectClass   *object_class;
143   GtkWidgetClass   *widget_class;
144   GtkEditableClass *editable_class;
145
146   object_class   = (GtkObjectClass*)   class;
147   widget_class   = (GtkWidgetClass*)   class;
148   editable_class = (GtkEditableClass*) class; 
149
150   parent_class = gtk_type_class (GTK_TYPE_ENTRY);
151
152   gtk_object_add_arg_type ("GtkSpinButton::adjustment",
153                            GTK_TYPE_ADJUSTMENT,
154                            GTK_ARG_READWRITE,
155                            ARG_ADJUSTMENT);
156   gtk_object_add_arg_type ("GtkSpinButton::climb_rate",
157                            GTK_TYPE_FLOAT,
158                            GTK_ARG_READWRITE,
159                            ARG_CLIMB_RATE);
160   gtk_object_add_arg_type ("GtkSpinButton::digits",
161                            GTK_TYPE_UINT,
162                            GTK_ARG_READWRITE,
163                            ARG_DIGITS);
164   gtk_object_add_arg_type ("GtkSpinButton::snap_to_ticks",
165                            GTK_TYPE_BOOL,
166                            GTK_ARG_READWRITE,
167                            ARG_SNAP_TO_TICKS);
168   gtk_object_add_arg_type ("GtkSpinButton::numeric",
169                            GTK_TYPE_BOOL,
170                            GTK_ARG_READWRITE,
171                            ARG_NUMERIC);
172   gtk_object_add_arg_type ("GtkSpinButton::wrap",
173                            GTK_TYPE_BOOL,
174                            GTK_ARG_READWRITE,
175                            ARG_WRAP);
176   gtk_object_add_arg_type ("GtkSpinButton::update_policy",
177                            GTK_TYPE_SPIN_BUTTON_UPDATE_POLICY,
178                            GTK_ARG_READWRITE,
179                            ARG_UPDATE_POLICY);
180   gtk_object_add_arg_type ("GtkSpinButton::shadow_type",
181                            GTK_TYPE_SHADOW_TYPE,
182                            GTK_ARG_READWRITE,
183                            ARG_SHADOW_TYPE);
184   gtk_object_add_arg_type ("GtkSpinButton::value",
185                            GTK_TYPE_FLOAT,
186                            GTK_ARG_READWRITE,
187                            ARG_VALUE);
188   
189
190   object_class->set_arg = gtk_spin_button_set_arg;
191   object_class->get_arg = gtk_spin_button_get_arg;
192   object_class->finalize = gtk_spin_button_finalize;
193
194   widget_class->map = gtk_spin_button_map;
195   widget_class->unmap = gtk_spin_button_unmap;
196   widget_class->realize = gtk_spin_button_realize;
197   widget_class->unrealize = gtk_spin_button_unrealize;
198   widget_class->size_request = gtk_spin_button_size_request;
199   widget_class->size_allocate = gtk_spin_button_size_allocate;
200   widget_class->draw = gtk_spin_button_draw;
201   widget_class->expose_event = gtk_spin_button_expose;
202   widget_class->button_press_event = gtk_spin_button_button_press;
203   widget_class->button_release_event = gtk_spin_button_button_release;
204   widget_class->motion_notify_event = gtk_spin_button_motion_notify;
205   widget_class->key_press_event = gtk_spin_button_key_press;
206   widget_class->key_release_event = gtk_spin_button_key_release;
207   widget_class->enter_notify_event = gtk_spin_button_enter_notify;
208   widget_class->leave_notify_event = gtk_spin_button_leave_notify;
209   widget_class->focus_out_event = gtk_spin_button_focus_out;
210
211   editable_class->insert_text = gtk_spin_button_insert_text;
212   editable_class->activate = gtk_spin_button_activate;
213 }
214
215 static void
216 gtk_spin_button_set_arg (GtkObject        *object,
217                          GtkArg           *arg,
218                          guint             arg_id)
219 {
220   GtkSpinButton *spin_button;
221
222   spin_button = GTK_SPIN_BUTTON (object);
223   
224   switch (arg_id)
225     {
226       GtkAdjustment *adjustment;
227
228     case ARG_ADJUSTMENT:
229       adjustment = GTK_VALUE_POINTER (*arg);
230       if (!adjustment)
231         adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
232       gtk_spin_button_set_adjustment (spin_button, adjustment);
233       break;
234     case ARG_CLIMB_RATE:
235       gtk_spin_button_configure (spin_button,
236                                  spin_button->adjustment,
237                                  GTK_VALUE_FLOAT (*arg),
238                                  spin_button->digits);
239       break;
240     case ARG_DIGITS:
241       gtk_spin_button_configure (spin_button,
242                                  spin_button->adjustment,
243                                  spin_button->climb_rate,
244                                  GTK_VALUE_UINT (*arg));
245       break;
246     case ARG_SNAP_TO_TICKS:
247       gtk_spin_button_set_snap_to_ticks (spin_button, GTK_VALUE_BOOL (*arg));
248       break;
249     case ARG_NUMERIC:
250       gtk_spin_button_set_numeric (spin_button, GTK_VALUE_BOOL (*arg));
251       break;
252     case ARG_WRAP:
253       gtk_spin_button_set_wrap (spin_button, GTK_VALUE_BOOL (*arg));
254       break;
255     case ARG_UPDATE_POLICY:
256       gtk_spin_button_set_update_policy (spin_button, GTK_VALUE_ENUM (*arg));
257       break;
258     case ARG_SHADOW_TYPE:
259       gtk_spin_button_set_shadow_type (spin_button, GTK_VALUE_ENUM (*arg));
260       break;
261     case ARG_VALUE:
262       gtk_spin_button_set_value (spin_button, GTK_VALUE_FLOAT (*arg));
263       break;
264     default:
265       break;
266     }
267 }
268
269 static void
270 gtk_spin_button_get_arg (GtkObject        *object,
271                          GtkArg           *arg,
272                          guint             arg_id)
273 {
274   GtkSpinButton *spin_button;
275
276   spin_button = GTK_SPIN_BUTTON (object);
277   
278   switch (arg_id)
279     {
280     case ARG_ADJUSTMENT:
281       GTK_VALUE_POINTER (*arg) = spin_button->adjustment;
282       break;
283     case ARG_CLIMB_RATE:
284       GTK_VALUE_FLOAT (*arg) = spin_button->climb_rate;
285       break;
286     case ARG_DIGITS:
287       GTK_VALUE_UINT (*arg) = spin_button->digits;
288       break;
289     case ARG_SNAP_TO_TICKS:
290       GTK_VALUE_BOOL (*arg) = spin_button->snap_to_ticks;
291       break;
292     case ARG_NUMERIC:
293       GTK_VALUE_BOOL (*arg) = spin_button->numeric;
294       break;
295     case ARG_WRAP:
296       GTK_VALUE_BOOL (*arg) = spin_button->wrap;
297       break;
298     case ARG_UPDATE_POLICY:
299       GTK_VALUE_ENUM (*arg) = spin_button->update_policy;
300       break;
301     case ARG_SHADOW_TYPE:
302       GTK_VALUE_ENUM (*arg) = spin_button->shadow_type;
303       break;
304     case ARG_VALUE:
305       GTK_VALUE_FLOAT (*arg) = spin_button->adjustment->value;
306       break;
307     default:
308       arg->type = GTK_TYPE_INVALID;
309       break;
310     }
311 }
312
313 static void
314 gtk_spin_button_init (GtkSpinButton *spin_button)
315 {
316   spin_button->adjustment = NULL;
317   spin_button->panel = NULL;
318   spin_button->shadow_type = GTK_SHADOW_NONE;
319   spin_button->timer = 0;
320   spin_button->ev_time = 0;
321   spin_button->climb_rate = 0.0;
322   spin_button->timer_step = 0.0;
323   spin_button->update_policy = GTK_UPDATE_ALWAYS;
324   spin_button->in_child = 2;
325   spin_button->click_child = 2;
326   spin_button->button = 0;
327   spin_button->need_timer = FALSE;
328   spin_button->timer_calls = 0;
329   spin_button->digits = 0;
330   spin_button->numeric = FALSE;
331   spin_button->wrap = FALSE;
332   spin_button->snap_to_ticks = FALSE;
333
334   gtk_spin_button_set_adjustment (spin_button,
335                                   (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0));
336 }
337
338 static void
339 gtk_spin_button_finalize (GtkObject *object)
340 {
341   g_return_if_fail (object != NULL);
342   g_return_if_fail (GTK_IS_SPIN_BUTTON (object));
343
344   gtk_object_unref (GTK_OBJECT (GTK_SPIN_BUTTON (object)->adjustment));
345   
346   GTK_OBJECT_CLASS (parent_class)->finalize (object);
347 }
348
349 static void
350 gtk_spin_button_map (GtkWidget *widget)
351 {
352   g_return_if_fail (widget != NULL);
353   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
354
355   if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
356     {
357       GTK_WIDGET_CLASS (parent_class)->map (widget);
358       gdk_window_show (GTK_SPIN_BUTTON (widget)->panel);
359     }
360 }
361
362 static void
363 gtk_spin_button_unmap (GtkWidget *widget)
364 {
365   g_return_if_fail (widget != NULL);
366   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
367
368   if (GTK_WIDGET_MAPPED (widget))
369     {
370       gdk_window_hide (GTK_SPIN_BUTTON (widget)->panel);
371       GTK_WIDGET_CLASS (parent_class)->unmap (widget);
372     }
373 }
374
375 static void
376 gtk_spin_button_realize (GtkWidget *widget)
377 {
378   GtkSpinButton *spin;
379   GdkWindowAttr attributes;
380   gint attributes_mask;
381   guint real_width;
382
383   g_return_if_fail (widget != NULL);
384   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
385   
386   spin = GTK_SPIN_BUTTON (widget);
387
388   real_width = widget->allocation.width;
389   widget->allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
390   gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
391                          GDK_KEY_RELEASE_MASK);
392   GTK_WIDGET_CLASS (parent_class)->realize (widget);
393
394   widget->allocation.width = real_width;
395   
396   attributes.window_type = GDK_WINDOW_CHILD;
397   attributes.wclass = GDK_INPUT_OUTPUT;
398   attributes.visual = gtk_widget_get_visual (widget);
399   attributes.colormap = gtk_widget_get_colormap (widget);
400   attributes.event_mask = gtk_widget_get_events (widget);
401   attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK 
402     | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK 
403     | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
404
405   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
406
407   attributes.x = (widget->allocation.x + widget->allocation.width - ARROW_SIZE -
408                   2 * widget->style->klass->xthickness);
409   attributes.y = widget->allocation.y + (widget->allocation.height -
410                                          widget->requisition.height) / 2;
411   attributes.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
412   attributes.height = widget->requisition.height;
413   
414   spin->panel = gdk_window_new (gtk_widget_get_parent_window (widget), 
415                                 &attributes, attributes_mask);
416   gdk_window_set_user_data (spin->panel, widget);
417
418   gtk_style_set_background (widget->style, spin->panel, GTK_STATE_NORMAL);
419 }
420
421 static void
422 gtk_spin_button_unrealize (GtkWidget *widget)
423 {
424   GtkSpinButton *spin;
425
426   g_return_if_fail (widget != NULL);
427   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
428
429   spin = GTK_SPIN_BUTTON (widget);
430
431   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
432
433   if (spin->panel)
434     {
435       gdk_window_set_user_data (spin->panel, NULL);
436       gdk_window_destroy (spin->panel);
437       spin->panel = NULL;
438     }
439 }
440
441 static void
442 gtk_spin_button_size_request (GtkWidget      *widget,
443                               GtkRequisition *requisition)
444 {
445   g_return_if_fail (widget != NULL);
446   g_return_if_fail (requisition != NULL);
447   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
448
449   GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
450   
451   requisition->width = MIN_SPIN_BUTTON_WIDTH + ARROW_SIZE 
452     + 2 * widget->style->klass->xthickness;
453 }
454
455 static void
456 gtk_spin_button_size_allocate (GtkWidget     *widget,
457                                GtkAllocation *allocation)
458 {
459   GtkAllocation child_allocation;
460
461   g_return_if_fail (widget != NULL);
462   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
463   g_return_if_fail (allocation != NULL);
464
465   child_allocation = *allocation;
466   child_allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
467
468   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &child_allocation);
469
470   widget->allocation = *allocation;
471
472   if (GTK_WIDGET_REALIZED (widget))
473     {
474       child_allocation.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
475       child_allocation.height = widget->requisition.height;  
476       child_allocation.x = (allocation->x + allocation->width - ARROW_SIZE - 
477                             2 * widget->style->klass->xthickness);
478       child_allocation.y = allocation->y + (allocation->height - widget->requisition.height) / 2;
479
480       gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel, 
481                               child_allocation.x,
482                               child_allocation.y,
483                               child_allocation.width,
484                               child_allocation.height); 
485     }
486 }
487
488 static void
489 gtk_spin_button_paint (GtkWidget    *widget,
490                        GdkRectangle *area)
491 {
492   GtkSpinButton *spin;
493
494   g_return_if_fail (widget != NULL);
495   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
496
497   spin = GTK_SPIN_BUTTON (widget);
498
499   if (GTK_WIDGET_DRAWABLE (widget))
500     {
501       if (spin->shadow_type != GTK_SHADOW_NONE)
502         gtk_paint_box (widget->style, spin->panel,
503                        GTK_STATE_NORMAL, spin->shadow_type,
504                        area, widget, "spinbutton",
505                        0, 0, 
506                        ARROW_SIZE + 2 * widget->style->klass->xthickness,
507                        widget->requisition.height); 
508       else
509          {
510             gdk_window_set_back_pixmap (spin->panel, NULL, TRUE);
511             gdk_window_clear_area (spin->panel, area->x, area->y, area->width, area->height);
512          }
513        gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
514        gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
515
516        GTK_WIDGET_CLASS (parent_class)->draw (widget, area);
517     }
518 }
519
520 static void
521 gtk_spin_button_draw (GtkWidget    *widget,
522                       GdkRectangle *area)
523 {
524   g_return_if_fail (widget != NULL);
525   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
526   g_return_if_fail (area != NULL);
527
528   if (GTK_WIDGET_DRAWABLE (widget))
529     gtk_spin_button_paint (widget, area);
530 }
531
532 static gint
533 gtk_spin_button_expose (GtkWidget      *widget,
534                         GdkEventExpose *event)
535 {
536   g_return_val_if_fail (widget != NULL, FALSE);
537   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
538   g_return_val_if_fail (event != NULL, FALSE);
539
540   if (GTK_WIDGET_DRAWABLE (widget))
541     gtk_spin_button_paint (widget, &event->area);
542
543   return FALSE;
544 }
545
546 static void
547 gtk_spin_button_draw_arrow (GtkSpinButton *spin_button, 
548                             guint          arrow)
549 {
550   GtkStateType state_type;
551   GtkShadowType shadow_type;
552   GtkWidget *widget;
553   gint x;
554   gint y;
555
556   g_return_if_fail (spin_button != NULL);
557   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
558   
559   widget = GTK_WIDGET (spin_button);
560
561   if (GTK_WIDGET_DRAWABLE (spin_button))
562     {
563       if (!spin_button->wrap &&
564           (((arrow == GTK_ARROW_UP &&
565           (spin_button->adjustment->upper - spin_button->adjustment->value
566            <= EPSILON))) ||
567           ((arrow == GTK_ARROW_DOWN &&
568           (spin_button->adjustment->value - spin_button->adjustment->lower
569            <= EPSILON)))))
570         {
571           shadow_type = GTK_SHADOW_ETCHED_IN;
572           state_type = GTK_STATE_NORMAL;
573         }
574       else
575         {
576           if (spin_button->in_child == arrow)
577             {
578               if (spin_button->click_child == arrow)
579                 state_type = GTK_STATE_ACTIVE;
580               else
581                 state_type = GTK_STATE_PRELIGHT;
582             }
583           else
584             state_type = GTK_STATE_NORMAL;
585           
586           if (spin_button->click_child == arrow)
587             shadow_type = GTK_SHADOW_IN;
588           else
589             shadow_type = GTK_SHADOW_OUT;
590         }
591       if (arrow == GTK_ARROW_UP)
592         {
593           if (spin_button->shadow_type != GTK_SHADOW_NONE)
594             {
595               x = widget->style->klass->xthickness;
596               y = widget->style->klass->ythickness;
597             }
598           else
599             {
600               x = widget->style->klass->xthickness - 1;
601               y = widget->style->klass->ythickness - 1;
602             }
603           gtk_paint_arrow (widget->style, spin_button->panel,
604                            state_type, shadow_type, 
605                            NULL, widget, "spinbutton",
606                            arrow, TRUE, 
607                            x, y, ARROW_SIZE, widget->requisition.height / 2 
608                            - widget->style->klass->ythickness);
609         }
610       else
611         {
612           if (spin_button->shadow_type != GTK_SHADOW_NONE)
613             {
614               x = widget->style->klass->xthickness;
615               y = widget->requisition.height / 2;
616             }
617           else
618             {
619               x = widget->style->klass->xthickness - 1;
620               y = widget->requisition.height / 2 + 1;
621             }
622           gtk_paint_arrow (widget->style, spin_button->panel,
623                            state_type, shadow_type, 
624                            NULL, widget, "spinbutton",
625                            arrow, TRUE, 
626                            x, y, ARROW_SIZE, widget->requisition.height / 2 
627                            - widget->style->klass->ythickness);
628         }
629     }
630 }
631
632 static gint
633 gtk_spin_button_enter_notify (GtkWidget        *widget,
634                               GdkEventCrossing *event)
635 {
636   GtkSpinButton *spin;
637
638   g_return_val_if_fail (widget != NULL, FALSE);
639   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
640   g_return_val_if_fail (event != NULL, FALSE);
641
642   spin = GTK_SPIN_BUTTON (widget);
643
644   if (event->window == spin->panel)
645     {
646       gint x;
647       gint y;
648
649       gdk_window_get_pointer (spin->panel, &x, &y, NULL);
650
651       if (y <= widget->requisition.height / 2)
652         {
653           spin->in_child = GTK_ARROW_UP;
654           if (spin->click_child == 2) 
655             gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
656         }
657       else
658         {
659           spin->in_child = GTK_ARROW_DOWN;
660           if (spin->click_child == 2) 
661             gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
662         }
663     }
664   return FALSE;
665 }
666
667 static gint
668 gtk_spin_button_leave_notify (GtkWidget        *widget,
669                               GdkEventCrossing *event)
670 {
671   GtkSpinButton *spin;
672
673   g_return_val_if_fail (widget != NULL, FALSE);
674   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
675   g_return_val_if_fail (event != NULL, FALSE);
676
677   spin = GTK_SPIN_BUTTON (widget);
678
679   if (event->window == spin->panel && spin->click_child == 2)
680     {
681       if (spin->in_child == GTK_ARROW_UP) 
682         {
683           spin->in_child = 2;
684           gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
685         }
686       else
687         {
688           spin->in_child = 2;
689           gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
690         }
691     }
692   return FALSE;
693 }
694
695 static gint
696 gtk_spin_button_focus_out (GtkWidget     *widget,
697                            GdkEventFocus *event)
698 {
699   g_return_val_if_fail (widget != NULL, FALSE);
700   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
701   g_return_val_if_fail (event != NULL, FALSE);
702
703   gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
704
705   return GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
706 }
707
708 static gint
709 gtk_spin_button_button_press (GtkWidget      *widget,
710                               GdkEventButton *event)
711 {
712   GtkSpinButton *spin;
713
714   g_return_val_if_fail (widget != NULL, FALSE);
715   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
716   g_return_val_if_fail (event != NULL, FALSE);
717
718   spin = GTK_SPIN_BUTTON (widget);
719
720   if (!spin->button)
721     {
722       if (event->window == spin->panel)
723         {
724           if (!GTK_WIDGET_HAS_FOCUS (widget))
725             gtk_widget_grab_focus (widget);
726           gtk_grab_add (widget);
727           spin->button = event->button;
728           
729           gtk_spin_button_update (spin);
730           
731           if (event->y <= widget->requisition.height / 2)
732             {
733               spin->click_child = GTK_ARROW_UP;
734               if (event->button == 1)
735                 {
736                  gtk_spin_button_real_spin (spin, 
737                                             spin->adjustment->step_increment);
738                   if (!spin->timer)
739                     {
740                       spin->timer_step = spin->adjustment->step_increment;
741                       spin->need_timer = TRUE;
742                       spin->timer = gtk_timeout_add 
743                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
744                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
745                     }
746                 }
747               else if (event->button == 2)
748                 {
749                  gtk_spin_button_real_spin (spin, 
750                                             spin->adjustment->page_increment);
751                   if (!spin->timer) 
752                     {
753                       spin->timer_step = spin->adjustment->page_increment;
754                       spin->need_timer = TRUE;
755                       spin->timer = gtk_timeout_add 
756                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
757                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
758                     }
759                 }
760               gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
761             }
762           else 
763             {
764               spin->click_child = GTK_ARROW_DOWN;
765               if (event->button == 1)
766                 {
767                   gtk_spin_button_real_spin (spin,
768                                              -spin->adjustment->step_increment);
769                   if (!spin->timer)
770                     {
771                       spin->timer_step = spin->adjustment->step_increment;
772                       spin->need_timer = TRUE;
773                       spin->timer = gtk_timeout_add 
774                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
775                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
776                     }
777                 }      
778               else if (event->button == 2)
779                 {
780                   gtk_spin_button_real_spin (spin,
781                                              -spin->adjustment->page_increment);
782                   if (!spin->timer) 
783                     {
784                       spin->timer_step = spin->adjustment->page_increment;
785                       spin->need_timer = TRUE;
786                       spin->timer = gtk_timeout_add 
787                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
788                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
789                     }
790                 }
791               gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
792             }
793         }
794       else
795         GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
796     }
797   return FALSE;
798 }
799
800 static gint
801 gtk_spin_button_button_release (GtkWidget      *widget,
802                                 GdkEventButton *event)
803 {
804   GtkSpinButton *spin;
805
806   g_return_val_if_fail (widget != NULL, FALSE);
807   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
808   g_return_val_if_fail (event != NULL, FALSE);
809
810   spin = GTK_SPIN_BUTTON (widget);
811
812   if (event->button == spin->button)
813     {
814       guint click_child;
815
816       if (spin->timer)
817         {
818           gtk_timeout_remove (spin->timer);
819           spin->timer = 0;
820           spin->timer_calls = 0;
821           spin->need_timer = FALSE;
822         }
823
824       if (event->button == 3)
825         {
826           if (event->y >= 0 && event->x >= 0 && 
827               event->y <= widget->requisition.height &&
828               event->x <= ARROW_SIZE + 2 * widget->style->klass->xthickness)
829             {
830               if (spin->click_child == GTK_ARROW_UP &&
831                   event->y <= widget->requisition.height / 2)
832                 {
833                   gfloat diff;
834
835                   diff = spin->adjustment->upper - spin->adjustment->value;
836                   if (diff > EPSILON)
837                     gtk_spin_button_real_spin (spin, diff);
838                 }
839               else if (spin->click_child == GTK_ARROW_DOWN &&
840                        event->y > widget->requisition.height / 2)
841                 {
842                   gfloat diff;
843
844                   diff = spin->adjustment->value - spin->adjustment->lower;
845                   if (diff > EPSILON)
846                     gtk_spin_button_real_spin (spin, -diff);
847                 }
848             }
849         }                 
850       gtk_grab_remove (widget);
851       click_child = spin->click_child;
852       spin->click_child = 2;
853       spin->button = 0;
854       gtk_spin_button_draw_arrow (spin, click_child);
855     }
856   else
857     GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
858
859   return FALSE;
860 }
861
862 static gint
863 gtk_spin_button_motion_notify (GtkWidget      *widget,
864                                GdkEventMotion *event)
865 {
866   GtkSpinButton *spin;
867
868   g_return_val_if_fail (widget != NULL, FALSE);
869   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
870   g_return_val_if_fail (event != NULL, FALSE);
871
872   spin = GTK_SPIN_BUTTON (widget);
873   
874   if (spin->button)
875     return FALSE;
876
877   if (event->window == spin->panel)
878     {
879       gint y;
880
881       y = event->y;
882       if (event->is_hint)
883         gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
884
885       if (y <= widget->requisition.height / 2 && 
886           spin->in_child == GTK_ARROW_DOWN)
887         {
888           spin->in_child = GTK_ARROW_UP;
889           gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
890           gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
891         }
892       else if (y > widget->requisition.height / 2 && 
893           spin->in_child == GTK_ARROW_UP)
894         {
895           spin->in_child = GTK_ARROW_DOWN;
896           gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
897           gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
898         }
899       return FALSE;
900     }
901           
902   return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
903 }
904
905 static gint
906 gtk_spin_button_timer (GtkSpinButton *spin_button)
907 {
908   g_return_val_if_fail (spin_button != NULL, FALSE);
909   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
910
911   if (spin_button->timer)
912     {
913       if (spin_button->click_child == GTK_ARROW_UP)
914         gtk_spin_button_real_spin (spin_button, spin_button->timer_step);
915       else
916         gtk_spin_button_real_spin (spin_button, -spin_button->timer_step);
917
918       if (spin_button->need_timer)
919         {
920           spin_button->need_timer = FALSE;
921           spin_button->timer = gtk_timeout_add 
922             (SPIN_BUTTON_TIMER_DELAY, (GtkFunction) gtk_spin_button_timer, 
923              (gpointer) spin_button);
924           return FALSE;
925         }
926       else if (spin_button->climb_rate > 0.0 && spin_button->timer_step 
927                < spin_button->adjustment->page_increment)
928         {
929           if (spin_button->timer_calls < MAX_TIMER_CALLS)
930             spin_button->timer_calls++;
931           else 
932             {
933               spin_button->timer_calls = 0;
934               spin_button->timer_step += spin_button->climb_rate;
935             }
936         }
937       return TRUE;
938     }
939   return FALSE;
940 }
941
942 static void
943 gtk_spin_button_value_changed (GtkAdjustment *adjustment,
944                                GtkSpinButton *spin_button)
945 {
946   char buf[MAX_TEXT_LENGTH];
947
948   g_return_if_fail (adjustment != NULL);
949   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
950
951   sprintf (buf, "%0.*f", spin_button->digits, adjustment->value);
952   gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
953 }
954
955 static gint
956 gtk_spin_button_key_press (GtkWidget     *widget,
957                            GdkEventKey   *event)
958 {
959   GtkSpinButton *spin;
960   gint key;
961   gboolean key_repeat = FALSE;
962
963   g_return_val_if_fail (widget != NULL, FALSE);
964   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
965   g_return_val_if_fail (event != NULL, FALSE);
966   
967   spin = GTK_SPIN_BUTTON (widget);
968   key = event->keyval;
969
970   key_repeat = (event->time == spin->ev_time);
971
972   if (key == GDK_Up || key == GDK_Down || 
973       key == GDK_Page_Up || key == GDK_Page_Down)
974     gtk_spin_button_update (spin);
975
976   switch (key)
977     {
978     case GDK_Up:
979
980       if (GTK_WIDGET_HAS_FOCUS (widget))
981         {
982           gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), 
983                                         "key_press_event");
984           if (!key_repeat)
985             spin->timer_step = spin->adjustment->step_increment;
986
987          gtk_spin_button_real_spin (spin, spin->timer_step);
988
989           if (key_repeat)
990             {
991               if (spin->climb_rate > 0.0 && spin->timer_step
992                   < spin->adjustment->page_increment)
993                 {
994                   if (spin->timer_calls < MAX_TIMER_CALLS)
995                     spin->timer_calls++;
996                   else 
997                     {
998                       spin->timer_calls = 0;
999                       spin->timer_step += spin->climb_rate;
1000                     }
1001                 }
1002             }
1003           return TRUE;
1004         }
1005       return FALSE;
1006
1007     case GDK_Down:
1008
1009       if (GTK_WIDGET_HAS_FOCUS (widget))
1010         {
1011           gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), 
1012                                         "key_press_event");
1013           if (!key_repeat)
1014             spin->timer_step = spin->adjustment->step_increment;
1015
1016          gtk_spin_button_real_spin (spin, -spin->timer_step);
1017
1018           if (key_repeat)
1019             {
1020               if (spin->climb_rate > 0.0 && spin->timer_step
1021                   < spin->adjustment->page_increment)
1022                 {
1023                   if (spin->timer_calls < MAX_TIMER_CALLS)
1024                     spin->timer_calls++;
1025                   else 
1026                     {
1027                       spin->timer_calls = 0;
1028                       spin->timer_step += spin->climb_rate;
1029                     }
1030                 }
1031             }
1032           return TRUE;
1033         }
1034       return FALSE;
1035
1036     case GDK_Page_Up:
1037
1038       if (event->state & GDK_CONTROL_MASK)
1039         {
1040           gfloat diff = spin->adjustment->upper - spin->adjustment->value;
1041           if (diff > EPSILON)
1042             gtk_spin_button_real_spin (spin, diff);
1043         }
1044       else
1045         gtk_spin_button_real_spin (spin, spin->adjustment->page_increment);
1046       return TRUE;
1047
1048     case GDK_Page_Down:
1049
1050       if (event->state & GDK_CONTROL_MASK)
1051         {
1052           gfloat diff = spin->adjustment->value - spin->adjustment->lower;
1053           if (diff > EPSILON)
1054             gtk_spin_button_real_spin (spin, -diff);
1055         }
1056       else
1057         gtk_spin_button_real_spin (spin, -spin->adjustment->page_increment);
1058       return TRUE;
1059
1060     default:
1061       break;
1062     }
1063
1064   return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
1065 }
1066
1067 static gint
1068 gtk_spin_button_key_release (GtkWidget   *widget,
1069                              GdkEventKey *event)
1070 {
1071   GtkSpinButton *spin;
1072
1073   g_return_val_if_fail (widget != NULL, FALSE);
1074   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1075   
1076   spin = GTK_SPIN_BUTTON (widget);
1077   
1078   spin->ev_time = event->time;
1079   return TRUE;
1080 }
1081
1082 static void
1083 gtk_spin_button_snap (GtkSpinButton *spin_button,
1084                       gfloat         val)
1085 {
1086   gfloat inc;
1087   gfloat tmp;
1088   
1089   inc = spin_button->adjustment->step_increment;
1090   tmp = (val - spin_button->adjustment->lower) / inc;
1091   if (tmp - floor (tmp) < ceil (tmp) - tmp)
1092     val = spin_button->adjustment->lower + floor (tmp) * inc;
1093   else
1094     val = spin_button->adjustment->lower + ceil (tmp) * inc;
1095
1096   if (fabs (val - spin_button->adjustment->value) > EPSILON)
1097     gtk_adjustment_set_value (spin_button->adjustment, val);
1098   else
1099     {
1100       char buf[MAX_TEXT_LENGTH];
1101
1102       sprintf (buf, "%0.*f", spin_button->digits, 
1103                spin_button->adjustment->value);
1104       if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1105         gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1106     }
1107 }
1108
1109 static void 
1110 gtk_spin_button_update (GtkSpinButton *spin_button)
1111 {
1112   gfloat val;
1113   gchar *error = NULL;
1114
1115   g_return_if_fail (spin_button != NULL);
1116   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1117
1118   val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
1119
1120   if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
1121     {
1122       if (val < spin_button->adjustment->lower)
1123         val = spin_button->adjustment->lower;
1124       else if (val > spin_button->adjustment->upper)
1125         val = spin_button->adjustment->upper;
1126     }
1127   else if ((spin_button->update_policy == GTK_UPDATE_IF_VALID) && 
1128            (*error ||
1129            val < spin_button->adjustment->lower ||
1130            val > spin_button->adjustment->upper))
1131     {
1132       gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1133       return;
1134     }
1135
1136   if (spin_button->snap_to_ticks)
1137     gtk_spin_button_snap (spin_button, val);
1138   else
1139     {
1140       if (fabs (val - spin_button->adjustment->value) > EPSILON)
1141         gtk_adjustment_set_value (spin_button->adjustment, val);
1142       else
1143         {
1144           char buf[MAX_TEXT_LENGTH];
1145           
1146           sprintf (buf, "%0.*f", spin_button->digits, 
1147                    spin_button->adjustment->value);
1148           if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1149             gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1150         }
1151     }
1152 }
1153
1154 static void
1155 gtk_spin_button_activate (GtkEditable *editable)
1156 {
1157   g_return_if_fail (editable != NULL);
1158   g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
1159
1160   if (editable->editable)
1161     gtk_spin_button_update (GTK_SPIN_BUTTON (editable));
1162 }
1163
1164 static void
1165 gtk_spin_button_insert_text (GtkEditable *editable,
1166                              const gchar *new_text,
1167                              gint         new_text_length,
1168                              gint        *position)
1169 {
1170   GtkEntry *entry;
1171   GtkSpinButton *spin;
1172  
1173   g_return_if_fail (editable != NULL);
1174   g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
1175
1176   entry = GTK_ENTRY (editable);
1177   spin  = GTK_SPIN_BUTTON (editable);
1178
1179   if (spin->numeric)
1180     {
1181       struct lconv *lc;
1182       gboolean sign;
1183       gint dotpos = -1;
1184       gint i;
1185       gchar pos_sign;
1186       gchar neg_sign;
1187
1188       lc = localeconv ();
1189
1190       if (*(lc->negative_sign))
1191         neg_sign = *(lc->negative_sign);
1192       else 
1193         neg_sign = '-';
1194
1195       if (*(lc->positive_sign))
1196         pos_sign = *(lc->positive_sign);
1197       else 
1198         pos_sign = '+';
1199
1200       sign = ((strchr (entry->text, neg_sign) != 0) ||
1201               (strchr (entry->text, pos_sign) != 0));
1202
1203       if (sign && !(*position))
1204         return;
1205
1206       dotpos = strchr (entry->text, *(lc->decimal_point)) - entry->text;
1207       
1208       if (dotpos > -1 && *position > dotpos &&
1209           spin->digits - entry->text_length + dotpos - new_text_length + 1 < 0)
1210         return;
1211
1212       for (i = 0; i < new_text_length; i++)
1213         {
1214           if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1215             {
1216               if (sign || (*position) || i)
1217                 return;
1218               sign = TRUE;
1219             }
1220           else if (new_text[i] == *(lc->decimal_point))
1221             {
1222               if (!spin->digits || dotpos > -1 || 
1223                   (new_text_length - 1 - i + entry->text_length - *position > 
1224                    spin->digits)) 
1225                 return;
1226               dotpos = *position + i;
1227             }
1228           else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1229             return;
1230         }
1231     }
1232
1233   GTK_EDITABLE_CLASS (parent_class)->insert_text (editable, new_text,
1234                                                   new_text_length, position);
1235 }
1236
1237 static void
1238 gtk_spin_button_real_spin (GtkSpinButton *spin_button,
1239                            gfloat         increment)
1240 {
1241   GtkAdjustment *adj;
1242   gfloat new_value = 0.0;
1243
1244   g_return_if_fail (spin_button != NULL);
1245   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1246   
1247   adj = spin_button->adjustment;
1248
1249   new_value = adj->value + increment;
1250
1251   if (increment > 0)
1252     {
1253       if (spin_button->wrap)
1254         {
1255           if (fabs (adj->value - adj->upper) < EPSILON)
1256             new_value = adj->lower;
1257           else if (new_value > adj->upper)
1258             new_value = adj->upper;
1259         }
1260       else
1261         new_value = MIN (new_value, adj->upper);
1262     }
1263   else if (increment < 0) 
1264     {
1265       if (spin_button->wrap)
1266         {
1267           if (fabs (adj->value - adj->lower) < EPSILON)
1268             new_value = adj->upper;
1269           else if (new_value < adj->lower)
1270             new_value = adj->lower;
1271         }
1272       else
1273         new_value = MAX (new_value, adj->lower);
1274     }
1275
1276   if (fabs (new_value - adj->value) > EPSILON)
1277     gtk_adjustment_set_value (adj, new_value);
1278 }
1279
1280
1281 /***********************************************************
1282  ***********************************************************
1283  ***                  Public interface                   ***
1284  ***********************************************************
1285  ***********************************************************/
1286
1287
1288 void
1289 gtk_spin_button_construct (GtkSpinButton  *spin_button,
1290                            GtkAdjustment  *adjustment,
1291                            gfloat           climb_rate,
1292                            guint            digits)
1293 {
1294   g_message ("gtk_spin_button_construct() is deprecated");
1295
1296   gtk_spin_button_configure (spin_button, adjustment, climb_rate, digits);
1297 }
1298
1299 void
1300 gtk_spin_button_configure (GtkSpinButton  *spin_button,
1301                            GtkAdjustment  *adjustment,
1302                            gfloat          climb_rate,
1303                            guint           digits)
1304 {
1305   g_return_if_fail (spin_button != NULL);
1306   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1307   g_return_if_fail (digits < 6);
1308
1309   if (adjustment)
1310     gtk_spin_button_set_adjustment (spin_button, adjustment);
1311   else
1312     adjustment = spin_button->adjustment;
1313
1314   spin_button->digits = digits;
1315   spin_button->climb_rate = climb_rate;
1316   gtk_adjustment_value_changed (adjustment);
1317 }
1318
1319 GtkWidget *
1320 gtk_spin_button_new (GtkAdjustment *adjustment,
1321                      gfloat         climb_rate,
1322                      guint          digits)
1323 {
1324   GtkSpinButton *spin;
1325
1326   if (adjustment)
1327     g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
1328   g_return_val_if_fail (digits < 6, NULL);
1329
1330   spin = gtk_type_new (GTK_TYPE_SPIN_BUTTON);
1331
1332   gtk_spin_button_configure (spin, adjustment, climb_rate, digits);
1333
1334   return GTK_WIDGET (spin);
1335 }
1336
1337 void
1338 gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
1339                                 GtkAdjustment *adjustment)
1340 {
1341   g_return_if_fail (spin_button != NULL);
1342   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1343
1344   if (spin_button->adjustment != adjustment)
1345     {
1346       if (spin_button->adjustment)
1347         {
1348           gtk_signal_disconnect_by_data (GTK_OBJECT (spin_button->adjustment),
1349                                          (gpointer) spin_button);
1350           gtk_object_unref (GTK_OBJECT (spin_button->adjustment));
1351         }
1352       spin_button->adjustment = adjustment;
1353       if (adjustment)
1354         {
1355           gtk_object_ref (GTK_OBJECT (adjustment));
1356           gtk_object_sink (GTK_OBJECT (adjustment));
1357           gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
1358                               (GtkSignalFunc) gtk_spin_button_value_changed,
1359                               (gpointer) spin_button);
1360         }
1361     }
1362 }
1363
1364 GtkAdjustment *
1365 gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
1366 {
1367   g_return_val_if_fail (spin_button != NULL, NULL);
1368   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
1369
1370   return spin_button->adjustment;
1371 }
1372
1373 void
1374 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
1375                             guint          digits)
1376 {
1377   g_return_if_fail (spin_button != NULL);
1378   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1379   g_return_if_fail (digits < 6);
1380
1381   if (spin_button->digits != digits)
1382     {
1383       spin_button->digits = digits;
1384       gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1385     }
1386 }
1387
1388 gfloat
1389 gtk_spin_button_get_value_as_float (GtkSpinButton *spin_button)
1390 {
1391   g_return_val_if_fail (spin_button != NULL, 0.0);
1392   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0.0);
1393
1394   return spin_button->adjustment->value;
1395 }
1396
1397 gint
1398 gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
1399 {
1400   gfloat val;
1401
1402   g_return_val_if_fail (spin_button != NULL, 0);
1403   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1404
1405   val = spin_button->adjustment->value;
1406   if (val - floor (val) < ceil (val) - val)
1407     return floor (val);
1408   else
1409     return ceil (val);
1410 }
1411
1412 void 
1413 gtk_spin_button_set_value (GtkSpinButton *spin_button, 
1414                            gfloat         value)
1415 {
1416   g_return_if_fail (spin_button != NULL);
1417   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1418
1419   if (fabs (value - spin_button->adjustment->value) > EPSILON)
1420     gtk_adjustment_set_value (spin_button->adjustment, value);
1421   else
1422     {
1423       char buf[MAX_TEXT_LENGTH];
1424
1425       sprintf (buf, "%0.*f", spin_button->digits, 
1426                spin_button->adjustment->value);
1427       if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1428         gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1429     }
1430 }
1431
1432 void
1433 gtk_spin_button_set_update_policy (GtkSpinButton             *spin_button,
1434                                    GtkSpinButtonUpdatePolicy  policy)
1435 {
1436   g_return_if_fail (spin_button != NULL);
1437   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1438
1439   spin_button->update_policy = policy;
1440 }
1441
1442 void
1443 gtk_spin_button_set_numeric (GtkSpinButton  *spin_button,
1444                              gboolean        numeric)
1445 {
1446   g_return_if_fail (spin_button != NULL);
1447   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1448
1449   spin_button->numeric = (numeric != 0);
1450 }
1451
1452 void
1453 gtk_spin_button_set_wrap (GtkSpinButton  *spin_button,
1454                           gboolean        wrap)
1455 {
1456   g_return_if_fail (spin_button != NULL);
1457   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1458
1459   spin_button->wrap = (wrap != 0);
1460 }
1461
1462 void
1463 gtk_spin_button_set_shadow_type (GtkSpinButton *spin_button,
1464                                  GtkShadowType  shadow_type)
1465 {
1466   g_return_if_fail (spin_button != NULL);
1467   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1468
1469   if (shadow_type != spin_button->shadow_type)
1470     {
1471       spin_button->shadow_type = shadow_type;
1472       if (GTK_WIDGET_DRAWABLE (spin_button))
1473         gtk_widget_queue_draw (GTK_WIDGET (spin_button));
1474     }
1475 }
1476
1477 void
1478 gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
1479                                    gboolean       snap_to_ticks)
1480 {
1481   guint new_val;
1482
1483   g_return_if_fail (spin_button != NULL);
1484   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1485
1486   new_val = (snap_to_ticks != 0);
1487
1488   if (new_val != spin_button->snap_to_ticks)
1489     {
1490       spin_button->snap_to_ticks = new_val;
1491       if (new_val)
1492         {
1493           gchar *error = NULL;
1494           gfloat val;
1495
1496           val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
1497           gtk_spin_button_snap (spin_button, val);
1498         }
1499     }
1500 }
1501
1502 void
1503 gtk_spin_button_spin (GtkSpinButton *spin_button,
1504                       GtkSpinType    direction,
1505                       gfloat         increment)
1506 {
1507   GtkAdjustment *adj;
1508   gfloat diff;
1509
1510   g_return_if_fail (spin_button != NULL);
1511   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1512   
1513   adj = spin_button->adjustment;
1514
1515   /* for compatibility with the 1.0.x version of this function */
1516   if (increment != 0 && increment != adj->step_increment &&
1517       (direction == GTK_SPIN_STEP_FORWARD ||
1518        direction == GTK_SPIN_STEP_BACKWARD))
1519     {
1520       if (direction == GTK_SPIN_STEP_BACKWARD && increment > 0)
1521         increment = -increment;
1522       direction = GTK_SPIN_USER_DEFINED;
1523     }
1524
1525   switch (direction)
1526     {
1527     case GTK_SPIN_STEP_FORWARD:
1528
1529       gtk_spin_button_real_spin (spin_button, adj->step_increment);
1530       break;
1531
1532     case GTK_SPIN_STEP_BACKWARD:
1533
1534       gtk_spin_button_real_spin (spin_button, -adj->step_increment);
1535       break;
1536
1537     case GTK_SPIN_PAGE_FORWARD:
1538
1539       gtk_spin_button_real_spin (spin_button, adj->page_increment);
1540       break;
1541
1542     case GTK_SPIN_PAGE_BACKWARD:
1543
1544       gtk_spin_button_real_spin (spin_button, -adj->page_increment);
1545       break;
1546
1547     case GTK_SPIN_HOME:
1548
1549       diff = adj->value - adj->lower;
1550       if (diff > EPSILON)
1551         gtk_spin_button_real_spin (spin_button, -diff);
1552       break;
1553
1554     case GTK_SPIN_END:
1555
1556       diff = adj->upper - adj->value;
1557       if (diff > EPSILON)
1558         gtk_spin_button_real_spin (spin_button, diff);
1559       break;
1560
1561     case GTK_SPIN_USER_DEFINED:
1562
1563       if (increment != 0)
1564         gtk_spin_button_real_spin (spin_button, increment);
1565       break;
1566
1567     default:
1568       break;
1569     }
1570 }