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