]> Pileus Git - ~andy/gtk/blob - gtk/gtkspinbutton.c
Changed GtkSpinButtonUpdatePolicy enum, added keyboard
[~andy/gtk] / gtk / gtkspinbutton.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GtkSpinButton widget for GTK+
5  * Copyright (C) 1998 Lars Hamann and Stefan Jeske
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <math.h>
26 #include <string.h>
27 #include <locale.h>
28 #include "gdk/gdkkeysyms.h"
29 #include "gtkspinbutton.h"
30 #include "gtkmain.h"
31 #include "gtksignal.h"
32
33
34 #define MIN_SPIN_BUTTON_WIDTH              30
35 #define ARROW_SIZE                         11
36 #define SPIN_BUTTON_INITIAL_TIMER_DELAY    200
37 #define SPIN_BUTTON_TIMER_DELAY            20
38 #define MAX_TEXT_LENGTH                    256
39 #define MAX_TIMER_CALLS                    5
40
41
42 static void gtk_spin_button_class_init     (GtkSpinButtonClass *klass);
43 static void gtk_spin_button_init           (GtkSpinButton      *spin_button);
44 static void gtk_spin_button_finalize       (GtkObject          *object);
45 static void gtk_spin_button_map            (GtkWidget          *widget);
46 static void gtk_spin_button_unmap          (GtkWidget          *widget);
47 static void gtk_spin_button_realize        (GtkWidget          *widget);
48 static void gtk_spin_button_unrealize      (GtkWidget          *widget);
49 static void gtk_spin_button_size_request   (GtkWidget          *widget,
50                                             GtkRequisition     *requisition);
51 static void gtk_spin_button_size_allocate  (GtkWidget          *widget,
52                                             GtkAllocation      *allocation);
53 static void gtk_spin_button_paint          (GtkWidget          *widget,
54                                             GdkRectangle       *area);
55 static void gtk_spin_button_draw           (GtkWidget          *widget,
56                                             GdkRectangle       *area);
57 static gint gtk_spin_button_expose         (GtkWidget          *widget,
58                                             GdkEventExpose     *event);
59 static gint gtk_spin_button_button_press   (GtkWidget          *widget,
60                                             GdkEventButton     *event);
61 static gint gtk_spin_button_button_release (GtkWidget          *widget,
62                                             GdkEventButton     *event);
63 static gint gtk_spin_button_motion_notify  (GtkWidget          *widget,
64                                             GdkEventMotion     *event);
65 static gint gtk_spin_button_enter_notify   (GtkWidget          *widget,
66                                             GdkEventCrossing   *event);
67 static gint gtk_spin_button_leave_notify   (GtkWidget          *widget,
68                                             GdkEventCrossing   *event);
69 static gint gtk_spin_button_focus_out      (GtkWidget          *widget,
70                                             GdkEventFocus      *event);
71 static void gtk_spin_button_draw_arrow     (GtkSpinButton      *spin_button, 
72                                             guint               arrow);
73 static gint gtk_spin_button_timer          (GtkSpinButton      *spin_button);
74 static void gtk_spin_button_value_changed  (GtkWidget          *widget,
75                                             GtkSpinButton      *spin_button); 
76 static gint gtk_spin_button_key_press      (GtkWidget          *widget,
77                                             GdkEventKey        *event);
78 static gint gtk_spin_button_key_release    (GtkWidget          *widget,
79                                             GdkEventKey        *event);
80 static void gtk_spin_button_update         (GtkSpinButton      *spin_button);
81 static void gtk_spin_button_activate       (GtkEditable        *editable);
82 static void gtk_spin_button_snap           (GtkSpinButton      *spin_button,
83                                             gfloat              val);
84 static void gtk_spin_button_insert_text    (GtkEditable        *editable,
85                                             const gchar        *new_text,
86                                             gint               new_text_length,
87                                             gint               *position);
88
89
90 static GtkWidgetClass *parent_class = NULL;
91
92
93 guint
94 gtk_spin_button_get_type (void)
95 {
96   static guint spin_button_type = 0;
97
98   if (!spin_button_type)
99     {
100       GtkTypeInfo spin_button_info =
101       {
102         "GtkSpinButton",
103         sizeof (GtkSpinButton),
104         sizeof (GtkSpinButtonClass),
105         (GtkClassInitFunc) gtk_spin_button_class_init,
106         (GtkObjectInitFunc) gtk_spin_button_init,
107         (GtkArgSetFunc) NULL,
108         (GtkArgGetFunc) NULL,
109       };
110
111       spin_button_type = gtk_type_unique (gtk_entry_get_type (), 
112                                           &spin_button_info);
113     }
114   return spin_button_type;
115 }
116
117 static void
118 gtk_spin_button_class_init (GtkSpinButtonClass *class)
119 {
120   GtkObjectClass   *object_class;
121   GtkWidgetClass   *widget_class;
122   GtkEditableClass *editable_class;
123
124   object_class   = (GtkObjectClass*)   class;
125   widget_class   = (GtkWidgetClass*)   class;
126   editable_class = (GtkEditableClass*) class; 
127
128   parent_class = gtk_type_class (gtk_entry_get_type ());
129
130   object_class->finalize = gtk_spin_button_finalize;
131
132   widget_class->map = gtk_spin_button_map;
133   widget_class->unmap = gtk_spin_button_unmap;
134   widget_class->realize = gtk_spin_button_realize;
135   widget_class->unrealize = gtk_spin_button_unrealize;
136   widget_class->size_request = gtk_spin_button_size_request;
137   widget_class->size_allocate = gtk_spin_button_size_allocate;
138   widget_class->draw = gtk_spin_button_draw;
139   widget_class->expose_event = gtk_spin_button_expose;
140   widget_class->button_press_event = gtk_spin_button_button_press;
141   widget_class->button_release_event = gtk_spin_button_button_release;
142   widget_class->motion_notify_event = gtk_spin_button_motion_notify;
143   widget_class->key_press_event = gtk_spin_button_key_press;
144   widget_class->key_release_event = gtk_spin_button_key_release;
145   widget_class->enter_notify_event = gtk_spin_button_enter_notify;
146   widget_class->leave_notify_event = gtk_spin_button_leave_notify;
147   widget_class->focus_out_event = gtk_spin_button_focus_out;
148
149   editable_class->insert_text = gtk_spin_button_insert_text;
150   editable_class->activate = gtk_spin_button_activate;
151 }
152
153 static void
154 gtk_spin_button_init (GtkSpinButton *spin_button)
155 {
156   spin_button->adjustment = NULL;
157   spin_button->panel = NULL;
158   spin_button->shadow_type = GTK_SHADOW_OUT;
159   spin_button->timer = 0;
160   spin_button->ev_time = 0;
161   spin_button->climb_rate = 0.0;
162   spin_button->timer_step = 0.0;
163   spin_button->update_policy = GTK_UPDATE_ALWAYS;
164   spin_button->in_child = 2;
165   spin_button->click_child = 2;
166   spin_button->button = 0;
167   spin_button->need_timer = 0;
168   spin_button->timer_calls = 0;
169   spin_button->digits = 0;
170   spin_button->numeric = FALSE;
171   spin_button->wrap = FALSE;
172   spin_button->snap_to_ticks = FALSE;
173 }
174
175 void
176 gtk_spin_button_construct (GtkSpinButton  *spin_button,
177                            GtkAdjustment  *adjustment,
178                            gfloat          climb_rate,
179                            gint            digits)
180 {
181   char buf[MAX_TEXT_LENGTH];
182
183   g_return_if_fail (spin_button != NULL);
184   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
185   g_return_if_fail (digits >= 0 && digits < 6);
186
187   if (!adjustment)
188     adjustment = (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0);
189
190   gtk_spin_button_set_adjustment (spin_button, adjustment);
191   spin_button->digits = digits;
192   sprintf (buf, "%0.*f", digits, adjustment->value);
193   gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
194   spin_button->climb_rate = climb_rate;
195 }
196
197 GtkWidget *
198 gtk_spin_button_new (GtkAdjustment *adjustment,
199                      gfloat         climb_rate,
200                      gint           digits)
201 {
202   GtkSpinButton *spin;
203
204   g_return_val_if_fail (digits >= 0 && digits < 6, NULL);
205
206   spin = gtk_type_new (gtk_spin_button_get_type ());
207
208   gtk_spin_button_construct (spin, adjustment, climb_rate, digits);
209
210   return GTK_WIDGET (spin);
211 }
212
213 static void
214 gtk_spin_button_finalize (GtkObject *object)
215 {
216   g_return_if_fail (object != NULL);
217   g_return_if_fail (GTK_IS_SPIN_BUTTON (object));
218
219   gtk_object_unref (GTK_OBJECT (GTK_SPIN_BUTTON (object)->adjustment));
220   
221   GTK_OBJECT_CLASS (parent_class)->finalize (object);
222 }
223
224 static void
225 gtk_spin_button_map (GtkWidget *widget)
226 {
227   g_return_if_fail (widget != NULL);
228   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
229
230   if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
231     {
232       GTK_WIDGET_CLASS (parent_class)->map (widget);
233       gdk_window_show (GTK_SPIN_BUTTON (widget)->panel);
234     }
235 }
236
237 static void
238 gtk_spin_button_unmap (GtkWidget *widget)
239 {
240   g_return_if_fail (widget != NULL);
241   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
242
243   if (GTK_WIDGET_MAPPED (widget))
244     {
245       gdk_window_hide (GTK_SPIN_BUTTON (widget)->panel);
246       GTK_WIDGET_CLASS (parent_class)->unmap (widget);
247     }
248 }
249
250 static void
251 gtk_spin_button_realize (GtkWidget *widget)
252 {
253   GtkSpinButton *spin;
254   GdkWindowAttr attributes;
255   gint attributes_mask;
256   guint real_width;
257
258   g_return_if_fail (widget != NULL);
259   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
260   
261   spin = GTK_SPIN_BUTTON (widget);
262
263   real_width = widget->allocation.width;
264   widget->allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
265   gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
266                          GDK_KEY_RELEASE_MASK);
267   GTK_WIDGET_CLASS (parent_class)->realize (widget);
268
269   widget->allocation.width = real_width;
270   
271   attributes.window_type = GDK_WINDOW_CHILD;
272   attributes.wclass = GDK_INPUT_OUTPUT;
273   attributes.visual = gtk_widget_get_visual (widget);
274   attributes.colormap = gtk_widget_get_colormap (widget);
275   attributes.event_mask = gtk_widget_get_events (widget);
276   attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK 
277     | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK 
278     | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
279
280   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
281
282   attributes.x = (widget->allocation.x + widget->allocation.width - ARROW_SIZE -
283                   2 * widget->style->klass->xthickness);
284   attributes.y = widget->allocation.y + (widget->allocation.height -
285                                          widget->requisition.height) / 2;
286   attributes.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
287   attributes.height = widget->requisition.height;
288   
289   spin->panel = gdk_window_new (gtk_widget_get_parent_window (widget), 
290                                 &attributes, attributes_mask);
291   gdk_window_set_user_data (spin->panel, widget);
292
293   gtk_style_set_background (widget->style, spin->panel, GTK_STATE_NORMAL);
294 }
295
296 static void
297 gtk_spin_button_unrealize (GtkWidget *widget)
298 {
299   GtkSpinButton *spin;
300
301   g_return_if_fail (widget != NULL);
302   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
303
304   spin = GTK_SPIN_BUTTON (widget);
305
306   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
307
308   if (spin->panel)
309     {
310       gdk_window_set_user_data (spin->panel, NULL);
311       gdk_window_destroy (spin->panel);
312       spin->panel = NULL;
313     }
314 }
315
316 static void
317 gtk_spin_button_size_request (GtkWidget      *widget,
318                               GtkRequisition *requisition)
319 {
320   g_return_if_fail (widget != NULL);
321   g_return_if_fail (requisition != NULL);
322   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
323
324   GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
325   
326   requisition->width = MIN_SPIN_BUTTON_WIDTH + ARROW_SIZE 
327     + 2 * widget->style->klass->xthickness;
328 }
329
330 static void
331 gtk_spin_button_size_allocate (GtkWidget     *widget,
332                                GtkAllocation *allocation)
333 {
334   GtkAllocation child_allocation;
335
336   g_return_if_fail (widget != NULL);
337   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
338   g_return_if_fail (allocation != NULL);
339
340   child_allocation = *allocation;
341   child_allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
342
343   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &child_allocation);
344
345   widget->allocation = *allocation;
346
347   if (GTK_WIDGET_REALIZED (widget))
348     {
349       child_allocation.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
350       child_allocation.height = widget->requisition.height;  
351       child_allocation.x = (allocation->x + allocation->width - ARROW_SIZE - 
352                             2 * widget->style->klass->xthickness);
353       child_allocation.y = allocation->y + (allocation->height - widget->requisition.height) / 2;
354
355       gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel, 
356                               child_allocation.x,
357                               child_allocation.y,
358                               child_allocation.width,
359                               child_allocation.height); 
360     }
361 }
362
363 static void
364 gtk_spin_button_paint (GtkWidget    *widget,
365                        GdkRectangle *area)
366 {
367   GtkSpinButton *spin;
368
369   g_return_if_fail (widget != NULL);
370   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
371
372   spin = GTK_SPIN_BUTTON (widget);
373
374   if (GTK_WIDGET_DRAWABLE (widget))
375     {
376       if (spin->shadow_type != GTK_SHADOW_NONE)
377         gtk_draw_shadow (widget->style, spin->panel,
378                          GTK_STATE_NORMAL, spin->shadow_type,
379                          0, 0, 
380                          ARROW_SIZE + 2 * widget->style->klass->xthickness,
381                          widget->requisition.height); 
382       gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
383       gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
384
385       GTK_WIDGET_CLASS (parent_class)->draw (widget, area);
386     }
387 }
388
389 static void
390 gtk_spin_button_draw (GtkWidget    *widget,
391                       GdkRectangle *area)
392 {
393   g_return_if_fail (widget != NULL);
394   g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
395   g_return_if_fail (area != NULL);
396
397   if (GTK_WIDGET_DRAWABLE (widget))
398     gtk_spin_button_paint (widget, area);
399 }
400
401 static gint
402 gtk_spin_button_expose (GtkWidget      *widget,
403                         GdkEventExpose *event)
404 {
405   g_return_val_if_fail (widget != NULL, FALSE);
406   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
407   g_return_val_if_fail (event != NULL, FALSE);
408
409   if (GTK_WIDGET_DRAWABLE (widget))
410     gtk_spin_button_paint (widget, &event->area);
411
412   return FALSE;
413 }
414
415 static void
416 gtk_spin_button_draw_arrow (GtkSpinButton *spin_button, 
417                             guint          arrow)
418 {
419   GtkStateType state_type;
420   GtkShadowType shadow_type;
421   GtkWidget *widget;
422   gint x;
423   gint y;
424
425   g_return_if_fail (spin_button != NULL);
426   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
427   
428   widget = GTK_WIDGET (spin_button);
429
430   if (GTK_WIDGET_DRAWABLE (spin_button))
431     {
432       if (spin_button->in_child == arrow)
433         {
434           if (spin_button->click_child == arrow)
435             state_type = GTK_STATE_ACTIVE;
436           else
437             state_type = GTK_STATE_PRELIGHT;
438         }
439       else
440         state_type = GTK_STATE_NORMAL;
441
442       if (spin_button->click_child == arrow)
443         shadow_type = GTK_SHADOW_IN;
444       else
445         shadow_type = GTK_SHADOW_OUT;
446
447       if (arrow == GTK_ARROW_UP)
448         {
449           if (spin_button->shadow_type != GTK_SHADOW_NONE)
450             {
451               x = widget->style->klass->xthickness;
452               y = widget->style->klass->ythickness;
453             }
454           else
455             {
456               x = widget->style->klass->xthickness - 1;
457               y = widget->style->klass->ythickness - 1;
458             }
459           gtk_draw_arrow (widget->style, spin_button->panel,
460                           state_type, shadow_type, arrow, TRUE, 
461                           x, y, ARROW_SIZE, widget->requisition.height / 2 
462                           - widget->style->klass->ythickness);
463         }
464       else
465         {
466           if (spin_button->shadow_type != GTK_SHADOW_NONE)
467             {
468               x = widget->style->klass->xthickness;
469               y = widget->requisition.height / 2;
470             }
471           else
472             {
473               x = widget->style->klass->xthickness - 1;
474               y = widget->requisition.height / 2 + 1;
475             }
476           gtk_draw_arrow (widget->style, spin_button->panel,
477                           state_type, shadow_type, arrow, TRUE, 
478                           x, y, ARROW_SIZE, widget->requisition.height / 2 
479                           - widget->style->klass->ythickness);
480         }
481     }
482 }
483
484 static gint
485 gtk_spin_button_enter_notify (GtkWidget        *widget,
486                               GdkEventCrossing *event)
487 {
488   GtkSpinButton *spin;
489
490   g_return_val_if_fail (widget != NULL, FALSE);
491   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
492   g_return_val_if_fail (event != NULL, FALSE);
493
494   spin = GTK_SPIN_BUTTON (widget);
495
496   if (event->window == spin->panel)
497     {
498       gint x;
499       gint y;
500
501       gdk_window_get_pointer (spin->panel, &x, &y, NULL);
502
503       if (y <= widget->requisition.height / 2)
504         {
505           spin->in_child = GTK_ARROW_UP;
506           if (spin->click_child == 2) 
507             gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
508         }
509       else
510         {
511           spin->in_child = GTK_ARROW_DOWN;
512           if (spin->click_child == 2) 
513             gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
514         }
515     }
516   return FALSE;
517 }
518
519 static gint
520 gtk_spin_button_leave_notify (GtkWidget        *widget,
521                               GdkEventCrossing *event)
522 {
523   GtkSpinButton *spin;
524
525   g_return_val_if_fail (widget != NULL, FALSE);
526   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
527   g_return_val_if_fail (event != NULL, FALSE);
528
529   spin = GTK_SPIN_BUTTON (widget);
530
531   if (event->window == spin->panel && spin->click_child == 2)
532     {
533       if (spin->in_child == GTK_ARROW_UP) 
534         {
535           spin->in_child = 2;
536           gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
537         }
538       else
539         {
540           spin->in_child = 2;
541           gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
542         }
543     }
544   return FALSE;
545 }
546
547 static gint
548 gtk_spin_button_focus_out (GtkWidget     *widget,
549                            GdkEventFocus *event)
550 {
551   g_return_val_if_fail (widget != NULL, FALSE);
552   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
553   g_return_val_if_fail (event != NULL, FALSE);
554
555   gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
556
557   return GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
558 }
559
560 static gint
561 gtk_spin_button_button_press (GtkWidget      *widget,
562                               GdkEventButton *event)
563 {
564   GtkSpinButton *spin;
565
566   g_return_val_if_fail (widget != NULL, FALSE);
567   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
568   g_return_val_if_fail (event != NULL, FALSE);
569
570   spin = GTK_SPIN_BUTTON (widget);
571
572   if (!spin->button)
573     {
574       if (event->window == spin->panel)
575         {
576           if (!GTK_WIDGET_HAS_FOCUS (widget))
577             gtk_widget_grab_focus (widget);
578           gtk_grab_add (widget);
579           spin->button = event->button;
580           
581           gtk_spin_button_update (spin);
582           
583           if (event->y <= widget->requisition.height / 2)
584             {
585               spin->click_child = GTK_ARROW_UP;
586               if (event->button == 1)
587                 {
588                   gtk_spin_button_spin (spin, spin->click_child,
589                                         spin->adjustment->step_increment);
590                   if (!spin->timer)
591                     {
592                       spin->timer_step = spin->adjustment->step_increment;
593                       spin->need_timer = TRUE;
594                       spin->timer = gtk_timeout_add 
595                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
596                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
597                     }
598                 }
599               else if (event->button == 2)
600                 {
601                   gtk_spin_button_spin (spin, spin->click_child,
602                                         spin->adjustment->page_increment);
603                   if (!spin->timer) 
604                     {
605                       spin->timer_step = spin->adjustment->page_increment;
606                       spin->need_timer = TRUE;
607                       spin->timer = gtk_timeout_add 
608                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
609                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
610                     }
611                 }
612               gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
613             }
614           else 
615             {
616               spin->click_child = GTK_ARROW_DOWN;
617               if (event->button == 1)
618                 {
619                   gtk_spin_button_spin (spin, spin->click_child,
620                                         spin->adjustment->step_increment);
621                   if (!spin->timer)
622                     {
623                       spin->timer_step = spin->adjustment->step_increment;
624                       spin->need_timer = TRUE;
625                       spin->timer = gtk_timeout_add 
626                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
627                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
628                     }
629                 }      
630               else if (event->button == 2)
631                 {
632                   gtk_spin_button_spin (spin, spin->click_child,
633                                         spin->adjustment->page_increment);
634                   if (!spin->timer) 
635                     {
636                       spin->timer_step = spin->adjustment->page_increment;
637                       spin->need_timer = TRUE;
638                       spin->timer = gtk_timeout_add 
639                         (SPIN_BUTTON_INITIAL_TIMER_DELAY, 
640                          (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
641                     }
642                 }
643               gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
644             }
645         }
646       else
647         GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
648     }
649   return FALSE;
650 }
651
652 static gint
653 gtk_spin_button_button_release (GtkWidget      *widget,
654                                 GdkEventButton *event)
655 {
656   GtkSpinButton *spin;
657
658   g_return_val_if_fail (widget != NULL, FALSE);
659   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
660   g_return_val_if_fail (event != NULL, FALSE);
661
662   spin = GTK_SPIN_BUTTON (widget);
663
664   if (event->button == spin->button)
665     {
666       guint click_child;
667
668       if (spin->timer)
669         {
670           gtk_timeout_remove (spin->timer);
671           spin->timer = 0;
672           spin->timer_calls = 0;
673           spin->need_timer = FALSE;
674         }
675
676       if (event->button == 3)
677         {
678           if (event->y >= 0 && event->x >= 0 && 
679               event->y <= widget->requisition.height &&
680               event->x <= ARROW_SIZE + 2 * widget->style->klass->xthickness)
681             {
682               if (spin->click_child == GTK_ARROW_UP &&
683                   spin->adjustment->value < spin->adjustment->upper &&
684                   event->y <= widget->requisition.height / 2)
685                 gtk_adjustment_set_value (spin->adjustment, 
686                                           spin->adjustment->upper);
687               else if (spin->click_child == GTK_ARROW_DOWN &&
688                        spin->adjustment->value > spin->adjustment->lower &&
689                        event->y > widget->requisition.height / 2)
690                 gtk_adjustment_set_value (spin->adjustment, 
691                                           spin->adjustment->lower);
692             }
693         }                 
694       gtk_grab_remove (widget);
695       click_child = spin->click_child;
696       spin->click_child = 2;
697       spin->button = 0;
698       gtk_spin_button_draw_arrow (spin, click_child);
699     }
700   else
701     GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
702
703   return FALSE;
704 }
705
706 static gint
707 gtk_spin_button_motion_notify (GtkWidget      *widget,
708                                GdkEventMotion *event)
709 {
710   GtkSpinButton *spin;
711
712   g_return_val_if_fail (widget != NULL, FALSE);
713   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
714   g_return_val_if_fail (event != NULL, FALSE);
715
716   spin = GTK_SPIN_BUTTON (widget);
717   
718   if (spin->button)
719     return FALSE;
720
721   if (event->window == spin->panel)
722     {
723       gint y;
724
725       y = event->y;
726       if (event->is_hint)
727         gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
728
729       if (y <= widget->requisition.height / 2 && 
730           spin->in_child == GTK_ARROW_DOWN)
731         {
732           spin->in_child = GTK_ARROW_UP;
733           gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
734           gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
735         }
736       else if (y > widget->requisition.height / 2 && 
737           spin->in_child == GTK_ARROW_UP)
738         {
739           spin->in_child = GTK_ARROW_DOWN;
740           gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
741           gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
742         }
743       return FALSE;
744     }
745           
746   return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
747 }
748
749 static gint
750 gtk_spin_button_timer (GtkSpinButton *spin_button)
751 {
752   g_return_val_if_fail (spin_button != NULL, FALSE);
753   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
754
755   if (spin_button->timer)
756     {
757       gtk_spin_button_spin (spin_button, spin_button->click_child, 
758                             spin_button->timer_step);
759
760       if (spin_button->need_timer) {
761         spin_button->need_timer = FALSE;
762         spin_button->timer = gtk_timeout_add 
763           (SPIN_BUTTON_TIMER_DELAY, (GtkFunction) gtk_spin_button_timer, 
764            (gpointer) spin_button);
765         return FALSE;
766       }
767       else if (spin_button->climb_rate > 0.0 && 
768            spin_button->timer_step < spin_button->adjustment->page_increment)
769         {
770           if (spin_button->timer_calls < MAX_TIMER_CALLS)
771             spin_button->timer_calls++;
772           else 
773             {
774               spin_button->timer_calls = 0;
775               spin_button->timer_step += spin_button->climb_rate;
776             }
777       }
778       return TRUE;
779     }
780   return FALSE;
781 }
782
783 void
784 gtk_spin_button_spin (GtkSpinButton *spin_button,
785                       guint          direction,
786                       gfloat         step)
787 {
788   gfloat new_value = 0.0;
789   
790   g_return_if_fail (spin_button != NULL);
791   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
792   
793   if (direction == GTK_ARROW_UP)
794     {
795       new_value = spin_button->adjustment->value + step;
796       if (spin_button->wrap)
797         {
798           if (spin_button->adjustment->value == spin_button->adjustment->upper)
799             new_value = spin_button->adjustment->lower;
800           else if (new_value > spin_button->adjustment->upper)
801             new_value = spin_button->adjustment->upper;
802         }
803       else
804         new_value = MIN (new_value, spin_button->adjustment->upper);
805     }
806   else if (direction == GTK_ARROW_DOWN) 
807     {
808       new_value = spin_button->adjustment->value - step;
809       if (spin_button->wrap)
810         {
811           if (spin_button->adjustment->value == spin_button->adjustment->lower)
812             new_value = spin_button->adjustment->upper;
813           else if (new_value < spin_button->adjustment->lower)
814             new_value = spin_button->adjustment->lower;
815         }
816       else
817         new_value = MAX (new_value, spin_button->adjustment->lower);
818     }
819   
820   if (new_value != spin_button->adjustment->value)
821     gtk_adjustment_set_value (spin_button->adjustment, new_value);
822 }
823
824 static void
825 gtk_spin_button_value_changed (GtkWidget     *widget,
826                                GtkSpinButton *spin_button)
827 {
828   char buf[MAX_TEXT_LENGTH];
829
830   g_return_if_fail (widget != NULL);
831   g_return_if_fail (GTK_IS_ADJUSTMENT (widget));
832
833   sprintf (buf, "%0.*f", spin_button->digits, spin_button->adjustment->value);
834
835   gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
836 }
837
838 static gint
839 gtk_spin_button_key_press (GtkWidget     *widget,
840                            GdkEventKey   *event)
841 {
842   GtkSpinButton *spin;
843   gint key;
844   gboolean key_repeat = FALSE;
845
846   g_return_val_if_fail (widget != NULL, FALSE);
847   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
848   
849   spin = GTK_SPIN_BUTTON (widget);
850   key = event->keyval;
851
852   key_repeat = (event->time == spin->ev_time);
853
854   if (key == GDK_Up || key == GDK_Down || 
855       key == GDK_Page_Up || key == GDK_Page_Down)
856     gtk_spin_button_update (spin);
857
858   switch (key)
859     {
860     case GDK_Up:
861       if (GTK_WIDGET_HAS_FOCUS (widget))
862         {
863           gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), 
864                                         "key_press_event");
865           if (!key_repeat)
866             spin->timer_step = spin->adjustment->step_increment;
867
868           gtk_spin_button_spin (spin, GTK_ARROW_UP, spin->timer_step);
869
870           if (key_repeat)
871             {
872               if (spin->climb_rate > 0.0 && spin->timer_step
873                   < spin->adjustment->page_increment)
874                 {
875                   if (spin->timer_calls < MAX_TIMER_CALLS)
876                     spin->timer_calls++;
877                   else 
878                     {
879                       spin->timer_calls = 0;
880                       spin->timer_step += spin->climb_rate;
881                     }
882                 }
883             }
884           return TRUE;
885         }
886       return FALSE;
887     case GDK_Down:
888       if (GTK_WIDGET_HAS_FOCUS (widget))
889         {
890           gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), 
891                                         "key_press_event");
892           if (!key_repeat)
893             spin->timer_step = spin->adjustment->step_increment;
894
895           gtk_spin_button_spin (spin, GTK_ARROW_DOWN, spin->timer_step);
896
897           if (key_repeat)
898             {
899               if (spin->climb_rate > 0.0 && spin->timer_step
900                   < spin->adjustment->page_increment)
901                 {
902                   if (spin->timer_calls < MAX_TIMER_CALLS)
903                     spin->timer_calls++;
904                   else 
905                     {
906                       spin->timer_calls = 0;
907                       spin->timer_step += spin->climb_rate;
908                     }
909                 }
910             }
911           return TRUE;
912         }
913       return FALSE;
914     case GDK_Page_Up:
915       if (event->state & GDK_CONTROL_MASK)
916         gtk_adjustment_set_value (spin->adjustment, spin->adjustment->upper);
917       else
918         gtk_spin_button_spin (spin, GTK_ARROW_UP,
919                               spin->adjustment->page_increment);
920       return TRUE;
921     case GDK_Page_Down:
922       if (event->state & GDK_CONTROL_MASK)
923         gtk_adjustment_set_value (spin->adjustment, spin->adjustment->lower);
924       else
925         gtk_spin_button_spin (spin, GTK_ARROW_DOWN,
926                               spin->adjustment->page_increment);
927       return TRUE;
928     default:
929       break;
930     }
931   return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
932 }
933
934 static gint
935 gtk_spin_button_key_release (GtkWidget   *widget,
936                              GdkEventKey *event)
937 {
938   GtkSpinButton *spin;
939
940   g_return_val_if_fail (widget != NULL, FALSE);
941   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
942   
943   spin = GTK_SPIN_BUTTON (widget);
944   
945   spin->ev_time = event->time;
946   return TRUE;
947 }
948
949 static void
950 gtk_spin_button_snap (GtkSpinButton *spin_button,
951                       gfloat         val)
952 {
953   gfloat inc;
954   gfloat tmp;
955   
956   inc = spin_button->adjustment->step_increment;
957   tmp = (val - spin_button->adjustment->lower) / inc;
958   if (tmp - floor (tmp) < ceil (tmp) - tmp)
959     val = spin_button->adjustment->lower + floor (tmp) * inc;
960   else
961     val = spin_button->adjustment->lower + ceil (tmp) * inc;
962   gtk_adjustment_set_value (spin_button->adjustment, val);
963 }
964
965 static void 
966 gtk_spin_button_update (GtkSpinButton *spin_button)
967 {
968   gfloat val;
969   gchar *error = NULL;
970
971   g_return_if_fail (spin_button != NULL);
972   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
973
974   val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
975
976   if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
977     {
978       if (val < spin_button->adjustment->lower)
979         val = spin_button->adjustment->lower;
980       else if (val > spin_button->adjustment->upper)
981         val = spin_button->adjustment->upper;
982     }
983   else if ((spin_button->update_policy == GTK_UPDATE_IF_VALID) && 
984            (*error ||
985            val < spin_button->adjustment->lower ||
986            val > spin_button->adjustment->upper))
987     {
988       gtk_signal_emit_by_name (GTK_OBJECT (spin_button->adjustment),
989                                "value_changed"); 
990       return;
991     }
992
993   if (spin_button->snap_to_ticks)
994     gtk_spin_button_snap (spin_button, val);
995   else
996     gtk_adjustment_set_value (spin_button->adjustment, val);
997 }
998
999 static void
1000 gtk_spin_button_activate (GtkEditable *editable)
1001 {
1002   g_return_if_fail (editable != NULL);
1003   g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
1004
1005   if (editable->editable)
1006     gtk_spin_button_update (GTK_SPIN_BUTTON (editable));
1007 }
1008
1009 void
1010 gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
1011                                 GtkAdjustment *adjustment)
1012 {
1013   g_return_if_fail (spin_button != NULL);
1014   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1015
1016   if (spin_button->adjustment != adjustment)
1017     {
1018       if (spin_button->adjustment)
1019         {
1020           gtk_signal_disconnect_by_data (GTK_OBJECT (spin_button->adjustment),
1021                                          (gpointer) spin_button);
1022           gtk_object_unref (GTK_OBJECT (spin_button->adjustment));
1023         }
1024       spin_button->adjustment = adjustment;
1025       if (adjustment)
1026         {
1027           gtk_object_ref (GTK_OBJECT (adjustment));
1028           gtk_object_sink (GTK_OBJECT (adjustment));
1029           gtk_signal_connect 
1030             (GTK_OBJECT (adjustment), "value_changed",
1031              (GtkSignalFunc) gtk_spin_button_value_changed,
1032              (gpointer) spin_button);
1033         }
1034     }
1035 }
1036
1037 GtkAdjustment *
1038 gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
1039 {
1040   g_return_val_if_fail (spin_button != NULL, NULL);
1041   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
1042
1043   return spin_button->adjustment;
1044 }
1045
1046 void
1047 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
1048                             gint           digits)
1049 {
1050   g_return_if_fail (spin_button != NULL);
1051   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1052   g_return_if_fail (digits >= 0 || digits < 6);
1053
1054   if (spin_button->digits != digits)
1055     {
1056       spin_button->digits = digits;
1057       gtk_signal_emit_by_name (GTK_OBJECT (spin_button->adjustment),
1058                                "value_changed"); 
1059     }
1060 }
1061
1062 gfloat
1063 gtk_spin_button_get_value_as_float (GtkSpinButton *spin_button)
1064 {
1065   g_return_val_if_fail (spin_button != NULL, 0.0);
1066   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0.0);
1067
1068   return spin_button->adjustment->value;
1069 }
1070
1071 gint
1072 gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
1073 {
1074   gfloat val;
1075
1076   g_return_val_if_fail (spin_button != NULL, 0);
1077   g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1078
1079   val = spin_button->adjustment->value;
1080   if (val - floor (val) < ceil (val) - val)
1081     return floor (val);
1082   else
1083     return ceil (val);
1084 }
1085
1086 void 
1087 gtk_spin_button_set_value (GtkSpinButton *spin_button, 
1088                            gfloat         value)
1089 {
1090   g_return_if_fail (spin_button != NULL);
1091   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1092
1093   if (spin_button->adjustment->value != value)
1094     gtk_adjustment_set_value (spin_button->adjustment, value);
1095 }
1096
1097 void
1098 gtk_spin_button_set_update_policy (GtkSpinButton             *spin_button,
1099                                    GtkSpinButtonUpdatePolicy  policy)
1100 {
1101   g_return_if_fail (spin_button != NULL);
1102   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1103
1104   spin_button->update_policy = policy;
1105 }
1106
1107 void
1108 gtk_spin_button_set_numeric (GtkSpinButton  *spin_button,
1109                              gint            numeric)
1110 {
1111   g_return_if_fail (spin_button != NULL);
1112   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1113
1114   spin_button->numeric = (numeric != 0);
1115 }
1116
1117 static void
1118 gtk_spin_button_insert_text (GtkEditable *editable,
1119                              const gchar *new_text,
1120                              gint         new_text_length,
1121                              gint        *position)
1122 {
1123   GtkEntry *entry;
1124   GtkSpinButton *spin;
1125  
1126   g_return_if_fail (editable != NULL);
1127   g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
1128
1129   entry = GTK_ENTRY (editable);
1130   spin  = GTK_SPIN_BUTTON (editable);
1131
1132   if (spin->numeric)
1133     {
1134       struct lconv *lc;
1135       gboolean sign;
1136       gint dotpos = -1;
1137       gint i;
1138       gchar pos_sign;
1139       gchar neg_sign;
1140
1141       lc = localeconv ();
1142
1143       if (*(lc->negative_sign))
1144         neg_sign = *(lc->negative_sign);
1145       else 
1146         neg_sign = '-';
1147
1148       if (*(lc->positive_sign))
1149         pos_sign = *(lc->positive_sign);
1150       else 
1151         pos_sign = '+';
1152
1153       sign = ((strchr (entry->text, neg_sign) != 0) ||
1154               (strchr (entry->text, pos_sign) != 0));
1155
1156       if (sign && !(*position))
1157         return;
1158
1159       dotpos = strchr (entry->text, *(lc->decimal_point)) - entry->text;
1160       
1161       if (dotpos > -1 && *position > dotpos &&
1162           spin->digits - entry->text_length + dotpos - new_text_length + 1 < 0)
1163         return;
1164
1165       for (i = 0; i < new_text_length; i++)
1166         {
1167           if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1168             {
1169               if (sign || (*position) || i)
1170                 return;
1171               sign = TRUE;
1172             }
1173           else if (new_text[i] == *(lc->decimal_point))
1174             {
1175               if (!spin->digits || dotpos > -1 || 
1176                   (new_text_length - 1 - i + entry->text_length - *position > 
1177                    spin->digits)) 
1178                 return;
1179               dotpos = *position + i;
1180             }
1181           else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1182             return;
1183         }
1184     }
1185
1186   GTK_EDITABLE_CLASS (parent_class)->insert_text (editable, new_text,
1187                                                   new_text_length, position);
1188 }
1189
1190 void
1191 gtk_spin_button_set_wrap (GtkSpinButton  *spin_button,
1192                           gint            wrap)
1193 {
1194   g_return_if_fail (spin_button != NULL);
1195   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1196
1197   spin_button->wrap = (wrap != 0);
1198 }
1199
1200 void
1201 gtk_spin_button_set_shadow_type (GtkSpinButton *spin_button,
1202                                  GtkShadowType  shadow_type)
1203 {
1204   g_return_if_fail (spin_button != NULL);
1205   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1206
1207   if (shadow_type != spin_button->shadow_type)
1208     {
1209       spin_button->shadow_type = shadow_type;
1210       if (GTK_WIDGET_DRAWABLE (spin_button))
1211         gdk_window_clear (spin_button->panel);
1212     }
1213 }
1214
1215 void
1216 gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
1217                                    gint           snap_to_ticks)
1218 {
1219   guint new_val;
1220
1221   g_return_if_fail (spin_button != NULL);
1222   g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1223
1224   new_val = (snap_to_ticks != 0);
1225
1226   if (new_val != spin_button->snap_to_ticks)
1227     {
1228       spin_button->snap_to_ticks = new_val;
1229       if (new_val)
1230         {
1231           gchar *error = NULL;
1232           gfloat val;
1233
1234           val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
1235           gtk_spin_button_snap (spin_button, val);
1236         }
1237     }
1238 }