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