1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * GtkSpinButton widget for GTK+
5 * Copyright (C) 1998 Lars Hamann and Stefan Jeske
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.
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.
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.
28 #include "gdk/gdkkeysyms.h"
29 #include "gtkspinbutton.h"
31 #include "gtksignal.h"
34 #define MIN_SPIN_BUTTON_WIDTH 30
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
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,
55 static void gtk_spin_button_draw (GtkWidget *widget,
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,
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,
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,
82 static void gtk_spin_button_insert_text (GtkEditable *editable,
83 const gchar *new_text,
88 static GtkWidgetClass *parent_class = NULL;
92 gtk_spin_button_get_type ()
94 static guint spin_button_type = 0;
96 if (!spin_button_type)
98 GtkTypeInfo spin_button_info =
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,
109 spin_button_type = gtk_type_unique (gtk_entry_get_type (),
112 return spin_button_type;
116 gtk_spin_button_class_init (GtkSpinButtonClass *class)
118 GtkObjectClass *object_class;
119 GtkWidgetClass *widget_class;
120 GtkEditableClass *editable_class;
122 object_class = (GtkObjectClass*) class;
123 widget_class = (GtkWidgetClass*) class;
124 editable_class = (GtkEditableClass*) class;
126 parent_class = gtk_type_class (gtk_entry_get_type ());
128 object_class->finalize = gtk_spin_button_finalize;
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;
146 editable_class->insert_text = gtk_spin_button_insert_text;
147 editable_class->activate = gtk_spin_button_activate;
151 gtk_spin_button_init (GtkSpinButton *spin_button)
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;
170 gtk_spin_button_construct (GtkSpinButton *spin_button,
171 GtkAdjustment *adjustment,
175 char buf[MAX_TEXT_LENGTH];
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);
182 adjustment = (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0);
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;
192 gtk_spin_button_new (GtkAdjustment *adjustment,
198 g_return_val_if_fail (digits >= 0 && digits < 6, NULL);
200 spin = gtk_type_new (gtk_spin_button_get_type ());
202 gtk_spin_button_construct (spin, adjustment, climb_rate, digits);
204 return GTK_WIDGET (spin);
208 gtk_spin_button_finalize (GtkObject *object)
210 g_return_if_fail (object != NULL);
211 g_return_if_fail (GTK_IS_SPIN_BUTTON (object));
213 gtk_object_unref (GTK_OBJECT (GTK_SPIN_BUTTON (object)->adjustment));
215 (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
219 gtk_spin_button_map (GtkWidget *widget)
221 g_return_if_fail (widget != NULL);
222 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
224 if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
226 GTK_WIDGET_CLASS (parent_class)->map (widget);
227 gdk_window_show (GTK_SPIN_BUTTON (widget)->panel);
232 gtk_spin_button_unmap (GtkWidget *widget)
234 g_return_if_fail (widget != NULL);
235 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
237 if (GTK_WIDGET_MAPPED (widget))
239 gdk_window_hide (GTK_SPIN_BUTTON (widget)->panel);
240 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
245 gtk_spin_button_realize (GtkWidget *widget)
248 GdkWindowAttr attributes;
249 gint attributes_mask;
252 g_return_if_fail (widget != NULL);
253 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
255 spin = GTK_SPIN_BUTTON (widget);
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;
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;
271 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
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;
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);
284 gtk_style_set_background (widget->style, spin->panel, GTK_STATE_NORMAL);
288 gtk_spin_button_unrealize (GtkWidget *widget)
292 g_return_if_fail (widget != NULL);
293 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
295 spin = GTK_SPIN_BUTTON (widget);
297 GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
301 gdk_window_set_user_data (spin->panel, NULL);
302 gdk_window_destroy (spin->panel);
308 gtk_spin_button_size_request (GtkWidget *widget,
309 GtkRequisition *requisition)
311 g_return_if_fail (widget != NULL);
312 g_return_if_fail (requisition != NULL);
313 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
315 GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
317 requisition->width = MIN_SPIN_BUTTON_WIDTH + ARROW_SIZE
318 + 2 * widget->style->klass->xthickness;
322 gtk_spin_button_size_allocate (GtkWidget *widget,
323 GtkAllocation *allocation)
325 GtkAllocation child_allocation;
327 g_return_if_fail (widget != NULL);
328 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
329 g_return_if_fail (allocation != NULL);
331 child_allocation = *allocation;
332 child_allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
334 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &child_allocation);
336 widget->allocation = *allocation;
338 if (GTK_WIDGET_REALIZED (widget))
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;
346 gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel,
349 child_allocation.width,
350 child_allocation.height);
355 gtk_spin_button_paint (GtkWidget *widget,
360 g_return_if_fail (widget != NULL);
361 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
363 spin = GTK_SPIN_BUTTON (widget);
365 if (GTK_WIDGET_DRAWABLE (widget))
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);
372 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
373 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
375 GTK_WIDGET_CLASS (parent_class)->draw (widget, area);
380 gtk_spin_button_draw (GtkWidget *widget,
383 g_return_if_fail (widget != NULL);
384 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
385 g_return_if_fail (area != NULL);
387 if (GTK_WIDGET_DRAWABLE (widget))
388 gtk_spin_button_paint (widget, area);
392 gtk_spin_button_expose (GtkWidget *widget,
393 GdkEventExpose *event)
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);
399 if (GTK_WIDGET_DRAWABLE (widget))
400 gtk_spin_button_paint (widget, &event->area);
406 gtk_spin_button_draw_arrow (GtkSpinButton *spin_button,
409 GtkStateType state_type;
410 GtkShadowType shadow_type;
413 g_return_if_fail (spin_button != NULL);
414 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
416 widget = GTK_WIDGET (spin_button);
418 if (GTK_WIDGET_DRAWABLE (spin_button))
420 if (spin_button->in_child == arrow)
422 if (spin_button->click_child == arrow)
423 state_type = GTK_STATE_ACTIVE;
425 state_type = GTK_STATE_PRELIGHT;
428 state_type = GTK_STATE_NORMAL;
430 if (spin_button->click_child == arrow)
431 shadow_type = GTK_SHADOW_IN;
433 shadow_type = GTK_SHADOW_OUT;
435 if (arrow == GTK_ARROW_UP)
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,
442 widget->requisition.height / 2
443 - widget->style->klass->ythickness);
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,
452 widget->requisition.height / 2
453 - widget->style->klass->ythickness);
459 gtk_spin_button_enter_notify (GtkWidget *widget,
460 GdkEventCrossing *event)
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);
468 spin = GTK_SPIN_BUTTON (widget);
470 if (event->window == spin->panel)
475 gdk_window_get_pointer (spin->panel, &x, &y, NULL);
477 if (y <= widget->requisition.height / 2)
479 spin->in_child = GTK_ARROW_UP;
480 if (spin->click_child == 2)
481 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
485 spin->in_child = GTK_ARROW_DOWN;
486 if (spin->click_child == 2)
487 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
494 gtk_spin_button_leave_notify (GtkWidget *widget,
495 GdkEventCrossing *event)
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);
503 spin = GTK_SPIN_BUTTON (widget);
505 if (event->window == spin->panel && spin->click_child == 2)
507 if (spin->in_child == GTK_ARROW_UP)
510 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
515 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
522 gtk_spin_button_focus_out (GtkWidget *widget,
523 GdkEventFocus *event)
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);
529 gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
531 return GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
535 gtk_spin_button_button_press (GtkWidget *widget,
536 GdkEventButton *event)
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);
544 spin = GTK_SPIN_BUTTON (widget);
548 if (event->window == spin->panel)
550 if (!GTK_WIDGET_HAS_FOCUS (widget))
551 gtk_widget_grab_focus (widget);
552 gtk_grab_add (widget);
553 spin->button = event->button;
555 gtk_spin_button_update (spin);
557 if (event->y <= widget->requisition.height / 2)
559 spin->click_child = GTK_ARROW_UP;
560 if (event->button == 1)
562 gtk_spin_button_spin (spin, spin->click_child,
563 spin->adjustment->step_increment);
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);
573 else if (event->button == 2)
575 gtk_spin_button_spin (spin, spin->click_child,
576 spin->adjustment->page_increment);
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);
586 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
590 spin->click_child = GTK_ARROW_DOWN;
591 if (event->button == 1)
593 gtk_spin_button_spin (spin, spin->click_child,
594 spin->adjustment->step_increment);
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);
604 else if (event->button == 2)
606 gtk_spin_button_spin (spin, spin->click_child,
607 spin->adjustment->page_increment);
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);
617 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
621 GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
627 gtk_spin_button_button_release (GtkWidget *widget,
628 GdkEventButton *event)
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);
636 spin = GTK_SPIN_BUTTON (widget);
638 if (event->button == spin->button)
644 gtk_timeout_remove (spin->timer);
646 spin->timer_calls = 0;
647 spin->need_timer = FALSE;
650 if (event->button == 3)
652 if (event->y >= 0 && event->x >= 0 &&
653 event->y <= widget->requisition.height &&
654 event->x <= ARROW_SIZE + 2 * widget->style->klass->xthickness)
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);
668 gtk_grab_remove (widget);
669 click_child = spin->click_child;
670 spin->click_child = 2;
672 gtk_spin_button_draw_arrow (spin, click_child);
675 GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
681 gtk_spin_button_motion_notify (GtkWidget *widget,
682 GdkEventMotion *event)
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);
690 spin = GTK_SPIN_BUTTON (widget);
695 if (event->window == spin->panel)
701 gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
703 if (y <= widget->requisition.height / 2 &&
704 spin->in_child == GTK_ARROW_DOWN)
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);
710 else if (y > widget->requisition.height / 2 &&
711 spin->in_child == GTK_ARROW_UP)
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);
720 return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
724 gtk_spin_button_timer (GtkSpinButton *spin_button)
726 g_return_val_if_fail (spin_button != NULL, FALSE);
727 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
729 if (spin_button->timer)
731 gtk_spin_button_spin (spin_button, spin_button->click_child,
732 spin_button->timer_step);
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);
741 else if (spin_button->climb_rate > 0.0 &&
742 spin_button->timer_step < spin_button->adjustment->page_increment)
744 if (spin_button->timer_calls < MAX_TIMER_CALLS)
745 spin_button->timer_calls++;
748 spin_button->timer_calls = 0;
749 spin_button->timer_step += spin_button->climb_rate;
758 gtk_spin_button_spin (GtkSpinButton *spin_button,
762 gfloat new_value = 0.0;
764 g_return_if_fail (spin_button != NULL);
765 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
767 if (direction == GTK_ARROW_UP)
769 new_value = spin_button->adjustment->value + step;
770 if (spin_button->wrap)
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;
779 new_value = MIN (new_value, spin_button->adjustment->upper);
782 else if (direction == GTK_ARROW_DOWN)
784 new_value = spin_button->adjustment->value - step;
785 if (spin_button->wrap)
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;
794 new_value = MAX (new_value, spin_button->adjustment->lower);
798 if (new_value != spin_button->adjustment->value)
799 gtk_adjustment_set_value (spin_button->adjustment, new_value);
803 gtk_spin_button_value_changed (GtkWidget *widget,
804 GtkSpinButton *spin_button)
806 char buf[MAX_TEXT_LENGTH];
808 g_return_if_fail (widget != NULL);
809 g_return_if_fail (GTK_IS_ADJUSTMENT (widget));
811 sprintf (buf, "%0.*f", spin_button->digits, spin_button->adjustment->value);
813 gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
817 gtk_spin_button_key_press (GtkWidget *widget,
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);
827 spin = GTK_SPIN_BUTTON (widget);
830 if (key == GDK_Up || key == GDK_Down ||
831 key == GDK_Page_Up || key == GDK_Page_Down)
832 gtk_spin_button_update (spin);
837 if (GTK_WIDGET_HAS_FOCUS (widget))
839 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
841 gtk_spin_button_spin (spin, GTK_ARROW_UP,
842 spin->adjustment->step_increment);
847 if (GTK_WIDGET_HAS_FOCUS (widget))
849 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
851 gtk_spin_button_spin (spin, GTK_ARROW_DOWN,
852 spin->adjustment->step_increment);
857 if (event->state & GDK_CONTROL_MASK)
858 gtk_adjustment_set_value (spin->adjustment, spin->adjustment->upper);
860 gtk_spin_button_spin (spin, GTK_ARROW_UP,
861 spin->adjustment->page_increment);
864 if (event->state & GDK_CONTROL_MASK)
865 gtk_adjustment_set_value (spin->adjustment, spin->adjustment->lower);
867 gtk_spin_button_spin (spin, GTK_ARROW_DOWN,
868 spin->adjustment->page_increment);
873 return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
877 gtk_spin_button_snap (GtkSpinButton *spin_button,
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;
888 val = spin_button->adjustment->lower + ceil (tmp) * inc;
889 gtk_adjustment_set_value (spin_button->adjustment, val);
893 gtk_spin_button_update (GtkSpinButton *spin_button)
898 g_return_if_fail (spin_button != NULL);
899 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
901 val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
903 if (spin_button->update_policy & GTK_UPDATE_ALWAYS)
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;
910 else if ((spin_button->update_policy & GTK_UPDATE_IF_VALID) &&
912 val < spin_button->adjustment->lower ||
913 val > spin_button->adjustment->upper))
915 gtk_signal_emit_by_name (GTK_OBJECT (spin_button->adjustment),
920 if (spin_button->update_policy & GTK_UPDATE_SNAP_TO_TICKS)
921 gtk_spin_button_snap (spin_button, val);
923 gtk_adjustment_set_value (spin_button->adjustment, val);
927 gtk_spin_button_activate (GtkEditable *editable)
929 g_return_if_fail (editable != NULL);
930 g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
932 if (editable->editable)
933 gtk_spin_button_update (GTK_SPIN_BUTTON (editable));
937 gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
938 GtkAdjustment *adjustment)
940 g_return_if_fail (spin_button != NULL);
941 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
943 if (spin_button->adjustment != adjustment)
945 if (spin_button->adjustment)
947 gtk_signal_disconnect_by_data (GTK_OBJECT (spin_button->adjustment),
948 (gpointer) spin_button);
949 gtk_object_unref (GTK_OBJECT (spin_button->adjustment));
951 spin_button->adjustment = adjustment;
954 gtk_object_ref (GTK_OBJECT (adjustment));
955 gtk_object_sink (GTK_OBJECT (adjustment));
957 (GTK_OBJECT (adjustment), "value_changed",
958 (GtkSignalFunc) gtk_spin_button_value_changed,
959 (gpointer) spin_button);
965 gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
967 g_return_val_if_fail (spin_button != NULL, NULL);
968 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
970 return spin_button->adjustment;
974 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
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);
981 if (spin_button->digits != digits)
983 spin_button->digits = digits;
984 gtk_signal_emit_by_name (GTK_OBJECT (spin_button->adjustment),
990 gtk_spin_button_get_value_as_float (GtkSpinButton *spin_button)
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);
995 return spin_button->adjustment->value;
999 gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
1003 g_return_val_if_fail (spin_button != NULL, 0);
1004 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1006 val = spin_button->adjustment->value;
1007 if (val - floor (val) < ceil (val) - val)
1014 gtk_spin_button_set_value (GtkSpinButton *spin_button,
1017 g_return_if_fail (spin_button != NULL);
1018 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1020 if (spin_button->adjustment->value != value)
1021 gtk_adjustment_set_value (spin_button->adjustment, value);
1025 gtk_spin_button_set_update_policy (GtkSpinButton *spin_button,
1026 GtkSpinButtonUpdatePolicy policy)
1028 g_return_if_fail (spin_button != NULL);
1029 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1031 spin_button->update_policy = policy;
1032 if (policy & GTK_UPDATE_SNAP_TO_TICKS)
1034 gchar *error = NULL;
1037 val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
1038 gtk_spin_button_snap (spin_button, val);
1043 gtk_spin_button_set_numeric (GtkSpinButton *spin_button,
1046 g_return_if_fail (spin_button != NULL);
1047 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1049 spin_button->numeric = (numeric != 0);
1053 gtk_spin_button_insert_text (GtkEditable *editable,
1054 const gchar *new_text,
1055 gint new_text_length,
1059 GtkSpinButton *spin;
1061 g_return_if_fail (editable != NULL);
1062 g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
1064 entry = GTK_ENTRY (editable);
1065 spin = GTK_SPIN_BUTTON (editable);
1078 if (*(lc->negative_sign))
1079 neg_sign = *(lc->negative_sign);
1083 if (*(lc->positive_sign))
1084 pos_sign = *(lc->positive_sign);
1088 sign = ((strchr (entry->text, neg_sign) != 0) ||
1089 (strchr (entry->text, pos_sign) != 0));
1091 if (sign && !(*position))
1094 dotpos = strchr (entry->text, *(lc->decimal_point)) - entry->text;
1096 if (dotpos > -1 && *position > dotpos &&
1097 spin->digits - entry->text_length + dotpos - new_text_length + 1 < 0)
1100 for (i = 0; i < new_text_length; i++)
1102 if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1104 if (sign || (*position) || i)
1108 else if (new_text[i] == *(lc->decimal_point))
1110 if (!spin->digits || dotpos > -1 ||
1111 (new_text_length - 1 - i + entry->text_length - *position >
1114 dotpos = *position + i;
1116 else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1121 GTK_EDITABLE_CLASS (parent_class)->insert_text (editable, new_text,
1122 new_text_length, position);
1126 gtk_spin_button_set_wrap (GtkSpinButton *spin_button,
1129 g_return_if_fail (spin_button != NULL);
1130 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1132 spin_button->wrap = (wrap != 0);