2 * This file is a part of hildon
4 * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
6 * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; version 2.1 of
11 * the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
26 * SECTION:hildon-volumebar
27 * @short_description: Base class for widgets that display a volume bar.
28 * @see_also: #HildonHVolumebar, #HildonVVolumebar
30 * #HildonVolumebar is a base class for widgets that display a volume bar that
31 * allows increasing or decreasing volume within a predefined range, and muting
32 * the volume when users click the mute icon.
39 #include "hildon-volumebar.h"
41 #include <gdk/gdkkeysyms.h>
42 #include "hildon-volumebar-range.h"
43 #include "hildon-volumebar-private.h"
45 static GtkContainerClass* parent_class;
48 hildon_volumebar_class_init (HildonVolumebarClass* volumebar_class);
51 hildon_volumebar_init (HildonVolumebar* volumebar);
54 hildon_child_forall (GtkContainer * container,
55 gboolean include_internals,
57 gpointer callback_data);
60 hildon_volumebar_destroy (GtkObject *self);
63 hildon_volumebar_set_property (GObject* object,
69 hildon_volumebar_get_property (GObject * object,
75 mute_toggled (HildonVolumebar *self);
78 hildon_volumebar_key_press (GtkWidget* widget,
82 hildon_volumebar_size_allocate (GtkWidget *widget,
83 GtkAllocation *allocation);
86 hildon_volumebar_realize (GtkWidget *widget);
89 hildon_volumebar_unrealize (GtkWidget *widget);
92 hildon_volumebar_map (GtkWidget *widget);
95 hildon_volumebar_unmap (GtkWidget *widget);
98 hildon_volumebar_grab_focus (GtkWidget *widget);
101 hildon_volumebar_focus (GtkWidget *widget,
102 GtkDirectionType direction);
105 hildon_volumebar_notify (GObject *self, GParamSpec *param);
110 LEVEL_CHANGED_SIGNAL,
116 PROP_HILDON_HAS_MUTE,
121 static guint signals [LAST_SIGNAL] = { 0 };
124 * hildon_volumebar_get_type:
126 * Initializes and returns the type of a hildon volumebar.
128 * @Returns: GType of #HildonVolumebar
131 hildon_volumebar_get_type (void)
133 static GType volumebar_type = 0;
135 if (!volumebar_type) {
136 static const GTypeInfo volumebar_info = {
137 sizeof(HildonVolumebarClass),
138 NULL, /* base_init */
139 NULL, /* base_finalize */
140 (GClassInitFunc) hildon_volumebar_class_init,
141 NULL, /* class_finalize */
142 NULL, /* class_data */
143 sizeof(HildonVolumebar),
145 (GInstanceInitFunc) hildon_volumebar_init,
147 volumebar_type = g_type_register_static(GTK_TYPE_CONTAINER,
151 return volumebar_type;
155 hildon_volumebar_class_init (HildonVolumebarClass *volumebar_class)
157 GObjectClass *gobject_class = G_OBJECT_CLASS (volumebar_class);
158 GtkObjectClass *object_class = GTK_OBJECT_CLASS (volumebar_class);
159 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (volumebar_class);
160 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (volumebar_class);
162 parent_class = g_type_class_peek_parent (volumebar_class);
164 g_type_class_add_private (volumebar_class,
165 sizeof (HildonVolumebarPrivate));
167 /* Because we derived our widget from GtkContainer, we should also
168 * override forall method
170 volumebar_class->mute_toggled = mute_toggled;
171 container_class->forall = hildon_child_forall;
172 widget_class->size_allocate = hildon_volumebar_size_allocate;
173 widget_class->realize = hildon_volumebar_realize;
174 widget_class->unrealize = hildon_volumebar_unrealize;
175 widget_class->map = hildon_volumebar_map;
176 widget_class->unmap = hildon_volumebar_unmap;
177 widget_class->grab_focus = hildon_volumebar_grab_focus;
178 widget_class->focus = hildon_volumebar_focus;
179 widget_class->key_press_event = hildon_volumebar_key_press;
180 object_class->destroy = hildon_volumebar_destroy;
182 signals[MUTE_TOGGLED_SIGNAL] = g_signal_new ("mute_toggled",
188 (HildonVolumebarClass,
189 mute_toggled), NULL, NULL,
190 gtk_marshal_VOID__VOID,
193 signals[LEVEL_CHANGED_SIGNAL] = g_signal_new ("level_changed",
199 (HildonVolumebarClass,
200 level_changed), NULL,
202 gtk_marshal_VOID__VOID,
205 gobject_class->notify = hildon_volumebar_notify;
206 gobject_class->set_property = hildon_volumebar_set_property;
207 gobject_class->get_property = hildon_volumebar_get_property;
210 * HildonVolumebar:has-mute:
212 * Whether the mute button is visibile.
214 g_object_class_install_property (gobject_class,
215 PROP_HILDON_HAS_MUTE,
216 g_param_spec_boolean ("has_mute",
217 "Show/Hide the mute button",
218 "Whether the mute button is visible. Default value: TRUE",
220 G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
223 * HildonVolumebar:level:
225 * Current volume level.
227 g_object_class_install_property (gobject_class,
229 g_param_spec_double ("level",
231 "Current volume level",
238 * HildonVolumebar:mute:
240 * Whether volume is muted.
242 g_object_class_install_property (gobject_class,
244 g_param_spec_boolean ("mute",
246 "Whether volume is muted",
252 hildon_volumebar_init (HildonVolumebar *volumebar)
254 HildonVolumebarPrivate *priv;
256 priv = HILDON_VOLUMEBAR_GET_PRIVATE(volumebar);
259 /* Should set GTK_NO_WINDOW flag, because widget is derived from
261 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (volumebar), GTK_NO_WINDOW);
262 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (volumebar), GTK_CAN_FOCUS);
264 /* Initialize mute button */
265 priv->tbutton = GTK_TOGGLE_BUTTON (gtk_toggle_button_new ());
266 g_object_set (G_OBJECT (priv->tbutton), "can-focus", FALSE, NULL);
270 hildon_volumebar_size_allocate (GtkWidget *widget,
271 GtkAllocation *allocation)
273 HildonVolumebarPrivate *priv;
275 priv = HILDON_VOLUMEBAR_GET_PRIVATE(widget);
278 if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
279 GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
281 if (GTK_WIDGET_REALIZED (widget))
282 gdk_window_move_resize (priv->event_window,
283 allocation->x, allocation->y,
284 allocation->width, allocation->height);
288 hildon_volumebar_realize (GtkWidget *widget)
290 HildonVolumebarPrivate *priv;
291 GdkWindowAttr attributes;
292 gint attributes_mask;
294 priv = HILDON_VOLUMEBAR_GET_PRIVATE (widget);
297 GTK_WIDGET_CLASS (parent_class)->realize (widget);
299 attributes.window_type = GDK_WINDOW_CHILD;
300 attributes.x = widget->allocation.x;
301 attributes.y = widget->allocation.y;
302 attributes.width = widget->allocation.width;
303 attributes.height = widget->allocation.height;
304 attributes.wclass = GDK_INPUT_ONLY;
305 attributes.event_mask = GDK_BUTTON_PRESS_MASK;
307 attributes_mask = GDK_WA_X | GDK_WA_Y;
309 priv->event_window = gdk_window_new (widget->window,
310 &attributes, attributes_mask);
312 gdk_window_set_user_data (priv->event_window, widget);
316 hildon_volumebar_unrealize (GtkWidget *widget)
318 HildonVolumebarPrivate *priv;
320 priv = HILDON_VOLUMEBAR_GET_PRIVATE(widget);
323 if (priv->event_window) {
324 gdk_window_set_user_data (priv->event_window, NULL);
325 gdk_window_destroy (priv->event_window);
326 priv->event_window = NULL;
329 GTK_WIDGET_CLASS (parent_class)->unrealize(widget);
333 hildon_volumebar_map (GtkWidget *widget)
335 HildonVolumebarPrivate *priv;
337 priv = HILDON_VOLUMEBAR_GET_PRIVATE(widget);
340 GTK_WIDGET_CLASS (parent_class)->map (widget);
342 /* the event window must be on top of all other widget windows, so show it
344 if (! GTK_WIDGET_SENSITIVE (widget))
345 gdk_window_show (priv->event_window);
349 hildon_volumebar_unmap (GtkWidget *widget)
351 HildonVolumebarPrivate *priv;
353 priv = HILDON_VOLUMEBAR_GET_PRIVATE (widget);
356 gdk_window_hide (priv->event_window);
358 GTK_WIDGET_CLASS (parent_class)->unmap(widget);
362 hildon_volumebar_grab_focus (GtkWidget *widget)
364 HildonVolumebarPrivate *priv;
366 priv = HILDON_VOLUMEBAR_GET_PRIVATE (widget);
369 if (GTK_WIDGET_CAN_FOCUS (widget)) {
370 if (gtk_toggle_button_get_active (priv->tbutton))
371 gtk_widget_grab_focus (GTK_WIDGET (priv->tbutton));
373 gtk_widget_grab_focus (GTK_WIDGET (priv->volumebar));
378 hildon_volumebar_focus (GtkWidget *widget,
379 GtkDirectionType direction)
381 HildonVolumebarPrivate *priv;
382 GtkOrientation orientation;
385 priv = HILDON_VOLUMEBAR_GET_PRIVATE (widget);
388 orientation = GTK_RANGE (priv->volumebar)->orientation;
390 has_focus = (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (priv->volumebar)) ||
391 GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (priv->tbutton)));
396 case GTK_DIR_TAB_FORWARD:
397 case GTK_DIR_TAB_BACKWARD:
398 if (has_focus && orientation == GTK_ORIENTATION_HORIZONTAL)
404 if (has_focus && orientation == GTK_ORIENTATION_VERTICAL)
412 return GTK_WIDGET_CLASS (parent_class)->focus (widget, direction);
416 hildon_child_forall (GtkContainer *container,
417 gboolean include_internals,
418 GtkCallback callback,
419 gpointer callback_data)
421 HildonVolumebarPrivate *priv;
423 priv = HILDON_VOLUMEBAR_GET_PRIVATE (container);
424 g_assert (callback != NULL);
427 /* No external children */
428 if (! include_internals)
431 /* Execute callback for both internals */
432 (*callback) (GTK_WIDGET (priv->tbutton), callback_data);
433 (*callback) (GTK_WIDGET (priv->volumebar), callback_data);
437 hildon_volumebar_notify (GObject *self,
440 HildonVolumebarPrivate *priv;
442 priv = HILDON_VOLUMEBAR_GET_PRIVATE(self);
445 if (g_str_equal (param->name, "can-focus")) {
446 /* call set_mute() because that updates the widget's UI state */
447 hildon_volumebar_set_mute (HILDON_VOLUMEBAR (self),
448 hildon_volumebar_get_mute (HILDON_VOLUMEBAR (self)));
451 if (GTK_WIDGET_MAPPED (self)) {
452 /* show/hide the event window on sensitivity change */
453 if (g_str_equal (param->name, "sensitive")) {
454 if (GTK_WIDGET_SENSITIVE (self))
455 gdk_window_hide (priv->event_window);
457 gdk_window_show (priv->event_window);
461 if (G_OBJECT_CLASS(parent_class)->notify)
462 G_OBJECT_CLASS(parent_class)->notify (self, param);
466 hildon_volumebar_destroy (GtkObject *self)
468 HildonVolumebarPrivate *priv;
470 priv = HILDON_VOLUMEBAR_GET_PRIVATE(self);
474 gtk_widget_unparent (GTK_WIDGET (priv->tbutton));
475 priv->tbutton = NULL;
477 if (priv->volumebar) {
478 gtk_widget_unparent (GTK_WIDGET (priv->volumebar));
479 priv->volumebar = NULL;
482 if (GTK_OBJECT_CLASS (parent_class)->destroy)
483 GTK_OBJECT_CLASS (parent_class)->destroy (self);
487 hildon_volumebar_set_property (GObject *object,
492 HildonVolumebarPrivate *priv;
494 priv = HILDON_VOLUMEBAR_GET_PRIVATE(object);
499 case PROP_HILDON_HAS_MUTE:
500 /* Mute button always exists but might be hidden */
501 if (g_value_get_boolean (value))
502 gtk_widget_show (GTK_WIDGET (priv->tbutton));
504 gtk_widget_hide (GTK_WIDGET (priv->tbutton));
507 case PROP_HILDON_LEVEL:
508 hildon_volumebar_set_level (HILDON_VOLUMEBAR (object),
509 g_value_get_double (value));
512 case PROP_HILDON_MUTE:
513 hildon_volumebar_set_mute (HILDON_VOLUMEBAR (object),
514 g_value_get_boolean (value));
518 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
525 hildon_volumebar_get_property (GObject *object,
530 HildonVolumebarPrivate *priv;
533 priv = HILDON_VOLUMEBAR_GET_PRIVATE(object);
536 vb = HILDON_VOLUMEBAR (object);
540 case PROP_HILDON_HAS_MUTE:
541 g_value_set_boolean (value, GTK_WIDGET_VISIBLE (priv->tbutton));
544 case PROP_HILDON_LEVEL:
545 g_value_set_double (value, hildon_volumebar_get_level (vb));
548 case PROP_HILDON_MUTE:
549 g_value_set_boolean (value, hildon_volumebar_get_mute (vb));
553 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
560 * hildon_volumebar_set_level:
561 * @self: volume bar to change level on
564 * Sets new volume level for this #HildonVolumebar.
567 hildon_volumebar_set_level (HildonVolumebar *self,
570 HildonVolumebarPrivate *priv;
572 g_return_if_fail(HILDON_IS_VOLUMEBAR (self));
574 priv = HILDON_VOLUMEBAR_GET_PRIVATE (self);
577 hildon_volumebar_range_set_level (priv->volumebar, level);
581 * hildon_volumebar_get_level:
582 * @self: volume bar to query level on
584 * Gets the volume level of this #HildonVolumebar.
586 * Returns: volume level or -1 on error
589 hildon_volumebar_get_level (HildonVolumebar *self)
591 HildonVolumebarPrivate *priv;
593 g_return_val_if_fail(HILDON_IS_VOLUMEBAR (self), -1);
595 priv = HILDON_VOLUMEBAR_GET_PRIVATE(self);
598 return hildon_volumebar_range_get_level (priv->volumebar);
602 * hildon_volumebar_set_mute:
603 * @self: volume bar to work on
606 * Sets mute status for this #HildonVolumebar.
609 hildon_volumebar_set_mute (HildonVolumebar *self,
612 HildonVolumebarPrivate *priv;
615 g_return_if_fail (HILDON_IS_VOLUMEBAR (self));
617 priv = HILDON_VOLUMEBAR_GET_PRIVATE (self);
620 has_focus = (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (priv->volumebar)) ||
621 GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (priv->tbutton)));
623 /* Slider should be insensitive when mute is on */
624 gtk_widget_set_sensitive (GTK_WIDGET (priv->volumebar), !mute);
627 /* Make mute button focusable since the slider isn't anymore */
628 g_object_set (G_OBJECT (priv->tbutton), "can-focus", TRUE, NULL);
631 gtk_widget_grab_focus (GTK_WIDGET (priv->tbutton));
635 g_object_set (G_OBJECT (priv->tbutton), "can-focus", FALSE, NULL);
638 gtk_widget_grab_focus (GTK_WIDGET (priv->volumebar));
641 /* Update mute button state and redraw */
642 gtk_toggle_button_set_active (priv->tbutton, mute);
646 * hildon_volumebar_get_mute:
647 * @self: volume bar to query mute status
649 * Gets mute status of this #HildonVolumebar (ON/OFF).
651 * Returns: Mute status as #gboolean value.
654 hildon_volumebar_get_mute (HildonVolumebar *self)
656 HildonVolumebarPrivate *priv;
658 g_return_val_if_fail (HILDON_IS_VOLUMEBAR (self), TRUE);
660 priv = HILDON_VOLUMEBAR_GET_PRIVATE(self);
663 return gtk_toggle_button_get_active (priv->tbutton);
667 * hildon_volumebar_get_adjustment
668 * @self : a #HildonVolumebar
670 * Gets the GtkAdjustment used in volume bar. This can be handy
671 * to pass to hildon_appview_set_connected_adjustment which
672 * will allow changing the volume with 'increase' / 'decrease'
675 * Returns: a #GtkAdjustment used by volume bar.
678 hildon_volumebar_get_adjustment (HildonVolumebar *self)
680 HildonVolumebarPrivate *priv;
682 g_return_val_if_fail(HILDON_IS_VOLUMEBAR(self), NULL);
684 priv = HILDON_VOLUMEBAR_GET_PRIVATE(self);
687 return gtk_range_get_adjustment (GTK_RANGE (priv->volumebar));
691 mute_toggled (HildonVolumebar *self)
693 /* This looks like no-op, but it still does something meaningfull!
694 set_mute also updates the ui to match new state that
695 is already reported by get_mute */
697 hildon_volumebar_set_mute (self, hildon_volumebar_get_mute (self));
701 hildon_volumebar_key_press (GtkWidget *widget,
704 HildonVolumebarPrivate *priv;
706 priv = HILDON_VOLUMEBAR_GET_PRIVATE(widget);
707 g_assert (priv != NULL);
709 /* Enter key toggles mute button (unless it is hidden) */
710 if (event->keyval == GDK_Return && GTK_WIDGET_VISIBLE (priv->tbutton)) {
711 gtk_toggle_button_set_active (priv->tbutton,
712 ! hildon_volumebar_get_mute(HILDON_VOLUMEBAR(widget)));
717 return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
721 * Sends mute-toggled signal to widget, used as a callback in derived classes.
724 hildon_volumebar_mute_toggled (HildonVolumebar * self)
726 g_return_if_fail (HILDON_IS_VOLUMEBAR (self));
727 /* FIXME Emit by id */
728 g_signal_emit_by_name (self, "mute_toggled");
732 hildon_volumebar_level_change (HildonVolumebar *self)
734 g_return_if_fail (HILDON_IS_VOLUMEBAR (self));
736 /* FIXME Use numerical val, id */
737 g_signal_emit_by_name (GTK_WIDGET (self), "level_changed");
741 * hildon_volumebar_set_range_insensitive_message:
742 * @widget: A @GtkWidget to assign the banner to
743 * @message: A message to display to the user
745 * Used to asign an insensitive message to the slider of the given volumebar.
746 * It simply calls hildon_helper_set_insensitive_message on the slider/range of
750 hildon_volumebar_set_range_insensitive_message (HildonVolumebar *widget,
751 const gchar *message)
753 g_return_if_fail (HILDON_IS_VOLUMEBAR (widget));
755 HildonVolumebarPrivate *priv;
756 priv = HILDON_VOLUMEBAR_GET_PRIVATE (widget);
758 hildon_helper_set_insensitive_message ((GtkWidget *) priv->volumebar, message);
762 * hildon_volumebar_set_range_insensitive_messagef:
763 * @widget: A @GtkWidget to assign the banner to
764 * @format : a printf-like format string
765 * @varargs : arguments for the format string
767 * A helper printf-like variant of hildon_helper_set_insensitive_message.
770 hildon_volumebar_set_range_insensitive_messagef (HildonVolumebar *widget,
774 g_return_if_fail (HILDON_IS_VOLUMEBAR (widget));
776 HildonVolumebarPrivate *priv;
777 priv = HILDON_VOLUMEBAR_GET_PRIVATE (widget);
782 va_start (args, format);
783 message = g_strdup_vprintf (format, args);
786 hildon_helper_set_insensitive_message ((GtkWidget *) priv->volumebar, message);