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
43 static void gtk_spin_button_class_init (GtkSpinButtonClass *klass);
44 static void gtk_spin_button_init (GtkSpinButton *spin_button);
45 static void gtk_spin_button_finalize (GtkObject *object);
46 static void gtk_spin_button_map (GtkWidget *widget);
47 static void gtk_spin_button_unmap (GtkWidget *widget);
48 static void gtk_spin_button_realize (GtkWidget *widget);
49 static void gtk_spin_button_unrealize (GtkWidget *widget);
50 static void gtk_spin_button_size_request (GtkWidget *widget,
51 GtkRequisition *requisition);
52 static void gtk_spin_button_size_allocate (GtkWidget *widget,
53 GtkAllocation *allocation);
54 static void gtk_spin_button_paint (GtkWidget *widget,
56 static void gtk_spin_button_draw (GtkWidget *widget,
58 static gint gtk_spin_button_expose (GtkWidget *widget,
59 GdkEventExpose *event);
60 static gint gtk_spin_button_button_press (GtkWidget *widget,
61 GdkEventButton *event);
62 static gint gtk_spin_button_button_release (GtkWidget *widget,
63 GdkEventButton *event);
64 static gint gtk_spin_button_motion_notify (GtkWidget *widget,
65 GdkEventMotion *event);
66 static gint gtk_spin_button_enter_notify (GtkWidget *widget,
67 GdkEventCrossing *event);
68 static gint gtk_spin_button_leave_notify (GtkWidget *widget,
69 GdkEventCrossing *event);
70 static gint gtk_spin_button_focus_out (GtkWidget *widget,
71 GdkEventFocus *event);
72 static void gtk_spin_button_draw_arrow (GtkSpinButton *spin_button,
74 static gint gtk_spin_button_timer (GtkSpinButton *spin_button);
75 static void gtk_spin_button_value_changed (GtkAdjustment *adjustment,
76 GtkSpinButton *spin_button);
77 static gint gtk_spin_button_key_press (GtkWidget *widget,
79 static gint gtk_spin_button_key_release (GtkWidget *widget,
81 static void gtk_spin_button_update (GtkSpinButton *spin_button);
82 static void gtk_spin_button_activate (GtkEditable *editable);
83 static void gtk_spin_button_snap (GtkSpinButton *spin_button,
85 static void gtk_spin_button_insert_text (GtkEditable *editable,
86 const gchar *new_text,
89 static void gtk_spin_button_real_spin (GtkSpinButton *spin_button,
93 static GtkEntryClass *parent_class = NULL;
97 gtk_spin_button_get_type (void)
99 static guint spin_button_type = 0;
101 if (!spin_button_type)
103 GtkTypeInfo spin_button_info =
106 sizeof (GtkSpinButton),
107 sizeof (GtkSpinButtonClass),
108 (GtkClassInitFunc) gtk_spin_button_class_init,
109 (GtkObjectInitFunc) gtk_spin_button_init,
110 /* reversed_1 */ NULL,
111 /* reversed_2 */ NULL,
112 (GtkClassInitFunc) NULL,
115 spin_button_type = gtk_type_unique (gtk_entry_get_type (),
118 return spin_button_type;
122 gtk_spin_button_class_init (GtkSpinButtonClass *class)
124 GtkObjectClass *object_class;
125 GtkWidgetClass *widget_class;
126 GtkEditableClass *editable_class;
128 object_class = (GtkObjectClass*) class;
129 widget_class = (GtkWidgetClass*) class;
130 editable_class = (GtkEditableClass*) class;
132 parent_class = gtk_type_class (gtk_entry_get_type ());
134 object_class->finalize = gtk_spin_button_finalize;
136 widget_class->map = gtk_spin_button_map;
137 widget_class->unmap = gtk_spin_button_unmap;
138 widget_class->realize = gtk_spin_button_realize;
139 widget_class->unrealize = gtk_spin_button_unrealize;
140 widget_class->size_request = gtk_spin_button_size_request;
141 widget_class->size_allocate = gtk_spin_button_size_allocate;
142 widget_class->draw = gtk_spin_button_draw;
143 widget_class->expose_event = gtk_spin_button_expose;
144 widget_class->button_press_event = gtk_spin_button_button_press;
145 widget_class->button_release_event = gtk_spin_button_button_release;
146 widget_class->motion_notify_event = gtk_spin_button_motion_notify;
147 widget_class->key_press_event = gtk_spin_button_key_press;
148 widget_class->key_release_event = gtk_spin_button_key_release;
149 widget_class->enter_notify_event = gtk_spin_button_enter_notify;
150 widget_class->leave_notify_event = gtk_spin_button_leave_notify;
151 widget_class->focus_out_event = gtk_spin_button_focus_out;
153 editable_class->insert_text = gtk_spin_button_insert_text;
154 editable_class->activate = gtk_spin_button_activate;
158 gtk_spin_button_init (GtkSpinButton *spin_button)
160 spin_button->adjustment = NULL;
161 spin_button->panel = NULL;
162 spin_button->shadow_type = GTK_SHADOW_NONE;
163 spin_button->timer = 0;
164 spin_button->ev_time = 0;
165 spin_button->climb_rate = 0.0;
166 spin_button->timer_step = 0.0;
167 spin_button->update_policy = GTK_UPDATE_ALWAYS;
168 spin_button->in_child = 2;
169 spin_button->click_child = 2;
170 spin_button->button = 0;
171 spin_button->need_timer = FALSE;
172 spin_button->timer_calls = 0;
173 spin_button->digits = 0;
174 spin_button->numeric = FALSE;
175 spin_button->wrap = FALSE;
176 spin_button->snap_to_ticks = FALSE;
180 gtk_spin_button_finalize (GtkObject *object)
182 g_return_if_fail (object != NULL);
183 g_return_if_fail (GTK_IS_SPIN_BUTTON (object));
185 gtk_object_unref (GTK_OBJECT (GTK_SPIN_BUTTON (object)->adjustment));
187 GTK_OBJECT_CLASS (parent_class)->finalize (object);
191 gtk_spin_button_map (GtkWidget *widget)
193 g_return_if_fail (widget != NULL);
194 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
196 if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
198 GTK_WIDGET_CLASS (parent_class)->map (widget);
199 gdk_window_show (GTK_SPIN_BUTTON (widget)->panel);
204 gtk_spin_button_unmap (GtkWidget *widget)
206 g_return_if_fail (widget != NULL);
207 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
209 if (GTK_WIDGET_MAPPED (widget))
211 gdk_window_hide (GTK_SPIN_BUTTON (widget)->panel);
212 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
217 gtk_spin_button_realize (GtkWidget *widget)
220 GdkWindowAttr attributes;
221 gint attributes_mask;
224 g_return_if_fail (widget != NULL);
225 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
227 spin = GTK_SPIN_BUTTON (widget);
229 real_width = widget->allocation.width;
230 widget->allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
231 gtk_widget_set_events (widget, gtk_widget_get_events (widget) |
232 GDK_KEY_RELEASE_MASK);
233 GTK_WIDGET_CLASS (parent_class)->realize (widget);
235 widget->allocation.width = real_width;
237 attributes.window_type = GDK_WINDOW_CHILD;
238 attributes.wclass = GDK_INPUT_OUTPUT;
239 attributes.visual = gtk_widget_get_visual (widget);
240 attributes.colormap = gtk_widget_get_colormap (widget);
241 attributes.event_mask = gtk_widget_get_events (widget);
242 attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
243 | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
244 | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
246 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
248 attributes.x = (widget->allocation.x + widget->allocation.width - ARROW_SIZE -
249 2 * widget->style->klass->xthickness);
250 attributes.y = widget->allocation.y + (widget->allocation.height -
251 widget->requisition.height) / 2;
252 attributes.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
253 attributes.height = widget->requisition.height;
255 spin->panel = gdk_window_new (gtk_widget_get_parent_window (widget),
256 &attributes, attributes_mask);
257 gdk_window_set_user_data (spin->panel, widget);
259 gtk_style_set_background (widget->style, spin->panel, GTK_STATE_NORMAL);
263 gtk_spin_button_unrealize (GtkWidget *widget)
267 g_return_if_fail (widget != NULL);
268 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
270 spin = GTK_SPIN_BUTTON (widget);
272 GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
276 gdk_window_set_user_data (spin->panel, NULL);
277 gdk_window_destroy (spin->panel);
283 gtk_spin_button_size_request (GtkWidget *widget,
284 GtkRequisition *requisition)
286 g_return_if_fail (widget != NULL);
287 g_return_if_fail (requisition != NULL);
288 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
290 GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
292 requisition->width = MIN_SPIN_BUTTON_WIDTH + ARROW_SIZE
293 + 2 * widget->style->klass->xthickness;
297 gtk_spin_button_size_allocate (GtkWidget *widget,
298 GtkAllocation *allocation)
300 GtkAllocation child_allocation;
302 g_return_if_fail (widget != NULL);
303 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
304 g_return_if_fail (allocation != NULL);
306 child_allocation = *allocation;
307 child_allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
309 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &child_allocation);
311 widget->allocation = *allocation;
313 if (GTK_WIDGET_REALIZED (widget))
315 child_allocation.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
316 child_allocation.height = widget->requisition.height;
317 child_allocation.x = (allocation->x + allocation->width - ARROW_SIZE -
318 2 * widget->style->klass->xthickness);
319 child_allocation.y = allocation->y + (allocation->height - widget->requisition.height) / 2;
321 gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel,
324 child_allocation.width,
325 child_allocation.height);
330 gtk_spin_button_paint (GtkWidget *widget,
335 g_return_if_fail (widget != NULL);
336 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
338 spin = GTK_SPIN_BUTTON (widget);
340 if (GTK_WIDGET_DRAWABLE (widget))
342 if (spin->shadow_type != GTK_SHADOW_NONE)
343 gtk_draw_shadow (widget->style, spin->panel,
344 GTK_STATE_NORMAL, spin->shadow_type,
346 ARROW_SIZE + 2 * widget->style->klass->xthickness,
347 widget->requisition.height);
348 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
349 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
351 GTK_WIDGET_CLASS (parent_class)->draw (widget, area);
356 gtk_spin_button_draw (GtkWidget *widget,
359 g_return_if_fail (widget != NULL);
360 g_return_if_fail (GTK_IS_SPIN_BUTTON (widget));
361 g_return_if_fail (area != NULL);
363 if (GTK_WIDGET_DRAWABLE (widget))
364 gtk_spin_button_paint (widget, area);
368 gtk_spin_button_expose (GtkWidget *widget,
369 GdkEventExpose *event)
371 g_return_val_if_fail (widget != NULL, FALSE);
372 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
373 g_return_val_if_fail (event != NULL, FALSE);
375 if (GTK_WIDGET_DRAWABLE (widget))
376 gtk_spin_button_paint (widget, &event->area);
382 gtk_spin_button_draw_arrow (GtkSpinButton *spin_button,
385 GtkStateType state_type;
386 GtkShadowType shadow_type;
391 g_return_if_fail (spin_button != NULL);
392 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
394 widget = GTK_WIDGET (spin_button);
396 if (GTK_WIDGET_DRAWABLE (spin_button))
398 if (spin_button->in_child == arrow)
400 if (spin_button->click_child == arrow)
401 state_type = GTK_STATE_ACTIVE;
403 state_type = GTK_STATE_PRELIGHT;
406 state_type = GTK_STATE_NORMAL;
408 if (spin_button->click_child == arrow)
409 shadow_type = GTK_SHADOW_IN;
411 shadow_type = GTK_SHADOW_OUT;
413 if (arrow == GTK_ARROW_UP)
415 if (spin_button->shadow_type != GTK_SHADOW_NONE)
417 x = widget->style->klass->xthickness;
418 y = widget->style->klass->ythickness;
422 x = widget->style->klass->xthickness - 1;
423 y = widget->style->klass->ythickness - 1;
425 gtk_draw_arrow (widget->style, spin_button->panel,
426 state_type, shadow_type, arrow, TRUE,
427 x, y, ARROW_SIZE, widget->requisition.height / 2
428 - widget->style->klass->ythickness);
432 if (spin_button->shadow_type != GTK_SHADOW_NONE)
434 x = widget->style->klass->xthickness;
435 y = widget->requisition.height / 2;
439 x = widget->style->klass->xthickness - 1;
440 y = widget->requisition.height / 2 + 1;
442 gtk_draw_arrow (widget->style, spin_button->panel,
443 state_type, shadow_type, arrow, TRUE,
444 x, y, ARROW_SIZE, widget->requisition.height / 2
445 - widget->style->klass->ythickness);
451 gtk_spin_button_enter_notify (GtkWidget *widget,
452 GdkEventCrossing *event)
456 g_return_val_if_fail (widget != NULL, FALSE);
457 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
458 g_return_val_if_fail (event != NULL, FALSE);
460 spin = GTK_SPIN_BUTTON (widget);
462 if (event->window == spin->panel)
467 gdk_window_get_pointer (spin->panel, &x, &y, NULL);
469 if (y <= widget->requisition.height / 2)
471 spin->in_child = GTK_ARROW_UP;
472 if (spin->click_child == 2)
473 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
477 spin->in_child = GTK_ARROW_DOWN;
478 if (spin->click_child == 2)
479 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
486 gtk_spin_button_leave_notify (GtkWidget *widget,
487 GdkEventCrossing *event)
491 g_return_val_if_fail (widget != NULL, FALSE);
492 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
493 g_return_val_if_fail (event != NULL, FALSE);
495 spin = GTK_SPIN_BUTTON (widget);
497 if (event->window == spin->panel && spin->click_child == 2)
499 if (spin->in_child == GTK_ARROW_UP)
502 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
507 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
514 gtk_spin_button_focus_out (GtkWidget *widget,
515 GdkEventFocus *event)
517 g_return_val_if_fail (widget != NULL, FALSE);
518 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
519 g_return_val_if_fail (event != NULL, FALSE);
521 gtk_spin_button_update (GTK_SPIN_BUTTON (widget));
523 return GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
527 gtk_spin_button_button_press (GtkWidget *widget,
528 GdkEventButton *event)
532 g_return_val_if_fail (widget != NULL, FALSE);
533 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
534 g_return_val_if_fail (event != NULL, FALSE);
536 spin = GTK_SPIN_BUTTON (widget);
540 if (event->window == spin->panel)
542 if (!GTK_WIDGET_HAS_FOCUS (widget))
543 gtk_widget_grab_focus (widget);
544 gtk_grab_add (widget);
545 spin->button = event->button;
547 gtk_spin_button_update (spin);
549 if (event->y <= widget->requisition.height / 2)
551 spin->click_child = GTK_ARROW_UP;
552 if (event->button == 1)
554 gtk_spin_button_real_spin (spin,
555 spin->adjustment->step_increment);
558 spin->timer_step = spin->adjustment->step_increment;
559 spin->need_timer = TRUE;
560 spin->timer = gtk_timeout_add
561 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
562 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
565 else if (event->button == 2)
567 gtk_spin_button_real_spin (spin,
568 spin->adjustment->page_increment);
571 spin->timer_step = spin->adjustment->page_increment;
572 spin->need_timer = TRUE;
573 spin->timer = gtk_timeout_add
574 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
575 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
578 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
582 spin->click_child = GTK_ARROW_DOWN;
583 if (event->button == 1)
585 gtk_spin_button_real_spin (spin,
586 -spin->adjustment->step_increment);
589 spin->timer_step = spin->adjustment->step_increment;
590 spin->need_timer = TRUE;
591 spin->timer = gtk_timeout_add
592 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
593 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
596 else if (event->button == 2)
598 gtk_spin_button_real_spin (spin,
599 -spin->adjustment->page_increment);
602 spin->timer_step = spin->adjustment->page_increment;
603 spin->need_timer = TRUE;
604 spin->timer = gtk_timeout_add
605 (SPIN_BUTTON_INITIAL_TIMER_DELAY,
606 (GtkFunction) gtk_spin_button_timer, (gpointer) spin);
609 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
613 GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
619 gtk_spin_button_button_release (GtkWidget *widget,
620 GdkEventButton *event)
624 g_return_val_if_fail (widget != NULL, FALSE);
625 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
626 g_return_val_if_fail (event != NULL, FALSE);
628 spin = GTK_SPIN_BUTTON (widget);
630 if (event->button == spin->button)
636 gtk_timeout_remove (spin->timer);
638 spin->timer_calls = 0;
639 spin->need_timer = FALSE;
642 if (event->button == 3)
644 if (event->y >= 0 && event->x >= 0 &&
645 event->y <= widget->requisition.height &&
646 event->x <= ARROW_SIZE + 2 * widget->style->klass->xthickness)
648 if (spin->click_child == GTK_ARROW_UP &&
649 event->y <= widget->requisition.height / 2)
653 diff = spin->adjustment->upper - spin->adjustment->value;
655 gtk_spin_button_real_spin (spin, diff);
657 else if (spin->click_child == GTK_ARROW_DOWN &&
658 event->y > widget->requisition.height / 2)
662 diff = spin->adjustment->value - spin->adjustment->lower;
664 gtk_spin_button_real_spin (spin, -diff);
668 gtk_grab_remove (widget);
669 click_child = spin->click_child;
670 spin->click_child = 2;
672 gtk_spin_button_draw_arrow (spin, click_child);
675 GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
681 gtk_spin_button_motion_notify (GtkWidget *widget,
682 GdkEventMotion *event)
686 g_return_val_if_fail (widget != NULL, FALSE);
687 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
688 g_return_val_if_fail (event != NULL, FALSE);
690 spin = GTK_SPIN_BUTTON (widget);
695 if (event->window == spin->panel)
701 gdk_window_get_pointer (spin->panel, NULL, &y, NULL);
703 if (y <= widget->requisition.height / 2 &&
704 spin->in_child == GTK_ARROW_DOWN)
706 spin->in_child = GTK_ARROW_UP;
707 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
708 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
710 else if (y > widget->requisition.height / 2 &&
711 spin->in_child == GTK_ARROW_UP)
713 spin->in_child = GTK_ARROW_DOWN;
714 gtk_spin_button_draw_arrow (spin, GTK_ARROW_UP);
715 gtk_spin_button_draw_arrow (spin, GTK_ARROW_DOWN);
720 return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
724 gtk_spin_button_timer (GtkSpinButton *spin_button)
726 g_return_val_if_fail (spin_button != NULL, FALSE);
727 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), FALSE);
729 if (spin_button->timer)
731 if (spin_button->click_child == GTK_ARROW_UP)
732 gtk_spin_button_real_spin (spin_button, spin_button->timer_step);
734 gtk_spin_button_real_spin (spin_button, -spin_button->timer_step);
736 if (spin_button->need_timer)
738 spin_button->need_timer = FALSE;
739 spin_button->timer = gtk_timeout_add
740 (SPIN_BUTTON_TIMER_DELAY, (GtkFunction) gtk_spin_button_timer,
741 (gpointer) spin_button);
744 else if (spin_button->climb_rate > 0.0 && spin_button->timer_step
745 < spin_button->adjustment->page_increment)
747 if (spin_button->timer_calls < MAX_TIMER_CALLS)
748 spin_button->timer_calls++;
751 spin_button->timer_calls = 0;
752 spin_button->timer_step += spin_button->climb_rate;
761 gtk_spin_button_value_changed (GtkAdjustment *adjustment,
762 GtkSpinButton *spin_button)
764 char buf[MAX_TEXT_LENGTH];
766 g_return_if_fail (adjustment != NULL);
767 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
769 sprintf (buf, "%0.*f", spin_button->digits, adjustment->value);
770 gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
774 gtk_spin_button_key_press (GtkWidget *widget,
779 gboolean key_repeat = FALSE;
781 g_return_val_if_fail (widget != NULL, FALSE);
782 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
783 g_return_val_if_fail (event != NULL, FALSE);
785 spin = GTK_SPIN_BUTTON (widget);
788 key_repeat = (event->time == spin->ev_time);
790 if (key == GDK_Up || key == GDK_Down ||
791 key == GDK_Page_Up || key == GDK_Page_Down)
792 gtk_spin_button_update (spin);
798 if (GTK_WIDGET_HAS_FOCUS (widget))
800 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
803 spin->timer_step = spin->adjustment->step_increment;
805 gtk_spin_button_real_spin (spin, spin->timer_step);
809 if (spin->climb_rate > 0.0 && spin->timer_step
810 < spin->adjustment->page_increment)
812 if (spin->timer_calls < MAX_TIMER_CALLS)
816 spin->timer_calls = 0;
817 spin->timer_step += spin->climb_rate;
827 if (GTK_WIDGET_HAS_FOCUS (widget))
829 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
832 spin->timer_step = spin->adjustment->step_increment;
834 gtk_spin_button_real_spin (spin, -spin->timer_step);
838 if (spin->climb_rate > 0.0 && spin->timer_step
839 < spin->adjustment->page_increment)
841 if (spin->timer_calls < MAX_TIMER_CALLS)
845 spin->timer_calls = 0;
846 spin->timer_step += spin->climb_rate;
856 if (event->state & GDK_CONTROL_MASK)
858 gfloat diff = spin->adjustment->upper - spin->adjustment->value;
860 gtk_spin_button_real_spin (spin, diff);
863 gtk_spin_button_real_spin (spin, spin->adjustment->page_increment);
868 if (event->state & GDK_CONTROL_MASK)
870 gfloat diff = spin->adjustment->value - spin->adjustment->lower;
872 gtk_spin_button_real_spin (spin, -diff);
875 gtk_spin_button_real_spin (spin, -spin->adjustment->page_increment);
882 return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
886 gtk_spin_button_key_release (GtkWidget *widget,
891 g_return_val_if_fail (widget != NULL, FALSE);
892 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), FALSE);
894 spin = GTK_SPIN_BUTTON (widget);
896 spin->ev_time = event->time;
901 gtk_spin_button_snap (GtkSpinButton *spin_button,
907 inc = spin_button->adjustment->step_increment;
908 tmp = (val - spin_button->adjustment->lower) / inc;
909 if (tmp - floor (tmp) < ceil (tmp) - tmp)
910 val = spin_button->adjustment->lower + floor (tmp) * inc;
912 val = spin_button->adjustment->lower + ceil (tmp) * inc;
914 if (fabs (val - spin_button->adjustment->value) > EPSILON)
915 gtk_adjustment_set_value (spin_button->adjustment, val);
918 char buf[MAX_TEXT_LENGTH];
920 sprintf (buf, "%0.*f", spin_button->digits,
921 spin_button->adjustment->value);
922 if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
923 gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
928 gtk_spin_button_update (GtkSpinButton *spin_button)
933 g_return_if_fail (spin_button != NULL);
934 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
936 val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
938 if (spin_button->update_policy == GTK_UPDATE_ALWAYS)
940 if (val < spin_button->adjustment->lower)
941 val = spin_button->adjustment->lower;
942 else if (val > spin_button->adjustment->upper)
943 val = spin_button->adjustment->upper;
945 else if ((spin_button->update_policy == GTK_UPDATE_IF_VALID) &&
947 val < spin_button->adjustment->lower ||
948 val > spin_button->adjustment->upper))
950 gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
954 if (spin_button->snap_to_ticks)
955 gtk_spin_button_snap (spin_button, val);
958 if (fabs (val - spin_button->adjustment->value) > EPSILON)
959 gtk_adjustment_set_value (spin_button->adjustment, val);
962 char buf[MAX_TEXT_LENGTH];
964 sprintf (buf, "%0.*f", spin_button->digits,
965 spin_button->adjustment->value);
966 if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
967 gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
973 gtk_spin_button_activate (GtkEditable *editable)
975 g_return_if_fail (editable != NULL);
976 g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
978 if (editable->editable)
979 gtk_spin_button_update (GTK_SPIN_BUTTON (editable));
983 gtk_spin_button_insert_text (GtkEditable *editable,
984 const gchar *new_text,
985 gint new_text_length,
991 g_return_if_fail (editable != NULL);
992 g_return_if_fail (GTK_IS_SPIN_BUTTON (editable));
994 entry = GTK_ENTRY (editable);
995 spin = GTK_SPIN_BUTTON (editable);
1008 if (*(lc->negative_sign))
1009 neg_sign = *(lc->negative_sign);
1013 if (*(lc->positive_sign))
1014 pos_sign = *(lc->positive_sign);
1018 sign = ((strchr (entry->text, neg_sign) != 0) ||
1019 (strchr (entry->text, pos_sign) != 0));
1021 if (sign && !(*position))
1024 dotpos = strchr (entry->text, *(lc->decimal_point)) - entry->text;
1026 if (dotpos > -1 && *position > dotpos &&
1027 spin->digits - entry->text_length + dotpos - new_text_length + 1 < 0)
1030 for (i = 0; i < new_text_length; i++)
1032 if (new_text[i] == neg_sign || new_text[i] == pos_sign)
1034 if (sign || (*position) || i)
1038 else if (new_text[i] == *(lc->decimal_point))
1040 if (!spin->digits || dotpos > -1 ||
1041 (new_text_length - 1 - i + entry->text_length - *position >
1044 dotpos = *position + i;
1046 else if (new_text[i] < 0x30 || new_text[i] > 0x39)
1051 GTK_EDITABLE_CLASS (parent_class)->insert_text (editable, new_text,
1052 new_text_length, position);
1056 gtk_spin_button_real_spin (GtkSpinButton *spin_button,
1060 gfloat new_value = 0.0;
1062 g_return_if_fail (spin_button != NULL);
1063 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1065 adj = spin_button->adjustment;
1067 new_value = adj->value + increment;
1071 if (spin_button->wrap)
1073 if (fabs (adj->value - adj->upper) < EPSILON)
1074 new_value = adj->lower;
1075 else if (new_value > adj->upper)
1076 new_value = adj->upper;
1079 new_value = MIN (new_value, adj->upper);
1081 else if (increment < 0)
1083 if (spin_button->wrap)
1085 if (fabs (adj->value - adj->lower) < EPSILON)
1086 new_value = adj->upper;
1087 else if (new_value < adj->lower)
1088 new_value = adj->lower;
1091 new_value = MAX (new_value, adj->lower);
1094 if (fabs (new_value - adj->value) > EPSILON)
1095 gtk_adjustment_set_value (adj, new_value);
1099 /***********************************************************
1100 ***********************************************************
1101 *** Public interface ***
1102 ***********************************************************
1103 ***********************************************************/
1107 gtk_spin_button_construct (GtkSpinButton *spin_button,
1108 GtkAdjustment *adjustment,
1112 char buf[MAX_TEXT_LENGTH];
1114 g_return_if_fail (spin_button != NULL);
1115 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1116 g_return_if_fail (digits >= 0 && digits < 6);
1119 adjustment = (GtkAdjustment*) gtk_adjustment_new (0, 0, 0, 0, 0, 0);
1121 gtk_spin_button_set_adjustment (spin_button, adjustment);
1122 spin_button->digits = digits;
1123 sprintf (buf, "%0.*f", digits, adjustment->value);
1124 gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1125 spin_button->climb_rate = climb_rate;
1129 gtk_spin_button_new (GtkAdjustment *adjustment,
1133 GtkSpinButton *spin;
1135 g_return_val_if_fail (digits >= 0 && digits < 6, NULL);
1137 spin = gtk_type_new (gtk_spin_button_get_type ());
1139 gtk_spin_button_construct (spin, adjustment, climb_rate, digits);
1141 return GTK_WIDGET (spin);
1145 gtk_spin_button_set_adjustment (GtkSpinButton *spin_button,
1146 GtkAdjustment *adjustment)
1148 g_return_if_fail (spin_button != NULL);
1149 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1151 if (spin_button->adjustment != adjustment)
1153 if (spin_button->adjustment)
1155 gtk_signal_disconnect_by_data (GTK_OBJECT (spin_button->adjustment),
1156 (gpointer) spin_button);
1157 gtk_object_unref (GTK_OBJECT (spin_button->adjustment));
1159 spin_button->adjustment = adjustment;
1162 gtk_object_ref (GTK_OBJECT (adjustment));
1163 gtk_object_sink (GTK_OBJECT (adjustment));
1164 gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
1165 (GtkSignalFunc) gtk_spin_button_value_changed,
1166 (gpointer) spin_button);
1172 gtk_spin_button_get_adjustment (GtkSpinButton *spin_button)
1174 g_return_val_if_fail (spin_button != NULL, NULL);
1175 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), NULL);
1177 return spin_button->adjustment;
1181 gtk_spin_button_set_digits (GtkSpinButton *spin_button,
1184 g_return_if_fail (spin_button != NULL);
1185 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1186 g_return_if_fail (digits >= 0 || digits < 6);
1188 if (spin_button->digits != digits)
1190 spin_button->digits = digits;
1191 gtk_spin_button_value_changed (spin_button->adjustment, spin_button);
1196 gtk_spin_button_get_value_as_float (GtkSpinButton *spin_button)
1198 g_return_val_if_fail (spin_button != NULL, 0.0);
1199 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0.0);
1201 return spin_button->adjustment->value;
1205 gtk_spin_button_get_value_as_int (GtkSpinButton *spin_button)
1209 g_return_val_if_fail (spin_button != NULL, 0);
1210 g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spin_button), 0);
1212 val = spin_button->adjustment->value;
1213 if (val - floor (val) < ceil (val) - val)
1220 gtk_spin_button_set_value (GtkSpinButton *spin_button,
1223 g_return_if_fail (spin_button != NULL);
1224 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1226 if (fabs (value - spin_button->adjustment->value) > EPSILON)
1227 gtk_adjustment_set_value (spin_button->adjustment, value);
1230 char buf[MAX_TEXT_LENGTH];
1232 sprintf (buf, "%0.*f", spin_button->digits,
1233 spin_button->adjustment->value);
1234 if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (spin_button))))
1235 gtk_entry_set_text (GTK_ENTRY (spin_button), buf);
1240 gtk_spin_button_set_update_policy (GtkSpinButton *spin_button,
1241 GtkSpinButtonUpdatePolicy policy)
1243 g_return_if_fail (spin_button != NULL);
1244 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1246 spin_button->update_policy = policy;
1250 gtk_spin_button_set_numeric (GtkSpinButton *spin_button,
1253 g_return_if_fail (spin_button != NULL);
1254 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1256 spin_button->numeric = (numeric != 0);
1260 gtk_spin_button_set_wrap (GtkSpinButton *spin_button,
1263 g_return_if_fail (spin_button != NULL);
1264 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1266 spin_button->wrap = (wrap != 0);
1270 gtk_spin_button_set_shadow_type (GtkSpinButton *spin_button,
1271 GtkShadowType shadow_type)
1273 g_return_if_fail (spin_button != NULL);
1274 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1276 if (shadow_type != spin_button->shadow_type)
1278 spin_button->shadow_type = shadow_type;
1279 if (GTK_WIDGET_DRAWABLE (spin_button))
1280 gdk_window_clear (spin_button->panel);
1285 gtk_spin_button_set_snap_to_ticks (GtkSpinButton *spin_button,
1290 g_return_if_fail (spin_button != NULL);
1291 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1293 new_val = (snap_to_ticks != 0);
1295 if (new_val != spin_button->snap_to_ticks)
1297 spin_button->snap_to_ticks = new_val;
1300 gchar *error = NULL;
1303 val = strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &error);
1304 gtk_spin_button_snap (spin_button, val);
1310 gtk_spin_button_spin (GtkSpinButton *spin_button,
1311 GtkSpinType direction,
1317 g_return_if_fail (spin_button != NULL);
1318 g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
1320 adj = spin_button->adjustment;
1322 /* for compatibility with the 1.0.x version of this function */
1323 if (increment != 0 && increment != adj->step_increment &&
1324 (direction == GTK_SPIN_STEP_FORWARD ||
1325 direction == GTK_SPIN_STEP_BACKWARD))
1327 if (direction == GTK_SPIN_STEP_BACKWARD && increment > 0)
1328 increment = -increment;
1329 direction = GTK_SPIN_USER_DEFINED;
1334 case GTK_SPIN_STEP_FORWARD:
1336 gtk_spin_button_real_spin (spin_button, adj->step_increment);
1339 case GTK_SPIN_STEP_BACKWARD:
1341 gtk_spin_button_real_spin (spin_button, -adj->step_increment);
1344 case GTK_SPIN_PAGE_FORWARD:
1346 gtk_spin_button_real_spin (spin_button, adj->page_increment);
1349 case GTK_SPIN_PAGE_BACKWARD:
1351 gtk_spin_button_real_spin (spin_button, -adj->page_increment);
1356 diff = adj->value - adj->lower;
1358 gtk_spin_button_real_spin (spin_button, -diff);
1363 diff = adj->upper - adj->value;
1365 gtk_spin_button_real_spin (spin_button, diff);
1368 case GTK_SPIN_USER_DEFINED:
1371 gtk_spin_button_real_spin (spin_button, increment);