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