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
56 static void gtk_spin_button_class_init (GtkSpinButtonClass *klass);
57 static void gtk_spin_button_init (GtkSpinButton *spin_button);
58 static void gtk_spin_button_finalize (GtkObject *object);
59 static void gtk_spin_button_set_arg (GtkObject *object,
62 static void gtk_spin_button_get_arg (GtkObject *object,
65 static void gtk_spin_button_map (GtkWidget *widget);
66 static void gtk_spin_button_unmap (GtkWidget *widget);
67 static void gtk_spin_button_realize (GtkWidget *widget);
68 static void gtk_spin_button_unrealize (GtkWidget *widget);
69 static void gtk_spin_button_size_request (GtkWidget *widget,
70 GtkRequisition *requisition);
71 static void gtk_spin_button_size_allocate (GtkWidget *widget,
72 GtkAllocation *allocation);
73 static void gtk_spin_button_paint (GtkWidget *widget,
75 static void gtk_spin_button_draw (GtkWidget *widget,
77 static gint gtk_spin_button_expose (GtkWidget *widget,
78 GdkEventExpose *event);
79 static gint gtk_spin_button_button_press (GtkWidget *widget,
80 GdkEventButton *event);
81 static gint gtk_spin_button_button_release (GtkWidget *widget,
82 GdkEventButton *event);
83 static gint gtk_spin_button_motion_notify (GtkWidget *widget,
84 GdkEventMotion *event);
85 static gint gtk_spin_button_enter_notify (GtkWidget *widget,
86 GdkEventCrossing *event);
87 static gint gtk_spin_button_leave_notify (GtkWidget *widget,
88 GdkEventCrossing *event);
89 static gint gtk_spin_button_focus_out (GtkWidget *widget,
90 GdkEventFocus *event);
91 static void gtk_spin_button_draw_arrow (GtkSpinButton *spin_button,
93 static gint gtk_spin_button_timer (GtkSpinButton *spin_button);
94 static void gtk_spin_button_value_changed (GtkAdjustment *adjustment,
95 GtkSpinButton *spin_button);
96 static gint gtk_spin_button_key_press (GtkWidget *widget,
98 static gint gtk_spin_button_key_release (GtkWidget *widget,
100 static void gtk_spin_button_update (GtkSpinButton *spin_button);
101 static void gtk_spin_button_activate (GtkEditable *editable);
102 static void gtk_spin_button_snap (GtkSpinButton *spin_button,
104 static void gtk_spin_button_insert_text (GtkEditable *editable,
105 const gchar *new_text,
106 gint new_text_length,
108 static void gtk_spin_button_real_spin (GtkSpinButton *spin_button,
112 static GtkEntryClass *parent_class = NULL;
116 gtk_spin_button_get_type (void)
118 static guint spin_button_type = 0;
120 if (!spin_button_type)
122 static const GtkTypeInfo spin_button_info =
125 sizeof (GtkSpinButton),
126 sizeof (GtkSpinButtonClass),
127 (GtkClassInitFunc) gtk_spin_button_class_init,
128 (GtkObjectInitFunc) gtk_spin_button_init,
129 /* reserved_1 */ NULL,
130 /* reserved_2 */ NULL,
131 (GtkClassInitFunc) NULL,
134 spin_button_type = gtk_type_unique (GTK_TYPE_ENTRY, &spin_button_info);
136 return spin_button_type;
140 gtk_spin_button_class_init (GtkSpinButtonClass *class)
142 GtkObjectClass *object_class;
143 GtkWidgetClass *widget_class;
144 GtkEditableClass *editable_class;
146 object_class = (GtkObjectClass*) class;
147 widget_class = (GtkWidgetClass*) class;
148 editable_class = (GtkEditableClass*) class;
150 parent_class = gtk_type_class (GTK_TYPE_ENTRY);
152 gtk_object_add_arg_type ("GtkSpinButton::adjustment",
156 gtk_object_add_arg_type ("GtkSpinButton::climb_rate",
160 gtk_object_add_arg_type ("GtkSpinButton::digits",
164 gtk_object_add_arg_type ("GtkSpinButton::snap_to_ticks",
168 gtk_object_add_arg_type ("GtkSpinButton::numeric",
172 gtk_object_add_arg_type ("GtkSpinButton::wrap",
176 gtk_object_add_arg_type ("GtkSpinButton::update_policy",
177 GTK_TYPE_SPIN_BUTTON_UPDATE_POLICY,
180 gtk_object_add_arg_type ("GtkSpinButton::shadow_type",
181 GTK_TYPE_SHADOW_TYPE,
184 gtk_object_add_arg_type ("GtkSpinButton::value",
190 object_class->set_arg = gtk_spin_button_set_arg;
191 object_class->get_arg = gtk_spin_button_get_arg;
192 object_class->finalize = gtk_spin_button_finalize;
194 widget_class->map = gtk_spin_button_map;
195 widget_class->unmap = gtk_spin_button_unmap;
196 widget_class->realize = gtk_spin_button_realize;
197 widget_class->unrealize = gtk_spin_button_unrealize;
198 widget_class->size_request = gtk_spin_button_size_request;
199 widget_class->size_allocate = gtk_spin_button_size_allocate;
200 widget_class->draw = gtk_spin_button_draw;
201 widget_class->expose_event = gtk_spin_button_expose;
202 widget_class->button_press_event = gtk_spin_button_button_press;
203 widget_class->button_release_event = gtk_spin_button_button_release;
204 widget_class->motion_notify_event = gtk_spin_button_motion_notify;
205 widget_class->key_press_event = gtk_spin_button_key_press;
206 widget_class->key_release_event = gtk_spin_button_key_release;
207 widget_class->enter_notify_event = gtk_spin_button_enter_notify;
208 widget_class->leave_notify_event = gtk_spin_button_leave_notify;
209 widget_class->focus_out_event = gtk_spin_button_focus_out;
211 editable_class->insert_text = gtk_spin_button_insert_text;
212 editable_class->activate = gtk_spin_button_activate;
216 gtk_spin_button_set_arg (GtkObject *object,
220 GtkSpinButton *spin_button;
222 spin_button = GTK_SPIN_BUTTON (object);
226 GtkAdjustment *adjustment;
229 adjustment = GTK_VALUE_POINTER (*arg);
231 adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
232 gtk_spin_button_set_adjustment (spin_button, adjustment);
235 gtk_spin_button_configure (spin_button,
236 spin_button->adjustment,
237 GTK_VALUE_FLOAT (*arg),
238 spin_button->digits);
241 gtk_spin_button_configure (spin_button,
242 spin_button->adjustment,
243 spin_button->climb_rate,
244 GTK_VALUE_UINT (*arg));
246 case ARG_SNAP_TO_TICKS:
247 gtk_spin_button_set_snap_to_ticks (spin_button, GTK_VALUE_BOOL (*arg));
250 gtk_spin_button_set_numeric (spin_button, GTK_VALUE_BOOL (*arg));
253 gtk_spin_button_set_wrap (spin_button, GTK_VALUE_BOOL (*arg));
255 case ARG_UPDATE_POLICY:
256 gtk_spin_button_set_update_policy (spin_button, GTK_VALUE_ENUM (*arg));
258 case ARG_SHADOW_TYPE:
259 gtk_spin_button_set_shadow_type (spin_button, GTK_VALUE_ENUM (*arg));
262 gtk_spin_button_set_value (spin_button, GTK_VALUE_FLOAT (*arg));
270 gtk_spin_button_get_arg (GtkObject *object,
274 GtkSpinButton *spin_button;
276 spin_button = GTK_SPIN_BUTTON (object);
281 GTK_VALUE_POINTER (*arg) = spin_button->adjustment;
284 GTK_VALUE_FLOAT (*arg) = spin_button->climb_rate;
287 GTK_VALUE_UINT (*arg) = spin_button->digits;
289 case ARG_SNAP_TO_TICKS:
290 GTK_VALUE_BOOL (*arg) = spin_button->snap_to_ticks;
293 GTK_VALUE_BOOL (*arg) = spin_button->numeric;
296 GTK_VALUE_BOOL (*arg) = spin_button->wrap;
298 case ARG_UPDATE_POLICY:
299 GTK_VALUE_ENUM (*arg) = spin_button->update_policy;
301 case ARG_SHADOW_TYPE:
302 GTK_VALUE_ENUM (*arg) = spin_button->shadow_type;
305 GTK_VALUE_FLOAT (*arg) = spin_button->adjustment->value;
308 arg->type = GTK_TYPE_INVALID;
314 gtk_spin_button_init (GtkSpinButton *spin_button)
316 spin_button->adjustment = NULL;
317 spin_button->panel = NULL;
318 spin_button->shadow_type = GTK_SHADOW_NONE;
319 spin_button->timer = 0;
320 spin_button->ev_time = 0;
321 spin_button->climb_rate = 0.0;
322 spin_button->timer_step = 0.0;
323 spin_button->update_policy = GTK_UPDATE_ALWAYS;
324 spin_button->in_child = 2;
325 spin_button->click_child = 2;
326 spin_button->button = 0;
327 spin_button->need_timer = FALSE;
328 spin_button->timer_calls = 0;
329 spin_button->digits = 0;
330 spin_button->numeric = FALSE;
331 spin_button->wrap = FALSE;
332 spin_button->snap_to_ticks = FALSE;
334 gtk_spin_button_set_adjustment (spin_button,
335 (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0));
339 gtk_spin_button_finalize (GtkObject *object)
341 g_return_if_fail (object != NULL);
342 g_return_if_fail (GTK_IS_SPIN_BUTTON (object));
344 gtk_object_unref (GTK_OBJECT (GTK_SPIN_BUTTON (object)->adjustment));
346 GTK_OBJECT_CLASS (parent_class)->finalize (object);
350 gtk_spin_button_map (GtkWidget *widget)
352 g_return_if_fail (widget != NULL);
353 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
355 if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
357 GTK_WIDGET_CLASS (parent_class)->map (widget);
358 gdk_window_show (GTK_SPIN_BUTTON (widget)->panel);
363 gtk_spin_button_unmap (GtkWidget *widget)
365 g_return_if_fail (widget != NULL);
366 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
368 if (GTK_WIDGET_MAPPED (widget))
370 gdk_window_hide (GTK_SPIN_BUTTON (widget)->panel);
371 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
376 gtk_spin_button_realize (GtkWidget *widget)
379 GdkWindowAttr attributes;
380 gint attributes_mask;
383 g_return_if_fail (widget != NULL);
384 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
386 spin = GTK_SPIN_BUTTON (widget);
388 real_width = widget->allocation.width;
389 widget->allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
390 gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
391 GDK_KEY_RELEASE_MASK);
392 GTK_WIDGET_CLASS (parent_class)->realize (widget);
394 widget->allocation.width = real_width;
396 attributes.window_type = GDK_WINDOW_CHILD;
397 attributes.wclass = GDK_INPUT_OUTPUT;
398 attributes.visual = gtk_widget_get_visual (widget);
399 attributes.colormap = gtk_widget_get_colormap (widget);
400 attributes.event_mask = gtk_widget_get_events (widget);
401 attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
402 | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
403 | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
405 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
407 attributes.x = (widget->allocation.x + widget->allocation.width - ARROW_SIZE -
408 2 * widget->style->klass->xthickness);
409 attributes.y = widget->allocation.y + (widget->allocation.height -
410 widget->requisition.height) / 2;
411 attributes.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
412 attributes.height = widget->requisition.height;
414 spin->panel = gdk_window_new (gtk_widget_get_parent_window (widget),
415 &attributes, attributes_mask);
416 gdk_window_set_user_data (spin->panel, widget);
418 gtk_style_set_background (widget->style, spin->panel, GTK_STATE_NORMAL);
422 gtk_spin_button_unrealize (GtkWidget *widget)
426 g_return_if_fail (widget != NULL);
427 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
429 spin = GTK_SPIN_BUTTON (widget);
431 GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
435 gdk_window_set_user_data (spin->panel, NULL);
436 gdk_window_destroy (spin->panel);
442 gtk_spin_button_size_request (GtkWidget *widget,
443 GtkRequisition *requisition)
445 g_return_if_fail (widget != NULL);
446 g_return_if_fail (requisition != NULL);
447 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
449 GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
451 requisition->width = MIN_SPIN_BUTTON_WIDTH + ARROW_SIZE
452 + 2 * widget->style->klass->xthickness;
456 gtk_spin_button_size_allocate (GtkWidget *widget,
457 GtkAllocation *allocation)
459 GtkAllocation child_allocation;
461 g_return_if_fail (widget != NULL);
462 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
463 g_return_if_fail (allocation != NULL);
465 child_allocation = *allocation;
466 child_allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
468 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &child_allocation);
470 widget->allocation = *allocation;
472 if (GTK_WIDGET_REALIZED (widget))
474 child_allocation.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
475 child_allocation.height = widget->requisition.height;
476 child_allocation.x = (allocation->x + allocation->width - ARROW_SIZE -
477 2 * widget->style->klass->xthickness);
478 child_allocation.y = allocation->y + (allocation->height - widget->requisition.height) / 2;
480 gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel,
483 child_allocation.width,
484 child_allocation.height);
489 gtk_spin_button_paint (GtkWidget *widget,
494 g_return_if_fail (widget != NULL);
495 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
497 spin = GTK_SPIN_BUTTON (widget);
499 if (GTK_WIDGET_DRAWABLE (widget))
501 if (spin->shadow_type != GTK_SHADOW_NONE)
502 gtk_paint_box (widget->style, spin->panel,
503 GTK_STATE_NORMAL, spin->shadow_type,
504 area, widget, "spinbutton",
506 ARROW_SIZE + 2 * widget->style->klass->xthickness,
507 widget->requisition.height);
510 gdk_window_set_back_pixmap (spin->panel, NULL, TRUE);
511 gdk_window_clear_area (spin->panel, area->x, area->y, area->width, area->height);
513 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
514 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
516 GTK_WIDGET_CLASS (parent_class)->draw (widget, area);
521 gtk_spin_button_draw (GtkWidget *widget,
524 g_return_if_fail (widget != NULL);
525 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
526 g_return_if_fail (area != NULL);
528 if (GTK_WIDGET_DRAWABLE (widget))
529 gtk_spin_button_paint (widget, area);
533 gtk_spin_button_expose (GtkWidget *widget,
534 GdkEventExpose *event)
536 g_return_val_if_fail (widget != NULL, FALSE);
537 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
538 g_return_val_if_fail (event != NULL, FALSE);
540 if (GTK_WIDGET_DRAWABLE (widget))
541 gtk_spin_button_paint (widget, &event->area);
547 gtk_spin_button_draw_arrow (GtkSpinButton *spin_button,
550 GtkStateType state_type;
551 GtkShadowType shadow_type;
556 g_return_if_fail (spin_button != NULL);
557 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
559 widget = GTK_WIDGET (spin_button);
561 if (GTK_WIDGET_DRAWABLE (spin_button))
563 if (!spin_button->wrap &&
564 (((arrow == GTK_ARROW_UP &&
565 (spin_button->adjustment->upper - spin_button->adjustment->value
567 ((arrow == GTK_ARROW_DOWN &&
568 (spin_button->adjustment->value - spin_button->adjustment->lower
571 shadow_type = GTK_SHADOW_ETCHED_IN;
572 state_type = GTK_STATE_NORMAL;
576 if (spin_button->in_child == arrow)
578 if (spin_button->click_child == arrow)
579 state_type = GTK_STATE_ACTIVE;
581 state_type = GTK_STATE_PRELIGHT;
584 state_type = GTK_STATE_NORMAL;
586 if (spin_button->click_child == arrow)
587 shadow_type = GTK_SHADOW_IN;
589 shadow_type = GTK_SHADOW_OUT;
591 if (arrow == GTK_ARROW_UP)
593 if (spin_button->shadow_type != GTK_SHADOW_NONE)
595 x = widget->style->klass->xthickness;
596 y = widget->style->klass->ythickness;
600 x = widget->style->klass->xthickness - 1;
601 y = widget->style->klass->ythickness - 1;
603 gtk_paint_arrow (widget->style, spin_button->panel,
604 state_type, shadow_type,
605 NULL, widget, "spinbutton",
607 x, y, ARROW_SIZE, widget->requisition.height / 2
608 - widget->style->klass->ythickness);
612 if (spin_button->shadow_type != GTK_SHADOW_NONE)
614 x = widget->style->klass->xthickness;
615 y = widget->requisition.height / 2;
619 x = widget->style->klass->xthickness - 1;
620 y = widget->requisition.height / 2 + 1;
622 gtk_paint_arrow (widget->style, spin_button->panel,
623 state_type, shadow_type,
624 NULL, widget, "spinbutton",
626 x, y, ARROW_SIZE, widget->requisition.height / 2
627 - widget->style->klass->ythickness);
633 gtk_spin_button_enter_notify (GtkWidget *widget,
634 GdkEventCrossing *event)
638 g_return_val_if_fail (widget != NULL, FALSE);
639 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
640 g_return_val_if_fail (event != NULL, FALSE);
642 spin = GTK_SPIN_BUTTON (widget);
644 if (event->window == spin->panel)
649 gdk_window_get_pointer (spin->panel, &x, &y, NULL);
651 if (y <= widget->requisition.height / 2)
653 spin->in_child = GTK_ARROW_UP;
654 if (spin->click_child == 2)
655 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
659 spin->in_child = GTK_ARROW_DOWN;
660 if (spin->click_child == 2)
661 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
668 gtk_spin_button_leave_notify (GtkWidget *widget,
669 GdkEventCrossing *event)
673 g_return_val_if_fail (widget != NULL, FALSE);
674 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
675 g_return_val_if_fail (event != NULL, FALSE);
677 spin = GTK_SPIN_BUTTON (widget);
679 if (event->window == spin->panel && spin->click_child == 2)
681 if (spin->in_child == GTK_ARROW_UP)
684 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
689 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
696 gtk_spin_button_focus_out (GtkWidget *widget,
697 GdkEventFocus *event)
699 g_return_val_if_fail (widget != NULL, FALSE);
700 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
701 g_return_val_if_fail (event != NULL, FALSE);
703 gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
705 return GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
709 gtk_spin_button_button_press (GtkWidget *widget,
710 GdkEventButton *event)
714 g_return_val_if_fail (widget != NULL, FALSE);
715 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
716 g_return_val_if_fail (event != NULL, FALSE);
718 spin = GTK_SPIN_BUTTON (widget);
722 if (event->window == spin->panel)
724 if (!GTK_WIDGET_HAS_FOCUS (widget))
725 gtk_widget_grab_focus (widget);
726 gtk_grab_add (widget);
727 spin->button = event->button;
729 gtk_spin_button_update (spin);
731 if (event->y <= widget->requisition.height / 2)
733 spin->click_child = GTK_ARROW_UP;
734 if (event->button == 1)
736 gtk_spin_button_real_spin (spin,
737 spin->adjustment->step_increment);
740 spin->timer_step = spin->adjustment->step_increment;
741 spin->need_timer = TRUE;
742 spin->timer = gtk_timeout_add
743 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
744 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
747 else if (event->button == 2)
749 gtk_spin_button_real_spin (spin,
750 spin->adjustment->page_increment);
753 spin->timer_step = spin->adjustment->page_increment;
754 spin->need_timer = TRUE;
755 spin->timer = gtk_timeout_add
756 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
757 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
760 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
764 spin->click_child = GTK_ARROW_DOWN;
765 if (event->button == 1)
767 gtk_spin_button_real_spin (spin,
768 -spin->adjustment->step_increment);
771 spin->timer_step = spin->adjustment->step_increment;
772 spin->need_timer = TRUE;
773 spin->timer = gtk_timeout_add
774 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
775 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
778 else if (event->button == 2)
780 gtk_spin_button_real_spin (spin,
781 -spin->adjustment->page_increment);
784 spin->timer_step = spin->adjustment->page_increment;
785 spin->need_timer = TRUE;
786 spin->timer = gtk_timeout_add
787 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
788 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
791 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
795 GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
801 gtk_spin_button_button_release (GtkWidget *widget,
802 GdkEventButton *event)
806 g_return_val_if_fail (widget != NULL, FALSE);
807 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
808 g_return_val_if_fail (event != NULL, FALSE);
810 spin = GTK_SPIN_BUTTON (widget);
812 if (event->button == spin->button)
818 gtk_timeout_remove (spin->timer);
820 spin->timer_calls = 0;
821 spin->need_timer = FALSE;
824 if (event->button == 3)
826 if (event->y >= 0 && event->x >= 0 &&
827 event->y <= widget->requisition.height &&
828 event->x <= ARROW_SIZE + 2 * widget->style->klass->xthickness)
830 if (spin->click_child == GTK_ARROW_UP &&
831 event->y <= widget->requisition.height / 2)
835 diff = spin->adjustment->upper - spin->adjustment->value;
837 gtk_spin_button_real_spin (spin, diff);
839 else if (spin->click_child == GTK_ARROW_DOWN &&
840 event->y > widget->requisition.height / 2)
844 diff = spin->adjustment->value - spin->adjustment->lower;
846 gtk_spin_button_real_spin (spin, -diff);
850 gtk_grab_remove (widget);
851 click_child = spin->click_child;
852 spin->click_child = 2;
854 gtk_spin_button_draw_arrow (spin, click_child);
857 GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
863 gtk_spin_button_motion_notify (GtkWidget *widget,
864 GdkEventMotion *event)
868 g_return_val_if_fail (widget != NULL, FALSE);
869 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
870 g_return_val_if_fail (event != NULL, FALSE);
872 spin = GTK_SPIN_BUTTON (widget);
877 if (event->window == spin->panel)
883 gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
885 if (y <= widget->requisition.height / 2 &&
886 spin->in_child == GTK_ARROW_DOWN)
888 spin->in_child = GTK_ARROW_UP;
889 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
890 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
892 else if (y > widget->requisition.height / 2 &&
893 spin->in_child == GTK_ARROW_UP)
895 spin->in_child = GTK_ARROW_DOWN;
896 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
897 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
902 return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
906 gtk_spin_button_timer (GtkSpinButton *spin_button)
908 g_return_val_if_fail (spin_button != NULL, FALSE);
909 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
911 if (spin_button->timer)
913 if (spin_button->click_child == GTK_ARROW_UP)
914 gtk_spin_button_real_spin (spin_button, spin_button->timer_step);
916 gtk_spin_button_real_spin (spin_button, -spin_button->timer_step);
918 if (spin_button->need_timer)
920 spin_button->need_timer = FALSE;
921 spin_button->timer = gtk_timeout_add
922 (SPIN_BUTTON_TIMER_DELAY, (GtkFunction) gtk_spin_button_timer,
923 (gpointer) spin_button);
926 else if (spin_button->climb_rate > 0.0 && spin_button->timer_step
927 < spin_button->adjustment->page_increment)
929 if (spin_button->timer_calls < MAX_TIMER_CALLS)
930 spin_button->timer_calls++;
933 spin_button->timer_calls = 0;
934 spin_button->timer_step += spin_button->climb_rate;
943 gtk_spin_button_value_changed (GtkAdjustment *adjustment,
944 GtkSpinButton *spin_button)
946 char buf[MAX_TEXT_LENGTH];
948 g_return_if_fail (adjustment != NULL);
949 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
951 sprintf (buf, "%0.*f", spin_button->digits, adjustment->value);
952 gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
956 gtk_spin_button_key_press (GtkWidget *widget,
961 gboolean key_repeat = FALSE;
963 g_return_val_if_fail (widget != NULL, FALSE);
964 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
965 g_return_val_if_fail (event != NULL, FALSE);
967 spin = GTK_SPIN_BUTTON (widget);
970 key_repeat = (event->time == spin->ev_time);
972 if (key == GDK_Up || key == GDK_Down ||
973 key == GDK_Page_Up || key == GDK_Page_Down)
974 gtk_spin_button_update (spin);
980 if (GTK_WIDGET_HAS_FOCUS (widget))
982 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
985 spin->timer_step = spin->adjustment->step_increment;
987 gtk_spin_button_real_spin (spin, spin->timer_step);
991 if (spin->climb_rate > 0.0 && spin->timer_step
992 < spin->adjustment->page_increment)
994 if (spin->timer_calls < MAX_TIMER_CALLS)
998 spin->timer_calls = 0;
999 spin->timer_step += spin->climb_rate;
1009 if (GTK_WIDGET_HAS_FOCUS (widget))
1011 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
1014 spin->timer_step = spin->adjustment->step_increment;
1016 gtk_spin_button_real_spin (spin, -spin->timer_step);
1020 if (spin->climb_rate > 0.0 && spin->timer_step
1021 < spin->adjustment->page_increment)
1023 if (spin->timer_calls < MAX_TIMER_CALLS)
1024 spin->timer_calls++;
1027 spin->timer_calls = 0;
1028 spin->timer_step += spin->climb_rate;
1038 if (event->state & GDK_CONTROL_MASK)
1040 gfloat diff = spin->adjustment->upper - spin->adjustment->value;
1042 gtk_spin_button_real_spin (spin, diff);
1045 gtk_spin_button_real_spin (spin, spin->adjustment->page_increment);
1050 if (event->state & GDK_CONTROL_MASK)
1052 gfloat diff = spin->adjustment->value - spin->adjustment->lower;
1054 gtk_spin_button_real_spin (spin, -diff);
1057 gtk_spin_button_real_spin (spin, -spin->adjustment->page_increment);
1064 return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
1068 gtk_spin_button_key_release (GtkWidget *widget,
1071 GtkSpinButton *spin;
1073 g_return_val_if_fail (widget != NULL, FALSE);
1074 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1076 spin = GTK_SPIN_BUTTON (widget);
1078 spin->ev_time = event->time;
1083 gtk_spin_button_snap (GtkSpinButton *spin_button,
1089 inc = spin_button->adjustment->step_increment;
1090 tmp = (val - spin_button->adjustment->lower) / inc;
1091 if (tmp - floor (tmp) < ceil (tmp) - tmp)
1092 val = spin_button->adjustment->lower + floor (tmp) * inc;
1094 val = spin_button->adjustment->lower + ceil (tmp) * inc;
1096 if (fabs (val - spin_button->adjustment->value) > EPSILON)
1097 gtk_adjustment_set_value (spin_button->adjustment, val);
1100 char buf[MAX_TEXT_LENGTH];
1102 sprintf (buf, "%0.*f", spin_button->digits,
1103 spin_button->adjustment->value);
1104 if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1105 gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1110 gtk_spin_button_update (GtkSpinButton *spin_button)
1113 gchar *error = NULL;
1115 g_return_if_fail (spin_button != NULL);
1116 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1118 val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
1120 if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
1122 if (val < spin_button->adjustment->lower)
1123 val = spin_button->adjustment->lower;
1124 else if (val > spin_button->adjustment->upper)
1125 val = spin_button->adjustment->upper;
1127 else if ((spin_button->update_policy == GTK_UPDATE_IF_VALID) &&
1129 val < spin_button->adjustment->lower ||
1130 val > spin_button->adjustment->upper))
1132 gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1136 if (spin_button->snap_to_ticks)
1137 gtk_spin_button_snap (spin_button, val);
1140 if (fabs (val - spin_button->adjustment->value) > EPSILON)
1141 gtk_adjustment_set_value (spin_button->adjustment, val);
1144 char buf[MAX_TEXT_LENGTH];
1146 sprintf (buf, "%0.*f", spin_button->digits,
1147 spin_button->adjustment->value);
1148 if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1149 gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1155 gtk_spin_button_activate (GtkEditable *editable)
1157 g_return_if_fail (editable != NULL);
1158 g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
1160 if (editable->editable)
1161 gtk_spin_button_update (GTK_SPIN_BUTTON (editable));
1165 gtk_spin_button_insert_text (GtkEditable *editable,
1166 const gchar *new_text,
1167 gint new_text_length,
1171 GtkSpinButton *spin;
1173 g_return_if_fail (editable != NULL);
1174 g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
1176 entry = GTK_ENTRY (editable);
1177 spin = GTK_SPIN_BUTTON (editable);
1190 if (*(lc->negative_sign))
1191 neg_sign = *(lc->negative_sign);
1195 if (*(lc->positive_sign))
1196 pos_sign = *(lc->positive_sign);
1200 sign = ((strchr (entry->text, neg_sign) != 0) ||
1201 (strchr (entry->text, pos_sign) != 0));
1203 if (sign && !(*position))
1206 dotpos = strchr (entry->text, *(lc->decimal_point)) - entry->text;
1208 if (dotpos > -1 && *position > dotpos &&
1209 spin->digits - entry->text_length + dotpos - new_text_length + 1 < 0)
1212 for (i = 0; i < new_text_length; i++)
1214 if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1216 if (sign || (*position) || i)
1220 else if (new_text[i] == *(lc->decimal_point))
1222 if (!spin->digits || dotpos > -1 ||
1223 (new_text_length - 1 - i + entry->text_length - *position >
1226 dotpos = *position + i;
1228 else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1233 GTK_EDITABLE_CLASS (parent_class)->insert_text (editable, new_text,
1234 new_text_length, position);
1238 gtk_spin_button_real_spin (GtkSpinButton *spin_button,
1242 gfloat new_value = 0.0;
1244 g_return_if_fail (spin_button != NULL);
1245 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1247 adj = spin_button->adjustment;
1249 new_value = adj->value + increment;
1253 if (spin_button->wrap)
1255 if (fabs (adj->value - adj->upper) < EPSILON)
1256 new_value = adj->lower;
1257 else if (new_value > adj->upper)
1258 new_value = adj->upper;
1261 new_value = MIN (new_value, adj->upper);
1263 else if (increment < 0)
1265 if (spin_button->wrap)
1267 if (fabs (adj->value - adj->lower) < EPSILON)
1268 new_value = adj->upper;
1269 else if (new_value < adj->lower)
1270 new_value = adj->lower;
1273 new_value = MAX (new_value, adj->lower);
1276 if (fabs (new_value - adj->value) > EPSILON)
1277 gtk_adjustment_set_value (adj, new_value);
1281 /***********************************************************
1282 ***********************************************************
1283 *** Public interface ***
1284 ***********************************************************
1285 ***********************************************************/
1289 gtk_spin_button_construct (GtkSpinButton *spin_button,
1290 GtkAdjustment *adjustment,
1294 g_message ("gtk_spin_button_construct() is deprecated");
1296 gtk_spin_button_configure (spin_button, adjustment, climb_rate, digits);
1300 gtk_spin_button_configure (GtkSpinButton *spin_button,
1301 GtkAdjustment *adjustment,
1305 g_return_if_fail (spin_button != NULL);
1306 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1307 g_return_if_fail (digits < 6);
1310 gtk_spin_button_set_adjustment (spin_button, adjustment);
1312 adjustment = spin_button->adjustment;
1314 spin_button->digits = digits;
1315 spin_button->climb_rate = climb_rate;
1316 gtk_adjustment_value_changed (adjustment);
1320 gtk_spin_button_new (GtkAdjustment *adjustment,
1324 GtkSpinButton *spin;
1327 g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
1328 g_return_val_if_fail (digits < 6, NULL);
1330 spin = gtk_type_new (GTK_TYPE_SPIN_BUTTON);
1332 gtk_spin_button_configure (spin, adjustment, climb_rate, digits);
1334 return GTK_WIDGET (spin);
1338 gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
1339 GtkAdjustment *adjustment)
1341 g_return_if_fail (spin_button != NULL);
1342 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1344 if (spin_button->adjustment != adjustment)
1346 if (spin_button->adjustment)
1348 gtk_signal_disconnect_by_data (GTK_OBJECT (spin_button->adjustment),
1349 (gpointer) spin_button);
1350 gtk_object_unref (GTK_OBJECT (spin_button->adjustment));
1352 spin_button->adjustment = adjustment;
1355 gtk_object_ref (GTK_OBJECT (adjustment));
1356 gtk_object_sink (GTK_OBJECT (adjustment));
1357 gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
1358 (GtkSignalFunc) gtk_spin_button_value_changed,
1359 (gpointer) spin_button);
1365 gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
1367 g_return_val_if_fail (spin_button != NULL, NULL);
1368 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
1370 return spin_button->adjustment;
1374 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
1377 g_return_if_fail (spin_button != NULL);
1378 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1379 g_return_if_fail (digits < 6);
1381 if (spin_button->digits != digits)
1383 spin_button->digits = digits;
1384 gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1389 gtk_spin_button_get_value_as_float (GtkSpinButton *spin_button)
1391 g_return_val_if_fail (spin_button != NULL, 0.0);
1392 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0.0);
1394 return spin_button->adjustment->value;
1398 gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
1402 g_return_val_if_fail (spin_button != NULL, 0);
1403 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1405 val = spin_button->adjustment->value;
1406 if (val - floor (val) < ceil (val) - val)
1413 gtk_spin_button_set_value (GtkSpinButton *spin_button,
1416 g_return_if_fail (spin_button != NULL);
1417 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1419 if (fabs (value - spin_button->adjustment->value) > EPSILON)
1420 gtk_adjustment_set_value (spin_button->adjustment, value);
1423 char buf[MAX_TEXT_LENGTH];
1425 sprintf (buf, "%0.*f", spin_button->digits,
1426 spin_button->adjustment->value);
1427 if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1428 gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1433 gtk_spin_button_set_update_policy (GtkSpinButton *spin_button,
1434 GtkSpinButtonUpdatePolicy policy)
1436 g_return_if_fail (spin_button != NULL);
1437 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1439 spin_button->update_policy = policy;
1443 gtk_spin_button_set_numeric (GtkSpinButton *spin_button,
1446 g_return_if_fail (spin_button != NULL);
1447 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1449 spin_button->numeric = (numeric != 0);
1453 gtk_spin_button_set_wrap (GtkSpinButton *spin_button,
1456 g_return_if_fail (spin_button != NULL);
1457 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1459 spin_button->wrap = (wrap != 0);
1463 gtk_spin_button_set_shadow_type (GtkSpinButton *spin_button,
1464 GtkShadowType shadow_type)
1466 g_return_if_fail (spin_button != NULL);
1467 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1469 if (shadow_type != spin_button->shadow_type)
1471 spin_button->shadow_type = shadow_type;
1472 if (GTK_WIDGET_DRAWABLE (spin_button))
1473 gtk_widget_queue_draw (GTK_WIDGET (spin_button));
1478 gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
1479 gboolean snap_to_ticks)
1483 g_return_if_fail (spin_button != NULL);
1484 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1486 new_val = (snap_to_ticks != 0);
1488 if (new_val != spin_button->snap_to_ticks)
1490 spin_button->snap_to_ticks = new_val;
1493 gchar *error = NULL;
1496 val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
1497 gtk_spin_button_snap (spin_button, val);
1503 gtk_spin_button_spin (GtkSpinButton *spin_button,
1504 GtkSpinType direction,
1510 g_return_if_fail (spin_button != NULL);
1511 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1513 adj = spin_button->adjustment;
1515 /* for compatibility with the 1.0.x version of this function */
1516 if (increment != 0 && increment != adj->step_increment &&
1517 (direction == GTK_SPIN_STEP_FORWARD ||
1518 direction == GTK_SPIN_STEP_BACKWARD))
1520 if (direction == GTK_SPIN_STEP_BACKWARD && increment > 0)
1521 increment = -increment;
1522 direction = GTK_SPIN_USER_DEFINED;
1527 case GTK_SPIN_STEP_FORWARD:
1529 gtk_spin_button_real_spin (spin_button, adj->step_increment);
1532 case GTK_SPIN_STEP_BACKWARD:
1534 gtk_spin_button_real_spin (spin_button, -adj->step_increment);
1537 case GTK_SPIN_PAGE_FORWARD:
1539 gtk_spin_button_real_spin (spin_button, adj->page_increment);
1542 case GTK_SPIN_PAGE_BACKWARD:
1544 gtk_spin_button_real_spin (spin_button, -adj->page_increment);
1549 diff = adj->value - adj->lower;
1551 gtk_spin_button_real_spin (spin_button, -diff);
1556 diff = adj->upper - adj->value;
1558 gtk_spin_button_real_spin (spin_button, diff);
1561 case GTK_SPIN_USER_DEFINED:
1564 gtk_spin_button_real_spin (spin_button, increment);