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/.
46 #include "gtkbindings.h"
48 #include "gtkvscale.h"
51 #include "gtkwindow.h"
52 #include "gtkmarshalers.h"
55 #include <gdk-pixbuf/gdk-pixbuf.h>
56 #include <gdk/gdkkeysyms.h>
57 #include "gtkscalebutton.h"
59 #define SCALE_SIZE 100
60 #define CLICK_TIMEOUT 250
76 struct _GtkScaleButtonPrivate {
77 GtkWidget *dock, *scale, *image;
85 } _GtkScaleButtonPrivate;
87 static void gtk_scale_button_class_init (GtkScaleButtonClass *klass);
88 static void gtk_scale_button_init (GtkScaleButton *button);
89 static void gtk_scale_button_dispose (GObject *object);
90 static void gtk_scale_button_set_property (GObject *object,
94 static void gtk_scale_button_get_property (GObject *object,
98 static gboolean gtk_scale_button_scroll (GtkWidget *widget,
99 GdkEventScroll *event);
100 static void gtk_scale_button_screen_changed (GtkWidget *widget,
101 GdkScreen *previous_screen);
102 static gboolean gtk_scale_button_press (GtkWidget *widget,
103 GdkEventButton *event);
104 static gboolean gtk_scale_button_key_release (GtkWidget *widget,
106 static void gtk_scale_button_popup_from_bindings (GtkWidget *widget);
107 static void gtk_scale_button_popdown_from_bindings (GtkWidget *widget);
108 static gboolean cb_dock_button_press (GtkWidget *widget,
109 GdkEventButton *event,
111 static gboolean cb_dock_key_release (GtkWidget *widget,
114 static gboolean cb_button_press (GtkWidget *widget,
115 GdkEventButton *event,
117 static gboolean cb_button_release (GtkWidget *widget,
118 GdkEventButton *event,
120 static void cb_dock_grab_notify (GtkWidget *widget,
121 gboolean was_grabbed,
123 static gboolean cb_dock_grab_broken_event (GtkWidget *widget,
124 gboolean was_grabbed,
126 static void cb_scale_grab_notify (GtkWidget *widget,
127 gboolean was_grabbed,
129 static void gtk_scale_button_update_icon (GtkScaleButton *button);
130 static void gtk_scale_button_scale_value_changed(GtkRange *range);
132 /* see below for scale definitions */
133 static GtkWidget *gtk_scale_button_scale_new (GtkScaleButton *button,
138 static GtkButtonClass *parent_class = NULL;
139 static guint signals[LAST_SIGNAL] = { 0 };
141 G_DEFINE_TYPE (GtkScaleButton, gtk_scale_button, GTK_TYPE_BUTTON)
144 gtk_scale_button_class_init (GtkScaleButtonClass *klass)
146 GtkBindingSet *binding_set;
147 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
148 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
150 parent_class = g_type_class_peek_parent (klass);
152 gobject_class->dispose = gtk_scale_button_dispose;
153 gobject_class->set_property = gtk_scale_button_set_property;
154 gobject_class->get_property = gtk_scale_button_get_property;
155 widget_class->button_press_event = gtk_scale_button_press;
156 widget_class->key_release_event = gtk_scale_button_key_release;
157 widget_class->scroll_event = gtk_scale_button_scroll;
158 widget_class->screen_changed = gtk_scale_button_screen_changed;
160 g_object_class_install_property (gobject_class,
162 g_param_spec_double ("value",
164 P_("The value of the scale"),
170 g_object_class_install_property (gobject_class,
172 g_param_spec_enum ("size",
176 GTK_ICON_SIZE_SMALL_TOOLBAR,
179 g_object_class_install_property (gobject_class,
181 g_param_spec_object ("adjustment",
183 P_("The GtkAdjustment that contains the current value of this scale button object"),
188 * GtkScaleButton::value-changed:
189 * @button: the object that received the signal
191 * Emitted when the value field has changed.
196 signals[VALUE_CHANGED] =
197 g_signal_new (I_("value-changed"),
198 G_TYPE_FROM_CLASS (klass),
200 G_STRUCT_OFFSET (GtkScaleButtonClass, value_changed),
202 _gtk_marshal_VOID__DOUBLE,
203 G_TYPE_NONE, 1, G_TYPE_DOUBLE);
205 _gtk_binding_signal_new (I_("popup"),
206 G_OBJECT_CLASS_TYPE (klass),
207 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
208 G_CALLBACK (gtk_scale_button_popup_from_bindings),
210 g_cclosure_marshal_VOID__VOID,
213 _gtk_binding_signal_new (I_("popdown"),
214 G_OBJECT_CLASS_TYPE (klass),
215 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
216 G_CALLBACK (gtk_scale_button_popdown_from_bindings),
218 g_cclosure_marshal_VOID__VOID,
222 binding_set = gtk_binding_set_by_class (widget_class);
224 gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
226 gtk_binding_entry_add_signal (binding_set, GDK_Return, 0,
228 gtk_binding_entry_add_signal (binding_set, GDK_ISO_Enter, 0,
230 gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0,
232 gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
237 gtk_scale_button_init (GtkScaleButton *button)
239 GtkWidget *frame, *box;
240 GtkScaleButtonPrivate *priv;
242 button->priv = g_new0 (GtkScaleButtonPrivate, 1);
245 priv->timeout = FALSE;
247 priv->click_timeout = CLICK_TIMEOUT;
249 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
250 gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
253 priv->image = gtk_image_new ();
254 gtk_container_add (GTK_CONTAINER (button), priv->image);
255 gtk_widget_show_all (priv->image);
258 priv->dock = gtk_window_new (GTK_WINDOW_POPUP);
259 g_signal_connect (priv->dock, "button-press-event",
260 G_CALLBACK (cb_dock_button_press), button);
261 g_signal_connect (priv->dock, "key-release-event",
262 G_CALLBACK (cb_dock_key_release), button);
263 g_signal_connect (priv->dock, "grab-notify",
264 G_CALLBACK (cb_dock_grab_notify), button);
265 g_signal_connect (priv->dock, "grab-broken-event",
266 G_CALLBACK (cb_dock_grab_broken_event), button);
267 gtk_window_set_decorated (GTK_WINDOW (priv->dock), FALSE);
270 frame = gtk_frame_new (NULL);
271 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
272 gtk_container_add (GTK_CONTAINER (priv->dock), frame);
273 box = gtk_vbox_new (FALSE, 0);
274 gtk_container_add (GTK_CONTAINER (frame), box);
277 button->plus_button = gtk_button_new_with_label ("+");
278 gtk_button_set_relief (GTK_BUTTON (button->plus_button), GTK_RELIEF_NONE);
279 g_signal_connect (button->plus_button, "button-press-event",
280 G_CALLBACK (cb_button_press), button);
281 g_signal_connect (button->plus_button, "button-release-event",
282 G_CALLBACK (cb_button_release), button);
283 gtk_box_pack_start (GTK_BOX (box), button->plus_button, TRUE, FALSE, 0);
286 priv->scale = gtk_scale_button_scale_new (button, 0., 100., 2.);
287 gtk_widget_set_size_request (priv->scale, -1, SCALE_SIZE);
288 gtk_scale_set_draw_value (GTK_SCALE (priv->scale), FALSE);
289 gtk_range_set_inverted (GTK_RANGE (priv->scale), TRUE);
290 gtk_box_pack_start (GTK_BOX (box), priv->scale, TRUE, FALSE, 0);
291 g_signal_connect (priv->scale, "grab-notify",
292 G_CALLBACK (cb_scale_grab_notify), button);
295 button->minus_button = gtk_button_new_with_label ("-");
296 gtk_button_set_relief (GTK_BUTTON (button->minus_button), GTK_RELIEF_NONE);
297 g_signal_connect (button->minus_button, "button-press-event",
298 G_CALLBACK (cb_button_press), button);
299 g_signal_connect (button->minus_button, "button-release-event",
300 G_CALLBACK (cb_button_release), button);
301 gtk_box_pack_start (GTK_BOX (box), button->minus_button, TRUE, FALSE, 0);
303 /* set button text and size */
304 priv->size = GTK_ICON_SIZE_SMALL_TOOLBAR;
305 gtk_scale_button_update_icon (button);
309 gtk_scale_button_set_property (GObject *object,
314 GtkScaleButton *button;
316 button = GTK_SCALE_BUTTON (object);
321 gtk_scale_button_set_value (button, g_value_get_double (value));
326 size = g_value_get_enum (value);
327 if (button->priv->size != size)
329 button->priv->size = size;
330 gtk_scale_button_update_icon (button);
334 case PROP_ADJUSTMENT:
335 gtk_scale_button_set_adjustment (button, g_value_get_object (value));
338 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
344 gtk_scale_button_get_property (GObject *object,
349 GtkScaleButton *button;
350 GtkScaleButtonPrivate *priv;
352 button = GTK_SCALE_BUTTON (object);
358 g_value_set_float (value, gtk_scale_button_get_value (button));
361 g_value_set_enum (value, priv->size);
363 case PROP_ADJUSTMENT:
364 g_value_set_object (value, gtk_scale_button_get_adjustment (button));
367 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
373 gtk_scale_button_dispose (GObject *object)
375 GtkScaleButton *button;
376 GtkScaleButtonPrivate *priv;
378 button = GTK_SCALE_BUTTON (object);
382 if (priv && priv->dock)
384 gtk_widget_destroy (priv->dock);
388 if (priv && priv->click_id != 0)
390 g_source_remove (priv->click_id);
394 if (priv && priv->icon_list)
396 g_strfreev (priv->icon_list);
397 priv->icon_list = NULL;
403 G_OBJECT_CLASS (parent_class)->dispose (object);
407 * gtk_scale_button_new
408 * @size: a stock icon size
409 * @lower: the minimum value of the scale (usually 0)
410 * @upper: the maximum value of the scale (usually 100)
411 * @step_increment: the stepping of value when a scroll-wheel event,
412 * or up/down arrow event occurs (usually 2)
413 * @icons: a %NULL-terminated array of icon names, or %NULL if
414 * you want to set the list later with gtk_scale_button_set_icons(). See
415 * gtk_scale_button_set_icons() for details.
417 * Creates a #GtkScaleButton, with a range between lower and upper, with
418 * a stepping of step_increment.
420 * Return value: a new #GtkScaleButton
425 gtk_scale_button_new (GtkIconSize size,
428 gdouble step_increment,
431 GtkScaleButton *button;
432 GtkScaleButtonPrivate *priv;
434 button = g_object_new (GTK_TYPE_SCALE_BUTTON, NULL);
438 gtk_scale_button_set_icons (button, icons);
440 gtk_range_set_range (GTK_RANGE (priv->scale), lower, upper);
441 gtk_range_set_increments (GTK_RANGE (priv->scale),
443 10 * step_increment);
445 if (priv->size != size)
448 gtk_scale_button_update_icon (button);
451 return GTK_WIDGET (button);
455 * gtk_scale_button_get_value
456 * @button: a #GtkScaleButton
458 * Gets the current value of the scale button.
460 * Return value: current value of the scale button
465 gtk_scale_button_get_value (GtkScaleButton * button)
467 GtkScaleButtonPrivate *priv;
469 g_return_val_if_fail (GTK_IS_SCALE_BUTTON (button), 0);
473 return gtk_range_get_value (GTK_RANGE (priv->scale));
477 * gtk_scale_button_set_value
478 * @button: a #GtkScaleButton
479 * @value: new value of the scale button
481 * Sets the current value of the scale; if the value is outside the minimum or
482 * maximum range values, it will be clamped to fit inside them. The scale button
483 * emits the "value_changed" signal if the value changes.
488 gtk_scale_button_set_value (GtkScaleButton *button,
491 GtkScaleButtonPrivate *priv;
493 g_return_if_fail (GTK_IS_SCALE_BUTTON (button));
497 gtk_range_set_value (GTK_RANGE (priv->scale), value);
501 * gtk_scale_button_set_icons
502 * @button: a #GtkScaleButton
503 * @icons: a %NULL-terminated array of icon names
505 * Sets the icons to be used by the scale button. The first item in the array
506 * will be used in the button when the current value is the lowest value, the
507 * second item for the highest value. All the subsequent icons will be used for
508 * all the other values, spread evenly over the range of values.
510 * If there's only one icon in the @icons array, it will be used for all the
511 * values. If only two icons are in the @icons array, the first one will be
512 * used for the bottom 50% of the scale, and the second one for the top 50%.
513 * So it is recommended to use at least 3 icons so that the #GtkScaleButton
514 * reflects the current value of the scale better for the users.
519 gtk_scale_button_set_icons (GtkScaleButton *button,
522 GtkScaleButtonPrivate *priv;
524 g_return_if_fail (GTK_IS_SCALE_BUTTON (button));
525 g_return_if_fail (icons != NULL);
526 g_return_if_fail (icons[0] != NULL);
530 g_strfreev (priv->icon_list);
531 priv->icon_list = g_strdupv ((gchar **) icons);
532 gtk_scale_button_update_icon (button);
536 * gtk_scale_button_get_adjustment
537 * @button: a #GtkScaleButton
539 * Gets the #GtkAdjustment associated with the #GtkScaleButton's scale.
540 * See gtk_range_get_adjustment() for details.
545 gtk_scale_button_get_adjustment (GtkScaleButton *button)
549 g_return_val_if_fail (GTK_IS_SCALE_BUTTON (button), NULL);
551 range = GTK_RANGE (button->priv->scale);
552 g_return_val_if_fail (range != NULL, NULL);
554 return gtk_range_get_adjustment (range);
558 * gtk_scale_button_set_adjustment
559 * @button: a #GtkScaleButton
560 * @adjustment: a #GtkAdjustment
562 * Sets the #GtkAdjustment to be used as a model for the #GtkScaleButton's scale.
563 * See gtk_range_set_adjustment() for details.
568 gtk_scale_button_set_adjustment (GtkScaleButton *button,
569 GtkAdjustment *adjustment)
571 g_return_if_fail (GTK_IS_SCALE_BUTTON (button));
572 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
574 gtk_range_set_adjustment (GTK_RANGE (button->priv->scale),
583 gtk_scale_button_scroll (GtkWidget *widget,
584 GdkEventScroll *event)
586 GtkScaleButton *button;
587 GtkScaleButtonPrivate *priv;
591 button = GTK_SCALE_BUTTON (widget);
593 adj = gtk_range_get_adjustment (GTK_RANGE (priv->scale));
595 if (event->type != GDK_SCROLL)
598 d = gtk_scale_button_get_value (button);
599 if (event->direction == GDK_SCROLL_UP)
601 d += adj->step_increment;
607 d -= adj->step_increment;
611 gtk_scale_button_set_value (button, d);
617 gtk_scale_button_screen_changed (GtkWidget *widget,
618 GdkScreen *previous_screen)
620 GtkScaleButton *button = (GtkScaleButton *) widget;
621 GtkScaleButtonPrivate *priv;
623 GValue value = { 0, };
625 if (gtk_widget_has_screen (widget) == FALSE)
630 screen = gtk_widget_get_screen (widget);
631 g_value_init (&value, G_TYPE_INT);
632 if (gdk_screen_get_setting (screen,
633 "gtk-double-click-time",
636 priv->click_timeout = CLICK_TIMEOUT;
640 priv->click_timeout = g_value_get_int (&value);
644 gtk_scale_popup (GtkWidget *widget,
648 GtkScaleButton *button;
649 GtkScaleButtonPrivate *priv;
651 gint x, y, m, dx, dy, sx, sy, ystartoff;
656 button = GTK_SCALE_BUTTON (widget);
658 adj = gtk_range_get_adjustment (GTK_RANGE (priv->scale));
660 display = gtk_widget_get_display (widget);
661 screen = gtk_widget_get_screen (widget);
663 /* position roughly */
664 gtk_window_set_screen (GTK_WINDOW (priv->dock), screen);
666 gdk_window_get_origin (widget->window, &x, &y);
667 x += widget->allocation.x;
668 y += widget->allocation.y;
669 gtk_window_move (GTK_WINDOW (priv->dock), x, y - (SCALE_SIZE / 2));
670 gtk_widget_show_all (priv->dock);
671 gdk_window_get_origin (priv->dock->window, &dx, &dy);
672 dy += priv->dock->allocation.y;
673 gdk_window_get_origin (priv->scale->window, &sx, &sy);
674 sy += priv->scale->allocation.y;
676 priv->timeout = TRUE;
678 /* position (needs widget to be shown already) */
679 v = gtk_scale_button_get_value (button) / (adj->upper - adj->lower);
680 x += (widget->allocation.width - priv->dock->allocation.width) / 2;
682 y -= GTK_RANGE (priv->scale)->min_slider_size / 2;
683 m = priv->scale->allocation.height -
684 GTK_RANGE (priv->scale)->min_slider_size;
687 /* Make sure the dock stays inside the monitor */
688 if (event->type == GDK_BUTTON_PRESS)
691 GdkEventButton *button_event = (GdkEventButton *) event;
695 d = GTK_WIDGET (priv->dock);
696 monitor = gdk_screen_get_monitor_at_point (screen,
697 button_event->x_root,
698 button_event->y_root);
699 gdk_screen_get_monitor_geometry (screen, monitor, &rect);
701 y += button_event->y;
704 else if (y + d->allocation.height > rect.height + rect.y)
705 y = rect.y + rect.height - d->allocation.height;
709 else if (x + d->allocation.width > rect.width + rect.x)
710 x = rect.x + rect.width - d->allocation.width;
713 gtk_window_move (GTK_WINDOW (priv->dock), x, y);
715 if (event->type == GDK_BUTTON_PRESS)
716 GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, (GdkEventButton *) event);
719 gtk_grab_add (priv->dock);
721 if (gdk_pointer_grab (priv->dock->window, TRUE,
722 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
723 GDK_POINTER_MOTION_MASK, NULL, NULL, time)
726 gtk_grab_remove (priv->dock);
727 gtk_widget_hide (priv->dock);
731 if (gdk_keyboard_grab (priv->dock->window, TRUE, time) != GDK_GRAB_SUCCESS)
733 gdk_display_pointer_ungrab (display, time);
734 gtk_grab_remove (priv->dock);
735 gtk_widget_hide (priv->dock);
739 gtk_widget_grab_focus (priv->dock);
741 if (event->type == GDK_BUTTON_PRESS)
744 GdkEventButton *button_event = (GdkEventButton *) event;
746 /* forward event to the slider */
747 e = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
748 e->window = priv->scale->window;
750 /* position: the X position isn't relevant, halfway will work just fine.
751 * The vertical position should be *exactly* in the middle of the slider
752 * of the scale; if we don't do that correctly, it'll move from its current
753 * position, which means a position change on-click, which is bad. */
754 e->x = priv->scale->allocation.width / 2;
755 m = priv->scale->allocation.height -
756 GTK_RANGE (priv->scale)->min_slider_size;
757 e->y = ((1.0 - v) * m) + GTK_RANGE (priv->scale)->min_slider_size / 2;
758 gtk_widget_event (priv->scale, (GdkEvent *) e);
759 e->window = button_event->window;
760 gdk_event_free ((GdkEvent *) e);
763 gtk_widget_grab_focus (priv->scale);
765 priv->pop_time = time;
771 gtk_scale_button_press (GtkWidget *widget,
772 GdkEventButton *event)
774 return gtk_scale_popup (widget, (GdkEvent *) event, event->time);
778 gtk_scale_button_popup_from_bindings (GtkWidget *widget)
782 ev = gdk_event_new (GDK_KEY_RELEASE);
783 gtk_scale_popup (widget, ev, GDK_CURRENT_TIME);
788 gtk_scale_button_key_release (GtkWidget *widget,
791 return gtk_bindings_activate_event (GTK_OBJECT (widget), event);
794 /* This is called when the grab is broken for
795 * either the dock, or the scale itself */
797 gtk_scale_button_grab_notify (GtkScaleButton *button,
798 gboolean was_grabbed)
801 GtkScaleButtonPrivate *priv;
803 if (was_grabbed != FALSE)
808 if (gtk_widget_is_ancestor (gtk_grab_get_current (), priv->dock))
811 display = gtk_widget_get_display (priv->dock);
812 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
813 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
814 gtk_grab_remove (priv->dock);
817 gtk_widget_hide (priv->dock);
818 priv->timeout = FALSE;
822 * +/- button callbacks.
826 cb_button_timeout (gpointer user_data)
828 GtkScaleButton *button;
829 GtkScaleButtonPrivate *priv;
834 button = GTK_SCALE_BUTTON (user_data);
837 if (priv->click_id == 0)
840 adj = gtk_range_get_adjustment (GTK_RANGE (priv->scale));
842 val = gtk_scale_button_get_value (button);
843 val += priv->direction;
844 if (val <= adj->lower)
849 else if (val > adj->upper)
854 gtk_scale_button_set_value (button, val);
858 g_source_remove (priv->click_id);
866 cb_button_press (GtkWidget *widget,
867 GdkEventButton *event,
870 GtkScaleButton *button;
871 GtkScaleButtonPrivate *priv;
874 button = GTK_SCALE_BUTTON (user_data);
876 adj = gtk_range_get_adjustment (GTK_RANGE (priv->scale));
878 if (priv->click_id != 0)
879 g_source_remove (priv->click_id);
881 if (widget == button->plus_button)
882 priv->direction = fabs (adj->page_increment);
884 priv->direction = - fabs (adj->page_increment);
886 priv->click_id = g_timeout_add (priv->click_timeout,
887 (GSourceFunc) cb_button_timeout, button);
888 cb_button_timeout (button);
894 cb_button_release (GtkWidget *widget,
895 GdkEventButton *event,
898 GtkScaleButton *button;
899 GtkScaleButtonPrivate *priv;
901 button = GTK_SCALE_BUTTON (user_data);
904 if (priv->click_id != 0)
906 g_source_remove (priv->click_id);
914 cb_dock_grab_notify (GtkWidget *widget,
915 gboolean was_grabbed,
918 GtkScaleButton *button = (GtkScaleButton *) user_data;
920 gtk_scale_button_grab_notify (button, was_grabbed);
924 cb_dock_grab_broken_event (GtkWidget *widget,
925 gboolean was_grabbed,
928 GtkScaleButton *button = (GtkScaleButton *) user_data;
930 gtk_scale_button_grab_notify (button, FALSE);
940 gtk_scale_button_release_grab (GtkScaleButton *button,
941 GdkEventButton *event)
945 GtkScaleButtonPrivate *priv;
950 display = gtk_widget_get_display (GTK_WIDGET (button));
951 gdk_display_keyboard_ungrab (display, event->time);
952 gdk_display_pointer_ungrab (display, event->time);
953 gtk_grab_remove (priv->dock);
956 gtk_widget_hide (priv->dock);
957 priv->timeout = FALSE;
959 e = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
960 e->window = GTK_WIDGET (button)->window;
961 e->type = GDK_BUTTON_RELEASE;
962 gtk_widget_event (GTK_WIDGET (button), (GdkEvent *) e);
963 e->window = event->window;
964 gdk_event_free ((GdkEvent *) e);
968 cb_dock_button_press (GtkWidget *widget,
969 GdkEventButton *event,
972 GtkScaleButton *button = GTK_SCALE_BUTTON (user_data);
974 if (event->type == GDK_BUTTON_PRESS)
976 gtk_scale_button_release_grab (button, event);
984 gtk_scale_button_popdown_from_bindings (GtkWidget *widget)
986 GtkScaleButton *button;
987 GtkScaleButtonPrivate *priv;
990 button = GTK_SCALE_BUTTON (widget);
994 display = gtk_widget_get_display (widget);
995 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
996 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
997 gtk_grab_remove (priv->dock);
1000 gtk_widget_hide (priv->dock);
1001 priv->timeout = FALSE;
1005 cb_dock_key_release (GtkWidget *widget,
1009 if (event->keyval == GDK_Escape)
1011 gtk_scale_button_popdown_from_bindings (GTK_WIDGET (user_data));
1015 if (!gtk_bindings_activate_event (GTK_OBJECT (widget), event))
1017 /* The popup hasn't managed the event, pass onto the button */
1018 gtk_bindings_activate_event (GTK_OBJECT (user_data), event);
1025 cb_scale_grab_notify (GtkWidget *widget,
1026 gboolean was_grabbed,
1029 GtkScaleButton *button = (GtkScaleButton *) user_data;
1031 gtk_scale_button_grab_notify (button, was_grabbed);
1038 #define GTK_TYPE_SCALE_BUTTON_SCALE \
1039 (gtk_scale_button_scale_get_type ())
1040 #define GTK_SCALE_BUTTON_SCALE(obj) \
1041 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SCALE_BUTTON_SCALE, \
1042 GtkScaleButtonScale))
1044 typedef struct _GtkScaleButtonScaleClass {
1045 GtkVScaleClass parent_class;
1046 } GtkScaleButtonScaleClass;
1048 typedef struct _GtkScaleButtonScale {
1050 GtkScaleButton *button;
1051 } GtkScaleButtonScale;
1053 static GType gtk_scale_button_scale_get_type (void);
1054 static void gtk_scale_button_scale_class_init (GtkScaleButtonScaleClass *klass);
1055 static gboolean gtk_scale_button_scale_press (GtkWidget *widget,
1056 GdkEventButton *event);
1057 static gboolean gtk_scale_button_scale_release (GtkWidget *widget,
1058 GdkEventButton *event);
1060 static GtkVScaleClass *scale_parent_class = NULL;
1062 G_DEFINE_TYPE (GtkScaleButtonScale, gtk_scale_button_scale, GTK_TYPE_VSCALE)
1065 gtk_scale_button_scale_class_init (GtkScaleButtonScaleClass *klass)
1067 GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS (klass);
1068 GtkRangeClass *gtkrange_class = GTK_RANGE_CLASS (klass);
1070 scale_parent_class = g_type_class_peek_parent (klass);
1072 gtkwidget_class->button_press_event = gtk_scale_button_scale_press;
1073 gtkwidget_class->button_release_event = gtk_scale_button_scale_release;
1074 gtkrange_class->value_changed = gtk_scale_button_scale_value_changed;
1078 gtk_scale_button_scale_init (GtkScaleButtonScale *scale)
1083 gtk_scale_button_scale_new (GtkScaleButton *button,
1088 GtkScaleButtonScale *scale;
1089 GtkScaleButtonPrivate *priv;
1092 priv = button->priv;
1093 scale = g_object_new (GTK_TYPE_SCALE_BUTTON_SCALE, NULL);
1094 adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
1095 gtk_range_set_adjustment (GTK_RANGE (scale), GTK_ADJUSTMENT (adj));
1096 scale->button = button;
1098 return GTK_WIDGET (scale);
1102 gtk_scale_button_scale_press (GtkWidget *widget,
1103 GdkEventButton *event)
1105 GtkScaleButtonScale *scale;
1106 GtkScaleButtonPrivate *priv;
1108 scale = GTK_SCALE_BUTTON_SCALE (widget);
1109 priv = scale->button->priv;
1111 /* the scale will grab input; if we have input grabbed, all goes
1112 * horribly wrong, so let's not do that. */
1113 gtk_grab_remove (priv->dock);
1115 return GTK_WIDGET_CLASS (scale_parent_class)->button_press_event (widget, event);
1119 gtk_scale_button_scale_release (GtkWidget *widget,
1120 GdkEventButton *event)
1122 GtkScaleButtonScale *scale;
1123 GtkScaleButtonPrivate *priv;
1126 scale = GTK_SCALE_BUTTON_SCALE (widget);
1127 priv = scale->button->priv;
1131 /* if we did a quick click, leave the window open; else, hide it */
1132 if (event->time > priv->pop_time + priv->click_timeout)
1134 gtk_scale_button_release_grab (scale->button, event);
1135 GTK_WIDGET_CLASS (scale_parent_class)->button_release_event (widget, event);
1138 priv->timeout = FALSE;
1141 res = GTK_WIDGET_CLASS (scale_parent_class)->button_release_event (widget, event);
1143 /* the scale will release input; right after that, we *have to* grab
1144 * it back so we can catch out-of-scale clicks and hide the popup,
1145 * so I basically want a g_signal_connect_after_always(), but I can't
1146 * find that, so we do this complex 'first-call-parent-then-do-actual-
1147 * action' thingy... */
1148 gtk_grab_add (priv->dock);
1154 gtk_scale_button_update_icon (GtkScaleButton *button)
1156 GtkScaleButtonPrivate *priv;
1163 priv = button->priv;
1165 if (priv->icon_list == NULL || priv->icon_list[0] == NULL)
1167 gtk_image_set_from_stock (GTK_IMAGE (priv->image),
1168 GTK_STOCK_MISSING_IMAGE,
1173 for (num_icons = 0; priv->icon_list[num_icons] != NULL; num_icons++) {}
1175 /* The 1-icon special case */
1178 gtk_image_set_from_icon_name (GTK_IMAGE (priv->image),
1184 range = GTK_RANGE (priv->scale);
1185 adj = gtk_range_get_adjustment (range);
1186 value = gtk_scale_button_get_value (button);
1188 /* The 2-icons special case */
1192 limit = (adj->upper - adj->lower) / 2 + adj->lower;
1194 name = priv->icon_list[0];
1196 name = priv->icon_list[1];
1198 gtk_image_set_from_icon_name (GTK_IMAGE (priv->image),
1204 /* With 3 or more icons */
1205 if (value == adj->lower)
1207 name = priv->icon_list[0];
1209 else if (value == adj->upper)
1211 name = priv->icon_list[1];
1218 step = (adj->upper - adj->lower) / (num_icons - 2);
1219 i = (guint) (value - adj->lower) / step + 2;
1220 g_assert (i < num_icons);
1221 name = priv->icon_list[i];
1224 gtk_image_set_from_icon_name (GTK_IMAGE (priv->image),
1230 gtk_scale_button_scale_value_changed (GtkRange *range)
1232 GtkScaleButtonScale *scale = GTK_SCALE_BUTTON_SCALE (range);
1233 GtkScaleButton *button = scale->button;
1236 value = gtk_range_get_value (range);
1238 gtk_scale_button_update_icon (button);
1240 g_signal_emit (button, signals[VALUE_CHANGED], 0, value);
1241 g_object_notify (G_OBJECT (button), "value");
1245 * vim: sw=2 ts=8 cindent noai bs=2