1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2005 Ronald S. Bultje
3 * Copyright (C) 2006, 2007 Christian Persch
4 * Copyright (C) 2006 Jan Arne Petersen
5 * Copyright (C) 2005-2007 Red Hat, Inc.
8 * - Ronald S. Bultje <rbultje@ronald.bitfreak.net>
9 * - Bastien Nocera <bnocera@redhat.com>
10 * - Jan Arne Petersen <jpetersen@jpetersen.org>
11 * - Christian Persch <chpe@svn.gnome.org>
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the
25 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 * Boston, MA 02111-1307, USA.
30 * Modified by the GTK+ Team and others 2007. See the AUTHORS
31 * file for a list of people on the GTK+ Team. See the ChangeLog
32 * files for a list of changes. These files are distributed with
33 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
43 #include <gdk-pixbuf/gdk-pixbuf.h>
44 #include <gdk/gdkkeysyms.h>
48 #include "gtkbindings.h"
49 #include "gtkhscale.h"
50 #include "gtkvscale.h"
53 #include "gtkwindow.h"
54 #include "gtkmarshalers.h"
56 #include "gtkprivate.h"
57 #include "gtkscalebutton.h"
61 #define SCALE_SIZE 100
62 #define CLICK_TIMEOUT 250
84 #define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_SCALE_BUTTON, GtkScaleButtonPrivate))
86 struct _GtkScaleButtonPrivate
93 GtkOrientation orientation;
103 GtkAdjustment *adjustment; /* needed because it must be settable in init() */
106 static GObject* gtk_scale_button_constructor (GType type,
107 guint n_construct_properties,
108 GObjectConstructParam *construct_params);
109 static void gtk_scale_button_dispose (GObject *object);
110 static void gtk_scale_button_finalize (GObject *object);
111 static void gtk_scale_button_set_property (GObject *object,
115 static void gtk_scale_button_get_property (GObject *object,
119 static gboolean gtk_scale_button_scroll (GtkWidget *widget,
120 GdkEventScroll *event);
121 static void gtk_scale_button_screen_changed (GtkWidget *widget,
122 GdkScreen *previous_screen);
123 static gboolean gtk_scale_button_press (GtkWidget *widget,
124 GdkEventButton *event);
125 static gboolean gtk_scale_button_key_release (GtkWidget *widget,
127 static void gtk_scale_button_popup_from_bindings (GtkWidget *widget);
128 static void gtk_scale_button_popdown_from_bindings (GtkWidget *widget);
129 static gboolean cb_dock_button_press (GtkWidget *widget,
130 GdkEventButton *event,
132 static gboolean cb_dock_key_release (GtkWidget *widget,
135 static gboolean cb_button_press (GtkWidget *widget,
136 GdkEventButton *event,
138 static gboolean cb_button_release (GtkWidget *widget,
139 GdkEventButton *event,
141 static void cb_dock_grab_notify (GtkWidget *widget,
142 gboolean was_grabbed,
144 static gboolean cb_dock_grab_broken_event (GtkWidget *widget,
145 gboolean was_grabbed,
147 static void cb_scale_grab_notify (GtkWidget *widget,
148 gboolean was_grabbed,
150 static void gtk_scale_button_update_icon (GtkScaleButton *button);
151 static void gtk_scale_button_scale_value_changed(GtkRange *range);
153 /* see below for scale definitions */
154 static GtkWidget *gtk_scale_button_scale_new (GtkScaleButton *button,
159 static guint signals[LAST_SIGNAL] = { 0, };
161 G_DEFINE_TYPE (GtkScaleButton, gtk_scale_button, GTK_TYPE_BUTTON)
164 gtk_scale_button_class_init (GtkScaleButtonClass *klass)
166 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
167 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
168 GtkBindingSet *binding_set;
170 g_type_class_add_private (klass, sizeof (GtkScaleButtonPrivate));
172 gobject_class->constructor = gtk_scale_button_constructor;
173 gobject_class->finalize = gtk_scale_button_finalize;
174 gobject_class->dispose = gtk_scale_button_dispose;
175 gobject_class->set_property = gtk_scale_button_set_property;
176 gobject_class->get_property = gtk_scale_button_get_property;
178 widget_class->button_press_event = gtk_scale_button_press;
179 widget_class->key_release_event = gtk_scale_button_key_release;
180 widget_class->scroll_event = gtk_scale_button_scroll;
181 widget_class->screen_changed = gtk_scale_button_screen_changed;
183 g_object_class_install_property (gobject_class,
185 g_param_spec_enum ("orientation",
187 P_("The orientation of the scale"),
188 GTK_TYPE_ORIENTATION,
189 GTK_ORIENTATION_VERTICAL,
190 GTK_PARAM_READWRITE |
191 G_PARAM_CONSTRUCT_ONLY));
193 g_object_class_install_property (gobject_class,
195 g_param_spec_double ("value",
197 P_("The value of the scale"),
201 GTK_PARAM_READWRITE));
203 g_object_class_install_property (gobject_class,
205 g_param_spec_enum ("size",
209 GTK_ICON_SIZE_SMALL_TOOLBAR,
210 GTK_PARAM_READWRITE));
212 g_object_class_install_property (gobject_class,
214 g_param_spec_object ("adjustment",
216 P_("The GtkAdjustment that contains the current value of this scale button object"),
218 GTK_PARAM_READWRITE));
221 * GtkScaleButton:icons:
223 * The names of the icons to be used by the scale button.
224 * The first item in the array will be used in the button
225 * when the current value is the lowest value, the second
226 * item for the highest value. All the subsequent icons will
227 * be used for all the other values, spread evenly over the
230 * If there's only one icon name in the @icons array, it will
231 * be used for all the values. If only two icon names are in
232 * the @icons array, the first one will be used for the bottom
233 * 50% of the scale, and the second one for the top 50%.
235 * It is recommended to use at least 3 icons so that the
236 * #GtkScaleButton reflects the current value of the scale
237 * better for the users.
241 g_object_class_install_property (gobject_class,
243 g_param_spec_boxed ("icons",
245 P_("List of icon names"),
247 GTK_PARAM_READWRITE));
250 * GtkScaleButton::value-changed:
251 * @button: the object which received the signal
252 * @value: the new value
254 * The ::value-changed signal is emitted when the value field has
259 signals[VALUE_CHANGED] =
260 g_signal_new (I_("value-changed"),
261 G_TYPE_FROM_CLASS (klass),
263 G_STRUCT_OFFSET (GtkScaleButtonClass, value_changed),
265 _gtk_marshal_VOID__DOUBLE,
266 G_TYPE_NONE, 1, G_TYPE_DOUBLE);
269 * GtkScaleButton::popup:
270 * @button: the object which received the signal
272 * The ::popup signal is a
273 * <link linkend="keybinding-signals">keybinding signal</link>
274 * which gets emitted to popup the scale widget.
276 * The default bindings for this signal are Space, Enter and Return.
281 _gtk_binding_signal_new (I_("popup"),
282 G_OBJECT_CLASS_TYPE (klass),
283 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
284 G_CALLBACK (gtk_scale_button_popup_from_bindings),
286 g_cclosure_marshal_VOID__VOID,
290 * GtkScaleButton::popdown:
291 * @button: the object which received the signal
293 * The ::popdown signal is a
294 * <link linkend="keybinding-signals">keybinding signal</link>
295 * which gets emitted to popdown the scale widget.
297 * The default binding for this signal is Escape.
302 _gtk_binding_signal_new (I_("popdown"),
303 G_OBJECT_CLASS_TYPE (klass),
304 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
305 G_CALLBACK (gtk_scale_button_popdown_from_bindings),
307 g_cclosure_marshal_VOID__VOID,
311 binding_set = gtk_binding_set_by_class (widget_class);
313 gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
315 gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, 0,
317 gtk_binding_entry_add_signal (binding_set, GDK_Return, 0,
319 gtk_binding_entry_add_signal (binding_set, GDK_ISO_Enter, 0,
321 gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0,
323 gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
328 gtk_scale_button_init (GtkScaleButton *button)
330 GtkScaleButtonPrivate *priv;
332 button->priv = priv = GET_PRIVATE (button);
334 priv->timeout = FALSE;
336 priv->click_timeout = CLICK_TIMEOUT;
338 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
339 gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
342 priv->image = gtk_image_new ();
343 gtk_container_add (GTK_CONTAINER (button), priv->image);
344 gtk_widget_show_all (priv->image);
347 priv->dock = gtk_window_new (GTK_WINDOW_POPUP);
348 g_signal_connect (priv->dock, "button-press-event",
349 G_CALLBACK (cb_dock_button_press), button);
350 g_signal_connect (priv->dock, "key-release-event",
351 G_CALLBACK (cb_dock_key_release), button);
352 g_signal_connect (priv->dock, "grab-notify",
353 G_CALLBACK (cb_dock_grab_notify), button);
354 g_signal_connect (priv->dock, "grab-broken-event",
355 G_CALLBACK (cb_dock_grab_broken_event), button);
356 gtk_window_set_decorated (GTK_WINDOW (priv->dock), FALSE);
359 button->plus_button = gtk_button_new_with_label ("+");
360 gtk_button_set_relief (GTK_BUTTON (button->plus_button), GTK_RELIEF_NONE);
361 g_signal_connect (button->plus_button, "button-press-event",
362 G_CALLBACK (cb_button_press), button);
363 g_signal_connect (button->plus_button, "button-release-event",
364 G_CALLBACK (cb_button_release), button);
367 button->minus_button = gtk_button_new_with_label ("-");
368 gtk_button_set_relief (GTK_BUTTON (button->minus_button), GTK_RELIEF_NONE);
369 g_signal_connect (button->minus_button, "button-press-event",
370 G_CALLBACK (cb_button_press), button);
371 g_signal_connect (button->minus_button, "button-release-event",
372 G_CALLBACK (cb_button_release), button);
374 priv->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 100.0, 2, 20, 0));
375 g_object_ref_sink (priv->adjustment);
379 gtk_scale_button_constructor (GType type,
380 guint n_construct_properties,
381 GObjectConstructParam *construct_params)
384 GtkScaleButton *button;
385 GtkWidget *frame, *box;
386 GtkScaleButtonPrivate *priv;
388 object = G_OBJECT_CLASS (gtk_scale_button_parent_class)->constructor (type, n_construct_properties, construct_params);
390 button = GTK_SCALE_BUTTON (object);
395 frame = gtk_frame_new (NULL);
396 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
397 gtk_container_add (GTK_CONTAINER (priv->dock), frame);
398 box = gtk_vbox_new (FALSE, 0);
399 gtk_container_add (GTK_CONTAINER (frame), box);
402 gtk_box_pack_start (GTK_BOX (box), button->plus_button, TRUE, FALSE, 0);
405 priv->scale = gtk_scale_button_scale_new (button, 0., 100., 2.);
406 if (priv->orientation == GTK_ORIENTATION_VERTICAL)
408 gtk_widget_set_size_request (priv->scale, -1, SCALE_SIZE);
409 gtk_range_set_inverted (GTK_RANGE (priv->scale), TRUE);
413 gtk_widget_set_size_request (priv->scale, SCALE_SIZE, -1);
416 gtk_scale_set_draw_value (GTK_SCALE (priv->scale), FALSE);
417 gtk_box_pack_start (GTK_BOX (box), priv->scale, TRUE, FALSE, 0);
418 g_signal_connect (priv->scale, "grab-notify",
419 G_CALLBACK (cb_scale_grab_notify), button);
422 gtk_box_pack_start (GTK_BOX (box), button->minus_button, TRUE, FALSE, 0);
424 /* set button text and size */
425 priv->size = GTK_ICON_SIZE_SMALL_TOOLBAR;
426 gtk_scale_button_update_icon (button);
432 gtk_scale_button_set_property (GObject *object,
437 GtkScaleButton *button;
439 button = GTK_SCALE_BUTTON (object);
443 case PROP_ORIENTATION:
444 button->priv->orientation = g_value_get_enum (value);
447 gtk_scale_button_set_value (button, g_value_get_double (value));
452 size = g_value_get_enum (value);
453 if (button->priv->size != size)
455 button->priv->size = size;
456 gtk_scale_button_update_icon (button);
460 case PROP_ADJUSTMENT:
461 gtk_scale_button_set_adjustment (button, g_value_get_object (value));
464 gtk_scale_button_set_icons (button,
465 (const gchar **)g_value_get_boxed (value));
468 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
474 gtk_scale_button_get_property (GObject *object,
479 GtkScaleButton *button;
480 GtkScaleButtonPrivate *priv;
482 button = GTK_SCALE_BUTTON (object);
487 case PROP_ORIENTATION:
488 g_value_set_enum (value, priv->orientation);
491 g_value_set_double (value, gtk_scale_button_get_value (button));
494 g_value_set_enum (value, priv->size);
496 case PROP_ADJUSTMENT:
497 g_value_set_object (value, gtk_scale_button_get_adjustment (button));
500 g_value_set_boxed (value, priv->icon_list);
503 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
509 gtk_scale_button_finalize (GObject *object)
511 GtkScaleButton *button = GTK_SCALE_BUTTON (object);
512 GtkScaleButtonPrivate *priv = button->priv;
516 g_strfreev (priv->icon_list);
517 priv->icon_list = NULL;
520 if (priv->adjustment)
522 g_object_unref (priv->adjustment);
523 priv->adjustment = NULL;
526 G_OBJECT_CLASS (gtk_scale_button_parent_class)->finalize (object);
530 gtk_scale_button_dispose (GObject *object)
532 GtkScaleButton *button = GTK_SCALE_BUTTON (object);
533 GtkScaleButtonPrivate *priv = button->priv;
537 gtk_widget_destroy (priv->dock);
541 if (priv->click_id != 0)
543 g_source_remove (priv->click_id);
547 G_OBJECT_CLASS (gtk_scale_button_parent_class)->dispose (object);
551 * gtk_scale_button_new:
552 * @size: a stock icon size
553 * @min: the minimum value of the scale (usually 0)
554 * @max: the maximum value of the scale (usually 100)
555 * @step: the stepping of value when a scroll-wheel event,
556 * or up/down arrow event occurs (usually 2)
557 * @icons: a %NULL-terminated array of icon names, or %NULL if
558 * you want to set the list later with gtk_scale_button_set_icons()
560 * Creates a #GtkScaleButton, with a range between @min and @max, with
561 * a stepping of @step.
563 * Return value: a new #GtkScaleButton
568 gtk_scale_button_new (GtkIconSize size,
574 GtkScaleButton *button;
577 adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
579 button = g_object_new (GTK_TYPE_SCALE_BUTTON,
585 return GTK_WIDGET (button);
589 * gtk_scale_button_get_value:
590 * @button: a #GtkScaleButton
592 * Gets the current value of the scale button.
594 * Return value: current value of the scale button
599 gtk_scale_button_get_value (GtkScaleButton * button)
601 GtkScaleButtonPrivate *priv;
603 g_return_val_if_fail (GTK_IS_SCALE_BUTTON (button), 0);
607 return gtk_adjustment_get_value (priv->adjustment);
611 * gtk_scale_button_set_value:
612 * @button: a #GtkScaleButton
613 * @value: new value of the scale button
615 * Sets the current value of the scale; if the value is outside
616 * the minimum or maximum range values, it will be clamped to fit
617 * inside them. The scale button emits the #GtkScaleButton::value-changed
618 * signal if the value changes.
623 gtk_scale_button_set_value (GtkScaleButton *button,
626 GtkScaleButtonPrivate *priv;
628 g_return_if_fail (GTK_IS_SCALE_BUTTON (button));
632 gtk_range_set_value (GTK_RANGE (priv->scale), value);
636 * gtk_scale_button_set_icons:
637 * @button: a #GtkScaleButton
638 * @icons: a %NULL-terminated array of icon names
640 * Sets the icons to be used by the scale button.
641 * For details, see the #GtkScaleButton:icons property.
646 gtk_scale_button_set_icons (GtkScaleButton *button,
649 GtkScaleButtonPrivate *priv;
652 g_return_if_fail (GTK_IS_SCALE_BUTTON (button));
656 tmp = priv->icon_list;
657 priv->icon_list = g_strdupv ((gchar **) icons);
659 gtk_scale_button_update_icon (button);
661 g_object_notify (G_OBJECT (button), "icons");
665 * gtk_scale_button_get_adjustment:
666 * @button: a #GtkScaleButton
668 * Gets the #GtkAdjustment associated with the #GtkScaleButton's scale.
669 * See gtk_range_get_adjustment() for details.
671 * Returns: the adjustment associated with the scale
676 gtk_scale_button_get_adjustment (GtkScaleButton *button)
678 g_return_val_if_fail (GTK_IS_SCALE_BUTTON (button), NULL);
680 return button->priv->adjustment;
684 * gtk_scale_button_set_adjustment:
685 * @button: a #GtkScaleButton
686 * @adjustment: a #GtkAdjustment
688 * Sets the #GtkAdjustment to be used as a model
689 * for the #GtkScaleButton's scale.
690 * See gtk_range_set_adjustment() for details.
695 gtk_scale_button_set_adjustment (GtkScaleButton *button,
696 GtkAdjustment *adjustment)
698 g_return_if_fail (GTK_IS_SCALE_BUTTON (button));
699 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
701 if (button->priv->adjustment)
702 g_object_unref (button->priv->adjustment);
703 button->priv->adjustment = g_object_ref_sink (adjustment);
705 if (button->priv->scale)
706 gtk_range_set_adjustment (GTK_RANGE (button->priv->scale), adjustment);
708 g_object_notify (G_OBJECT (button), "adjustment");
716 gtk_scale_button_scroll (GtkWidget *widget,
717 GdkEventScroll *event)
719 GtkScaleButton *button;
720 GtkScaleButtonPrivate *priv;
724 button = GTK_SCALE_BUTTON (widget);
726 adj = priv->adjustment;
728 if (event->type != GDK_SCROLL)
731 d = gtk_scale_button_get_value (button);
732 if (event->direction == GDK_SCROLL_UP)
734 d += adj->step_increment;
740 d -= adj->step_increment;
744 gtk_scale_button_set_value (button, d);
750 gtk_scale_button_screen_changed (GtkWidget *widget,
751 GdkScreen *previous_screen)
753 GtkScaleButton *button = (GtkScaleButton *) widget;
754 GtkScaleButtonPrivate *priv;
756 GValue value = { 0, };
758 if (gtk_widget_has_screen (widget) == FALSE)
763 screen = gtk_widget_get_screen (widget);
764 g_value_init (&value, G_TYPE_INT);
765 if (gdk_screen_get_setting (screen,
766 "gtk-double-click-time",
769 priv->click_timeout = CLICK_TIMEOUT;
773 priv->click_timeout = g_value_get_int (&value);
777 gtk_scale_popup (GtkWidget *widget,
781 GtkScaleButton *button;
782 GtkScaleButtonPrivate *priv;
784 gint x, y, m, dx, dy, sx, sy, startoff;
789 button = GTK_SCALE_BUTTON (widget);
791 adj = priv->adjustment;
793 display = gtk_widget_get_display (widget);
794 screen = gtk_widget_get_screen (widget);
796 /* position roughly */
797 gtk_window_set_screen (GTK_WINDOW (priv->dock), screen);
799 gdk_window_get_origin (widget->window, &x, &y);
800 x += widget->allocation.x;
801 y += widget->allocation.y;
803 if (priv->orientation == GTK_ORIENTATION_VERTICAL)
804 gtk_window_move (GTK_WINDOW (priv->dock), x, y - (SCALE_SIZE / 2));
806 gtk_window_move (GTK_WINDOW (priv->dock), x - (SCALE_SIZE / 2), y);
808 gtk_widget_show_all (priv->dock);
810 gdk_window_get_origin (priv->dock->window, &dx, &dy);
811 dx += priv->dock->allocation.x;
812 dy += priv->dock->allocation.y;
814 gdk_window_get_origin (priv->scale->window, &sx, &sy);
815 sx += priv->scale->allocation.x;
816 sy += priv->scale->allocation.y;
818 priv->timeout = TRUE;
820 /* position (needs widget to be shown already) */
821 v = gtk_scale_button_get_value (button) / (adj->upper - adj->lower);
823 if (priv->orientation == GTK_ORIENTATION_VERTICAL)
827 x += (widget->allocation.width - priv->dock->allocation.width) / 2;
829 y -= GTK_RANGE (priv->scale)->min_slider_size / 2;
830 m = priv->scale->allocation.height -
831 GTK_RANGE (priv->scale)->min_slider_size;
839 y += (widget->allocation.height - priv->dock->allocation.height) / 2;
840 x -= GTK_RANGE (priv->scale)->min_slider_size / 2;
841 m = priv->scale->allocation.width -
842 GTK_RANGE (priv->scale)->min_slider_size;
846 /* Make sure the dock stays inside the monitor */
847 if (event->type == GDK_BUTTON_PRESS)
850 GdkEventButton *button_event = (GdkEventButton *) event;
854 d = GTK_WIDGET (priv->dock);
855 monitor = gdk_screen_get_monitor_at_point (screen,
856 button_event->x_root,
857 button_event->y_root);
858 gdk_screen_get_monitor_geometry (screen, monitor, &rect);
860 if (priv->orientation == GTK_ORIENTATION_VERTICAL)
861 y += button_event->y;
863 x += button_event->x;
867 else if (y + d->allocation.height > rect.height + rect.y)
868 y = rect.y + rect.height - d->allocation.height;
872 else if (x + d->allocation.width > rect.width + rect.x)
873 x = rect.x + rect.width - d->allocation.width;
876 gtk_window_move (GTK_WINDOW (priv->dock), x, y);
878 if (event->type == GDK_BUTTON_PRESS)
879 GTK_WIDGET_CLASS (gtk_scale_button_parent_class)->button_press_event (widget, (GdkEventButton *) event);
882 gtk_grab_add (priv->dock);
884 if (gdk_pointer_grab (priv->dock->window, TRUE,
885 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
886 GDK_POINTER_MOTION_MASK, NULL, NULL, time)
889 gtk_grab_remove (priv->dock);
890 gtk_widget_hide (priv->dock);
894 if (gdk_keyboard_grab (priv->dock->window, TRUE, time) != GDK_GRAB_SUCCESS)
896 gdk_display_pointer_ungrab (display, time);
897 gtk_grab_remove (priv->dock);
898 gtk_widget_hide (priv->dock);
902 gtk_widget_grab_focus (priv->dock);
904 if (event->type == GDK_BUTTON_PRESS)
907 GdkEventButton *button_event = (GdkEventButton *) event;
909 /* forward event to the slider */
910 e = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
911 e->window = priv->scale->window;
913 /* position: the X position isn't relevant, halfway will work just fine.
914 * The vertical position should be *exactly* in the middle of the slider
915 * of the scale; if we don't do that correctly, it'll move from its current
916 * position, which means a position change on-click, which is bad.
918 if (priv->orientation == GTK_ORIENTATION_VERTICAL)
920 e->x = priv->scale->allocation.width / 2;
921 m = priv->scale->allocation.height -
922 GTK_RANGE (priv->scale)->min_slider_size;
923 e->y = ((1.0 - v) * m) + GTK_RANGE (priv->scale)->min_slider_size / 2;
927 e->y = priv->scale->allocation.height / 2;
928 m = priv->scale->allocation.width -
929 GTK_RANGE (priv->scale)->min_slider_size;
930 e->x = (v * m) + GTK_RANGE (priv->scale)->min_slider_size / 2;
933 gtk_widget_event (priv->scale, (GdkEvent *) e);
934 e->window = button_event->window;
935 gdk_event_free ((GdkEvent *) e);
938 gtk_widget_grab_focus (priv->scale);
940 priv->pop_time = time;
946 gtk_scale_button_press (GtkWidget *widget,
947 GdkEventButton *event)
949 return gtk_scale_popup (widget, (GdkEvent *) event, event->time);
953 gtk_scale_button_popup_from_bindings (GtkWidget *widget)
957 ev = gdk_event_new (GDK_KEY_RELEASE);
958 gtk_scale_popup (widget, ev, GDK_CURRENT_TIME);
963 gtk_scale_button_key_release (GtkWidget *widget,
966 return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
969 /* This is called when the grab is broken for
970 * either the dock, or the scale itself */
972 gtk_scale_button_grab_notify (GtkScaleButton *button,
973 gboolean was_grabbed)
976 GtkScaleButtonPrivate *priv;
978 if (was_grabbed != FALSE)
983 if (!GTK_WIDGET_HAS_GRAB (priv->dock))
986 if (gtk_widget_is_ancestor (gtk_grab_get_current (), priv->dock))
989 display = gtk_widget_get_display (priv->dock);
990 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
991 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
992 gtk_grab_remove (priv->dock);
995 gtk_widget_hide (priv->dock);
996 priv->timeout = FALSE;
1000 * +/- button callbacks.
1004 cb_button_timeout (gpointer user_data)
1006 GtkScaleButton *button;
1007 GtkScaleButtonPrivate *priv;
1010 gboolean res = TRUE;
1012 button = GTK_SCALE_BUTTON (user_data);
1013 priv = button->priv;
1015 if (priv->click_id == 0)
1018 adj = priv->adjustment;
1020 val = gtk_scale_button_get_value (button);
1021 val += priv->direction;
1022 if (val <= adj->lower)
1027 else if (val > adj->upper)
1032 gtk_scale_button_set_value (button, val);
1036 g_source_remove (priv->click_id);
1044 cb_button_press (GtkWidget *widget,
1045 GdkEventButton *event,
1048 GtkScaleButton *button;
1049 GtkScaleButtonPrivate *priv;
1052 button = GTK_SCALE_BUTTON (user_data);
1053 priv = button->priv;
1054 adj = priv->adjustment;
1056 if (priv->click_id != 0)
1057 g_source_remove (priv->click_id);
1059 if (widget == button->plus_button)
1060 priv->direction = fabs (adj->page_increment);
1062 priv->direction = - fabs (adj->page_increment);
1064 priv->click_id = gdk_threads_add_timeout (priv->click_timeout,
1067 cb_button_timeout (button);
1073 cb_button_release (GtkWidget *widget,
1074 GdkEventButton *event,
1077 GtkScaleButton *button;
1078 GtkScaleButtonPrivate *priv;
1080 button = GTK_SCALE_BUTTON (user_data);
1081 priv = button->priv;
1083 if (priv->click_id != 0)
1085 g_source_remove (priv->click_id);
1093 cb_dock_grab_notify (GtkWidget *widget,
1094 gboolean was_grabbed,
1097 GtkScaleButton *button = (GtkScaleButton *) user_data;
1099 gtk_scale_button_grab_notify (button, was_grabbed);
1103 cb_dock_grab_broken_event (GtkWidget *widget,
1104 gboolean was_grabbed,
1107 GtkScaleButton *button = (GtkScaleButton *) user_data;
1109 gtk_scale_button_grab_notify (button, FALSE);
1119 gtk_scale_button_release_grab (GtkScaleButton *button,
1120 GdkEventButton *event)
1123 GdkDisplay *display;
1124 GtkScaleButtonPrivate *priv;
1126 priv = button->priv;
1129 display = gtk_widget_get_display (GTK_WIDGET (button));
1130 gdk_display_keyboard_ungrab (display, event->time);
1131 gdk_display_pointer_ungrab (display, event->time);
1132 gtk_grab_remove (priv->dock);
1135 gtk_widget_hide (priv->dock);
1136 priv->timeout = FALSE;
1138 e = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1139 e->window = GTK_WIDGET (button)->window;
1140 e->type = GDK_BUTTON_RELEASE;
1141 gtk_widget_event (GTK_WIDGET (button), (GdkEvent *) e);
1142 e->window = event->window;
1143 gdk_event_free ((GdkEvent *) e);
1147 cb_dock_button_press (GtkWidget *widget,
1148 GdkEventButton *event,
1151 GtkScaleButton *button = GTK_SCALE_BUTTON (user_data);
1153 if (event->type == GDK_BUTTON_PRESS)
1155 gtk_scale_button_release_grab (button, event);
1163 gtk_scale_button_popdown_from_bindings (GtkWidget *widget)
1165 GtkScaleButton *button;
1166 GtkScaleButtonPrivate *priv;
1167 GdkDisplay *display;
1169 button = GTK_SCALE_BUTTON (widget);
1170 priv = button->priv;
1173 display = gtk_widget_get_display (widget);
1174 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
1175 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
1176 gtk_grab_remove (priv->dock);
1179 gtk_widget_hide (priv->dock);
1180 priv->timeout = FALSE;
1184 cb_dock_key_release (GtkWidget *widget,
1188 if (event->keyval == GDK_Escape)
1190 gtk_scale_button_popdown_from_bindings (GTK_WIDGET (user_data));
1194 if (!gtk_bindings_activate_event (GTK_OBJECT (widget), event))
1196 /* The popup hasn't managed the event, pass onto the button */
1197 gtk_bindings_activate_event (GTK_OBJECT (user_data), event);
1204 cb_scale_grab_notify (GtkWidget *widget,
1205 gboolean was_grabbed,
1208 GtkScaleButton *button = (GtkScaleButton *) user_data;
1210 gtk_scale_button_grab_notify (button, was_grabbed);
1217 #define GTK_TYPE_SCALE_BUTTON_VSCALE (gtk_scale_button_vscale_get_type ())
1218 #define GTK_SCALE_BUTTON_VSCALE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SCALE_BUTTON_VSCALE, GtkScaleButtonVScale))
1219 #define GTK_IS_SCALE_BUTTON_VSCALE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SCALE_BUTTON_VSCALE))
1221 typedef struct _GtkScaleButtonVScale {
1223 GtkScaleButton *button;
1224 } GtkScaleButtonVScale;
1226 typedef struct _GtkScaleButtonVScaleClass {
1227 GtkVScaleClass parent_class;
1228 } GtkScaleButtonVScaleClass;
1230 #define GTK_TYPE_SCALE_BUTTON_HSCALE (gtk_scale_button_hscale_get_type ())
1231 #define GTK_SCALE_BUTTON_HSCALE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SCALE_BUTTON_HSCALE, GtkScaleButtonHScale))
1232 #define GTK_IS_SCALE_BUTTON_HSCALE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SCALE_BUTTON_HSCALE))
1234 typedef struct _GtkScaleButtonHScale {
1236 GtkScaleButton *button;
1237 } GtkScaleButtonHScale;
1239 typedef struct _GtkScaleButtonHScaleClass {
1240 GtkVScaleClass parent_class;
1241 } GtkScaleButtonHScaleClass;
1243 static gboolean gtk_scale_button_scale_press (GtkWidget *widget,
1244 GdkEventButton *event);
1245 static gboolean gtk_scale_button_scale_release (GtkWidget *widget,
1246 GdkEventButton *event);
1248 G_DEFINE_TYPE (GtkScaleButtonVScale, gtk_scale_button_vscale, GTK_TYPE_VSCALE)
1250 G_DEFINE_TYPE (GtkScaleButtonHScale, gtk_scale_button_hscale, GTK_TYPE_HSCALE)
1253 gtk_scale_button_vscale_class_init (GtkScaleButtonVScaleClass *klass)
1255 GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (klass);
1256 GtkRangeClass *gtkrange_class = GTK_RANGE_CLASS (klass);
1258 gtkwidget_class->button_press_event = gtk_scale_button_scale_press;
1259 gtkwidget_class->button_release_event = gtk_scale_button_scale_release;
1260 gtkrange_class->value_changed = gtk_scale_button_scale_value_changed;
1264 gtk_scale_button_hscale_class_init (GtkScaleButtonHScaleClass *klass)
1266 GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (klass);
1267 GtkRangeClass *gtkrange_class = GTK_RANGE_CLASS (klass);
1269 gtkwidget_class->button_press_event = gtk_scale_button_scale_press;
1270 gtkwidget_class->button_release_event = gtk_scale_button_scale_release;
1271 gtkrange_class->value_changed = gtk_scale_button_scale_value_changed;
1275 gtk_scale_button_vscale_init (GtkScaleButtonVScale *scale)
1280 gtk_scale_button_hscale_init (GtkScaleButtonHScale *scale)
1285 gtk_scale_button_scale_new (GtkScaleButton *button,
1290 GtkScaleButtonPrivate *priv = button->priv;
1291 GtkScaleButtonVScale *scale;
1293 if (priv->orientation == GTK_ORIENTATION_VERTICAL)
1294 scale = g_object_new (GTK_TYPE_SCALE_BUTTON_VSCALE, NULL);
1296 scale = g_object_new (GTK_TYPE_SCALE_BUTTON_HSCALE, NULL);
1298 gtk_range_set_adjustment (GTK_RANGE (scale),
1299 GTK_ADJUSTMENT (priv->adjustment));
1301 scale->button = button;
1303 return GTK_WIDGET (scale);
1307 gtk_scale_button_scale_press (GtkWidget *widget,
1308 GdkEventButton *event)
1310 GtkScaleButtonPrivate *priv;
1311 GtkWidgetClass *widget_class;
1313 if (GTK_IS_SCALE_BUTTON_VSCALE (widget))
1315 priv = GTK_SCALE_BUTTON_VSCALE (widget)->button->priv;
1316 widget_class = GTK_WIDGET_CLASS (gtk_scale_button_vscale_parent_class);
1320 priv = GTK_SCALE_BUTTON_HSCALE (widget)->button->priv;
1321 widget_class = GTK_WIDGET_CLASS (gtk_scale_button_hscale_parent_class);
1324 /* the scale will grab input; if we have input grabbed, all goes
1325 * horribly wrong, so let's not do that. */
1326 gtk_grab_remove (priv->dock);
1328 return widget_class->button_press_event (widget, event);
1332 gtk_scale_button_scale_release (GtkWidget *widget,
1333 GdkEventButton *event)
1335 GtkScaleButton *button;
1336 GtkWidgetClass *widget_class;
1339 if (GTK_IS_SCALE_BUTTON_VSCALE (widget))
1341 button = GTK_SCALE_BUTTON_VSCALE (widget)->button;
1342 widget_class = GTK_WIDGET_CLASS (gtk_scale_button_vscale_parent_class);
1346 button = GTK_SCALE_BUTTON_HSCALE (widget)->button;
1347 widget_class = GTK_WIDGET_CLASS (gtk_scale_button_hscale_parent_class);
1350 if (button->priv->timeout)
1352 /* if we did a quick click, leave the window open; else, hide it */
1353 if (event->time > button->priv->pop_time + button->priv->click_timeout)
1356 gtk_scale_button_release_grab (button, event);
1357 widget_class->button_release_event (widget, event);
1362 button->priv->timeout = FALSE;
1365 res = widget_class->button_release_event (widget, event);
1367 /* the scale will release input; right after that, we *have to* grab
1368 * it back so we can catch out-of-scale clicks and hide the popup,
1369 * so I basically want a g_signal_connect_after_always(), but I can't
1370 * find that, so we do this complex 'first-call-parent-then-do-actual-
1373 gtk_grab_add (button->priv->dock);
1379 gtk_scale_button_update_icon (GtkScaleButton *button)
1381 GtkScaleButtonPrivate *priv;
1388 priv = button->priv;
1390 if (!priv->icon_list || priv->icon_list[0] == '\0')
1392 gtk_image_set_from_stock (GTK_IMAGE (priv->image),
1393 GTK_STOCK_MISSING_IMAGE,
1398 num_icons = g_strv_length (priv->icon_list);
1400 /* The 1-icon special case */
1403 gtk_image_set_from_icon_name (GTK_IMAGE (priv->image),
1409 range = GTK_RANGE (priv->scale);
1410 adj = priv->adjustment;
1411 value = gtk_scale_button_get_value (button);
1413 /* The 2-icons special case */
1417 limit = (adj->upper - adj->lower) / 2 + adj->lower;
1419 name = priv->icon_list[0];
1421 name = priv->icon_list[1];
1423 gtk_image_set_from_icon_name (GTK_IMAGE (priv->image),
1429 /* With 3 or more icons */
1430 if (value == adj->lower)
1432 name = priv->icon_list[0];
1434 else if (value == adj->upper)
1436 name = priv->icon_list[1];
1443 step = (adj->upper - adj->lower) / (num_icons - 2);
1444 i = (guint) ((value - adj->lower) / step) + 2;
1445 g_assert (i < num_icons);
1446 name = priv->icon_list[i];
1449 gtk_image_set_from_icon_name (GTK_IMAGE (priv->image),
1455 gtk_scale_button_scale_value_changed (GtkRange *range)
1457 GtkScaleButton *button;
1460 if (GTK_IS_SCALE_BUTTON_VSCALE (range))
1461 button = GTK_SCALE_BUTTON_VSCALE (range)->button;
1463 button = GTK_SCALE_BUTTON_HSCALE (range)->button;
1465 value = gtk_range_get_value (range);
1467 gtk_scale_button_update_icon (button);
1469 g_signal_emit (button, signals[VALUE_CHANGED], 0, value);
1470 g_object_notify (G_OBJECT (button), "value");
1473 #define __GTK_SCALE_BUTTON_C__
1474 #include "gtkaliasdef.c"