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.
24 * Modified by the GTK+ Team and others 1997-1999. 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 (GtkObject *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 GtkObjectClass *object_class;
162 GtkWidgetClass *widget_class;
163 GtkEditableClass *editable_class;
165 object_class = (GtkObjectClass*) class;
166 widget_class = (GtkWidgetClass*) class;
167 editable_class = (GtkEditableClass*) class;
169 parent_class = gtk_type_class (GTK_TYPE_ENTRY);
171 gtk_object_add_arg_type ("GtkSpinButton::adjustment",
175 gtk_object_add_arg_type ("GtkSpinButton::climb_rate",
179 gtk_object_add_arg_type ("GtkSpinButton::digits",
183 gtk_object_add_arg_type ("GtkSpinButton::snap_to_ticks",
187 gtk_object_add_arg_type ("GtkSpinButton::numeric",
191 gtk_object_add_arg_type ("GtkSpinButton::wrap",
195 gtk_object_add_arg_type ("GtkSpinButton::update_policy",
196 GTK_TYPE_SPIN_BUTTON_UPDATE_POLICY,
199 gtk_object_add_arg_type ("GtkSpinButton::shadow_type",
200 GTK_TYPE_SHADOW_TYPE,
203 gtk_object_add_arg_type ("GtkSpinButton::value",
208 object_class->set_arg = gtk_spin_button_set_arg;
209 object_class->get_arg = gtk_spin_button_get_arg;
210 object_class->finalize = gtk_spin_button_finalize;
212 spinbutton_signals[INPUT] =
213 gtk_signal_new ("input",
216 GTK_SIGNAL_OFFSET (GtkSpinButtonClass, input),
217 gtk_marshal_INT__POINTER,
218 GTK_TYPE_INT, 1, GTK_TYPE_POINTER);
220 spinbutton_signals[OUTPUT] =
221 gtk_signal_new ("output",
224 GTK_SIGNAL_OFFSET (GtkSpinButtonClass, output),
225 gtk_marshal_BOOL__NONE,
228 gtk_object_class_add_signals (object_class, spinbutton_signals, LAST_SIGNAL);
230 widget_class->map = gtk_spin_button_map;
231 widget_class->unmap = gtk_spin_button_unmap;
232 widget_class->realize = gtk_spin_button_realize;
233 widget_class->unrealize = gtk_spin_button_unrealize;
234 widget_class->size_request = gtk_spin_button_size_request;
235 widget_class->size_allocate = gtk_spin_button_size_allocate;
236 widget_class->draw = gtk_spin_button_draw;
237 widget_class->expose_event = gtk_spin_button_expose;
238 widget_class->scroll_event = gtk_spin_button_scroll;
239 widget_class->button_press_event = gtk_spin_button_button_press;
240 widget_class->button_release_event = gtk_spin_button_button_release;
241 widget_class->motion_notify_event = gtk_spin_button_motion_notify;
242 widget_class->key_press_event = gtk_spin_button_key_press;
243 widget_class->key_release_event = gtk_spin_button_key_release;
244 widget_class->enter_notify_event = gtk_spin_button_enter_notify;
245 widget_class->leave_notify_event = gtk_spin_button_leave_notify;
246 widget_class->focus_out_event = gtk_spin_button_focus_out;
248 editable_class->insert_text = gtk_spin_button_insert_text;
249 editable_class->activate = gtk_spin_button_activate;
252 class->output = NULL;
256 gtk_spin_button_set_arg (GtkObject *object,
260 GtkSpinButton *spin_button;
262 spin_button = GTK_SPIN_BUTTON (object);
266 GtkAdjustment *adjustment;
269 adjustment = GTK_VALUE_POINTER (*arg);
271 adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
272 gtk_spin_button_set_adjustment (spin_button, adjustment);
275 gtk_spin_button_configure (spin_button,
276 spin_button->adjustment,
277 GTK_VALUE_FLOAT (*arg),
278 spin_button->digits);
281 gtk_spin_button_configure (spin_button,
282 spin_button->adjustment,
283 spin_button->climb_rate,
284 GTK_VALUE_UINT (*arg));
286 case ARG_SNAP_TO_TICKS:
287 gtk_spin_button_set_snap_to_ticks (spin_button, GTK_VALUE_BOOL (*arg));
290 gtk_spin_button_set_numeric (spin_button, GTK_VALUE_BOOL (*arg));
293 gtk_spin_button_set_wrap (spin_button, GTK_VALUE_BOOL (*arg));
295 case ARG_UPDATE_POLICY:
296 gtk_spin_button_set_update_policy (spin_button, GTK_VALUE_ENUM (*arg));
298 case ARG_SHADOW_TYPE:
299 gtk_spin_button_set_shadow_type (spin_button, GTK_VALUE_ENUM (*arg));
302 gtk_spin_button_set_value (spin_button, GTK_VALUE_FLOAT (*arg));
310 gtk_spin_button_get_arg (GtkObject *object,
314 GtkSpinButton *spin_button;
316 spin_button = GTK_SPIN_BUTTON (object);
321 GTK_VALUE_POINTER (*arg) = spin_button->adjustment;
324 GTK_VALUE_FLOAT (*arg) = spin_button->climb_rate;
327 GTK_VALUE_UINT (*arg) = spin_button->digits;
329 case ARG_SNAP_TO_TICKS:
330 GTK_VALUE_BOOL (*arg) = spin_button->snap_to_ticks;
333 GTK_VALUE_BOOL (*arg) = spin_button->numeric;
336 GTK_VALUE_BOOL (*arg) = spin_button->wrap;
338 case ARG_UPDATE_POLICY:
339 GTK_VALUE_ENUM (*arg) = spin_button->update_policy;
341 case ARG_SHADOW_TYPE:
342 GTK_VALUE_ENUM (*arg) = spin_button->shadow_type;
345 GTK_VALUE_FLOAT (*arg) = spin_button->adjustment->value;
348 arg->type = GTK_TYPE_INVALID;
354 gtk_spin_button_init (GtkSpinButton *spin_button)
356 spin_button->adjustment = NULL;
357 spin_button->panel = NULL;
358 spin_button->shadow_type = GTK_SHADOW_NONE;
359 spin_button->timer = 0;
360 spin_button->ev_time = 0;
361 spin_button->climb_rate = 0.0;
362 spin_button->timer_step = 0.0;
363 spin_button->update_policy = GTK_UPDATE_ALWAYS;
364 spin_button->in_child = 2;
365 spin_button->click_child = 2;
366 spin_button->button = 0;
367 spin_button->need_timer = FALSE;
368 spin_button->timer_calls = 0;
369 spin_button->digits = 0;
370 spin_button->numeric = FALSE;
371 spin_button->wrap = FALSE;
372 spin_button->snap_to_ticks = FALSE;
373 gtk_spin_button_set_adjustment (spin_button,
374 (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0));
378 gtk_spin_button_finalize (GtkObject *object)
380 g_return_if_fail (object != NULL);
381 g_return_if_fail (GTK_IS_SPIN_BUTTON (object));
383 gtk_object_unref (GTK_OBJECT (GTK_SPIN_BUTTON (object)->adjustment));
385 GTK_OBJECT_CLASS (parent_class)->finalize (object);
389 gtk_spin_button_map (GtkWidget *widget)
391 g_return_if_fail (widget != NULL);
392 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
394 if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
396 GTK_WIDGET_CLASS (parent_class)->map (widget);
397 gdk_window_show (GTK_SPIN_BUTTON (widget)->panel);
402 gtk_spin_button_unmap (GtkWidget *widget)
404 g_return_if_fail (widget != NULL);
405 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
407 if (GTK_WIDGET_MAPPED (widget))
409 gdk_window_hide (GTK_SPIN_BUTTON (widget)->panel);
410 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
415 gtk_spin_button_realize (GtkWidget *widget)
417 GtkSpinButton *spin_button;
418 GdkWindowAttr attributes;
419 gint attributes_mask;
423 g_return_if_fail (widget != NULL);
424 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
426 spin_button = GTK_SPIN_BUTTON (widget);
428 real_width = widget->allocation.width;
429 widget->allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
430 gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
431 GDK_KEY_RELEASE_MASK);
432 GTK_WIDGET_CLASS (parent_class)->realize (widget);
434 widget->allocation.width = real_width;
436 attributes.window_type = GDK_WINDOW_CHILD;
437 attributes.wclass = GDK_INPUT_OUTPUT;
438 attributes.visual = gtk_widget_get_visual (widget);
439 attributes.colormap = gtk_widget_get_colormap (widget);
440 attributes.event_mask = gtk_widget_get_events (widget);
441 attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
442 | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
443 | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
445 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
447 attributes.x = (widget->allocation.x + widget->allocation.width - ARROW_SIZE -
448 2 * widget->style->klass->xthickness);
449 attributes.y = widget->allocation.y + (widget->allocation.height -
450 widget->requisition.height) / 2;
451 attributes.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
452 attributes.height = widget->requisition.height;
454 spin_button->panel = gdk_window_new (gtk_widget_get_parent_window (widget),
455 &attributes, attributes_mask);
456 gdk_window_set_user_data (spin_button->panel, widget);
458 gtk_style_set_background (widget->style, spin_button->panel, GTK_STATE_NORMAL);
461 gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
463 if (return_val == FALSE)
464 gtk_spin_button_default_output (spin_button);
468 gtk_spin_button_unrealize (GtkWidget *widget)
472 g_return_if_fail (widget != NULL);
473 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
475 spin = GTK_SPIN_BUTTON (widget);
477 GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
481 gdk_window_set_user_data (spin->panel, NULL);
482 gdk_window_destroy (spin->panel);
488 gtk_spin_button_size_request (GtkWidget *widget,
489 GtkRequisition *requisition)
491 g_return_if_fail (widget != NULL);
492 g_return_if_fail (requisition != NULL);
493 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
495 GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
497 requisition->width = MIN_SPIN_BUTTON_WIDTH + ARROW_SIZE
498 + 2 * widget->style->klass->xthickness;
502 gtk_spin_button_size_allocate (GtkWidget *widget,
503 GtkAllocation *allocation)
505 GtkAllocation child_allocation;
507 g_return_if_fail (widget != NULL);
508 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
509 g_return_if_fail (allocation != NULL);
511 child_allocation = *allocation;
512 child_allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
514 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &child_allocation);
516 widget->allocation = *allocation;
518 if (GTK_WIDGET_REALIZED (widget))
520 child_allocation.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
521 child_allocation.height = widget->requisition.height;
522 child_allocation.x = (allocation->x + allocation->width - ARROW_SIZE -
523 2 * widget->style->klass->xthickness);
524 child_allocation.y = allocation->y + (allocation->height - widget->requisition.height) / 2;
526 gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel,
529 child_allocation.width,
530 child_allocation.height);
535 gtk_spin_button_paint (GtkWidget *widget,
540 g_return_if_fail (widget != NULL);
541 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
543 spin = GTK_SPIN_BUTTON (widget);
545 if (GTK_WIDGET_DRAWABLE (widget))
547 if (spin->shadow_type != GTK_SHADOW_NONE)
548 gtk_paint_box (widget->style, spin->panel,
549 GTK_STATE_NORMAL, spin->shadow_type,
550 area, widget, "spinbutton",
552 ARROW_SIZE + 2 * widget->style->klass->xthickness,
553 widget->requisition.height);
556 gdk_window_set_back_pixmap (spin->panel, NULL, TRUE);
557 gdk_window_clear_area (spin->panel, area->x, area->y, area->width, area->height);
559 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
560 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
562 GTK_WIDGET_CLASS (parent_class)->draw (widget, area);
567 gtk_spin_button_draw (GtkWidget *widget,
570 g_return_if_fail (widget != NULL);
571 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
572 g_return_if_fail (area != NULL);
574 if (GTK_WIDGET_DRAWABLE (widget))
575 gtk_spin_button_paint (widget, area);
579 gtk_spin_button_expose (GtkWidget *widget,
580 GdkEventExpose *event)
582 g_return_val_if_fail (widget != NULL, FALSE);
583 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
584 g_return_val_if_fail (event != NULL, FALSE);
586 if (GTK_WIDGET_DRAWABLE (widget))
587 gtk_spin_button_paint (widget, &event->area);
593 gtk_spin_button_draw_arrow (GtkSpinButton *spin_button,
596 GtkStateType state_type;
597 GtkShadowType shadow_type;
602 g_return_if_fail (spin_button != NULL);
603 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
605 widget = GTK_WIDGET (spin_button);
607 if (GTK_WIDGET_DRAWABLE (spin_button))
609 if (!spin_button->wrap &&
610 (((arrow == GTK_ARROW_UP &&
611 (spin_button->adjustment->upper - spin_button->adjustment->value
613 ((arrow == GTK_ARROW_DOWN &&
614 (spin_button->adjustment->value - spin_button->adjustment->lower
617 shadow_type = GTK_SHADOW_ETCHED_IN;
618 state_type = GTK_STATE_NORMAL;
622 if (spin_button->in_child == arrow)
624 if (spin_button->click_child == arrow)
625 state_type = GTK_STATE_ACTIVE;
627 state_type = GTK_STATE_PRELIGHT;
630 state_type = GTK_STATE_NORMAL;
632 if (spin_button->click_child == arrow)
633 shadow_type = GTK_SHADOW_IN;
635 shadow_type = GTK_SHADOW_OUT;
637 if (arrow == GTK_ARROW_UP)
639 if (spin_button->shadow_type != GTK_SHADOW_NONE)
641 x = widget->style->klass->xthickness;
642 y = widget->style->klass->ythickness;
646 x = widget->style->klass->xthickness - 1;
647 y = widget->style->klass->ythickness - 1;
649 gtk_paint_arrow (widget->style, spin_button->panel,
650 state_type, shadow_type,
651 NULL, widget, "spinbutton",
653 x, y, ARROW_SIZE, widget->requisition.height / 2
654 - widget->style->klass->ythickness);
658 if (spin_button->shadow_type != GTK_SHADOW_NONE)
660 x = widget->style->klass->xthickness;
661 y = widget->requisition.height / 2;
665 x = widget->style->klass->xthickness - 1;
666 y = widget->requisition.height / 2 + 1;
668 gtk_paint_arrow (widget->style, spin_button->panel,
669 state_type, shadow_type,
670 NULL, widget, "spinbutton",
672 x, y, ARROW_SIZE, widget->requisition.height / 2
673 - widget->style->klass->ythickness);
679 gtk_spin_button_enter_notify (GtkWidget *widget,
680 GdkEventCrossing *event)
684 g_return_val_if_fail (widget != NULL, FALSE);
685 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
686 g_return_val_if_fail (event != NULL, FALSE);
688 spin = GTK_SPIN_BUTTON (widget);
690 if (event->window == spin->panel)
695 gdk_window_get_pointer (spin->panel, &x, &y, NULL);
697 if (y <= widget->requisition.height / 2)
699 spin->in_child = GTK_ARROW_UP;
700 if (spin->click_child == 2)
701 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
705 spin->in_child = GTK_ARROW_DOWN;
706 if (spin->click_child == 2)
707 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
714 gtk_spin_button_leave_notify (GtkWidget *widget,
715 GdkEventCrossing *event)
719 g_return_val_if_fail (widget != NULL, FALSE);
720 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
721 g_return_val_if_fail (event != NULL, FALSE);
723 spin = GTK_SPIN_BUTTON (widget);
725 if (event->window == spin->panel && spin->click_child == 2)
727 if (spin->in_child == GTK_ARROW_UP)
730 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
735 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
742 gtk_spin_button_focus_out (GtkWidget *widget,
743 GdkEventFocus *event)
745 g_return_val_if_fail (widget != NULL, FALSE);
746 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
747 g_return_val_if_fail (event != NULL, FALSE);
749 if (GTK_EDITABLE (widget)->editable)
750 gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
752 return GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
756 gtk_spin_button_scroll (GtkWidget *widget,
757 GdkEventScroll *event)
761 g_return_val_if_fail (widget != NULL, FALSE);
762 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
763 g_return_val_if_fail (event != NULL, FALSE);
765 spin = GTK_SPIN_BUTTON (widget);
767 if (event->direction == GDK_SCROLL_UP)
769 if (!GTK_WIDGET_HAS_FOCUS (widget))
770 gtk_widget_grab_focus (widget);
771 gtk_spin_button_real_spin (spin, spin->adjustment->step_increment);
773 else if (event->direction == GDK_SCROLL_DOWN)
775 if (!GTK_WIDGET_HAS_FOCUS (widget))
776 gtk_widget_grab_focus (widget);
777 gtk_spin_button_real_spin (spin, -spin->adjustment->step_increment);
786 gtk_spin_button_button_press (GtkWidget *widget,
787 GdkEventButton *event)
791 g_return_val_if_fail (widget != NULL, FALSE);
792 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
793 g_return_val_if_fail (event != NULL, FALSE);
795 spin = GTK_SPIN_BUTTON (widget);
799 if (event->window == spin->panel)
801 if (!GTK_WIDGET_HAS_FOCUS (widget))
802 gtk_widget_grab_focus (widget);
803 gtk_grab_add (widget);
804 spin->button = event->button;
806 if (GTK_EDITABLE (widget)->editable)
807 gtk_spin_button_update (spin);
809 if (event->y <= widget->requisition.height / 2)
811 spin->click_child = GTK_ARROW_UP;
812 if (event->button == 1)
814 gtk_spin_button_real_spin (spin,
815 spin->adjustment->step_increment);
818 spin->timer_step = spin->adjustment->step_increment;
819 spin->need_timer = TRUE;
820 spin->timer = gtk_timeout_add
821 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
822 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
825 else if (event->button == 2)
827 gtk_spin_button_real_spin (spin,
828 spin->adjustment->page_increment);
831 spin->timer_step = spin->adjustment->page_increment;
832 spin->need_timer = TRUE;
833 spin->timer = gtk_timeout_add
834 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
835 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
838 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
842 spin->click_child = GTK_ARROW_DOWN;
843 if (event->button == 1)
845 gtk_spin_button_real_spin (spin,
846 -spin->adjustment->step_increment);
849 spin->timer_step = spin->adjustment->step_increment;
850 spin->need_timer = TRUE;
851 spin->timer = gtk_timeout_add
852 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
853 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
856 else if (event->button == 2)
858 gtk_spin_button_real_spin (spin,
859 -spin->adjustment->page_increment);
862 spin->timer_step = spin->adjustment->page_increment;
863 spin->need_timer = TRUE;
864 spin->timer = gtk_timeout_add
865 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
866 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
869 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
873 GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
879 gtk_spin_button_button_release (GtkWidget *widget,
880 GdkEventButton *event)
884 g_return_val_if_fail (widget != NULL, FALSE);
885 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
886 g_return_val_if_fail (event != NULL, FALSE);
888 spin = GTK_SPIN_BUTTON (widget);
890 if (event->button == spin->button)
896 gtk_timeout_remove (spin->timer);
898 spin->timer_calls = 0;
899 spin->need_timer = FALSE;
902 if (event->button == 3)
904 if (event->y >= 0 && event->x >= 0 &&
905 event->y <= widget->requisition.height &&
906 event->x <= ARROW_SIZE + 2 * widget->style->klass->xthickness)
908 if (spin->click_child == GTK_ARROW_UP &&
909 event->y <= widget->requisition.height / 2)
913 diff = spin->adjustment->upper - spin->adjustment->value;
915 gtk_spin_button_real_spin (spin, diff);
917 else if (spin->click_child == GTK_ARROW_DOWN &&
918 event->y > widget->requisition.height / 2)
922 diff = spin->adjustment->value - spin->adjustment->lower;
924 gtk_spin_button_real_spin (spin, -diff);
928 gtk_grab_remove (widget);
929 click_child = spin->click_child;
930 spin->click_child = 2;
932 gtk_spin_button_draw_arrow (spin, click_child);
935 GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
941 gtk_spin_button_motion_notify (GtkWidget *widget,
942 GdkEventMotion *event)
946 g_return_val_if_fail (widget != NULL, FALSE);
947 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
948 g_return_val_if_fail (event != NULL, FALSE);
950 spin = GTK_SPIN_BUTTON (widget);
955 if (event->window == spin->panel)
961 gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
963 if (y <= widget->requisition.height / 2 &&
964 spin->in_child == GTK_ARROW_DOWN)
966 spin->in_child = GTK_ARROW_UP;
967 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
968 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
970 else if (y > widget->requisition.height / 2 &&
971 spin->in_child == GTK_ARROW_UP)
973 spin->in_child = GTK_ARROW_DOWN;
974 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
975 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
980 return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
984 gtk_spin_button_timer (GtkSpinButton *spin_button)
986 gboolean retval = FALSE;
988 GDK_THREADS_ENTER ();
990 if (spin_button->timer)
992 if (spin_button->click_child == GTK_ARROW_UP)
993 gtk_spin_button_real_spin (spin_button, spin_button->timer_step);
995 gtk_spin_button_real_spin (spin_button, -spin_button->timer_step);
997 if (spin_button->need_timer)
999 spin_button->need_timer = FALSE;
1000 spin_button->timer = gtk_timeout_add
1001 (SPIN_BUTTON_TIMER_DELAY, (GtkFunction) gtk_spin_button_timer,
1002 (gpointer) spin_button);
1006 if (spin_button->climb_rate > 0.0 && spin_button->timer_step
1007 < spin_button->adjustment->page_increment)
1009 if (spin_button->timer_calls < MAX_TIMER_CALLS)
1010 spin_button->timer_calls++;
1013 spin_button->timer_calls = 0;
1014 spin_button->timer_step += spin_button->climb_rate;
1021 GDK_THREADS_LEAVE ();
1027 gtk_spin_button_value_changed (GtkAdjustment *adjustment,
1028 GtkSpinButton *spin_button)
1032 g_return_if_fail (adjustment != NULL);
1033 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1036 gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1038 if (return_val == FALSE)
1039 gtk_spin_button_default_output (spin_button);
1043 gtk_spin_button_key_press (GtkWidget *widget,
1046 GtkSpinButton *spin;
1048 gboolean key_repeat = FALSE;
1050 g_return_val_if_fail (widget != NULL, FALSE);
1051 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1052 g_return_val_if_fail (event != NULL, FALSE);
1054 spin = GTK_SPIN_BUTTON (widget);
1055 key = event->keyval;
1057 key_repeat = (event->time == spin->ev_time);
1059 if (GTK_EDITABLE (widget)->editable &&
1060 (key == GDK_Up || key == GDK_Down ||
1061 key == GDK_Page_Up || key == GDK_Page_Down))
1062 gtk_spin_button_update (spin);
1068 if (GTK_WIDGET_HAS_FOCUS (widget))
1070 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
1073 spin->timer_step = spin->adjustment->step_increment;
1075 gtk_spin_button_real_spin (spin, spin->timer_step);
1079 if (spin->climb_rate > 0.0 && spin->timer_step
1080 < spin->adjustment->page_increment)
1082 if (spin->timer_calls < MAX_TIMER_CALLS)
1083 spin->timer_calls++;
1086 spin->timer_calls = 0;
1087 spin->timer_step += spin->climb_rate;
1097 if (GTK_WIDGET_HAS_FOCUS (widget))
1099 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
1102 spin->timer_step = spin->adjustment->step_increment;
1104 gtk_spin_button_real_spin (spin, -spin->timer_step);
1108 if (spin->climb_rate > 0.0 && spin->timer_step
1109 < spin->adjustment->page_increment)
1111 if (spin->timer_calls < MAX_TIMER_CALLS)
1112 spin->timer_calls++;
1115 spin->timer_calls = 0;
1116 spin->timer_step += spin->climb_rate;
1126 if (event->state & GDK_CONTROL_MASK)
1128 gfloat diff = spin->adjustment->upper - spin->adjustment->value;
1130 gtk_spin_button_real_spin (spin, diff);
1133 gtk_spin_button_real_spin (spin, spin->adjustment->page_increment);
1138 if (event->state & GDK_CONTROL_MASK)
1140 gfloat diff = spin->adjustment->value - spin->adjustment->lower;
1142 gtk_spin_button_real_spin (spin, -diff);
1145 gtk_spin_button_real_spin (spin, -spin->adjustment->page_increment);
1152 return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
1156 gtk_spin_button_key_release (GtkWidget *widget,
1159 GtkSpinButton *spin;
1161 g_return_val_if_fail (widget != NULL, FALSE);
1162 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
1164 spin = GTK_SPIN_BUTTON (widget);
1166 spin->ev_time = event->time;
1171 gtk_spin_button_snap (GtkSpinButton *spin_button,
1177 inc = spin_button->adjustment->step_increment;
1178 tmp = (val - spin_button->adjustment->lower) / inc;
1179 if (tmp - floor (tmp) < ceil (tmp) - tmp)
1180 val = spin_button->adjustment->lower + floor (tmp) * inc;
1182 val = spin_button->adjustment->lower + ceil (tmp) * inc;
1184 if (fabs (val - spin_button->adjustment->value) > EPSILON)
1185 gtk_adjustment_set_value (spin_button->adjustment, val);
1188 gint return_val = FALSE;
1189 gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1191 if (return_val == FALSE)
1192 gtk_spin_button_default_output (spin_button);
1197 gtk_spin_button_activate (GtkEditable *editable)
1199 g_return_if_fail (editable != NULL);
1200 g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
1202 if (editable->editable)
1203 gtk_spin_button_update (GTK_SPIN_BUTTON (editable));
1207 gtk_spin_button_insert_text (GtkEditable *editable,
1208 const gchar *new_text,
1209 gint new_text_length,
1213 GtkSpinButton *spin;
1215 g_return_if_fail (editable != NULL);
1216 g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
1218 entry = GTK_ENTRY (editable);
1219 spin = GTK_SPIN_BUTTON (editable);
1231 entry_length = entry->text_length;
1235 if (*(lc->negative_sign))
1236 neg_sign = *(lc->negative_sign);
1240 if (*(lc->positive_sign))
1241 pos_sign = *(lc->positive_sign);
1245 for (sign=0, i=0; i<entry_length; i++)
1246 if ((entry->text[i] == neg_sign) ||
1247 (entry->text[i] == pos_sign))
1253 if (sign && !(*position))
1256 for (dotpos=-1, i=0; i<entry_length; i++)
1257 if (entry->text[i] == *(lc->decimal_point))
1263 if (dotpos > -1 && *position > dotpos &&
1264 spin->digits - entry_length
1265 + dotpos - new_text_length + 1 < 0)
1268 for (i = 0; i < new_text_length; i++)
1270 if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1272 if (sign || (*position) || i)
1276 else if (new_text[i] == *(lc->decimal_point))
1278 if (!spin->digits || dotpos > -1 ||
1279 (new_text_length - 1 - i + entry_length
1280 - *position > spin->digits))
1282 dotpos = *position + i;
1284 else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1289 GTK_EDITABLE_CLASS (parent_class)->insert_text (editable, new_text,
1290 new_text_length, position);
1294 gtk_spin_button_real_spin (GtkSpinButton *spin_button,
1298 gfloat new_value = 0.0;
1300 g_return_if_fail (spin_button != NULL);
1301 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1303 adj = spin_button->adjustment;
1305 new_value = adj->value + increment;
1309 if (spin_button->wrap)
1311 if (fabs (adj->value - adj->upper) < EPSILON)
1312 new_value = adj->lower;
1313 else if (new_value > adj->upper)
1314 new_value = adj->upper;
1317 new_value = MIN (new_value, adj->upper);
1319 else if (increment < 0)
1321 if (spin_button->wrap)
1323 if (fabs (adj->value - adj->lower) < EPSILON)
1324 new_value = adj->upper;
1325 else if (new_value < adj->lower)
1326 new_value = adj->lower;
1329 new_value = MAX (new_value, adj->lower);
1332 if (fabs (new_value - adj->value) > EPSILON)
1333 gtk_adjustment_set_value (adj, new_value);
1337 gtk_spin_button_default_input (GtkSpinButton *spin_button,
1342 *new_val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &err);
1350 gtk_spin_button_default_output (GtkSpinButton *spin_button)
1352 gchar buf[MAX_TEXT_LENGTH];
1354 sprintf (buf, "%0.*f", spin_button->digits, spin_button->adjustment->value);
1355 if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1356 gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1361 /***********************************************************
1362 ***********************************************************
1363 *** Public interface ***
1364 ***********************************************************
1365 ***********************************************************/
1369 gtk_spin_button_configure (GtkSpinButton *spin_button,
1370 GtkAdjustment *adjustment,
1374 g_return_if_fail (spin_button != NULL);
1375 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1376 g_return_if_fail (digits < 6);
1379 gtk_spin_button_set_adjustment (spin_button, adjustment);
1381 adjustment = spin_button->adjustment;
1383 spin_button->digits = digits;
1384 spin_button->climb_rate = climb_rate;
1385 gtk_adjustment_value_changed (adjustment);
1389 gtk_spin_button_new (GtkAdjustment *adjustment,
1393 GtkSpinButton *spin;
1396 g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL);
1397 g_return_val_if_fail (digits < 6, NULL);
1399 spin = gtk_type_new (GTK_TYPE_SPIN_BUTTON);
1401 gtk_spin_button_configure (spin, adjustment, climb_rate, digits);
1403 return GTK_WIDGET (spin);
1407 gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
1408 GtkAdjustment *adjustment)
1410 g_return_if_fail (spin_button != NULL);
1411 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1413 if (spin_button->adjustment != adjustment)
1415 if (spin_button->adjustment)
1417 gtk_signal_disconnect_by_data (GTK_OBJECT (spin_button->adjustment),
1418 (gpointer) spin_button);
1419 gtk_object_unref (GTK_OBJECT (spin_button->adjustment));
1421 spin_button->adjustment = adjustment;
1424 gtk_object_ref (GTK_OBJECT (adjustment));
1425 gtk_object_sink (GTK_OBJECT (adjustment));
1426 gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
1427 (GtkSignalFunc) gtk_spin_button_value_changed,
1428 (gpointer) spin_button);
1434 gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
1436 g_return_val_if_fail (spin_button != NULL, NULL);
1437 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
1439 return spin_button->adjustment;
1443 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
1446 g_return_if_fail (spin_button != NULL);
1447 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1448 g_return_if_fail (digits < 6);
1450 if (spin_button->digits != digits)
1452 spin_button->digits = digits;
1453 gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1458 gtk_spin_button_get_value_as_float (GtkSpinButton *spin_button)
1460 g_return_val_if_fail (spin_button != NULL, 0.0);
1461 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0.0);
1463 return spin_button->adjustment->value;
1467 gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
1471 g_return_val_if_fail (spin_button != NULL, 0);
1472 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1474 val = spin_button->adjustment->value;
1475 if (val - floor (val) < ceil (val) - val)
1482 gtk_spin_button_set_value (GtkSpinButton *spin_button,
1485 g_return_if_fail (spin_button != NULL);
1486 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1488 if (fabs (value - spin_button->adjustment->value) > EPSILON)
1489 gtk_adjustment_set_value (spin_button->adjustment, value);
1492 gint return_val = FALSE;
1493 gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1495 if (return_val == FALSE)
1496 gtk_spin_button_default_output (spin_button);
1501 gtk_spin_button_set_update_policy (GtkSpinButton *spin_button,
1502 GtkSpinButtonUpdatePolicy policy)
1504 g_return_if_fail (spin_button != NULL);
1505 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1507 spin_button->update_policy = policy;
1511 gtk_spin_button_set_numeric (GtkSpinButton *spin_button,
1514 g_return_if_fail (spin_button != NULL);
1515 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1517 spin_button->numeric = (numeric != 0);
1521 gtk_spin_button_set_wrap (GtkSpinButton *spin_button,
1524 g_return_if_fail (spin_button != NULL);
1525 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1527 spin_button->wrap = (wrap != 0);
1531 gtk_spin_button_set_shadow_type (GtkSpinButton *spin_button,
1532 GtkShadowType shadow_type)
1534 g_return_if_fail (spin_button != NULL);
1535 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1537 if (shadow_type != spin_button->shadow_type)
1539 spin_button->shadow_type = shadow_type;
1540 if (GTK_WIDGET_DRAWABLE (spin_button))
1541 gtk_widget_queue_draw (GTK_WIDGET (spin_button));
1546 gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
1547 gboolean snap_to_ticks)
1551 g_return_if_fail (spin_button != NULL);
1552 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1554 new_val = (snap_to_ticks != 0);
1556 if (new_val != spin_button->snap_to_ticks)
1558 spin_button->snap_to_ticks = new_val;
1559 if (new_val && GTK_EDITABLE (spin_button)->editable)
1560 gtk_spin_button_update (spin_button);
1565 gtk_spin_button_spin (GtkSpinButton *spin_button,
1566 GtkSpinType direction,
1572 g_return_if_fail (spin_button != NULL);
1573 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1575 adj = spin_button->adjustment;
1577 /* for compatibility with the 1.0.x version of this function */
1578 if (increment != 0 && increment != adj->step_increment &&
1579 (direction == GTK_SPIN_STEP_FORWARD ||
1580 direction == GTK_SPIN_STEP_BACKWARD))
1582 if (direction == GTK_SPIN_STEP_BACKWARD && increment > 0)
1583 increment = -increment;
1584 direction = GTK_SPIN_USER_DEFINED;
1589 case GTK_SPIN_STEP_FORWARD:
1591 gtk_spin_button_real_spin (spin_button, adj->step_increment);
1594 case GTK_SPIN_STEP_BACKWARD:
1596 gtk_spin_button_real_spin (spin_button, -adj->step_increment);
1599 case GTK_SPIN_PAGE_FORWARD:
1601 gtk_spin_button_real_spin (spin_button, adj->page_increment);
1604 case GTK_SPIN_PAGE_BACKWARD:
1606 gtk_spin_button_real_spin (spin_button, -adj->page_increment);
1611 diff = adj->value - adj->lower;
1613 gtk_spin_button_real_spin (spin_button, -diff);
1618 diff = adj->upper - adj->value;
1620 gtk_spin_button_real_spin (spin_button, diff);
1623 case GTK_SPIN_USER_DEFINED:
1626 gtk_spin_button_real_spin (spin_button, increment);
1635 gtk_spin_button_update (GtkSpinButton *spin_button)
1641 g_return_if_fail (spin_button != NULL);
1642 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1645 gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[INPUT],
1647 if (return_val == FALSE)
1649 return_val = gtk_spin_button_default_input (spin_button, &val);
1650 error = (return_val == INPUT_ERROR);
1652 else if (return_val == INPUT_ERROR)
1655 if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
1657 if (val < spin_button->adjustment->lower)
1658 val = spin_button->adjustment->lower;
1659 else if (val > spin_button->adjustment->upper)
1660 val = spin_button->adjustment->upper;
1662 else if ((spin_button->update_policy == GTK_UPDATE_IF_VALID) &&
1664 val < spin_button->adjustment->lower ||
1665 val > spin_button->adjustment->upper))
1667 gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1671 if (spin_button->snap_to_ticks)
1672 gtk_spin_button_snap (spin_button, val);
1675 if (fabs (val - spin_button->adjustment->value) > EPSILON)
1676 gtk_adjustment_set_value (spin_button->adjustment, val);
1680 gtk_signal_emit (GTK_OBJECT (spin_button), spinbutton_signals[OUTPUT],
1682 if (return_val == FALSE)
1683 gtk_spin_button_default_output (spin_button);