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 Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
24 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
25 * file for a list of people on the GTK+ Team. See the ChangeLog
26 * files for a list of changes. These files are distributed with
27 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
35 #include "gdk/gdkkeysyms.h"
36 #include "gtkspinbutton.h"
38 #include "gtksignal.h"
41 #define MIN_SPIN_BUTTON_WIDTH 30
43 #define SPIN_BUTTON_INITIAL_TIMER_DELAY 200
44 #define SPIN_BUTTON_TIMER_DELAY 20
45 #define MAX_TEXT_LENGTH 256
46 #define MAX_TIMER_CALLS 5
70 static void gtk_spin_button_class_init (GtkSpinButtonClass *klass);
71 static void gtk_spin_button_init (GtkSpinButton *spin_button);
72 static void gtk_spin_button_finalize (GObject *object);
73 static void gtk_spin_button_set_arg (GtkObject *object,
76 static void gtk_spin_button_get_arg (GtkObject *object,
79 static void gtk_spin_button_map (GtkWidget *widget);
80 static void gtk_spin_button_unmap (GtkWidget *widget);
81 static void gtk_spin_button_realize (GtkWidget *widget);
82 static void gtk_spin_button_unrealize (GtkWidget *widget);
83 static void gtk_spin_button_size_request (GtkWidget *widget,
84 GtkRequisition *requisition);
85 static void gtk_spin_button_size_allocate (GtkWidget *widget,
86 GtkAllocation *allocation);
87 static void gtk_spin_button_paint (GtkWidget *widget,
89 static void gtk_spin_button_draw (GtkWidget *widget,
91 static gint gtk_spin_button_expose (GtkWidget *widget,
92 GdkEventExpose *event);
93 static gint gtk_spin_button_button_press (GtkWidget *widget,
94 GdkEventButton *event);
95 static gint gtk_spin_button_button_release (GtkWidget *widget,
96 GdkEventButton *event);
97 static gint gtk_spin_button_motion_notify (GtkWidget *widget,
98 GdkEventMotion *event);
99 static gint gtk_spin_button_enter_notify (GtkWidget *widget,
100 GdkEventCrossing *event);
101 static gint gtk_spin_button_leave_notify (GtkWidget *widget,
102 GdkEventCrossing *event);
103 static gint gtk_spin_button_focus_out (GtkWidget *widget,
104 GdkEventFocus *event);
105 static void gtk_spin_button_draw_arrow (GtkSpinButton *spin_button,
107 static gint gtk_spin_button_timer (GtkSpinButton *spin_button);
108 static void gtk_spin_button_value_changed (GtkAdjustment *adjustment,
109 GtkSpinButton *spin_button);
110 static gint gtk_spin_button_key_press (GtkWidget *widget,
112 static gint gtk_spin_button_key_release (GtkWidget *widget,
114 static gint gtk_spin_button_scroll (GtkWidget *widget,
115 GdkEventScroll *event);
116 static void gtk_spin_button_activate (GtkEditable *editable);
117 static void gtk_spin_button_snap (GtkSpinButton *spin_button,
119 static void gtk_spin_button_insert_text (GtkEditable *editable,
120 const gchar *new_text,
121 gint new_text_length,
123 static void gtk_spin_button_real_spin (GtkSpinButton *spin_button,
125 static gint gtk_spin_button_default_input (GtkSpinButton *spin_button,
127 static gint gtk_spin_button_default_output (GtkSpinButton *spin_button);
130 static GtkEntryClass *parent_class = NULL;
131 static guint spinbutton_signals[LAST_SIGNAL] = {0};
135 gtk_spin_button_get_type (void)
137 static guint spin_button_type = 0;
139 if (!spin_button_type)
141 static const GtkTypeInfo spin_button_info =
144 sizeof (GtkSpinButton),
145 sizeof (GtkSpinButtonClass),
146 (GtkClassInitFunc) gtk_spin_button_class_init,
147 (GtkObjectInitFunc) gtk_spin_button_init,
148 /* reserved_1 */ NULL,
149 /* reserved_2 */ NULL,
150 (GtkClassInitFunc) NULL,
153 spin_button_type = gtk_type_unique (GTK_TYPE_ENTRY, &spin_button_info);
155 return spin_button_type;
159 gtk_spin_button_class_init (GtkSpinButtonClass *class)
161 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
162 GtkObjectClass *object_class;
163 GtkWidgetClass *widget_class;
164 GtkEditableClass *editable_class;
166 object_class = (GtkObjectClass*) class;
167 widget_class = (GtkWidgetClass*) class;
168 editable_class = (GtkEditableClass*) class;
170 parent_class = gtk_type_class (GTK_TYPE_ENTRY);
172 gobject_class->finalize = gtk_spin_button_finalize;
174 object_class->set_arg = gtk_spin_button_set_arg;
175 object_class->get_arg = gtk_spin_button_get_arg;
177 widget_class->map = gtk_spin_button_map;
178 widget_class->unmap = gtk_spin_button_unmap;
179 widget_class->realize = gtk_spin_button_realize;
180 widget_class->unrealize = gtk_spin_button_unrealize;
181 widget_class->size_request = gtk_spin_button_size_request;
182 widget_class->size_allocate = gtk_spin_button_size_allocate;
183 widget_class->draw = gtk_spin_button_draw;
184 widget_class->expose_event = gtk_spin_button_expose;
185 widget_class->scroll_event = gtk_spin_button_scroll;
186 widget_class->button_press_event = gtk_spin_button_button_press;
187 widget_class->button_release_event = gtk_spin_button_button_release;
188 widget_class->motion_notify_event = gtk_spin_button_motion_notify;
189 widget_class->key_press_event = gtk_spin_button_key_press;
190 widget_class->key_release_event = gtk_spin_button_key_release;
191 widget_class->enter_notify_event = gtk_spin_button_enter_notify;
192 widget_class->leave_notify_event = gtk_spin_button_leave_notify;
193 widget_class->focus_out_event = gtk_spin_button_focus_out;
195 editable_class->insert_text = gtk_spin_button_insert_text;
196 editable_class->activate = gtk_spin_button_activate;
199 class->output = NULL;
201 gtk_object_add_arg_type ("GtkSpinButton::adjustment",
205 gtk_object_add_arg_type ("GtkSpinButton::climb_rate",
209 gtk_object_add_arg_type ("GtkSpinButton::digits",
213 gtk_object_add_arg_type ("GtkSpinButton::snap_to_ticks",
217 gtk_object_add_arg_type ("GtkSpinButton::numeric",
221 gtk_object_add_arg_type ("GtkSpinButton::wrap",
225 gtk_object_add_arg_type ("GtkSpinButton::update_policy",
226 GTK_TYPE_SPIN_BUTTON_UPDATE_POLICY,
229 gtk_object_add_arg_type ("GtkSpinButton::shadow_type",
230 GTK_TYPE_SHADOW_TYPE,
233 gtk_object_add_arg_type ("GtkSpinButton::value",
238 spinbutton_signals[INPUT] =
239 gtk_signal_new ("input",
241 GTK_CLASS_TYPE (object_class),
242 GTK_SIGNAL_OFFSET (GtkSpinButtonClass, input),
243 gtk_marshal_INT__POINTER,
244 GTK_TYPE_INT, 1, GTK_TYPE_POINTER);
246 spinbutton_signals[OUTPUT] =
247 gtk_signal_new ("output",
249 GTK_CLASS_TYPE (object_class),
250 GTK_SIGNAL_OFFSET (GtkSpinButtonClass, output),
251 gtk_marshal_BOOL__NONE,
254 gtk_object_class_add_signals (object_class, spinbutton_signals, LAST_SIGNAL);
258 gtk_spin_button_set_arg (GtkObject *object,
262 GtkSpinButton *spin_button;
264 spin_button = GTK_SPIN_BUTTON (object);
268 GtkAdjustment *adjustment;
271 adjustment = GTK_VALUE_POINTER (*arg);
273 adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
274 gtk_spin_button_set_adjustment (spin_button, adjustment);
277 gtk_spin_button_configure (spin_button,
278 spin_button->adjustment,
279 GTK_VALUE_FLOAT (*arg),
280 spin_button->digits);
283 gtk_spin_button_configure (spin_button,
284 spin_button->adjustment,
285 spin_button->climb_rate,
286 GTK_VALUE_UINT (*arg));
288 case ARG_SNAP_TO_TICKS:
289 gtk_spin_button_set_snap_to_ticks (spin_button, GTK_VALUE_BOOL (*arg));
292 gtk_spin_button_set_numeric (spin_button, GTK_VALUE_BOOL (*arg));
295 gtk_spin_button_set_wrap (spin_button, GTK_VALUE_BOOL (*arg));
297 case ARG_UPDATE_POLICY:
298 gtk_spin_button_set_update_policy (spin_button, GTK_VALUE_ENUM (*arg));
300 case ARG_SHADOW_TYPE:
301 gtk_spin_button_set_shadow_type (spin_button, GTK_VALUE_ENUM (*arg));
304 gtk_spin_button_set_value (spin_button, GTK_VALUE_FLOAT (*arg));
312 gtk_spin_button_get_arg (GtkObject *object,
316 GtkSpinButton *spin_button;
318 spin_button = GTK_SPIN_BUTTON (object);
323 GTK_VALUE_POINTER (*arg) = spin_button->adjustment;
326 GTK_VALUE_FLOAT (*arg) = spin_button->climb_rate;
329 GTK_VALUE_UINT (*arg) = spin_button->digits;
331 case ARG_SNAP_TO_TICKS:
332 GTK_VALUE_BOOL (*arg) = spin_button->snap_to_ticks;
335 GTK_VALUE_BOOL (*arg) = spin_button->numeric;
338 GTK_VALUE_BOOL (*arg) = spin_button->wrap;
340 case ARG_UPDATE_POLICY:
341 GTK_VALUE_ENUM (*arg) = spin_button->update_policy;
343 case ARG_SHADOW_TYPE:
344 GTK_VALUE_ENUM (*arg) = spin_button->shadow_type;
347 GTK_VALUE_FLOAT (*arg) = spin_button->adjustment->value;
350 arg->type = GTK_TYPE_INVALID;
356 gtk_spin_button_init (GtkSpinButton *spin_button)
358 spin_button->adjustment = NULL;
359 spin_button->panel = NULL;
360 spin_button->shadow_type = GTK_SHADOW_NONE;
361 spin_button->timer = 0;
362 spin_button->ev_time = 0;
363 spin_button->climb_rate = 0.0;
364 spin_button->timer_step = 0.0;
365 spin_button->update_policy = GTK_UPDATE_ALWAYS;
366 spin_button->in_child = 2;
367 spin_button->click_child = 2;
368 spin_button->button = 0;
369 spin_button->need_timer = FALSE;
370 spin_button->timer_calls = 0;
371 spin_button->digits = 0;
372 spin_button->numeric = FALSE;
373 spin_button->wrap = FALSE;
374 spin_button->snap_to_ticks = FALSE;
375 gtk_spin_button_set_adjustment (spin_button,
376 (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0));
380 gtk_spin_button_finalize (GObject *object)
382 g_return_if_fail (GTK_IS_SPIN_BUTTON (object));
384 gtk_object_unref (GTK_OBJECT (GTK_SPIN_BUTTON (object)->adjustment));
386 G_OBJECT_CLASS (parent_class)->finalize (object);
390 gtk_spin_button_map (GtkWidget *widget)
392 g_return_if_fail (widget != NULL);
393 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
395 if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
397 GTK_WIDGET_CLASS (parent_class)->map (widget);
398 gdk_window_show (GTK_SPIN_BUTTON (widget)->panel);
403 gtk_spin_button_unmap (GtkWidget *widget)
405 g_return_if_fail (widget != NULL);
406 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
408 if (GTK_WIDGET_MAPPED (widget))
410 gdk_window_hide (GTK_SPIN_BUTTON (widget)->panel);
411 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
416 gtk_spin_button_realize (GtkWidget *widget)
418 GtkSpinButton *spin_button;
419 GdkWindowAttr attributes;
420 gint attributes_mask;
424 g_return_if_fail (widget != NULL);
425 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
427 spin_button = GTK_SPIN_BUTTON (widget);
429 real_width = widget->allocation.width;
430 widget->allocation.width -= ARROW_SIZE + 2 * widget->style->xthickness;
431 gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
432 GDK_KEY_RELEASE_MASK);
433 GTK_WIDGET_CLASS (parent_class)->realize (widget);
435 widget->allocation.width = real_width;
437 attributes.window_type = GDK_WINDOW_CHILD;
438 attributes.wclass = GDK_INPUT_OUTPUT;
439 attributes.visual = gtk_widget_get_visual (widget);
440 attributes.colormap = gtk_widget_get_colormap (widget);
441 attributes.event_mask = gtk_widget_get_events (widget);
442 attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
443 | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
444 | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
446 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
448 attributes.x = (widget->allocation.x + widget->allocation.width - ARROW_SIZE -
449 2 * widget->style->xthickness);
450 attributes.y = widget->allocation.y + (widget->allocation.height -
451 widget->requisition.height) / 2;
452 attributes.width = ARROW_SIZE + 2 * widget->style->xthickness;
453 attributes.height = widget->requisition.height;
455 spin_button->panel = gdk_window_new (gtk_widget_get_parent_window (widget),
456 &attributes, attributes_mask);
457 gdk_window_set_user_data (spin_button->panel, widget);
459 gtk_style_set_background (widget->style, spin_button->panel, GTK_STATE_NORMAL);
462 gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
464 if (return_val == FALSE)
465 gtk_spin_button_default_output (spin_button);
469 gtk_spin_button_unrealize (GtkWidget *widget)
473 g_return_if_fail (widget != NULL);
474 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
476 spin = GTK_SPIN_BUTTON (widget);
478 GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
482 gdk_window_set_user_data (spin->panel, NULL);
483 gdk_window_destroy (spin->panel);
489 gtk_spin_button_size_request (GtkWidget *widget,
490 GtkRequisition *requisition)
492 g_return_if_fail (widget != NULL);
493 g_return_if_fail (requisition != NULL);
494 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
496 GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
498 requisition->width = MIN_SPIN_BUTTON_WIDTH + ARROW_SIZE
499 + 2 * widget->style->xthickness;
503 gtk_spin_button_size_allocate (GtkWidget *widget,
504 GtkAllocation *allocation)
506 GtkAllocation child_allocation;
508 g_return_if_fail (widget != NULL);
509 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
510 g_return_if_fail (allocation != NULL);
512 child_allocation = *allocation;
513 if (child_allocation.width > ARROW_SIZE + 2 * widget->style->xthickness)
514 child_allocation.width -= ARROW_SIZE + 2 * widget->style->xthickness;
516 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
517 child_allocation.x += ARROW_SIZE + 2 * widget->style->xthickness;
519 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &child_allocation);
521 widget->allocation = *allocation;
523 if (GTK_WIDGET_REALIZED (widget))
525 child_allocation.width = ARROW_SIZE + 2 * widget->style->xthickness;
526 child_allocation.height = widget->requisition.height;
528 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
529 child_allocation.x = (allocation->x + allocation->width - ARROW_SIZE -
530 2 * widget->style->xthickness);
532 child_allocation.x = allocation->x;
534 child_allocation.y = allocation->y + (allocation->height - widget->requisition.height) / 2;
536 gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel,
539 child_allocation.width,
540 child_allocation.height);
545 gtk_spin_button_paint (GtkWidget *widget,
550 g_return_if_fail (widget != NULL);
551 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
553 spin = GTK_SPIN_BUTTON (widget);
555 if (GTK_WIDGET_DRAWABLE (widget))
557 if (spin->shadow_type != GTK_SHADOW_NONE)
558 gtk_paint_box (widget->style, spin->panel,
559 GTK_STATE_NORMAL, spin->shadow_type,
560 area, widget, "spinbutton",
562 ARROW_SIZE + 2 * widget->style->xthickness,
563 widget->requisition.height);
566 gdk_window_set_back_pixmap (spin->panel, NULL, TRUE);
567 gdk_window_clear_area (spin->panel, area->x, area->y, area->width, area->height);
569 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
570 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
572 GTK_WIDGET_CLASS (parent_class)->draw (widget, area);
577 gtk_spin_button_draw (GtkWidget *widget,
580 g_return_if_fail (widget != NULL);
581 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
582 g_return_if_fail (area != NULL);
584 if (GTK_WIDGET_DRAWABLE (widget))
585 gtk_spin_button_paint (widget, area);
589 gtk_spin_button_expose (GtkWidget *widget,
590 GdkEventExpose *event)
592 g_return_val_if_fail (widget != NULL, FALSE);
593 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
594 g_return_val_if_fail (event != NULL, FALSE);
596 if (GTK_WIDGET_DRAWABLE (widget))
597 gtk_spin_button_paint (widget, &event->area);
603 gtk_spin_button_draw_arrow (GtkSpinButton *spin_button,
606 GtkStateType state_type;
607 GtkShadowType shadow_type;
612 g_return_if_fail (spin_button != NULL);
613 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
615 widget = GTK_WIDGET (spin_button);
617 if (GTK_WIDGET_DRAWABLE (spin_button))
619 if (!spin_button->wrap &&
620 (((arrow == GTK_ARROW_UP &&
621 (spin_button->adjustment->upper - spin_button->adjustment->value
623 ((arrow == GTK_ARROW_DOWN &&
624 (spin_button->adjustment->value - spin_button->adjustment->lower
627 shadow_type = GTK_SHADOW_ETCHED_IN;
628 state_type = GTK_STATE_NORMAL;
632 if (spin_button->in_child == arrow)
634 if (spin_button->click_child == arrow)
635 state_type = GTK_STATE_ACTIVE;
637 state_type = GTK_STATE_PRELIGHT;
640 state_type = GTK_STATE_NORMAL;
642 if (spin_button->click_child == arrow)
643 shadow_type = GTK_SHADOW_IN;
645 shadow_type = GTK_SHADOW_OUT;
647 if (arrow == GTK_ARROW_UP)
649 if (spin_button->shadow_type != GTK_SHADOW_NONE)
651 x = widget->style->xthickness;
652 y = widget->style->ythickness;
656 x = widget->style->xthickness - 1;
657 y = widget->style->ythickness - 1;
659 gtk_paint_arrow (widget->style, spin_button->panel,
660 state_type, shadow_type,
661 NULL, widget, "spinbutton",
663 x, y, ARROW_SIZE, widget->requisition.height / 2
664 - widget->style->ythickness);
668 if (spin_button->shadow_type != GTK_SHADOW_NONE)
670 x = widget->style->xthickness;
671 y = widget->requisition.height / 2;
675 x = widget->style->xthickness - 1;
676 y = widget->requisition.height / 2 + 1;
678 gtk_paint_arrow (widget->style, spin_button->panel,
679 state_type, shadow_type,
680 NULL, widget, "spinbutton",
682 x, y, ARROW_SIZE, widget->requisition.height / 2
683 - widget->style->ythickness);
689 gtk_spin_button_enter_notify (GtkWidget *widget,
690 GdkEventCrossing *event)
694 g_return_val_if_fail (widget != NULL, FALSE);
695 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
696 g_return_val_if_fail (event != NULL, FALSE);
698 spin = GTK_SPIN_BUTTON (widget);
700 if (event->window == spin->panel)
705 gdk_window_get_pointer (spin->panel, &x, &y, NULL);
707 if (y <= widget->requisition.height / 2)
709 spin->in_child = GTK_ARROW_UP;
710 if (spin->click_child == 2)
711 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
715 spin->in_child = GTK_ARROW_DOWN;
716 if (spin->click_child == 2)
717 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
724 gtk_spin_button_leave_notify (GtkWidget *widget,
725 GdkEventCrossing *event)
729 g_return_val_if_fail (widget != NULL, FALSE);
730 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
731 g_return_val_if_fail (event != NULL, FALSE);
733 spin = GTK_SPIN_BUTTON (widget);
735 if (event->window == spin->panel && spin->click_child == 2)
737 if (spin->in_child == GTK_ARROW_UP)
740 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
745 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
752 gtk_spin_button_focus_out (GtkWidget *widget,
753 GdkEventFocus *event)
755 g_return_val_if_fail (widget != NULL, FALSE);
756 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
757 g_return_val_if_fail (event != NULL, FALSE);
759 if (GTK_EDITABLE (widget)->editable)
760 gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
762 return GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
766 gtk_spin_button_scroll (GtkWidget *widget,
767 GdkEventScroll *event)
771 g_return_val_if_fail (widget != NULL, FALSE);
772 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
773 g_return_val_if_fail (event != NULL, FALSE);
775 spin = GTK_SPIN_BUTTON (widget);
777 if (event->direction == GDK_SCROLL_UP)
779 if (!GTK_WIDGET_HAS_FOCUS (widget))
780 gtk_widget_grab_focus (widget);
781 gtk_spin_button_real_spin (spin, spin->adjustment->step_increment);
783 else if (event->direction == GDK_SCROLL_DOWN)
785 if (!GTK_WIDGET_HAS_FOCUS (widget))
786 gtk_widget_grab_focus (widget);
787 gtk_spin_button_real_spin (spin, -spin->adjustment->step_increment);
796 gtk_spin_button_button_press (GtkWidget *widget,
797 GdkEventButton *event)
801 g_return_val_if_fail (widget != NULL, FALSE);
802 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
803 g_return_val_if_fail (event != NULL, FALSE);
805 spin = GTK_SPIN_BUTTON (widget);
809 if (event->window == spin->panel)
811 if (!GTK_WIDGET_HAS_FOCUS (widget))
812 gtk_widget_grab_focus (widget);
813 gtk_grab_add (widget);
814 spin->button = event->button;
816 if (GTK_EDITABLE (widget)->editable)
817 gtk_spin_button_update (spin);
819 if (event->y <= widget->requisition.height / 2)
821 spin->click_child = GTK_ARROW_UP;
822 if (event->button == 1)
824 gtk_spin_button_real_spin (spin,
825 spin->adjustment->step_increment);
828 spin->timer_step = spin->adjustment->step_increment;
829 spin->need_timer = TRUE;
830 spin->timer = gtk_timeout_add
831 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
832 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
835 else if (event->button == 2)
837 gtk_spin_button_real_spin (spin,
838 spin->adjustment->page_increment);
841 spin->timer_step = spin->adjustment->page_increment;
842 spin->need_timer = TRUE;
843 spin->timer = gtk_timeout_add
844 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
845 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
848 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
852 spin->click_child = GTK_ARROW_DOWN;
853 if (event->button == 1)
855 gtk_spin_button_real_spin (spin,
856 -spin->adjustment->step_increment);
859 spin->timer_step = spin->adjustment->step_increment;
860 spin->need_timer = TRUE;
861 spin->timer = gtk_timeout_add
862 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
863 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
866 else if (event->button == 2)
868 gtk_spin_button_real_spin (spin,
869 -spin->adjustment->page_increment);
872 spin->timer_step = spin->adjustment->page_increment;
873 spin->need_timer = TRUE;
874 spin->timer = gtk_timeout_add
875 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
876 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
879 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
883 GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
889 gtk_spin_button_button_release (GtkWidget *widget,
890 GdkEventButton *event)
894 g_return_val_if_fail (widget != NULL, FALSE);
895 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
896 g_return_val_if_fail (event != NULL, FALSE);
898 spin = GTK_SPIN_BUTTON (widget);
900 if (event->button == spin->button)
906 gtk_timeout_remove (spin->timer);
908 spin->timer_calls = 0;
909 spin->need_timer = FALSE;
912 if (event->button == 3)
914 if (event->y >= 0 && event->x >= 0 &&
915 event->y <= widget->requisition.height &&
916 event->x <= ARROW_SIZE + 2 * widget->style->xthickness)
918 if (spin->click_child == GTK_ARROW_UP &&
919 event->y <= widget->requisition.height / 2)
923 diff = spin->adjustment->upper - spin->adjustment->value;
925 gtk_spin_button_real_spin (spin, diff);
927 else if (spin->click_child == GTK_ARROW_DOWN &&
928 event->y > widget->requisition.height / 2)
932 diff = spin->adjustment->value - spin->adjustment->lower;
934 gtk_spin_button_real_spin (spin, -diff);
938 gtk_grab_remove (widget);
939 click_child = spin->click_child;
940 spin->click_child = 2;
942 gtk_spin_button_draw_arrow (spin, click_child);
945 GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
951 gtk_spin_button_motion_notify (GtkWidget *widget,
952 GdkEventMotion *event)
956 g_return_val_if_fail (widget != NULL, FALSE);
957 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
958 g_return_val_if_fail (event != NULL, FALSE);
960 spin = GTK_SPIN_BUTTON (widget);
965 if (event->window == spin->panel)
971 gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
973 if (y <= widget->requisition.height / 2 &&
974 spin->in_child == GTK_ARROW_DOWN)
976 spin->in_child = GTK_ARROW_UP;
977 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
978 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
980 else if (y > widget->requisition.height / 2 &&
981 spin->in_child == GTK_ARROW_UP)
983 spin->in_child = GTK_ARROW_DOWN;
984 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
985 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
990 return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
994 gtk_spin_button_timer (GtkSpinButton *spin_button)
996 gboolean retval = FALSE;
998 GDK_THREADS_ENTER ();
1000 if (spin_button->timer)
1002 if (spin_button->click_child == GTK_ARROW_UP)
1003 gtk_spin_button_real_spin (spin_button, spin_button->timer_step);
1005 gtk_spin_button_real_spin (spin_button, -spin_button->timer_step);
1007 if (spin_button->need_timer)
1009 spin_button->need_timer = FALSE;
1010 spin_button->timer = gtk_timeout_add
1011 (SPIN_BUTTON_TIMER_DELAY, (GtkFunction) gtk_spin_button_timer,
1012 (gpointer) spin_button);
1016 if (spin_button->climb_rate > 0.0 && spin_button->timer_step
1017 < spin_button->adjustment->page_increment)
1019 if (spin_button->timer_calls < MAX_TIMER_CALLS)
1020 spin_button->timer_calls++;
1023 spin_button->timer_calls = 0;
1024 spin_button->timer_step += spin_button->climb_rate;
1031 GDK_THREADS_LEAVE ();
1037 gtk_spin_button_value_changed (GtkAdjustment *adjustment,
1038 GtkSpinButton *spin_button)
1042 g_return_if_fail (adjustment != NULL);
1043 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1046 gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1048 if (return_val == FALSE)
1049 gtk_spin_button_default_output (spin_button);
1053 gtk_spin_button_key_press (GtkWidget *widget,
1056 GtkSpinButton *spin;
1058 gboolean key_repeat = FALSE;
1060 g_return_val_if_fail (widget != NULL, FALSE);
1061 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1062 g_return_val_if_fail (event != NULL, FALSE);
1064 spin = GTK_SPIN_BUTTON (widget);
1065 key = event->keyval;
1067 key_repeat = (event->time == spin->ev_time);
1069 if (GTK_EDITABLE (widget)->editable &&
1070 (key == GDK_Up || key == GDK_Down ||
1071 key == GDK_Page_Up || key == GDK_Page_Down))
1072 gtk_spin_button_update (spin);
1078 if (GTK_WIDGET_HAS_FOCUS (widget))
1080 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
1083 spin->timer_step = spin->adjustment->step_increment;
1085 gtk_spin_button_real_spin (spin, spin->timer_step);
1089 if (spin->climb_rate > 0.0 && spin->timer_step
1090 < spin->adjustment->page_increment)
1092 if (spin->timer_calls < MAX_TIMER_CALLS)
1093 spin->timer_calls++;
1096 spin->timer_calls = 0;
1097 spin->timer_step += spin->climb_rate;
1107 if (GTK_WIDGET_HAS_FOCUS (widget))
1109 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
1112 spin->timer_step = spin->adjustment->step_increment;
1114 gtk_spin_button_real_spin (spin, -spin->timer_step);
1118 if (spin->climb_rate > 0.0 && spin->timer_step
1119 < spin->adjustment->page_increment)
1121 if (spin->timer_calls < MAX_TIMER_CALLS)
1122 spin->timer_calls++;
1125 spin->timer_calls = 0;
1126 spin->timer_step += spin->climb_rate;
1136 if (event->state & GDK_CONTROL_MASK)
1138 gfloat diff = spin->adjustment->upper - spin->adjustment->value;
1140 gtk_spin_button_real_spin (spin, diff);
1143 gtk_spin_button_real_spin (spin, spin->adjustment->page_increment);
1148 if (event->state & GDK_CONTROL_MASK)
1150 gfloat diff = spin->adjustment->value - spin->adjustment->lower;
1152 gtk_spin_button_real_spin (spin, -diff);
1155 gtk_spin_button_real_spin (spin, -spin->adjustment->page_increment);
1162 return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
1166 gtk_spin_button_key_release (GtkWidget *widget,
1169 GtkSpinButton *spin;
1171 g_return_val_if_fail (widget != NULL, FALSE);
1172 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1174 spin = GTK_SPIN_BUTTON (widget);
1176 spin->ev_time = event->time;
1181 gtk_spin_button_snap (GtkSpinButton *spin_button,
1187 inc = spin_button->adjustment->step_increment;
1188 tmp = (val - spin_button->adjustment->lower) / inc;
1189 if (tmp - floor (tmp) < ceil (tmp) - tmp)
1190 val = spin_button->adjustment->lower + floor (tmp) * inc;
1192 val = spin_button->adjustment->lower + ceil (tmp) * inc;
1194 if (fabs (val - spin_button->adjustment->value) > EPSILON)
1195 gtk_adjustment_set_value (spin_button->adjustment, val);
1198 gint return_val = FALSE;
1199 gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1201 if (return_val == FALSE)
1202 gtk_spin_button_default_output (spin_button);
1207 gtk_spin_button_activate (GtkEditable *editable)
1209 g_return_if_fail (editable != NULL);
1210 g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
1212 if (editable->editable)
1213 gtk_spin_button_update (GTK_SPIN_BUTTON (editable));
1217 gtk_spin_button_insert_text (GtkEditable *editable,
1218 const gchar *new_text,
1219 gint new_text_length,
1223 GtkSpinButton *spin;
1225 g_return_if_fail (editable != NULL);
1226 g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
1228 entry = GTK_ENTRY (editable);
1229 spin = GTK_SPIN_BUTTON (editable);
1241 entry_length = entry->text_length;
1245 if (*(lc->negative_sign))
1246 neg_sign = *(lc->negative_sign);
1250 if (*(lc->positive_sign))
1251 pos_sign = *(lc->positive_sign);
1255 for (sign=0, i=0; i<entry_length; i++)
1256 if ((entry->text[i] == neg_sign) ||
1257 (entry->text[i] == pos_sign))
1263 if (sign && !(*position))
1266 for (dotpos=-1, i=0; i<entry_length; i++)
1267 if (entry->text[i] == *(lc->decimal_point))
1273 if (dotpos > -1 && *position > dotpos &&
1274 spin->digits - entry_length
1275 + dotpos - new_text_length + 1 < 0)
1278 for (i = 0; i < new_text_length; i++)
1280 if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1282 if (sign || (*position) || i)
1286 else if (new_text[i] == *(lc->decimal_point))
1288 if (!spin->digits || dotpos > -1 ||
1289 (new_text_length - 1 - i + entry_length
1290 - *position > spin->digits))
1292 dotpos = *position + i;
1294 else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1299 GTK_EDITABLE_CLASS (parent_class)->insert_text (editable, new_text,
1300 new_text_length, position);
1304 gtk_spin_button_real_spin (GtkSpinButton *spin_button,
1308 gfloat new_value = 0.0;
1310 g_return_if_fail (spin_button != NULL);
1311 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1313 adj = spin_button->adjustment;
1315 new_value = adj->value + increment;
1319 if (spin_button->wrap)
1321 if (fabs (adj->value - adj->upper) < EPSILON)
1322 new_value = adj->lower;
1323 else if (new_value > adj->upper)
1324 new_value = adj->upper;
1327 new_value = MIN (new_value, adj->upper);
1329 else if (increment < 0)
1331 if (spin_button->wrap)
1333 if (fabs (adj->value - adj->lower) < EPSILON)
1334 new_value = adj->upper;
1335 else if (new_value < adj->lower)
1336 new_value = adj->lower;
1339 new_value = MAX (new_value, adj->lower);
1342 if (fabs (new_value - adj->value) > EPSILON)
1343 gtk_adjustment_set_value (adj, new_value);
1347 gtk_spin_button_default_input (GtkSpinButton *spin_button,
1352 *new_val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &err);
1360 gtk_spin_button_default_output (GtkSpinButton *spin_button)
1362 gchar buf[MAX_TEXT_LENGTH];
1364 sprintf (buf, "%0.*f", spin_button->digits, spin_button->adjustment->value);
1365 if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1366 gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1371 /***********************************************************
1372 ***********************************************************
1373 *** Public interface ***
1374 ***********************************************************
1375 ***********************************************************/
1379 gtk_spin_button_configure (GtkSpinButton *spin_button,
1380 GtkAdjustment *adjustment,
1384 g_return_if_fail (spin_button != NULL);
1385 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1386 g_return_if_fail (digits < 6);
1389 gtk_spin_button_set_adjustment (spin_button, adjustment);
1391 adjustment = spin_button->adjustment;
1393 spin_button->digits = digits;
1394 spin_button->climb_rate = climb_rate;
1395 gtk_adjustment_value_changed (adjustment);
1399 gtk_spin_button_new (GtkAdjustment *adjustment,
1403 GtkSpinButton *spin;
1406 g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
1407 g_return_val_if_fail (digits < 6, NULL);
1409 spin = gtk_type_new (GTK_TYPE_SPIN_BUTTON);
1411 gtk_spin_button_configure (spin, adjustment, climb_rate, digits);
1413 return GTK_WIDGET (spin);
1417 gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
1418 GtkAdjustment *adjustment)
1420 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1422 if (spin_button->adjustment != adjustment)
1424 if (spin_button->adjustment)
1426 gtk_signal_disconnect_by_data (GTK_OBJECT (spin_button->adjustment),
1427 (gpointer) spin_button);
1428 gtk_object_unref (GTK_OBJECT (spin_button->adjustment));
1430 spin_button->adjustment = adjustment;
1433 gtk_object_ref (GTK_OBJECT (adjustment));
1434 gtk_object_sink (GTK_OBJECT (adjustment));
1435 gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
1436 (GtkSignalFunc) gtk_spin_button_value_changed,
1437 (gpointer) spin_button);
1443 gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
1445 g_return_val_if_fail (spin_button != NULL, NULL);
1446 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
1448 return spin_button->adjustment;
1452 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
1455 g_return_if_fail (spin_button != NULL);
1456 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1457 g_return_if_fail (digits < 6);
1459 if (spin_button->digits != digits)
1461 spin_button->digits = digits;
1462 gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1467 gtk_spin_button_get_value_as_float (GtkSpinButton *spin_button)
1469 g_return_val_if_fail (spin_button != NULL, 0.0);
1470 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0.0);
1472 return spin_button->adjustment->value;
1476 gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
1480 g_return_val_if_fail (spin_button != NULL, 0);
1481 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1483 val = spin_button->adjustment->value;
1484 if (val - floor (val) < ceil (val) - val)
1491 gtk_spin_button_set_value (GtkSpinButton *spin_button,
1494 g_return_if_fail (spin_button != NULL);
1495 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1497 if (fabs (value - spin_button->adjustment->value) > EPSILON)
1498 gtk_adjustment_set_value (spin_button->adjustment, value);
1501 gint return_val = FALSE;
1502 gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1504 if (return_val == FALSE)
1505 gtk_spin_button_default_output (spin_button);
1510 gtk_spin_button_set_update_policy (GtkSpinButton *spin_button,
1511 GtkSpinButtonUpdatePolicy policy)
1513 g_return_if_fail (spin_button != NULL);
1514 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1516 spin_button->update_policy = policy;
1520 gtk_spin_button_set_numeric (GtkSpinButton *spin_button,
1523 g_return_if_fail (spin_button != NULL);
1524 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1526 spin_button->numeric = (numeric != 0);
1530 gtk_spin_button_set_wrap (GtkSpinButton *spin_button,
1533 g_return_if_fail (spin_button != NULL);
1534 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1536 spin_button->wrap = (wrap != 0);
1540 gtk_spin_button_set_shadow_type (GtkSpinButton *spin_button,
1541 GtkShadowType shadow_type)
1543 g_return_if_fail (spin_button != NULL);
1544 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1546 if (shadow_type != spin_button->shadow_type)
1548 spin_button->shadow_type = shadow_type;
1549 if (GTK_WIDGET_DRAWABLE (spin_button))
1550 gtk_widget_queue_draw (GTK_WIDGET (spin_button));
1555 gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
1556 gboolean snap_to_ticks)
1560 g_return_if_fail (spin_button != NULL);
1561 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1563 new_val = (snap_to_ticks != 0);
1565 if (new_val != spin_button->snap_to_ticks)
1567 spin_button->snap_to_ticks = new_val;
1568 if (new_val && GTK_EDITABLE (spin_button)->editable)
1569 gtk_spin_button_update (spin_button);
1574 gtk_spin_button_spin (GtkSpinButton *spin_button,
1575 GtkSpinType direction,
1581 g_return_if_fail (spin_button != NULL);
1582 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1584 adj = spin_button->adjustment;
1586 /* for compatibility with the 1.0.x version of this function */
1587 if (increment != 0 && increment != adj->step_increment &&
1588 (direction == GTK_SPIN_STEP_FORWARD ||
1589 direction == GTK_SPIN_STEP_BACKWARD))
1591 if (direction == GTK_SPIN_STEP_BACKWARD && increment > 0)
1592 increment = -increment;
1593 direction = GTK_SPIN_USER_DEFINED;
1598 case GTK_SPIN_STEP_FORWARD:
1600 gtk_spin_button_real_spin (spin_button, adj->step_increment);
1603 case GTK_SPIN_STEP_BACKWARD:
1605 gtk_spin_button_real_spin (spin_button, -adj->step_increment);
1608 case GTK_SPIN_PAGE_FORWARD:
1610 gtk_spin_button_real_spin (spin_button, adj->page_increment);
1613 case GTK_SPIN_PAGE_BACKWARD:
1615 gtk_spin_button_real_spin (spin_button, -adj->page_increment);
1620 diff = adj->value - adj->lower;
1622 gtk_spin_button_real_spin (spin_button, -diff);
1627 diff = adj->upper - adj->value;
1629 gtk_spin_button_real_spin (spin_button, diff);
1632 case GTK_SPIN_USER_DEFINED:
1635 gtk_spin_button_real_spin (spin_button, increment);
1644 gtk_spin_button_update (GtkSpinButton *spin_button)
1650 g_return_if_fail (spin_button != NULL);
1651 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1654 gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[INPUT],
1656 if (return_val == FALSE)
1658 return_val = gtk_spin_button_default_input (spin_button, &val);
1659 error = (return_val == INPUT_ERROR);
1661 else if (return_val == INPUT_ERROR)
1664 if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
1666 if (val < spin_button->adjustment->lower)
1667 val = spin_button->adjustment->lower;
1668 else if (val > spin_button->adjustment->upper)
1669 val = spin_button->adjustment->upper;
1671 else if ((spin_button->update_policy == GTK_UPDATE_IF_VALID) &&
1673 val < spin_button->adjustment->lower ||
1674 val > spin_button->adjustment->upper))
1676 gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1680 if (spin_button->snap_to_ticks)
1681 gtk_spin_button_snap (spin_button, val);
1684 if (fabs (val - spin_button->adjustment->value) > EPSILON)
1685 gtk_adjustment_set_value (spin_button->adjustment, val);
1689 gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1691 if (return_val == FALSE)
1692 gtk_spin_button_default_output (spin_button);