Adding the proper HAVE_CONFIG where needed.
[hildon] / src / hildon-volumebar.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
7  *
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.
12  *
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.
17  *
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
21  * 02110-1301 USA
22  *
23  */
24
25 /**
26  * SECTION:hildon-volumebar
27  * @short_description: Base class for widgets that display a volume bar
28  * @see_also: #HildonHVolumebar, #HildonVVolumebar
29  *
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.
33  */
34
35 #ifdef                                          HAVE_CONFIG_H
36 #include                                        <config.h>
37 #endif
38
39 #include                                        "hildon-volumebar.h"
40 #include                                        <gtk/gtk.h>
41 #include                                        <gdk/gdkkeysyms.h>
42 #include                                        "hildon-volumebar-range.h"
43 #include                                        "hildon-volumebar-private.h"
44
45 static GtkContainerClass*                       parent_class;
46
47 static void
48 hildon_volumebar_class_init                     (HildonVolumebarClass* volumebar_class);       
49
50 static void 
51 hildon_volumebar_init                           (HildonVolumebar* volumebar);
52
53 static void 
54 hildon_child_forall                             (GtkContainer * container,
55                                                  gboolean include_internals,
56                                                  GtkCallback callback,
57                                                  gpointer callback_data);
58
59 static void 
60 hildon_volumebar_destroy                        (GtkObject *self);
61
62 static void 
63 hildon_volumebar_set_property                   (GObject* object,
64                                                  guint prop_id,
65                                                  const GValue* value,
66                                                  GParamSpec* pspec);
67
68 static void 
69 hildon_volumebar_get_property                   (GObject * object,
70                                                  guint prop_id,
71                                                  GValue* value, 
72                                                  GParamSpec* pspec);
73
74 static void 
75 mute_toggled                                    (HildonVolumebar *self);
76
77 static gboolean
78 hildon_volumebar_key_press                      (GtkWidget* widget,
79                                                  GdkEventKey* event);
80
81 static void 
82 hildon_volumebar_size_allocate                  (GtkWidget *widget,
83                                                  GtkAllocation *allocation);
84
85 static void 
86 hildon_volumebar_realize                        (GtkWidget *widget);
87
88 static void 
89 hildon_volumebar_unrealize                      (GtkWidget *widget);
90
91 static void 
92 hildon_volumebar_map                            (GtkWidget *widget);
93
94 static void 
95 hildon_volumebar_unmap                          (GtkWidget *widget);
96
97 static void 
98 hildon_volumebar_notify                         (GObject *self, GParamSpec *param);
99
100 enum 
101 {
102     MUTE_TOGGLED_SIGNAL,
103     LEVEL_CHANGED_SIGNAL,
104     LAST_SIGNAL
105 };
106
107 enum {
108     PROP_0,
109     PROP_HILDON_HAS_MUTE,
110     PROP_HILDON_FOCUSABLE,
111     PROP_HILDON_LEVEL,
112     PROP_HILDON_MUTE
113 };
114
115 static guint                                    signals [LAST_SIGNAL] = { 0 };
116
117 GType G_GNUC_CONST 
118 hildon_volumebar_get_type                       (void)
119 {
120     static GType volumebar_type = 0;
121
122     if (!volumebar_type) {
123         static const GTypeInfo volumebar_info = {
124             sizeof(HildonVolumebarClass),
125             NULL,       /* base_init */
126             NULL,       /* base_finalize */
127             (GClassInitFunc) hildon_volumebar_class_init,
128             NULL,       /* class_finalize */
129             NULL,       /* class_data */
130             sizeof(HildonVolumebar),
131             0,  /* n_preallocs */
132             (GInstanceInitFunc) hildon_volumebar_init,
133         };
134         volumebar_type = g_type_register_static(GTK_TYPE_CONTAINER,
135                 "HildonVolumebar",
136                 &volumebar_info, 0);
137     }
138     return volumebar_type;
139 }
140
141 static void 
142 hildon_volumebar_class_init                     (HildonVolumebarClass *volumebar_class)
143 {
144     GObjectClass      *gobject_class    = G_OBJECT_CLASS  (volumebar_class);
145     GtkObjectClass    *object_class     = GTK_OBJECT_CLASS (volumebar_class);
146     GtkWidgetClass    *widget_class     = GTK_WIDGET_CLASS (volumebar_class);
147     GtkContainerClass *container_class  = GTK_CONTAINER_CLASS (volumebar_class);
148
149     parent_class = g_type_class_peek_parent (volumebar_class);
150
151     g_type_class_add_private (volumebar_class,
152             sizeof (HildonVolumebarPrivate));
153
154    /* Because we derived our widget from GtkContainer, we should also
155     * override forall method 
156     */
157    volumebar_class->mute_toggled        = mute_toggled;
158    container_class->forall              = hildon_child_forall;
159    widget_class->size_allocate          = hildon_volumebar_size_allocate;
160    widget_class->realize                = hildon_volumebar_realize;
161    widget_class->unrealize              = hildon_volumebar_unrealize;
162    widget_class->map                    = hildon_volumebar_map;
163    widget_class->unmap                  = hildon_volumebar_unmap;
164    widget_class->key_press_event        = hildon_volumebar_key_press;
165    object_class->destroy                = hildon_volumebar_destroy;
166
167    signals[MUTE_TOGGLED_SIGNAL] = g_signal_new ("mute_toggled",
168            G_OBJECT_CLASS_TYPE
169            (object_class),
170            G_SIGNAL_RUN_LAST |
171            G_SIGNAL_ACTION,
172            G_STRUCT_OFFSET
173            (HildonVolumebarClass,
174             mute_toggled), NULL, NULL,
175            gtk_marshal_VOID__VOID,
176            G_TYPE_NONE, 0);
177
178    signals[LEVEL_CHANGED_SIGNAL] = g_signal_new ("level_changed",
179            G_OBJECT_CLASS_TYPE
180            (object_class),
181            G_SIGNAL_RUN_LAST |
182            G_SIGNAL_ACTION,
183            G_STRUCT_OFFSET
184            (HildonVolumebarClass,
185             level_changed), NULL,
186            NULL,
187            gtk_marshal_VOID__VOID,
188            G_TYPE_NONE, 0);
189
190    gobject_class->notify                = hildon_volumebar_notify;
191    gobject_class->set_property          = hildon_volumebar_set_property;
192    gobject_class->get_property          = hildon_volumebar_get_property; 
193
194    /*This kind of property could be usefull in the gtkcontainer*/
195    g_object_class_install_property (gobject_class,
196            PROP_HILDON_FOCUSABLE, 
197            g_param_spec_boolean ("can-focus",
198                "The widget focusablility",
199                "The widget focusablility. TRUE is focusable",
200                TRUE,
201                G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
202
203    g_object_class_install_property (gobject_class,
204            PROP_HILDON_HAS_MUTE, 
205            g_param_spec_boolean ("has_mute",
206                "Show/Hide the mute button",
207                "Whether the mute button is visible. Default value: TRUE",
208                TRUE,
209                G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
210
211    g_object_class_install_property (gobject_class,
212            PROP_HILDON_LEVEL,
213            g_param_spec_double ("level",
214                "Level",
215                "Current volume level",
216                0.0,
217                100.0,
218                50.0,
219                G_PARAM_READWRITE));
220
221    g_object_class_install_property (gobject_class,
222            PROP_HILDON_MUTE,
223            g_param_spec_boolean ("mute",
224                "Mute",
225                "Whether volume is muted",
226                FALSE,
227                G_PARAM_READWRITE));
228 }
229
230 static void 
231 hildon_volumebar_init                           (HildonVolumebar *volumebar)
232 {
233     HildonVolumebarPrivate *priv;
234
235     priv = HILDON_VOLUMEBAR_GET_PRIVATE(volumebar);
236     g_assert (priv);
237
238     /* Should set GTK_NO_WINDOW flag, because widget is derived from
239        GtkContainer */
240     GTK_WIDGET_SET_FLAGS (GTK_WIDGET (volumebar), GTK_NO_WINDOW);
241     GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (volumebar), GTK_CAN_FOCUS);
242
243     /* Initialize mute button */
244     priv->tbutton = GTK_TOGGLE_BUTTON (gtk_toggle_button_new ());
245     g_object_set (G_OBJECT (priv->tbutton), "can-focus", FALSE, NULL);
246 }
247
248 static void
249 hildon_volumebar_size_allocate                  (GtkWidget *widget, 
250                                                  GtkAllocation *allocation)
251 {
252     HildonVolumebarPrivate *priv;
253
254     priv = HILDON_VOLUMEBAR_GET_PRIVATE(widget);
255     g_assert (priv);
256
257     if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
258         GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
259
260     if (GTK_WIDGET_REALIZED (widget))
261         gdk_window_move_resize (priv->event_window,
262                 allocation->x, allocation->y,
263                 allocation->width, allocation->height);
264 }
265
266 static void
267 hildon_volumebar_realize                        (GtkWidget *widget)
268 {
269     HildonVolumebarPrivate *priv;
270     GdkWindowAttr attributes;
271     gint attributes_mask;
272
273     priv = HILDON_VOLUMEBAR_GET_PRIVATE (widget);
274     g_assert (priv);
275
276     GTK_WIDGET_CLASS (parent_class)->realize (widget);
277
278     attributes.window_type = GDK_WINDOW_CHILD;
279     attributes.x = widget->allocation.x;
280     attributes.y = widget->allocation.y;
281     attributes.width = widget->allocation.width;
282     attributes.height = widget->allocation.height;
283     attributes.wclass = GDK_INPUT_ONLY;
284     attributes.event_mask = GDK_BUTTON_PRESS_MASK;
285
286     attributes_mask = GDK_WA_X | GDK_WA_Y;
287
288     priv->event_window = gdk_window_new (widget->window,
289             &attributes, attributes_mask);
290
291     gdk_window_set_user_data (priv->event_window, widget);
292 }
293
294 static void
295 hildon_volumebar_unrealize                      (GtkWidget *widget)
296 {
297     HildonVolumebarPrivate *priv;
298
299     priv = HILDON_VOLUMEBAR_GET_PRIVATE(widget);
300     g_assert (priv);
301
302     if (priv->event_window) {
303         gdk_window_set_user_data (priv->event_window, NULL);
304         gdk_window_destroy (priv->event_window);
305         priv->event_window = NULL;
306     }
307
308     GTK_WIDGET_CLASS (parent_class)->unrealize(widget);
309 }
310
311 static void
312 hildon_volumebar_map                            (GtkWidget *widget)
313 {
314     HildonVolumebarPrivate *priv;
315
316     priv = HILDON_VOLUMEBAR_GET_PRIVATE(widget);
317     g_assert (priv);
318
319     GTK_WIDGET_CLASS (parent_class)->map (widget);
320
321     /* the event window must be on top of all other widget windows, so show it
322      * last */
323     if (! GTK_WIDGET_SENSITIVE (widget))
324         gdk_window_show (priv->event_window);
325 }
326
327 static void 
328 hildon_volumebar_unmap                          (GtkWidget *widget)
329 {
330     HildonVolumebarPrivate *priv;
331
332     priv = HILDON_VOLUMEBAR_GET_PRIVATE (widget);
333     g_assert (priv);
334
335     gdk_window_hide (priv->event_window);
336
337     GTK_WIDGET_CLASS (parent_class)->unmap(widget);
338 }
339
340 static void
341 hildon_child_forall                             (GtkContainer *container,
342                                                  gboolean include_internals,
343                                                  GtkCallback callback, 
344                                                  gpointer callback_data)
345 {
346     HildonVolumebarPrivate *priv;
347
348     priv = HILDON_VOLUMEBAR_GET_PRIVATE (container);
349     g_assert (callback != NULL);
350     g_assert (priv);
351
352     /* No external children */
353     if (! include_internals)
354         return;
355
356     /* Execute callback for both internals */
357     (*callback) (GTK_WIDGET (priv->tbutton), callback_data);
358     (*callback) (GTK_WIDGET (priv->volumebar), callback_data);
359 }
360
361 static void
362 hildon_volumebar_notify                         (GObject *self, 
363                                                  GParamSpec *param)
364 {
365     HildonVolumebarPrivate *priv;
366
367     priv = HILDON_VOLUMEBAR_GET_PRIVATE(self);
368     g_assert (priv);
369
370     if (GTK_WIDGET_MAPPED (self)) {
371         /* show/hide the event window on sensitivity change */
372         if (g_str_equal (param->name, "sensitive")) {
373             if (GTK_WIDGET_SENSITIVE (self))
374                 gdk_window_hide (priv->event_window);
375             else
376                 gdk_window_show (priv->event_window);
377         }
378     }
379
380     if (G_OBJECT_CLASS(parent_class)->notify)
381         G_OBJECT_CLASS(parent_class)->notify (self, param);
382 }
383
384 static void 
385 hildon_volumebar_destroy                        (GtkObject *self)
386 {
387     HildonVolumebarPrivate *priv;
388
389     priv = HILDON_VOLUMEBAR_GET_PRIVATE(self);
390     g_assert (priv);
391
392     if (priv->tbutton) {
393         gtk_widget_unparent (GTK_WIDGET (priv->tbutton));
394         priv->tbutton = NULL;
395     }
396     if (priv->volumebar) {
397         gtk_widget_unparent (GTK_WIDGET (priv->volumebar));
398         priv->volumebar = NULL;
399     }
400
401     if (GTK_OBJECT_CLASS (parent_class)->destroy)
402         GTK_OBJECT_CLASS (parent_class)->destroy (self);
403 }
404
405 static void
406 hildon_volumebar_set_property                   (GObject *object,
407                                                  guint prop_id,
408                                                  const GValue *value, 
409                                                  GParamSpec *pspec)
410 {  
411     HildonVolumebarPrivate *priv;
412
413     priv = HILDON_VOLUMEBAR_GET_PRIVATE(object);
414     g_assert (priv);
415
416     switch (prop_id) {
417
418         case PROP_HILDON_HAS_MUTE:
419             /* Mute button always exists but might be hidden */
420             if (g_value_get_boolean (value))
421                 gtk_widget_show (GTK_WIDGET (priv->tbutton));
422             else
423                 gtk_widget_hide (GTK_WIDGET (priv->tbutton));
424             break;
425
426         case PROP_HILDON_FOCUSABLE:
427             g_object_set (G_OBJECT (priv->volumebar), "can-focus", 
428                     g_value_get_boolean (value), NULL );
429             break;
430
431         case PROP_HILDON_LEVEL:
432             hildon_volumebar_set_level (HILDON_VOLUMEBAR (priv->volumebar),
433                     g_value_get_double (value));
434             break;
435
436         case PROP_HILDON_MUTE:
437             hildon_volumebar_set_mute (HILDON_VOLUMEBAR (priv->volumebar),
438                     g_value_get_boolean (value));
439             break;
440
441         default:
442             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
443
444             break;
445     }
446 }
447
448 static void
449 hildon_volumebar_get_property                   (GObject *object,
450                                                  guint prop_id, 
451                                                  GValue *value, 
452                                                  GParamSpec *pspec)
453 {
454     HildonVolumebarPrivate *priv;
455     HildonVolumebar *vb;
456         
457     priv = HILDON_VOLUMEBAR_GET_PRIVATE(object);
458     g_assert (priv);
459
460     vb = HILDON_VOLUMEBAR (object);
461
462     switch (prop_id) {
463
464         case PROP_HILDON_HAS_MUTE:
465             g_value_set_boolean (value, GTK_WIDGET_VISIBLE (priv->tbutton));
466             break;
467
468         case PROP_HILDON_FOCUSABLE:
469             g_value_set_boolean (value, GTK_WIDGET_CAN_FOCUS (priv->volumebar));
470             break;
471
472         case PROP_HILDON_LEVEL:
473             g_value_set_double (value, hildon_volumebar_get_level (vb));
474             break;
475
476         case PROP_HILDON_MUTE:
477             g_value_set_boolean (value, hildon_volumebar_get_mute (vb));
478             break;
479
480         default:
481             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
482             break;
483
484     }
485 }
486
487 /**
488  * hildon_volumebar_set_level:
489  * @self: volume bar to change level on
490  * @level: new level
491  *
492  * Sets new volume level for this #HildonVolumebar.
493  */
494 void 
495 hildon_volumebar_set_level                      (HildonVolumebar *self, 
496                                                  gdouble level)
497 {
498     HildonVolumebarPrivate *priv;
499
500     g_return_if_fail(HILDON_IS_VOLUMEBAR (self));
501
502     priv = HILDON_VOLUMEBAR_GET_PRIVATE (self);
503     g_assert (priv);
504
505     hildon_volumebar_range_set_level (priv->volumebar, level);
506 }
507
508 /**
509  * hildon_volumebar_get_level:
510  * @self: volume bar to query level on
511  *
512  * Gets the volume level of this #HildonVolumebar.
513  *
514  * Returns: volume level or -1 on error
515  */
516 gdouble 
517 hildon_volumebar_get_level                      (HildonVolumebar *self)
518 {
519     HildonVolumebarPrivate *priv;
520
521     g_return_val_if_fail(HILDON_IS_VOLUMEBAR (self), -1);
522
523     priv = HILDON_VOLUMEBAR_GET_PRIVATE(self);
524     g_assert (priv);
525
526     return hildon_volumebar_range_get_level (priv->volumebar);
527 }
528
529 /**
530  * hildon_volumebar_set_mute:
531  * @self: volume bar to work on
532  * @mute: mute ON/OFF
533  *
534  * Sets mute status for this #HildonVolumebar.
535  */
536 void 
537 hildon_volumebar_set_mute                       (HildonVolumebar *self, 
538                                                  gboolean mute)
539 {
540     HildonVolumebarPrivate *priv;
541     gboolean focusable = TRUE;
542
543     g_return_if_fail (HILDON_IS_VOLUMEBAR (self));
544
545     priv = HILDON_VOLUMEBAR_GET_PRIVATE (self);
546     g_assert (priv);
547
548     /* Slider should be insensitive when mute is on */
549     gtk_widget_set_sensitive (GTK_WIDGET (priv->volumebar), !mute);
550
551     focusable = GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (priv->volumebar));
552
553     if (mute){   
554         if (focusable) {
555             /* Make mute button focusable since the slider isn't anymore */
556             g_object_set (G_OBJECT (priv->tbutton), "can-focus", TRUE, NULL);
557             gtk_widget_grab_focus (GTK_WIDGET (priv->tbutton));
558         }
559     }
560     else
561     {
562         g_object_set (G_OBJECT (priv->tbutton), "can-focus", FALSE, NULL);
563
564         /* Mute off grabs focus */
565         if (focusable){
566             gtk_widget_grab_focus (GTK_WIDGET (self));
567         }
568         else{
569             /* If volumebar is not focusable, focus the parent window instead */
570             GtkWidget *win = gtk_widget_get_ancestor (GTK_WIDGET (self), 
571                     GTK_TYPE_WINDOW);
572             gtk_window_set_focus (GTK_WINDOW (win), NULL);
573         }
574     }
575
576     /* Update mute button state and redraw */
577     gtk_toggle_button_set_active (priv->tbutton, mute);
578
579     /* FIXME I'm darn sure that's not really needed */
580     gtk_widget_queue_draw (GTK_WIDGET (self));
581 }
582
583 /**
584  * hildon_volumebar_get_mute:
585  * @self: volume bar to query mute status
586  *
587  * Gets mute status of this #HildonVolumebar (ON/OFF).
588  *
589  * Returns: Mute status as #gboolean value.
590  */
591 gboolean 
592 hildon_volumebar_get_mute                       (HildonVolumebar *self)
593 {
594     HildonVolumebarPrivate *priv;
595
596     g_return_val_if_fail (HILDON_IS_VOLUMEBAR (self), TRUE);
597
598     priv = HILDON_VOLUMEBAR_GET_PRIVATE(self);
599     g_assert (priv);
600
601     return gtk_toggle_button_get_active (priv->tbutton);
602 }
603
604 /**
605  * hildon_volumebar_get_adjustment
606  * @self : a #HildonVolumebar
607  * 
608  * Gets the GtkAdjustment used in volume bar. This can be handy
609  * to pass to hildon_appview_set_connected_adjustment which
610  * will allow changing the volume with 'increase' / 'decrease'
611  * hardware buttons.
612  *
613  * Returns: a #GtkAdjustment used by volume bar.
614  */
615 GtkAdjustment* 
616 hildon_volumebar_get_adjustment                 (HildonVolumebar *self)
617 {
618     HildonVolumebarPrivate *priv;
619
620     g_return_val_if_fail(HILDON_IS_VOLUMEBAR(self), NULL);
621
622     priv = HILDON_VOLUMEBAR_GET_PRIVATE(self);
623     g_assert (priv);
624
625     return gtk_range_get_adjustment (GTK_RANGE (priv->volumebar));
626 }
627
628 static void
629 mute_toggled                                    (HildonVolumebar *self)
630 {
631     /* This looks like no-op, but it still does something meaningfull!
632        set_mute also updates the ui to match new state that
633        is already reported by get_mute */
634
635     hildon_volumebar_set_mute (self, hildon_volumebar_get_mute (self));
636 }
637
638 static gboolean
639 hildon_volumebar_key_press                      (GtkWidget *widget,
640                                                  GdkEventKey *event)
641 {
642     HildonVolumebarPrivate *priv;
643
644     priv = HILDON_VOLUMEBAR_GET_PRIVATE(widget);
645     g_assert (priv != NULL);
646
647     /* Enter key toggles mute button (unless it is hidden) */
648     if (event->keyval == GDK_Return && GTK_WIDGET_VISIBLE (priv->tbutton)) {
649         gtk_toggle_button_set_active (priv->tbutton, 
650                 ! hildon_volumebar_get_mute(HILDON_VOLUMEBAR(widget)));
651
652         return TRUE;
653     }
654
655     return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
656 }
657
658 /* 
659  * Sends mute-toggled signal to widget, used as a callback in derived classes.
660  */
661 void G_GNUC_INTERNAL
662 hildon_volumebar_mute_toggled                   (HildonVolumebar * self)
663 {
664     g_return_if_fail(HILDON_IS_VOLUMEBAR(self));
665     /* FIXME Emit by id */
666     g_signal_emit_by_name(self, "mute_toggled");
667 }
668
669 void G_GNUC_INTERNAL 
670 hildon_volumebar_level_change                   (HildonVolumebar *self)
671 {
672     g_return_if_fail (HILDON_IS_VOLUMEBAR (self));
673
674     /* FIXME Use numerical val, id */
675     g_signal_emit_by_name (GTK_WIDGET(self), "level_changed");
676 }