]> Pileus Git - ~andy/gtk/blob - gtk/gtkspinbutton.c
New default theme! A slighly improved version of the 2.0 Raleigh theme,
[~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 #include "gtksettings.h"
40
41
42 #define MIN_SPIN_BUTTON_WIDTH              30
43 #define ARROW_SIZE                         11
44 #define SPIN_BUTTON_INITIAL_TIMER_DELAY    200
45 #define SPIN_BUTTON_TIMER_DELAY            20
46 #define MAX_TEXT_LENGTH                    256
47 #define MAX_TIMER_CALLS                    5
48 #define EPSILON                            1e-5
49
50 enum {
51   ARG_0,
52   ARG_ADJUSTMENT,
53   ARG_CLIMB_RATE,
54   ARG_DIGITS,
55   ARG_SNAP_TO_TICKS,
56   ARG_NUMERIC,
57   ARG_WRAP,
58   ARG_UPDATE_POLICY,
59   ARG_VALUE
60 };
61
62 /* Signals */
63 enum
64 {
65   INPUT,
66   OUTPUT,
67   VALUE_CHANGED,
68   LAST_SIGNAL
69 };
70
71 static void gtk_spin_button_class_init     (GtkSpinButtonClass *klass);
72 static void gtk_spin_button_init           (GtkSpinButton      *spin_button);
73 static void gtk_spin_button_finalize       (GObject            *object);
74 static void gtk_spin_button_set_arg        (GtkObject          *object,
75                                             GtkArg             *arg,
76                                             guint               arg_id);
77 static void gtk_spin_button_get_arg        (GtkObject          *object,
78                                             GtkArg             *arg,
79                                             guint               arg_id);
80 static void gtk_spin_button_map            (GtkWidget          *widget);
81 static void gtk_spin_button_unmap          (GtkWidget          *widget);
82 static void gtk_spin_button_realize        (GtkWidget          *widget);
83 static void gtk_spin_button_unrealize      (GtkWidget          *widget);
84 static void gtk_spin_button_size_request   (GtkWidget          *widget,
85                                             GtkRequisition     *requisition);
86 static void gtk_spin_button_size_allocate  (GtkWidget          *widget,
87                                             GtkAllocation      *allocation);
88 static gint gtk_spin_button_expose         (GtkWidget          *widget,
89                                             GdkEventExpose     *event);
90 static gint gtk_spin_button_button_press   (GtkWidget          *widget,
91                                             GdkEventButton     *event);
92 static gint gtk_spin_button_button_release (GtkWidget          *widget,
93                                             GdkEventButton     *event);
94 static gint gtk_spin_button_motion_notify  (GtkWidget          *widget,
95                                             GdkEventMotion     *event);
96 static gint gtk_spin_button_enter_notify   (GtkWidget          *widget,
97                                             GdkEventCrossing   *event);
98 static gint gtk_spin_button_leave_notify   (GtkWidget          *widget,
99                                             GdkEventCrossing   *event);
100 static gint gtk_spin_button_focus_out      (GtkWidget          *widget,
101                                             GdkEventFocus      *event);
102 static void gtk_spin_button_draw_arrow     (GtkSpinButton      *spin_button, 
103                                             guint               arrow);
104 static gint gtk_spin_button_timer          (GtkSpinButton      *spin_button);
105 static void gtk_spin_button_value_changed  (GtkAdjustment      *adjustment,
106                                             GtkSpinButton      *spin_button); 
107 static gint gtk_spin_button_key_press      (GtkWidget          *widget,
108                                             GdkEventKey        *event);
109 static gint gtk_spin_button_key_release    (GtkWidget          *widget,
110                                             GdkEventKey        *event);
111 static gint gtk_spin_button_scroll         (GtkWidget          *widget,
112                                             GdkEventScroll     *event);
113 static void gtk_spin_button_activate       (GtkEntry           *entry);
114 static void gtk_spin_button_snap           (GtkSpinButton      *spin_button,
115                                             gdouble             val);
116 static void gtk_spin_button_insert_text    (GtkEntry           *entry,
117                                             const gchar        *new_text,
118                                             gint                new_text_length,
119                                             gint               *position);
120 static void gtk_spin_button_real_spin      (GtkSpinButton      *spin_button,
121                                             gdouble             step);
122 static gint gtk_spin_button_default_input  (GtkSpinButton      *spin_button,
123                                             gdouble            *new_val);
124 static gint gtk_spin_button_default_output (GtkSpinButton      *spin_button);
125 static gint spin_button_get_shadow_type    (GtkSpinButton      *spin_button);
126
127
128 static GtkEntryClass *parent_class = NULL;
129 static guint spinbutton_signals[LAST_SIGNAL] = {0};
130
131
132 GtkType
133 gtk_spin_button_get_type (void)
134 {
135   static guint spin_button_type = 0;
136
137   if (!spin_button_type)
138     {
139       static const GtkTypeInfo spin_button_info =
140       {
141         "GtkSpinButton",
142         sizeof (GtkSpinButton),
143         sizeof (GtkSpinButtonClass),
144         (GtkClassInitFunc) gtk_spin_button_class_init,
145         (GtkObjectInitFunc) gtk_spin_button_init,
146         /* reserved_1 */ NULL,
147         /* reserved_2 */ NULL,
148         (GtkClassInitFunc) NULL,
149       };
150
151       spin_button_type = gtk_type_unique (GTK_TYPE_ENTRY, &spin_button_info);
152     }
153   return spin_button_type;
154 }
155
156 static void
157 gtk_spin_button_class_init (GtkSpinButtonClass *class)
158 {
159   GObjectClass     *gobject_class = G_OBJECT_CLASS (class);
160   GtkObjectClass   *object_class;
161   GtkWidgetClass   *widget_class;
162   GtkEntryClass    *entry_class;
163
164   object_class   = (GtkObjectClass*)   class;
165   widget_class   = (GtkWidgetClass*)   class;
166   entry_class    = (GtkEntryClass*)    class; 
167
168   parent_class = gtk_type_class (GTK_TYPE_ENTRY);
169
170   gobject_class->finalize = gtk_spin_button_finalize;
171
172   object_class->set_arg = gtk_spin_button_set_arg;
173   object_class->get_arg = gtk_spin_button_get_arg;
174
175   widget_class->map = gtk_spin_button_map;
176   widget_class->unmap = gtk_spin_button_unmap;
177   widget_class->realize = gtk_spin_button_realize;
178   widget_class->unrealize = gtk_spin_button_unrealize;
179   widget_class->size_request = gtk_spin_button_size_request;
180   widget_class->size_allocate = gtk_spin_button_size_allocate;
181   widget_class->expose_event = gtk_spin_button_expose;
182   widget_class->scroll_event = gtk_spin_button_scroll;
183   widget_class->button_press_event = gtk_spin_button_button_press;
184   widget_class->button_release_event = gtk_spin_button_button_release;
185   widget_class->motion_notify_event = gtk_spin_button_motion_notify;
186   widget_class->key_press_event = gtk_spin_button_key_press;
187   widget_class->key_release_event = gtk_spin_button_key_release;
188   widget_class->enter_notify_event = gtk_spin_button_enter_notify;
189   widget_class->leave_notify_event = gtk_spin_button_leave_notify;
190   widget_class->focus_out_event = gtk_spin_button_focus_out;
191
192   entry_class->insert_text = gtk_spin_button_insert_text;
193   entry_class->activate = gtk_spin_button_activate;
194
195   class->input = NULL;
196   class->output = NULL;
197
198   gtk_object_add_arg_type ("GtkSpinButton::adjustment",
199                            GTK_TYPE_ADJUSTMENT,
200                            GTK_ARG_READWRITE,
201                            ARG_ADJUSTMENT);
202   gtk_object_add_arg_type ("GtkSpinButton::climb_rate",
203                            GTK_TYPE_DOUBLE,
204                            GTK_ARG_READWRITE,
205                            ARG_CLIMB_RATE);
206   gtk_object_add_arg_type ("GtkSpinButton::digits",
207                            GTK_TYPE_UINT,
208                            GTK_ARG_READWRITE,
209                            ARG_DIGITS);
210   gtk_object_add_arg_type ("GtkSpinButton::snap_to_ticks",
211                            GTK_TYPE_BOOL,
212                            GTK_ARG_READWRITE,
213                            ARG_SNAP_TO_TICKS);
214   gtk_object_add_arg_type ("GtkSpinButton::numeric",
215                            GTK_TYPE_BOOL,
216                            GTK_ARG_READWRITE,
217                            ARG_NUMERIC);
218   gtk_object_add_arg_type ("GtkSpinButton::wrap",
219                            GTK_TYPE_BOOL,
220                            GTK_ARG_READWRITE,
221                            ARG_WRAP);
222   gtk_object_add_arg_type ("GtkSpinButton::update_policy",
223                            GTK_TYPE_SPIN_BUTTON_UPDATE_POLICY,
224                            GTK_ARG_READWRITE,
225                            ARG_UPDATE_POLICY);
226   gtk_object_add_arg_type ("GtkSpinButton::value",
227                            GTK_TYPE_DOUBLE,
228                            GTK_ARG_READWRITE,
229                            ARG_VALUE);
230   
231   gtk_widget_class_install_style_property_parser (widget_class,
232                                                   g_param_spec_enum ("shadow_type", "Shadow Type", NULL,
233                                                                      GTK_TYPE_SHADOW_TYPE,
234                                                                      GTK_SHADOW_IN,
235                                                                      G_PARAM_READABLE),
236                                                   gtk_rc_property_parse_enum);
237   spinbutton_signals[INPUT] =
238     gtk_signal_new ("input",
239                     GTK_RUN_LAST,
240                     GTK_CLASS_TYPE (object_class),
241                     GTK_SIGNAL_OFFSET (GtkSpinButtonClass, input),
242                     gtk_marshal_INT__POINTER,
243                     GTK_TYPE_INT, 1, GTK_TYPE_POINTER);
244
245   spinbutton_signals[OUTPUT] =
246     g_signal_newc ("output",
247                    G_TYPE_FROM_CLASS(object_class),
248                    G_SIGNAL_RUN_LAST,
249                    G_STRUCT_OFFSET(GtkSpinButtonClass, output),
250                    _gtk_boolean_handled_accumulator, NULL,
251                    gtk_marshal_BOOLEAN__VOID,
252                    G_TYPE_BOOLEAN, 0);
253
254   spinbutton_signals[VALUE_CHANGED] =
255     gtk_signal_new ("value_changed",
256                     GTK_RUN_LAST,
257                     GTK_CLASS_TYPE (object_class),
258                     GTK_SIGNAL_OFFSET (GtkSpinButtonClass, value_changed),
259                     gtk_marshal_VOID__VOID,
260                     GTK_TYPE_NONE, 0);
261 }
262
263 static void
264 gtk_spin_button_set_arg (GtkObject        *object,
265                          GtkArg           *arg,
266                          guint             arg_id)
267 {
268   GtkSpinButton *spin_button;
269
270   spin_button = GTK_SPIN_BUTTON (object);
271   
272   switch (arg_id)
273     {
274       GtkAdjustment *adjustment;
275
276     case ARG_ADJUSTMENT:
277       adjustment = GTK_VALUE_POINTER (*arg);
278       if (!adjustment)
279         adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
280       gtk_spin_button_set_adjustment (spin_button, adjustment);
281       break;
282     case ARG_CLIMB_RATE:
283       gtk_spin_button_configure (spin_button,
284                                  spin_button->adjustment,
285                                  GTK_VALUE_DOUBLE (*arg),
286                                  spin_button->digits);
287       break;
288     case ARG_DIGITS:
289       gtk_spin_button_configure (spin_button,
290                                  spin_button->adjustment,
291                                  spin_button->climb_rate,
292                                  GTK_VALUE_UINT (*arg));
293       break;
294     case ARG_SNAP_TO_TICKS:
295       gtk_spin_button_set_snap_to_ticks (spin_button, GTK_VALUE_BOOL (*arg));
296       break;
297     case ARG_NUMERIC:
298       gtk_spin_button_set_numeric (spin_button, GTK_VALUE_BOOL (*arg));
299       break;
300     case ARG_WRAP:
301       gtk_spin_button_set_wrap (spin_button, GTK_VALUE_BOOL (*arg));
302       break;
303     case ARG_UPDATE_POLICY:
304       gtk_spin_button_set_update_policy (spin_button, GTK_VALUE_ENUM (*arg));
305       break;
306     case ARG_VALUE:
307       gtk_spin_button_set_value (spin_button, GTK_VALUE_DOUBLE (*arg));
308       break;
309     default:
310       break;
311     }
312 }
313
314 static void
315 gtk_spin_button_get_arg (GtkObject        *object,
316                          GtkArg           *arg,
317                          guint             arg_id)
318 {
319   GtkSpinButton *spin_button;
320
321   spin_button = GTK_SPIN_BUTTON (object);
322   
323   switch (arg_id)
324     {
325     case ARG_ADJUSTMENT:
326       GTK_VALUE_POINTER (*arg) = spin_button->adjustment;
327       break;
328     case ARG_CLIMB_RATE:
329       GTK_VALUE_DOUBLE (*arg) = spin_button->climb_rate;
330       break;
331     case ARG_DIGITS:
332       GTK_VALUE_UINT (*arg) = spin_button->digits;
333       break;
334     case ARG_SNAP_TO_TICKS:
335       GTK_VALUE_BOOL (*arg) = spin_button->snap_to_ticks;
336       break;
337     case ARG_NUMERIC:
338       GTK_VALUE_BOOL (*arg) = spin_button->numeric;
339       break;
340     case ARG_WRAP:
341       GTK_VALUE_BOOL (*arg) = spin_button->wrap;
342       break;
343     case ARG_UPDATE_POLICY:
344       GTK_VALUE_ENUM (*arg) = spin_button->update_policy;
345       break;
346     case ARG_VALUE:
347       GTK_VALUE_DOUBLE (*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->timer = 0;
361   spin_button->ev_time = 0;
362   spin_button->climb_rate = 0.0;
363   spin_button->timer_step = 0.0;
364   spin_button->update_policy = GTK_UPDATE_ALWAYS;
365   spin_button->in_child = 2;
366   spin_button->click_child = 2;
367   spin_button->button = 0;
368   spin_button->need_timer = FALSE;
369   spin_button->timer_calls = 0;
370   spin_button->digits = 0;
371   spin_button->numeric = FALSE;
372   spin_button->wrap = FALSE;
373   spin_button->snap_to_ticks = FALSE;
374   gtk_spin_button_set_adjustment (spin_button,
375           (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0));
376 }
377
378 static void
379 gtk_spin_button_finalize (GObject *object)
380 {
381   g_return_if_fail (GTK_IS_SPIN_BUTTON (object));
382
383   gtk_object_unref (GTK_OBJECT (GTK_SPIN_BUTTON (object)->adjustment));
384   
385   G_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->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->xthickness);
449   attributes.y = widget->allocation.y + (widget->allocation.height -
450                                          widget->requisition.height) / 2;
451   attributes.width = ARROW_SIZE + 2 * widget->style->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   GtkEntry *entry;
492   GtkSpinButton *spin_button;
493   
494   g_return_if_fail (widget != NULL);
495   g_return_if_fail (requisition != NULL);
496   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
497
498   entry = GTK_ENTRY (widget);
499   spin_button = GTK_SPIN_BUTTON (widget);
500   
501   GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
502
503   if (entry->width_chars < 0)
504     {
505       PangoFontMetrics metrics;
506       PangoFont *font;
507       gchar *lang;
508       gchar buf[MAX_TEXT_LENGTH];
509       gint width;
510       gint w;
511
512
513       font = pango_context_load_font (gtk_widget_get_pango_context (widget),
514                                       widget->style->font_desc);
515       lang = pango_context_get_lang (gtk_widget_get_pango_context (widget));
516       pango_font_get_metrics (font, lang, &metrics);
517       g_free (lang);
518       g_object_unref (G_OBJECT (font));
519       
520       /* Get max of MIN_SPIN_BUTTON_WIDTH, size of upper, size of lower */
521       
522       width = MIN_SPIN_BUTTON_WIDTH;
523       
524       sprintf (buf, "%0.*f", spin_button->digits, spin_button->adjustment->upper);
525       w = strlen (buf) * PANGO_PIXELS (metrics.approximate_digit_width);
526       width = MAX (width, w);
527       sprintf (buf, "%0.*f", spin_button->digits, spin_button->adjustment->lower);
528       w = strlen (buf) * PANGO_PIXELS (metrics.approximate_digit_width);
529       width = MAX (width, w);
530       
531       requisition->width = width + ARROW_SIZE + 2 * widget->style->xthickness;
532     }
533   else
534     requisition->width += ARROW_SIZE + 2 * widget->style->xthickness;
535 }
536
537 static void
538 gtk_spin_button_size_allocate (GtkWidget     *widget,
539                                GtkAllocation *allocation)
540 {
541   GtkAllocation child_allocation;
542
543   g_return_if_fail (widget != NULL);
544   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
545   g_return_if_fail (allocation != NULL);
546
547   child_allocation = *allocation;
548   if (child_allocation.width > ARROW_SIZE + 2 * widget->style->xthickness)
549     child_allocation.width -= ARROW_SIZE + 2 * widget->style->xthickness;
550
551   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
552     child_allocation.x += ARROW_SIZE + 2 * widget->style->xthickness;
553
554   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &child_allocation);
555
556   widget->allocation = *allocation;
557
558   if (GTK_WIDGET_REALIZED (widget))
559     {
560       child_allocation.width = ARROW_SIZE + 2 * widget->style->xthickness;
561       child_allocation.height = widget->requisition.height;
562
563       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
564         child_allocation.x = (allocation->x + allocation->width - ARROW_SIZE - 
565                               2 * widget->style->xthickness);
566       else
567         child_allocation.x = allocation->x;      
568
569       child_allocation.y = allocation->y + (allocation->height - widget->requisition.height) / 2;
570
571       gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel, 
572                               child_allocation.x,
573                               child_allocation.y,
574                               child_allocation.width,
575                               child_allocation.height); 
576     }
577 }
578
579 static gint
580 gtk_spin_button_expose (GtkWidget      *widget,
581                         GdkEventExpose *event)
582 {
583   GtkSpinButton *spin;
584
585   g_return_val_if_fail (widget != NULL, FALSE);
586   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
587   g_return_val_if_fail (event != NULL, FALSE);
588
589   spin = GTK_SPIN_BUTTON (widget);
590
591   if (GTK_WIDGET_DRAWABLE (widget))
592     {
593       GtkShadowType shadow_type;
594
595       /* FIXME this seems like really broken code -
596        * why aren't we looking at event->window
597        * and acting accordingly?
598        */
599
600       shadow_type = spin_button_get_shadow_type (spin);
601       if (shadow_type != GTK_SHADOW_NONE)
602         gtk_paint_box (widget->style, spin->panel,
603                        GTK_STATE_NORMAL, shadow_type,
604                        &event->area, widget, "spinbutton",
605                        0, 0, 
606                        ARROW_SIZE + 2 * widget->style->xthickness,
607                        widget->requisition.height); 
608       else
609          {
610             gdk_window_set_back_pixmap (spin->panel, NULL, TRUE);
611             gdk_window_clear_area (spin->panel,
612                                    event->area.x, event->area.y,
613                                    event->area.width, event->area.height);
614          }
615        gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
616        gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
617
618        GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
619     }
620
621   return FALSE;
622 }
623
624 static void
625 gtk_spin_button_draw_arrow (GtkSpinButton *spin_button, 
626                             guint          arrow)
627 {
628   GtkShadowType spin_shadow_type;
629   GtkStateType state_type;
630   GtkShadowType shadow_type;
631   GtkWidget *widget;
632   gint x;
633   gint y;
634
635   g_return_if_fail (spin_button != NULL);
636   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
637   
638   widget = GTK_WIDGET (spin_button);
639   spin_shadow_type = spin_button_get_shadow_type (spin_button);
640
641   if (GTK_WIDGET_DRAWABLE (spin_button))
642     {
643       if (!spin_button->wrap &&
644           (((arrow == GTK_ARROW_UP &&
645           (spin_button->adjustment->upper - spin_button->adjustment->value
646            <= EPSILON))) ||
647           ((arrow == GTK_ARROW_DOWN &&
648           (spin_button->adjustment->value - spin_button->adjustment->lower
649            <= EPSILON)))))
650         {
651           shadow_type = GTK_SHADOW_ETCHED_IN;
652           state_type = GTK_STATE_NORMAL;
653         }
654       else
655         {
656           if (spin_button->in_child == arrow)
657             {
658               if (spin_button->click_child == arrow)
659                 state_type = GTK_STATE_ACTIVE;
660               else
661                 state_type = GTK_STATE_PRELIGHT;
662             }
663           else
664             state_type = GTK_STATE_NORMAL;
665           
666           if (spin_button->click_child == arrow)
667             shadow_type = GTK_SHADOW_IN;
668           else
669             shadow_type = GTK_SHADOW_OUT;
670         }
671       if (arrow == GTK_ARROW_UP)
672         {
673           if (spin_shadow_type != GTK_SHADOW_NONE)
674             {
675               x = widget->style->xthickness;
676               y = widget->style->ythickness;
677             }
678           else
679             {
680               x = widget->style->xthickness - 1;
681               y = widget->style->ythickness - 1;
682             }
683           gtk_paint_arrow (widget->style, spin_button->panel,
684                            state_type, shadow_type, 
685                            NULL, widget, "spinbutton",
686                            arrow, TRUE, 
687                            x, y, ARROW_SIZE, widget->requisition.height / 2 
688                            - widget->style->ythickness);
689         }
690       else
691         {
692           if (spin_shadow_type != GTK_SHADOW_NONE)
693             {
694               x = widget->style->xthickness;
695               y = widget->requisition.height / 2;
696             }
697           else
698             {
699               x = widget->style->xthickness - 1;
700               y = widget->requisition.height / 2 + 1;
701             }
702           gtk_paint_arrow (widget->style, spin_button->panel,
703                            state_type, shadow_type, 
704                            NULL, widget, "spinbutton",
705                            arrow, TRUE, 
706                            x, y, ARROW_SIZE, widget->requisition.height / 2 
707                            - widget->style->ythickness);
708         }
709     }
710 }
711
712 static gint
713 gtk_spin_button_enter_notify (GtkWidget        *widget,
714                               GdkEventCrossing *event)
715 {
716   GtkSpinButton *spin;
717
718   g_return_val_if_fail (widget != NULL, FALSE);
719   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
720   g_return_val_if_fail (event != NULL, FALSE);
721
722   spin = GTK_SPIN_BUTTON (widget);
723
724   if (event->window == spin->panel)
725     {
726       gint x;
727       gint y;
728
729       gdk_window_get_pointer (spin->panel, &x, &y, NULL);
730
731       if (y <= widget->requisition.height / 2)
732         {
733           spin->in_child = GTK_ARROW_UP;
734           if (spin->click_child == 2) 
735             gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
736         }
737       else
738         {
739           spin->in_child = GTK_ARROW_DOWN;
740           if (spin->click_child == 2) 
741             gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
742         }
743     }
744   return FALSE;
745 }
746
747 static gint
748 gtk_spin_button_leave_notify (GtkWidget        *widget,
749                               GdkEventCrossing *event)
750 {
751   GtkSpinButton *spin;
752
753   g_return_val_if_fail (widget != NULL, FALSE);
754   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
755   g_return_val_if_fail (event != NULL, FALSE);
756
757   spin = GTK_SPIN_BUTTON (widget);
758
759   if (event->window == spin->panel && spin->click_child == 2)
760     {
761       if (spin->in_child == GTK_ARROW_UP) 
762         {
763           spin->in_child = 2;
764           gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
765         }
766       else
767         {
768           spin->in_child = 2;
769           gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
770         }
771     }
772   return FALSE;
773 }
774
775 static gint
776 gtk_spin_button_focus_out (GtkWidget     *widget,
777                            GdkEventFocus *event)
778 {
779   g_return_val_if_fail (widget != NULL, FALSE);
780   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
781   g_return_val_if_fail (event != NULL, FALSE);
782
783   if (GTK_ENTRY (widget)->editable)
784     gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
785
786   return GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
787 }
788
789 static gint
790 gtk_spin_button_scroll (GtkWidget      *widget,
791                         GdkEventScroll *event)
792 {
793   GtkSpinButton *spin;
794
795   g_return_val_if_fail (widget != NULL, FALSE);
796   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
797   g_return_val_if_fail (event != NULL, FALSE);
798
799   spin = GTK_SPIN_BUTTON (widget);
800
801   if (event->direction == GDK_SCROLL_UP)
802     {
803       if (!GTK_WIDGET_HAS_FOCUS (widget))
804         gtk_widget_grab_focus (widget);
805       gtk_spin_button_real_spin (spin, spin->adjustment->step_increment);
806     }
807   else if (event->direction == GDK_SCROLL_DOWN)
808     {
809       if (!GTK_WIDGET_HAS_FOCUS (widget))
810         gtk_widget_grab_focus (widget);
811       gtk_spin_button_real_spin (spin, -spin->adjustment->step_increment); 
812     }
813   else
814     return FALSE;
815
816   return TRUE;
817 }
818
819 static gint
820 gtk_spin_button_button_press (GtkWidget      *widget,
821                               GdkEventButton *event)
822 {
823   GtkSpinButton *spin;
824
825   g_return_val_if_fail (widget != NULL, FALSE);
826   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
827   g_return_val_if_fail (event != NULL, FALSE);
828
829   spin = GTK_SPIN_BUTTON (widget);
830
831   if (!spin->button)
832     {
833       if (event->window == spin->panel)
834         {
835           if (!GTK_WIDGET_HAS_FOCUS (widget))
836             gtk_widget_grab_focus (widget);
837           gtk_grab_add (widget);
838           spin->button = event->button;
839           
840           if (GTK_ENTRY (widget)->editable)
841             gtk_spin_button_update (spin);
842           
843           if (event->y <= widget->requisition.height / 2)
844             {
845               spin->click_child = GTK_ARROW_UP;
846               if (event->button == 1)
847                 {
848                  gtk_spin_button_real_spin (spin, 
849                                             spin->adjustment->step_increment);
850                   if (!spin->timer)
851                     {
852                       spin->timer_step = spin->adjustment->step_increment;
853                       spin->need_timer = TRUE;
854                       spin->timer = gtk_timeout_add 
855                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
856                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
857                     }
858                 }
859               else if (event->button == 2)
860                 {
861                  gtk_spin_button_real_spin (spin, 
862                                             spin->adjustment->page_increment);
863                   if (!spin->timer) 
864                     {
865                       spin->timer_step = spin->adjustment->page_increment;
866                       spin->need_timer = TRUE;
867                       spin->timer = gtk_timeout_add 
868                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
869                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
870                     }
871                 }
872               gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
873             }
874           else 
875             {
876               spin->click_child = GTK_ARROW_DOWN;
877               if (event->button == 1)
878                 {
879                   gtk_spin_button_real_spin (spin,
880                                              -spin->adjustment->step_increment);
881                   if (!spin->timer)
882                     {
883                       spin->timer_step = spin->adjustment->step_increment;
884                       spin->need_timer = TRUE;
885                       spin->timer = gtk_timeout_add 
886                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
887                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
888                     }
889                 }      
890               else if (event->button == 2)
891                 {
892                   gtk_spin_button_real_spin (spin,
893                                              -spin->adjustment->page_increment);
894                   if (!spin->timer) 
895                     {
896                       spin->timer_step = spin->adjustment->page_increment;
897                       spin->need_timer = TRUE;
898                       spin->timer = gtk_timeout_add 
899                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
900                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
901                     }
902                 }
903               gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
904             }
905           return TRUE;
906         }
907       else
908         return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
909     }
910   return FALSE;
911 }
912
913 static gint
914 gtk_spin_button_button_release (GtkWidget      *widget,
915                                 GdkEventButton *event)
916 {
917   GtkSpinButton *spin;
918
919   g_return_val_if_fail (widget != NULL, FALSE);
920   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
921   g_return_val_if_fail (event != NULL, FALSE);
922
923   spin = GTK_SPIN_BUTTON (widget);
924
925   if (event->button == spin->button)
926     {
927       guint click_child;
928
929       if (spin->timer)
930         {
931           gtk_timeout_remove (spin->timer);
932           spin->timer = 0;
933           spin->timer_calls = 0;
934           spin->need_timer = FALSE;
935         }
936
937       if (event->button == 3)
938         {
939           if (event->y >= 0 && event->x >= 0 && 
940               event->y <= widget->requisition.height &&
941               event->x <= ARROW_SIZE + 2 * widget->style->xthickness)
942             {
943               if (spin->click_child == GTK_ARROW_UP &&
944                   event->y <= widget->requisition.height / 2)
945                 {
946                   gdouble diff;
947
948                   diff = spin->adjustment->upper - spin->adjustment->value;
949                   if (diff > EPSILON)
950                     gtk_spin_button_real_spin (spin, diff);
951                 }
952               else if (spin->click_child == GTK_ARROW_DOWN &&
953                        event->y > widget->requisition.height / 2)
954                 {
955                   gdouble diff;
956
957                   diff = spin->adjustment->value - spin->adjustment->lower;
958                   if (diff > EPSILON)
959                     gtk_spin_button_real_spin (spin, -diff);
960                 }
961             }
962         }                 
963       gtk_grab_remove (widget);
964       click_child = spin->click_child;
965       spin->click_child = 2;
966       spin->button = 0;
967       gtk_spin_button_draw_arrow (spin, click_child);
968       return TRUE;
969     }
970   else
971     return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
972
973   return FALSE;
974 }
975
976 static gint
977 gtk_spin_button_motion_notify (GtkWidget      *widget,
978                                GdkEventMotion *event)
979 {
980   GtkSpinButton *spin;
981
982   g_return_val_if_fail (widget != NULL, FALSE);
983   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
984   g_return_val_if_fail (event != NULL, FALSE);
985
986   spin = GTK_SPIN_BUTTON (widget);
987   
988   if (spin->button)
989     return FALSE;
990
991   if (event->window == spin->panel)
992     {
993       gint y;
994
995       y = event->y;
996       if (event->is_hint)
997         gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
998
999       if (y <= widget->requisition.height / 2 && 
1000           spin->in_child == GTK_ARROW_DOWN)
1001         {
1002           spin->in_child = GTK_ARROW_UP;
1003           gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
1004           gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
1005         }
1006       else if (y > widget->requisition.height / 2 && 
1007           spin->in_child == GTK_ARROW_UP)
1008         {
1009           spin->in_child = GTK_ARROW_DOWN;
1010           gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
1011           gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
1012         }
1013       return FALSE;
1014     }
1015           
1016   return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
1017 }
1018
1019 static gint
1020 gtk_spin_button_timer (GtkSpinButton *spin_button)
1021 {
1022   gboolean retval = FALSE;
1023   
1024   GDK_THREADS_ENTER ();
1025
1026   if (spin_button->timer)
1027     {
1028       if (spin_button->click_child == GTK_ARROW_UP)
1029         gtk_spin_button_real_spin (spin_button, spin_button->timer_step);
1030       else
1031         gtk_spin_button_real_spin (spin_button, -spin_button->timer_step);
1032
1033       if (spin_button->need_timer)
1034         {
1035           spin_button->need_timer = FALSE;
1036           spin_button->timer = gtk_timeout_add 
1037             (SPIN_BUTTON_TIMER_DELAY, (GtkFunction) gtk_spin_button_timer, 
1038              (gpointer) spin_button);
1039         }
1040       else 
1041         {
1042           if (spin_button->climb_rate > 0.0 && spin_button->timer_step 
1043               < spin_button->adjustment->page_increment)
1044             {
1045               if (spin_button->timer_calls < MAX_TIMER_CALLS)
1046                 spin_button->timer_calls++;
1047               else 
1048                 {
1049                   spin_button->timer_calls = 0;
1050                   spin_button->timer_step += spin_button->climb_rate;
1051                 }
1052             }
1053           retval = TRUE;
1054         }
1055     }
1056
1057   GDK_THREADS_LEAVE ();
1058
1059   return retval;
1060 }
1061
1062 static void
1063 gtk_spin_button_value_changed (GtkAdjustment *adjustment,
1064                                GtkSpinButton *spin_button)
1065 {
1066   gint return_val;
1067
1068   g_return_if_fail (adjustment != NULL);
1069   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1070
1071   return_val = FALSE;
1072   gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1073                    &return_val);
1074   if (return_val == FALSE)
1075     gtk_spin_button_default_output (spin_button);
1076
1077   gtk_signal_emit (GTK_OBJECT (spin_button), 
1078                    spinbutton_signals[VALUE_CHANGED]);
1079
1080   gtk_spin_button_draw_arrow (spin_button, GTK_ARROW_UP);
1081   gtk_spin_button_draw_arrow (spin_button, GTK_ARROW_DOWN);
1082 }
1083
1084 static gint
1085 gtk_spin_button_key_press (GtkWidget     *widget,
1086                            GdkEventKey   *event)
1087 {
1088   GtkSpinButton *spin;
1089   gint key;
1090   gboolean key_repeat = FALSE;
1091
1092   g_return_val_if_fail (widget != NULL, FALSE);
1093   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1094   g_return_val_if_fail (event != NULL, FALSE);
1095   
1096   spin = GTK_SPIN_BUTTON (widget);
1097   key = event->keyval;
1098
1099   key_repeat = (event->time == spin->ev_time);
1100
1101   if (GTK_ENTRY (widget)->editable &&
1102       (key == GDK_Up || key == GDK_Down || 
1103        key == GDK_Page_Up || key == GDK_Page_Down))
1104     gtk_spin_button_update (spin);
1105
1106   switch (key)
1107     {
1108     case GDK_Up:
1109
1110       if (GTK_WIDGET_HAS_FOCUS (widget))
1111         {
1112           gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), 
1113                                         "key_press_event");
1114           if (!key_repeat)
1115             spin->timer_step = spin->adjustment->step_increment;
1116
1117          gtk_spin_button_real_spin (spin, spin->timer_step);
1118
1119           if (key_repeat)
1120             {
1121               if (spin->climb_rate > 0.0 && spin->timer_step
1122                   < spin->adjustment->page_increment)
1123                 {
1124                   if (spin->timer_calls < MAX_TIMER_CALLS)
1125                     spin->timer_calls++;
1126                   else 
1127                     {
1128                       spin->timer_calls = 0;
1129                       spin->timer_step += spin->climb_rate;
1130                     }
1131                 }
1132             }
1133           return TRUE;
1134         }
1135       return FALSE;
1136
1137     case GDK_Down:
1138
1139       if (GTK_WIDGET_HAS_FOCUS (widget))
1140         {
1141           gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), 
1142                                         "key_press_event");
1143           if (!key_repeat)
1144             spin->timer_step = spin->adjustment->step_increment;
1145
1146          gtk_spin_button_real_spin (spin, -spin->timer_step);
1147
1148           if (key_repeat)
1149             {
1150               if (spin->climb_rate > 0.0 && spin->timer_step
1151                   < spin->adjustment->page_increment)
1152                 {
1153                   if (spin->timer_calls < MAX_TIMER_CALLS)
1154                     spin->timer_calls++;
1155                   else 
1156                     {
1157                       spin->timer_calls = 0;
1158                       spin->timer_step += spin->climb_rate;
1159                     }
1160                 }
1161             }
1162           return TRUE;
1163         }
1164       return FALSE;
1165
1166     case GDK_Page_Up:
1167
1168       if (event->state & GDK_CONTROL_MASK)
1169         {
1170           gdouble diff = spin->adjustment->upper - spin->adjustment->value;
1171           if (diff > EPSILON)
1172             gtk_spin_button_real_spin (spin, diff);
1173         }
1174       else
1175         gtk_spin_button_real_spin (spin, spin->adjustment->page_increment);
1176       return TRUE;
1177
1178     case GDK_Page_Down:
1179
1180       if (event->state & GDK_CONTROL_MASK)
1181         {
1182           gdouble diff = spin->adjustment->value - spin->adjustment->lower;
1183           if (diff > EPSILON)
1184             gtk_spin_button_real_spin (spin, -diff);
1185         }
1186       else
1187         gtk_spin_button_real_spin (spin, -spin->adjustment->page_increment);
1188       return TRUE;
1189
1190     default:
1191       break;
1192     }
1193
1194   return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
1195 }
1196
1197 static gint
1198 gtk_spin_button_key_release (GtkWidget   *widget,
1199                              GdkEventKey *event)
1200 {
1201   GtkSpinButton *spin;
1202
1203   g_return_val_if_fail (widget != NULL, FALSE);
1204   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1205   
1206   spin = GTK_SPIN_BUTTON (widget);
1207   
1208   spin->ev_time = event->time;
1209   return TRUE;
1210 }
1211
1212 static void
1213 gtk_spin_button_snap (GtkSpinButton *spin_button,
1214                       gdouble        val)
1215 {
1216   gdouble inc;
1217   gdouble tmp;
1218   
1219   inc = spin_button->adjustment->step_increment;
1220   tmp = (val - spin_button->adjustment->lower) / inc;
1221   if (tmp - floor (tmp) < ceil (tmp) - tmp)
1222     val = spin_button->adjustment->lower + floor (tmp) * inc;
1223   else
1224     val = spin_button->adjustment->lower + ceil (tmp) * inc;
1225
1226   if (fabs (val - spin_button->adjustment->value) > EPSILON)
1227     gtk_adjustment_set_value (spin_button->adjustment, val);
1228   else
1229     {
1230       gint return_val = FALSE;
1231       gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1232                        &return_val);
1233       if (return_val == FALSE)
1234         gtk_spin_button_default_output (spin_button);
1235     }
1236 }
1237
1238 static void
1239 gtk_spin_button_activate (GtkEntry *entry)
1240 {
1241   if (entry->editable)
1242     gtk_spin_button_update (GTK_SPIN_BUTTON (entry));
1243 }
1244
1245 static void
1246 gtk_spin_button_insert_text (GtkEntry    *entry,
1247                              const gchar *new_text,
1248                              gint         new_text_length,
1249                              gint        *position)
1250 {
1251   GtkEditable *editable = GTK_EDITABLE (entry);
1252   GtkSpinButton *spin = GTK_SPIN_BUTTON (editable);
1253  
1254   if (spin->numeric)
1255     {
1256       struct lconv *lc;
1257       gboolean sign;
1258       gint dotpos = -1;
1259       gint i;
1260       GdkWChar pos_sign;
1261       GdkWChar neg_sign;
1262       gint entry_length;
1263
1264       entry_length = entry->text_length;
1265
1266       lc = localeconv ();
1267
1268       if (*(lc->negative_sign))
1269         neg_sign = *(lc->negative_sign);
1270       else 
1271         neg_sign = '-';
1272
1273       if (*(lc->positive_sign))
1274         pos_sign = *(lc->positive_sign);
1275       else 
1276         pos_sign = '+';
1277
1278       for (sign=0, i=0; i<entry_length; i++)
1279         if ((entry->text[i] == neg_sign) ||
1280             (entry->text[i] == pos_sign))
1281           {
1282             sign = 1;
1283             break;
1284           }
1285
1286       if (sign && !(*position))
1287         return;
1288
1289       for (dotpos=-1, i=0; i<entry_length; i++)
1290         if (entry->text[i] == *(lc->decimal_point))
1291           {
1292             dotpos = i;
1293             break;
1294           }
1295
1296       if (dotpos > -1 && *position > dotpos &&
1297           (gint)spin->digits - entry_length
1298             + dotpos - new_text_length + 1 < 0)
1299         return;
1300
1301       for (i = 0; i < new_text_length; i++)
1302         {
1303           if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1304             {
1305               if (sign || (*position) || i)
1306                 return;
1307               sign = TRUE;
1308             }
1309           else if (new_text[i] == *(lc->decimal_point))
1310             {
1311               if (!spin->digits || dotpos > -1 || 
1312                   (new_text_length - 1 - i + entry_length
1313                     - *position > (gint)spin->digits)) 
1314                 return;
1315               dotpos = *position + i;
1316             }
1317           else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1318             return;
1319         }
1320     }
1321
1322   GTK_ENTRY_CLASS (parent_class)->insert_text (entry, new_text,
1323                                                new_text_length, position);
1324 }
1325
1326 static void
1327 gtk_spin_button_real_spin (GtkSpinButton *spin_button,
1328                            gdouble        increment)
1329 {
1330   GtkAdjustment *adj;
1331   gdouble new_value = 0.0;
1332
1333   g_return_if_fail (spin_button != NULL);
1334   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1335   
1336   adj = spin_button->adjustment;
1337
1338   new_value = adj->value + increment;
1339
1340   if (increment > 0)
1341     {
1342       if (spin_button->wrap)
1343         {
1344           if (fabs (adj->value - adj->upper) < EPSILON)
1345             new_value = adj->lower;
1346           else if (new_value > adj->upper)
1347             new_value = adj->upper;
1348         }
1349       else
1350         new_value = MIN (new_value, adj->upper);
1351     }
1352   else if (increment < 0) 
1353     {
1354       if (spin_button->wrap)
1355         {
1356           if (fabs (adj->value - adj->lower) < EPSILON)
1357             new_value = adj->upper;
1358           else if (new_value < adj->lower)
1359             new_value = adj->lower;
1360         }
1361       else
1362         new_value = MAX (new_value, adj->lower);
1363     }
1364
1365   if (fabs (new_value - adj->value) > EPSILON)
1366     gtk_adjustment_set_value (adj, new_value);
1367 }
1368
1369 static gint
1370 gtk_spin_button_default_input (GtkSpinButton *spin_button,
1371                                gdouble       *new_val)
1372 {
1373   gchar *err = NULL;
1374
1375   *new_val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &err);
1376   if (*err)
1377     return GTK_INPUT_ERROR;
1378   else
1379     return FALSE;
1380 }
1381
1382 static gint
1383 gtk_spin_button_default_output (GtkSpinButton *spin_button)
1384 {
1385   gchar buf[MAX_TEXT_LENGTH];
1386
1387   sprintf (buf, "%0.*f", spin_button->digits, spin_button->adjustment->value);
1388   if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1389     gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1390   return FALSE;
1391 }
1392
1393
1394 /***********************************************************
1395  ***********************************************************
1396  ***                  Public interface                   ***
1397  ***********************************************************
1398  ***********************************************************/
1399
1400
1401 void
1402 gtk_spin_button_configure (GtkSpinButton  *spin_button,
1403                            GtkAdjustment  *adjustment,
1404                            gdouble         climb_rate,
1405                            guint           digits)
1406 {
1407   g_return_if_fail (spin_button != NULL);
1408   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1409   g_return_if_fail (digits < 6);
1410
1411   if (adjustment)
1412     gtk_spin_button_set_adjustment (spin_button, adjustment);
1413   else
1414     adjustment = spin_button->adjustment;
1415
1416   spin_button->digits = digits;
1417   spin_button->climb_rate = climb_rate;
1418   gtk_adjustment_value_changed (adjustment);
1419 }
1420
1421 GtkWidget *
1422 gtk_spin_button_new (GtkAdjustment *adjustment,
1423                      gdouble        climb_rate,
1424                      guint          digits)
1425 {
1426   GtkSpinButton *spin;
1427
1428   if (adjustment)
1429     g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
1430   g_return_val_if_fail (digits < 6, NULL);
1431
1432   spin = gtk_type_new (GTK_TYPE_SPIN_BUTTON);
1433
1434   gtk_spin_button_configure (spin, adjustment, climb_rate, digits);
1435
1436   return GTK_WIDGET (spin);
1437 }
1438
1439 /**
1440  * gtk_spin_button_new_with_range:
1441  * @min: Minimum allowable value
1442  * @max: Maximum allowable value
1443  * @step: Increment added or subtracted by spinning the widget
1444  * 
1445  * This is a convenience constructor that allows creation of a numeric 
1446  * #GtkSpinButton without manually creating an adjustment. The value is 
1447  * initially set to the minimum value and a page increment of 10 * @step
1448  * is the default. The precision of the spin button is equivalent to the 
1449  * precision of @step.
1450  * 
1451  * Return value: the newly instantiated spin button
1452  **/
1453 GtkWidget *
1454 gtk_spin_button_new_with_range (gdouble min,
1455                                 gdouble max,
1456                                 gdouble step)
1457 {
1458   GtkObject *adj;
1459   GtkSpinButton *spin;
1460   gint digits;
1461
1462   g_return_val_if_fail (min < max, NULL);
1463   g_return_val_if_fail (step != 0.0, NULL);
1464
1465   spin = gtk_type_new (GTK_TYPE_SPIN_BUTTON);
1466
1467   adj = gtk_adjustment_new (min, min, max, step, 10 * step, step);
1468
1469   if (fabs (step) >= 1.0)
1470     digits = 0;
1471   else {
1472     digits = abs ((gint) floor (log10 (fabs (step))));
1473     if (digits > 5)
1474       digits = 5;
1475   }
1476
1477   gtk_spin_button_configure (spin, GTK_ADJUSTMENT (adj), step, digits);
1478
1479   gtk_spin_button_set_numeric (spin, TRUE);
1480
1481   return GTK_WIDGET (spin);
1482 }
1483
1484 /* Callback used when the spin button's adjustment changes.  We need to redraw
1485  * the arrows when the adjustment's range changes, and reevaluate our size request.
1486  */
1487 static void
1488 adjustment_changed_cb (GtkAdjustment *adjustment, gpointer data)
1489 {
1490   GtkSpinButton *spin_button;
1491
1492   spin_button = GTK_SPIN_BUTTON (data);
1493
1494   gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1495 }
1496
1497 /**
1498  * gtk_spin_button_set_adjustment:
1499  * @spin_button: a #GtkSpinButton
1500  * @adjustment: a #GtkAdjustment to replace the existing adjustment
1501  * 
1502  * Replaces the #GtkAdjustment associated with @spin_button.
1503  **/
1504 void
1505 gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
1506                                 GtkAdjustment *adjustment)
1507 {
1508   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1509
1510   if (spin_button->adjustment != adjustment)
1511     {
1512       if (spin_button->adjustment)
1513         {
1514           gtk_signal_disconnect_by_data (GTK_OBJECT (spin_button->adjustment),
1515                                          (gpointer) spin_button);
1516           gtk_object_unref (GTK_OBJECT (spin_button->adjustment));
1517         }
1518       spin_button->adjustment = adjustment;
1519       if (adjustment)
1520         {
1521           gtk_object_ref (GTK_OBJECT (adjustment));
1522           gtk_object_sink (GTK_OBJECT (adjustment));
1523           gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
1524                               (GtkSignalFunc) gtk_spin_button_value_changed,
1525                               (gpointer) spin_button);
1526           gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
1527                               (GtkSignalFunc) adjustment_changed_cb,
1528                               (gpointer) spin_button);
1529         }
1530
1531       gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1532     }
1533 }
1534
1535 /**
1536  * gtk_spin_button_get_adjustment:
1537  * @spin_button: 
1538  * 
1539  * Get the adjustment associated with a #GtkSpinButton
1540  * 
1541  * Return value: the #GtkAdjustment of @spin_button
1542  **/
1543 GtkAdjustment *
1544 gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
1545 {
1546   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
1547
1548   return spin_button->adjustment;
1549 }
1550
1551 /**
1552  * gtk_spin_button_set_digits:
1553  * @spin_button: a #GtkSpinButton
1554  * @digits: the number of digits to be displayed for the spin button's value
1555  * 
1556  * Set the precision to be displayed by @spin_button. Up to 5 digit precision
1557  * is allowed.
1558  **/
1559 void
1560 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
1561                             guint          digits)
1562 {
1563   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1564   g_return_if_fail (digits < 6);
1565
1566   if (spin_button->digits != digits)
1567     {
1568       spin_button->digits = digits;
1569       gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1570       /* since lower/upper may have changed */
1571       gtk_widget_queue_resize (GTK_WIDGET (spin_button));
1572     }
1573 }
1574
1575 /**
1576  * gtk_spin_button_set_increments:
1577  * @spin_button: a #GtkSpinButton
1578  * @step: increment applied for a button 1 press.
1579  * @page: increment applied for a button 2 press.
1580  * 
1581  * Sets the step and page increments for spin_button.  This affects how 
1582  * quickly the value changes when the spin button's arrows are activated.
1583  **/
1584 void
1585 gtk_spin_button_set_increments (GtkSpinButton *spin_button,
1586                                 gdouble        step,
1587                                 gdouble        page)
1588 {
1589   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1590
1591   spin_button->adjustment->step_increment = step;
1592   spin_button->adjustment->page_increment = page;
1593 }
1594
1595 /**
1596  * gtk_spin_button_set_range:
1597  * @spin_button: a #GtkSpinButton
1598  * @min: minimum allowable value
1599  * @max: maximum allowable value
1600  * 
1601  * Sets the minimum and maximum allowable values for @spin_button
1602  **/
1603 void
1604 gtk_spin_button_set_range (GtkSpinButton *spin_button,
1605                            gdouble        min,
1606                            gdouble        max)
1607 {
1608   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1609
1610   spin_button->adjustment->lower = min;
1611   spin_button->adjustment->upper = max;
1612 }
1613
1614 /**
1615  * gtk_spin_button_get_value_as_float:
1616  * @spin_button: a #GtkSpinButton
1617  * 
1618  * Get the value @spin_button represented as a floating point number.
1619  * 
1620  * Return value: the value of @spin_button
1621  **/
1622 gdouble
1623 gtk_spin_button_get_value_as_float (GtkSpinButton *spin_button)
1624 {
1625   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0.0);
1626
1627   return spin_button->adjustment->value;
1628 }
1629
1630 /**
1631  * gtk_spin_button_get_value_as_int:
1632  * @spin_button: a #GtkSpinButton
1633  * 
1634  * Get the value @spin_button represented as an integer.
1635  * 
1636  * Return value: the value of @spin_button
1637  **/
1638 gint
1639 gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
1640 {
1641   gdouble val;
1642
1643   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1644
1645   val = spin_button->adjustment->value;
1646   if (val - floor (val) < ceil (val) - val)
1647     return floor (val);
1648   else
1649     return ceil (val);
1650 }
1651
1652 /**
1653  * gtk_spin_button_set_value:
1654  * @spin_button: a #GtkSpinButton
1655  * @value: the new value
1656  * 
1657  * Set the value of @spin_button.
1658  **/
1659 void 
1660 gtk_spin_button_set_value (GtkSpinButton *spin_button, 
1661                            gdouble        value)
1662 {
1663   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1664
1665   if (fabs (value - spin_button->adjustment->value) > EPSILON)
1666     gtk_adjustment_set_value (spin_button->adjustment, value);
1667   else
1668     {
1669       gint return_val = FALSE;
1670       gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1671                        &return_val);
1672       if (return_val == FALSE)
1673         gtk_spin_button_default_output (spin_button);
1674     }
1675 }
1676
1677 /**
1678  * gtk_spin_button_set_update_policy:
1679  * @spin_button: a #GtkSpinButton 
1680  * @policy: a #GtkSpinButtonUpdatePolicy value
1681  * 
1682  * Sets the update behavior of a spin button. This determines whether the
1683  * spin button is always updated or only when a valid value is set.
1684  **/
1685 void
1686 gtk_spin_button_set_update_policy (GtkSpinButton             *spin_button,
1687                                    GtkSpinButtonUpdatePolicy  policy)
1688 {
1689   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1690
1691   spin_button->update_policy = policy;
1692 }
1693
1694 /**
1695  * gtk_spin_button_set_numeric:
1696  * @spin_button: a #GtkSpinButton 
1697  * @numeric: flag indicating if only numeric entry is allowed. 
1698  * 
1699  * Sets the flag that determines if non-numeric text can be typed into
1700  * the spin button.
1701  **/
1702 void
1703 gtk_spin_button_set_numeric (GtkSpinButton  *spin_button,
1704                              gboolean        numeric)
1705 {
1706   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1707
1708   spin_button->numeric = (numeric != 0);
1709 }
1710
1711 /**
1712  * gtk_spin_button_set_wrap:
1713  * @spin_button: a #GtkSpinButton 
1714  * @wrap: a flag indicating if wrapping behavior is performed.
1715  * 
1716  * Sets the flag that determines if a spin button value wraps around to the
1717  * opposite limit when the upper or lower limit of the range is exceeded.
1718  **/
1719 void
1720 gtk_spin_button_set_wrap (GtkSpinButton  *spin_button,
1721                           gboolean        wrap)
1722 {
1723   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1724
1725   spin_button->wrap = (wrap != 0);
1726 }
1727
1728 /**
1729  * spin_button_get_shadow_type:
1730  * @spin_button: a #GtkSpinButton 
1731  * 
1732  * Convenience function to Get the shadow type from the underlying widget's
1733  * style.
1734  * 
1735  * Return value: the #GtkShadowType
1736  **/
1737 static gint
1738 spin_button_get_shadow_type (GtkSpinButton *spin_button)
1739 {
1740   GtkShadowType rc_shadow_type;
1741
1742   gtk_widget_style_get (GTK_WIDGET (spin_button), "shadow_type", &rc_shadow_type, NULL);
1743
1744   return rc_shadow_type;
1745 }
1746
1747 /**
1748  * gtk_spin_button_set_snap_to_ticks:
1749  * @spin_button: a #GtkSpinButton 
1750  * @snap_to_ticks: a flag indicating if invalid values should be corrected.
1751  * 
1752  * Sets the policy as to whether values are corrected to the nearest step 
1753  * increment when a spin button is activated after providing an invalid value.
1754  **/
1755 void
1756 gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
1757                                    gboolean       snap_to_ticks)
1758 {
1759   guint new_val;
1760
1761   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1762
1763   new_val = (snap_to_ticks != 0);
1764
1765   if (new_val != spin_button->snap_to_ticks)
1766     {
1767       spin_button->snap_to_ticks = new_val;
1768       if (new_val && GTK_ENTRY (spin_button)->editable)
1769         gtk_spin_button_update (spin_button);
1770     }
1771 }
1772
1773 /**
1774  * gtk_spin_button_spin:
1775  * @spin_button: a #GtkSpinButton 
1776  * @direction: a #GtkSpinType indicating the direction to spin.
1777  * @increment: step increment to apply in the specified direction.
1778  * 
1779  * Increment or decrement a spin button's value in a specified direction
1780  * by a specified amount. 
1781  **/
1782 void
1783 gtk_spin_button_spin (GtkSpinButton *spin_button,
1784                       GtkSpinType    direction,
1785                       gdouble        increment)
1786 {
1787   GtkAdjustment *adj;
1788   gdouble diff;
1789
1790   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1791   
1792   adj = spin_button->adjustment;
1793
1794   /* for compatibility with the 1.0.x version of this function */
1795   if (increment != 0 && increment != adj->step_increment &&
1796       (direction == GTK_SPIN_STEP_FORWARD ||
1797        direction == GTK_SPIN_STEP_BACKWARD))
1798     {
1799       if (direction == GTK_SPIN_STEP_BACKWARD && increment > 0)
1800         increment = -increment;
1801       direction = GTK_SPIN_USER_DEFINED;
1802     }
1803
1804   switch (direction)
1805     {
1806     case GTK_SPIN_STEP_FORWARD:
1807
1808       gtk_spin_button_real_spin (spin_button, adj->step_increment);
1809       break;
1810
1811     case GTK_SPIN_STEP_BACKWARD:
1812
1813       gtk_spin_button_real_spin (spin_button, -adj->step_increment);
1814       break;
1815
1816     case GTK_SPIN_PAGE_FORWARD:
1817
1818       gtk_spin_button_real_spin (spin_button, adj->page_increment);
1819       break;
1820
1821     case GTK_SPIN_PAGE_BACKWARD:
1822
1823       gtk_spin_button_real_spin (spin_button, -adj->page_increment);
1824       break;
1825
1826     case GTK_SPIN_HOME:
1827
1828       diff = adj->value - adj->lower;
1829       if (diff > EPSILON)
1830         gtk_spin_button_real_spin (spin_button, -diff);
1831       break;
1832
1833     case GTK_SPIN_END:
1834
1835       diff = adj->upper - adj->value;
1836       if (diff > EPSILON)
1837         gtk_spin_button_real_spin (spin_button, diff);
1838       break;
1839
1840     case GTK_SPIN_USER_DEFINED:
1841
1842       if (increment != 0)
1843         gtk_spin_button_real_spin (spin_button, increment);
1844       break;
1845
1846     default:
1847       break;
1848     }
1849 }
1850
1851 /**
1852  * gtk_spin_button_update:
1853  * @spin_button: a #GtkSpinButton 
1854  * 
1855  * Manually force an update of the spin button.
1856  **/
1857 void 
1858 gtk_spin_button_update (GtkSpinButton *spin_button)
1859 {
1860   gdouble val;
1861   gint error = 0;
1862   gint return_val;
1863
1864   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1865
1866   return_val = FALSE;
1867   gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[INPUT],
1868                    &val, &return_val);
1869   if (return_val == FALSE)
1870     {
1871       return_val = gtk_spin_button_default_input (spin_button, &val);
1872       error = (return_val == GTK_INPUT_ERROR);
1873     }
1874   else if (return_val == GTK_INPUT_ERROR)
1875     error = 1;
1876
1877   if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
1878     {
1879       if (val < spin_button->adjustment->lower)
1880         val = spin_button->adjustment->lower;
1881       else if (val > spin_button->adjustment->upper)
1882         val = spin_button->adjustment->upper;
1883     }
1884   else if ((spin_button->update_policy == GTK_UPDATE_IF_VALID) && 
1885            (error ||
1886            val < spin_button->adjustment->lower ||
1887            val > spin_button->adjustment->upper))
1888     {
1889       gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1890       return;
1891     }
1892
1893   if (spin_button->snap_to_ticks)
1894     gtk_spin_button_snap (spin_button, val);
1895   else
1896     {
1897       if (fabs (val - spin_button->adjustment->value) > EPSILON)
1898         gtk_adjustment_set_value (spin_button->adjustment, val);
1899       else
1900         {
1901           return_val = FALSE;
1902           gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1903                            &return_val);
1904           if (return_val == FALSE)
1905             gtk_spin_button_default_output (spin_button);
1906         }
1907     }
1908 }
1909