~cjcurran/gnome-control-center/fix-profiles

« back to all changes in this revision

Viewing changes to panels/sound-nua/gvc-stream-status-icon.c

  • Committer: Conor Curran
  • Date: 2012-02-02 23:14:14 UTC
  • Revision ID: conor.curran@canonical.com-20120202231414-xkpgkpevzwgdorq5
make new panel for sound redesign

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 
2
 *
 
3
 * Copyright (C) 2008 William Jon McCann
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
18
 *
 
19
 */
 
20
 
 
21
#include "config.h"
 
22
 
 
23
#include <stdlib.h>
 
24
#include <stdio.h>
 
25
#include <unistd.h>
 
26
 
 
27
#include <glib.h>
 
28
#include <glib/gi18n.h>
 
29
#include <gdk/gdkkeysyms.h>
 
30
#include <gtk/gtk.h>
 
31
#include <pulse/pulseaudio.h>
 
32
 
 
33
#include "gvc-mixer-stream.h"
 
34
#include "gvc-channel-bar.h"
 
35
#include "gvc-stream-status-icon.h"
 
36
 
 
37
#define GVC_STREAM_STATUS_ICON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_STREAM_STATUS_ICON, GvcStreamStatusIconPrivate))
 
38
 
 
39
struct GvcStreamStatusIconPrivate
 
40
{
 
41
        char          **icon_names;
 
42
        GvcMixerStream *mixer_stream;
 
43
        GtkWidget      *dock;
 
44
        GtkWidget      *bar;
 
45
        guint           current_icon;
 
46
        char           *display_name;
 
47
        gboolean        thaw;
 
48
};
 
49
 
 
50
enum
 
51
{
 
52
        PROP_0,
 
53
        PROP_DISPLAY_NAME,
 
54
        PROP_MIXER_STREAM,
 
55
        PROP_ICON_NAMES,
 
56
};
 
57
 
 
58
static void     gvc_stream_status_icon_class_init (GvcStreamStatusIconClass *klass);
 
59
static void     gvc_stream_status_icon_init       (GvcStreamStatusIcon      *stream_status_icon);
 
60
static void     gvc_stream_status_icon_finalize   (GObject                  *object);
 
61
 
 
62
G_DEFINE_TYPE (GvcStreamStatusIcon, gvc_stream_status_icon, GTK_TYPE_STATUS_ICON)
 
63
 
 
64
static void
 
65
on_adjustment_value_changed (GtkAdjustment *adjustment,
 
66
                             GvcStreamStatusIcon     *icon)
 
67
{
 
68
        gdouble volume;
 
69
 
 
70
        if (icon->priv->thaw)
 
71
                return;
 
72
 
 
73
        volume = gtk_adjustment_get_value (adjustment);
 
74
 
 
75
        /* Only push the volume if it's actually changed */
 
76
        if (gvc_mixer_stream_set_volume(icon->priv->mixer_stream,
 
77
                                    (pa_volume_t) round (volume)) != FALSE) {
 
78
                gvc_mixer_stream_push_volume(icon->priv->mixer_stream);
 
79
        }
 
80
}
 
81
 
 
82
static void
 
83
update_dock (GvcStreamStatusIcon *icon)
 
84
{
 
85
        GtkAdjustment *adj;
 
86
        gboolean       is_muted;
 
87
 
 
88
        g_return_if_fail (icon);
 
89
 
 
90
        adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (icon->priv->bar)));
 
91
 
 
92
        icon->priv->thaw = TRUE;
 
93
        gtk_adjustment_set_value (adj,
 
94
                                  gvc_mixer_stream_get_volume (icon->priv->mixer_stream));
 
95
        is_muted = gvc_mixer_stream_get_is_muted (icon->priv->mixer_stream);
 
96
        gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (icon->priv->bar), is_muted);
 
97
        icon->priv->thaw = FALSE;
 
98
}
 
99
 
 
100
static gboolean
 
101
popup_dock (GvcStreamStatusIcon *icon,
 
102
            guint                time)
 
103
{
 
104
        GdkRectangle   area;
 
105
        GtkOrientation orientation;
 
106
        GdkDisplay    *display;
 
107
        GdkScreen     *screen;
 
108
        gboolean       res;
 
109
        int            x;
 
110
        int            y;
 
111
        int            monitor_num;
 
112
        GdkRectangle   monitor;
 
113
        GtkRequisition dock_req;
 
114
 
 
115
        update_dock (icon);
 
116
 
 
117
        screen = gtk_status_icon_get_screen (GTK_STATUS_ICON (icon));
 
118
        res = gtk_status_icon_get_geometry (GTK_STATUS_ICON (icon),
 
119
                                            &screen,
 
120
                                            &area,
 
121
                                            &orientation);
 
122
        if (! res) {
 
123
                g_warning ("Unable to determine geometry of status icon");
 
124
                return FALSE;
 
125
        }
 
126
 
 
127
        /* position roughly */
 
128
        gtk_window_set_screen (GTK_WINDOW (icon->priv->dock), screen);
 
129
        gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (icon->priv->bar),
 
130
                                         1 - orientation);
 
131
 
 
132
        monitor_num = gdk_screen_get_monitor_at_point (screen, area.x, area.y);
 
133
        gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
 
134
 
 
135
        gtk_container_foreach (GTK_CONTAINER (icon->priv->dock),
 
136
                               (GtkCallback) gtk_widget_show_all, NULL);
 
137
        gtk_widget_get_preferred_size (icon->priv->dock, &dock_req, NULL);
 
138
 
 
139
        if (orientation == GTK_ORIENTATION_VERTICAL) {
 
140
                if (area.x + area.width + dock_req.width <= monitor.x + monitor.width) {
 
141
                        x = area.x + area.width;
 
142
                } else {
 
143
                        x = area.x - dock_req.width;
 
144
                }
 
145
                if (area.y + dock_req.height <= monitor.y + monitor.height) {
 
146
                        y = area.y;
 
147
                } else {
 
148
                        y = monitor.y + monitor.height - dock_req.height;
 
149
                }
 
150
        } else {
 
151
                if (area.y + area.height + dock_req.height <= monitor.y + monitor.height) {
 
152
                        y = area.y + area.height;
 
153
                } else {
 
154
                        y = area.y - dock_req.height;
 
155
                }
 
156
                if (area.x + dock_req.width <= monitor.x + monitor.width) {
 
157
                        x = area.x;
 
158
                } else {
 
159
                        x = monitor.x + monitor.width - dock_req.width;
 
160
                }
 
161
        }
 
162
 
 
163
        gtk_window_move (GTK_WINDOW (icon->priv->dock), x, y);
 
164
 
 
165
        /* FIXME: without this, the popup window appears as a square
 
166
         * after changing the orientation
 
167
         */
 
168
        gtk_window_resize (GTK_WINDOW (icon->priv->dock), 1, 1);
 
169
 
 
170
        gtk_widget_show_all (icon->priv->dock);
 
171
 
 
172
        /* grab focus */
 
173
        gtk_grab_add (icon->priv->dock);
 
174
 
 
175
        if (gdk_pointer_grab (gtk_widget_get_window (icon->priv->dock), TRUE,
 
176
                              GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
 
177
                              GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK, NULL, NULL,
 
178
                              time)
 
179
            != GDK_GRAB_SUCCESS) {
 
180
                gtk_grab_remove (icon->priv->dock);
 
181
                gtk_widget_hide (icon->priv->dock);
 
182
                return FALSE;
 
183
        }
 
184
 
 
185
        if (gdk_keyboard_grab (gtk_widget_get_window (icon->priv->dock), TRUE, time) != GDK_GRAB_SUCCESS) {
 
186
                display = gtk_widget_get_display (icon->priv->dock);
 
187
                gdk_display_pointer_ungrab (display, time);
 
188
                gtk_grab_remove (icon->priv->dock);
 
189
                gtk_widget_hide (icon->priv->dock);
 
190
                return FALSE;
 
191
        }
 
192
 
 
193
        gtk_widget_grab_focus (icon->priv->dock);
 
194
 
 
195
        return TRUE;
 
196
}
 
197
 
 
198
static void
 
199
on_status_icon_activate (GtkStatusIcon       *status_icon,
 
200
                         GvcStreamStatusIcon *icon)
 
201
{
 
202
        popup_dock (icon, GDK_CURRENT_TIME);
 
203
}
 
204
 
 
205
static void
 
206
on_menu_mute_toggled (GtkMenuItem         *item,
 
207
                      GvcStreamStatusIcon *icon)
 
208
{
 
209
        gboolean is_muted;
 
210
        is_muted = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item));
 
211
        gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (icon->priv->bar), is_muted);
 
212
}
 
213
 
 
214
static void
 
215
on_menu_activate_open_volume_control (GtkMenuItem *item,
 
216
                                      GvcStreamStatusIcon   *icon)
 
217
{
 
218
        GAppInfo *app;
 
219
        GdkAppLaunchContext *context;
 
220
        GError *error;
 
221
 
 
222
        error = NULL;
 
223
        context = gdk_app_launch_context_new ();
 
224
        app = g_app_info_create_from_commandline ("gnome-control-center sound", "Sound preferences", 0, &error);
 
225
        if (app)
 
226
                g_app_info_launch (app, NULL, G_APP_LAUNCH_CONTEXT (context), &error);
 
227
 
 
228
        if (error != NULL) {
 
229
                GtkWidget *dialog;
 
230
 
 
231
                dialog = gtk_message_dialog_new (NULL,
 
232
                                                 0,
 
233
                                                 GTK_MESSAGE_ERROR,
 
234
                                                 GTK_BUTTONS_CLOSE,
 
235
                                                 _("Failed to start Sound Preferences: %s"),
 
236
                                                 error->message);
 
237
                g_signal_connect (dialog,
 
238
                                  "response",
 
239
                                  G_CALLBACK (gtk_widget_destroy),
 
240
                                  NULL);
 
241
                gtk_widget_show (dialog);
 
242
                g_error_free (error);
 
243
        }
 
244
 
 
245
        g_object_unref (context);
 
246
        g_object_unref (app);
 
247
}
 
248
 
 
249
static void
 
250
on_status_icon_popup_menu (GtkStatusIcon       *status_icon,
 
251
                           guint                button,
 
252
                           guint                activate_time,
 
253
                           GvcStreamStatusIcon *icon)
 
254
{
 
255
        GtkWidget *menu;
 
256
        GtkWidget *item;
 
257
        GtkWidget *image;
 
258
 
 
259
        menu = gtk_menu_new ();
 
260
 
 
261
        item = gtk_check_menu_item_new_with_mnemonic (_("_Mute"));
 
262
        gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
 
263
                                        gvc_mixer_stream_get_is_muted (icon->priv->mixer_stream));
 
264
        g_signal_connect (item,
 
265
                          "toggled",
 
266
                          G_CALLBACK (on_menu_mute_toggled),
 
267
                          icon);
 
268
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
269
 
 
270
        item = gtk_image_menu_item_new_with_mnemonic (_("_Sound Preferences"));
 
271
        image = gtk_image_new_from_icon_name ("multimedia-volume-control",
 
272
                                              GTK_ICON_SIZE_MENU);
 
273
        gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
 
274
        g_signal_connect (item,
 
275
                          "activate",
 
276
                          G_CALLBACK (on_menu_activate_open_volume_control),
 
277
                          icon);
 
278
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
279
 
 
280
        gtk_widget_show_all (menu);
 
281
        gtk_menu_popup (GTK_MENU (menu),
 
282
                        NULL,
 
283
                        NULL,
 
284
                        gtk_status_icon_position_menu,
 
285
                        status_icon,
 
286
                        button,
 
287
                        activate_time);
 
288
}
 
289
 
 
290
static gboolean
 
291
on_status_icon_scroll_event (GtkStatusIcon       *status_icon,
 
292
                             GdkEventScroll      *event,
 
293
                             GvcStreamStatusIcon *icon)
 
294
{
 
295
        return gvc_channel_bar_scroll (GVC_CHANNEL_BAR (icon->priv->bar), event->direction);
 
296
}
 
297
 
 
298
static void
 
299
gvc_icon_release_grab (GvcStreamStatusIcon *icon,
 
300
                         GdkEventButton    *event)
 
301
{
 
302
        GdkDisplay     *display;
 
303
 
 
304
        /* ungrab focus */
 
305
        display = gtk_widget_get_display (GTK_WIDGET (icon->priv->dock));
 
306
        gdk_display_keyboard_ungrab (display, event->time);
 
307
        gdk_display_pointer_ungrab (display, event->time);
 
308
        gtk_grab_remove (icon->priv->dock);
 
309
 
 
310
        /* hide again */
 
311
        gtk_widget_hide (icon->priv->dock);
 
312
}
 
313
 
 
314
static gboolean
 
315
on_dock_button_press (GtkWidget      *widget,
 
316
                      GdkEventButton *event,
 
317
                      GvcStreamStatusIcon      *icon)
 
318
{
 
319
        if (event->type == GDK_BUTTON_PRESS) {
 
320
                gvc_icon_release_grab (icon, event);
 
321
                return TRUE;
 
322
        }
 
323
 
 
324
        return FALSE;
 
325
}
 
326
 
 
327
static void
 
328
popdown_dock (GvcStreamStatusIcon *icon)
 
329
{
 
330
        GdkDisplay *display;
 
331
 
 
332
        /* ungrab focus */
 
333
        display = gtk_widget_get_display (icon->priv->dock);
 
334
        gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
 
335
        gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
 
336
        gtk_grab_remove (icon->priv->dock);
 
337
 
 
338
        /* hide again */
 
339
        gtk_widget_hide (icon->priv->dock);
 
340
}
 
341
 
 
342
static gboolean
 
343
on_dock_key_release (GtkWidget           *widget,
 
344
                     GdkEventKey         *event,
 
345
                     GvcStreamStatusIcon *icon)
 
346
{
 
347
        if (event->keyval == GDK_KEY_Escape) {
 
348
                popdown_dock (icon);
 
349
                return TRUE;
 
350
        }
 
351
 
 
352
#if 0
 
353
        if (!gtk_bindings_activate_event (GTK_OBJECT (widget), event)) {
 
354
                /* The popup hasn't managed the event, pass onto the button */
 
355
                gtk_bindings_activate_event (GTK_OBJECT (user_data), event);
 
356
        }
 
357
#endif
 
358
        return TRUE;
 
359
}
 
360
 
 
361
static gboolean
 
362
on_dock_scroll_event (GtkWidget           *widget,
 
363
                      GdkEventScroll      *event,
 
364
                      GvcStreamStatusIcon *icon)
 
365
{
 
366
        /* Forward event to the status icon */
 
367
        on_status_icon_scroll_event (NULL, event, icon);
 
368
        return TRUE;
 
369
}
 
370
 
 
371
static void
 
372
update_icon (GvcStreamStatusIcon *icon)
 
373
{
 
374
        guint    volume;
 
375
        gboolean is_muted;
 
376
        guint    n;
 
377
        char    *markup;
 
378
        gboolean can_decibel;
 
379
        gdouble  db;
 
380
 
 
381
        if (icon->priv->mixer_stream == NULL) {
 
382
                return;
 
383
        }
 
384
 
 
385
        volume = gvc_mixer_stream_get_volume (icon->priv->mixer_stream);
 
386
        is_muted = gvc_mixer_stream_get_is_muted (icon->priv->mixer_stream);
 
387
        db = gvc_mixer_stream_get_decibel (icon->priv->mixer_stream);
 
388
        can_decibel = gvc_mixer_stream_get_can_decibel (icon->priv->mixer_stream);
 
389
 
 
390
        /* select image */
 
391
        if (volume <= 0 || is_muted) {
 
392
                n = 0;
 
393
        } else {
 
394
                n = 3 * volume / PA_VOLUME_NORM + 1;
 
395
                if (n < 1) {
 
396
                        n = 1;
 
397
                } else if (n > 3) {
 
398
                        n = 3;
 
399
                }
 
400
        }
 
401
 
 
402
        /* apparently status icon will reset icon even if
 
403
         * if doesn't change */
 
404
        if (icon->priv->current_icon != n) {
 
405
                gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON (icon),
 
406
                                                    icon->priv->icon_names [n]);
 
407
                icon->priv->current_icon = n;
 
408
        }
 
409
 
 
410
 
 
411
        if (is_muted) {
 
412
                markup = g_strdup_printf (
 
413
                                          "<b>%s: %s</b>\n<small>%s</small>",
 
414
                                          icon->priv->display_name,
 
415
                                          _("Muted"),
 
416
                                          gvc_mixer_stream_get_description (icon->priv->mixer_stream));
 
417
        } else if (can_decibel && (db > PA_DECIBEL_MININFTY)) {
 
418
                markup = g_strdup_printf (
 
419
                                          "<b>%s: %.0f%%</b>\n<small>%0.2f dB\n%s</small>",
 
420
                                          icon->priv->display_name,
 
421
                                          100 * (float)volume / PA_VOLUME_NORM,
 
422
                                          db,
 
423
                                          gvc_mixer_stream_get_description (icon->priv->mixer_stream));
 
424
        } else if (can_decibel) {
 
425
                markup = g_strdup_printf (
 
426
                                          "<b>%s: %.0f%%</b>\n<small>-&#8734; dB\n%s</small>",
 
427
                                          icon->priv->display_name,
 
428
                                          100 * (float)volume / PA_VOLUME_NORM,
 
429
                                          gvc_mixer_stream_get_description (icon->priv->mixer_stream));
 
430
        } else {
 
431
                markup = g_strdup_printf (
 
432
                                          "<b>%s: %.0f%%</b>\n<small>%s</small>",
 
433
                                          icon->priv->display_name,
 
434
                                          100 * (float)volume / PA_VOLUME_NORM,
 
435
                                          gvc_mixer_stream_get_description (icon->priv->mixer_stream));
 
436
        }
 
437
        gtk_status_icon_set_tooltip_markup (GTK_STATUS_ICON (icon), markup);
 
438
        g_free (markup);
 
439
}
 
440
 
 
441
void
 
442
gvc_stream_status_icon_set_icon_names (GvcStreamStatusIcon  *icon,
 
443
                                       const char          **names)
 
444
{
 
445
        g_return_if_fail (GVC_IS_STREAM_STATUS_ICON (icon));
 
446
 
 
447
        g_strfreev (icon->priv->icon_names);
 
448
        icon->priv->icon_names = g_strdupv ((char **)names);
 
449
        update_icon (icon);
 
450
        g_object_notify (G_OBJECT (icon), "icon-names");
 
451
}
 
452
 
 
453
static void
 
454
on_stream_volume_notify (GObject             *object,
 
455
                         GParamSpec          *pspec,
 
456
                         GvcStreamStatusIcon *icon)
 
457
{
 
458
        update_icon (icon);
 
459
        update_dock (icon);
 
460
}
 
461
 
 
462
static void
 
463
on_stream_is_muted_notify (GObject             *object,
 
464
                           GParamSpec          *pspec,
 
465
                           GvcStreamStatusIcon *icon)
 
466
{
 
467
        update_icon (icon);
 
468
        update_dock (icon);
 
469
}
 
470
 
 
471
void
 
472
gvc_stream_status_icon_set_display_name (GvcStreamStatusIcon *icon,
 
473
                                         const char          *name)
 
474
{
 
475
        g_return_if_fail (GVC_STREAM_STATUS_ICON (icon));
 
476
 
 
477
        g_free (icon->priv->display_name);
 
478
        icon->priv->display_name = g_strdup (name);
 
479
        update_icon (icon);
 
480
        g_object_notify (G_OBJECT (icon), "display-name");
 
481
}
 
482
 
 
483
void
 
484
gvc_stream_status_icon_set_mixer_stream (GvcStreamStatusIcon *icon,
 
485
                                         GvcMixerStream      *stream)
 
486
{
 
487
        g_return_if_fail (GVC_STREAM_STATUS_ICON (icon));
 
488
 
 
489
        if (stream != NULL) {
 
490
                g_object_ref (stream);
 
491
        }
 
492
 
 
493
        if (icon->priv->mixer_stream != NULL) {
 
494
                g_signal_handlers_disconnect_by_func (icon->priv->mixer_stream,
 
495
                                                      G_CALLBACK (on_stream_volume_notify),
 
496
                                                      icon);
 
497
                g_signal_handlers_disconnect_by_func (icon->priv->mixer_stream,
 
498
                                                      G_CALLBACK (on_stream_is_muted_notify),
 
499
                                                      icon);
 
500
                g_object_unref (icon->priv->mixer_stream);
 
501
                icon->priv->mixer_stream = NULL;
 
502
        }
 
503
 
 
504
        icon->priv->mixer_stream = stream;
 
505
 
 
506
        if (icon->priv->mixer_stream != NULL) {
 
507
                GtkAdjustment *adj;
 
508
 
 
509
                g_object_ref (icon->priv->mixer_stream);
 
510
 
 
511
                icon->priv->thaw = TRUE;
 
512
                adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (icon->priv->bar)));
 
513
                gtk_adjustment_set_value (adj,
 
514
                                          gvc_mixer_stream_get_volume (icon->priv->mixer_stream));
 
515
                icon->priv->thaw = FALSE;
 
516
 
 
517
                g_signal_connect (icon->priv->mixer_stream,
 
518
                                  "notify::volume",
 
519
                                  G_CALLBACK (on_stream_volume_notify),
 
520
                                  icon);
 
521
                g_signal_connect (icon->priv->mixer_stream,
 
522
                                  "notify::is-muted",
 
523
                                  G_CALLBACK (on_stream_is_muted_notify),
 
524
                                  icon);
 
525
        }
 
526
 
 
527
        update_icon (icon);
 
528
 
 
529
        g_object_notify (G_OBJECT (icon), "mixer-stream");
 
530
}
 
531
 
 
532
static void
 
533
gvc_stream_status_icon_set_property (GObject       *object,
 
534
                                     guint          prop_id,
 
535
                                     const GValue  *value,
 
536
                                     GParamSpec    *pspec)
 
537
{
 
538
        GvcStreamStatusIcon *self = GVC_STREAM_STATUS_ICON (object);
 
539
 
 
540
        switch (prop_id) {
 
541
        case PROP_MIXER_STREAM:
 
542
                gvc_stream_status_icon_set_mixer_stream (self, g_value_get_object (value));
 
543
                break;
 
544
        case PROP_DISPLAY_NAME:
 
545
                gvc_stream_status_icon_set_display_name (self, g_value_get_string (value));
 
546
                break;
 
547
        case PROP_ICON_NAMES:
 
548
                gvc_stream_status_icon_set_icon_names (self, g_value_get_boxed (value));
 
549
                break;
 
550
        default:
 
551
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
552
                break;
 
553
        }
 
554
}
 
555
 
 
556
static void
 
557
gvc_stream_status_icon_get_property (GObject     *object,
 
558
                                     guint        prop_id,
 
559
                                     GValue      *value,
 
560
                                     GParamSpec  *pspec)
 
561
{
 
562
        GvcStreamStatusIcon *self = GVC_STREAM_STATUS_ICON (object);
 
563
        GvcStreamStatusIconPrivate *priv = self->priv;
 
564
 
 
565
        switch (prop_id) {
 
566
        case PROP_MIXER_STREAM:
 
567
                g_value_set_object (value, priv->mixer_stream);
 
568
                break;
 
569
        case PROP_DISPLAY_NAME:
 
570
                g_value_set_string (value, priv->display_name);
 
571
                break;
 
572
        case PROP_ICON_NAMES:
 
573
                g_value_set_boxed (value, priv->icon_names);
 
574
                break;
 
575
        default:
 
576
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
577
                break;
 
578
        }
 
579
}
 
580
 
 
581
static void
 
582
on_bar_is_muted_notify (GObject             *object,
 
583
                        GParamSpec          *pspec,
 
584
                        GvcStreamStatusIcon *icon)
 
585
{
 
586
        gboolean is_muted;
 
587
 
 
588
        is_muted = gvc_channel_bar_get_is_muted (GVC_CHANNEL_BAR (object));
 
589
 
 
590
        if (gvc_mixer_stream_get_is_muted (icon->priv->mixer_stream) != is_muted) {
 
591
                /* Update the stream before pushing the change */
 
592
                gvc_mixer_stream_set_is_muted (icon->priv->mixer_stream, is_muted);
 
593
                gvc_mixer_stream_change_is_muted (icon->priv->mixer_stream,
 
594
                                                  is_muted);
 
595
        }
 
596
}
 
597
 
 
598
static GObject *
 
599
gvc_stream_status_icon_constructor (GType                  type,
 
600
                                    guint                  n_construct_properties,
 
601
                                    GObjectConstructParam *construct_params)
 
602
{
 
603
        GObject             *object;
 
604
        GvcStreamStatusIcon *icon;
 
605
        GtkWidget           *frame;
 
606
        GtkWidget           *box;
 
607
        GtkAdjustment       *adj;
 
608
 
 
609
        object = G_OBJECT_CLASS (gvc_stream_status_icon_parent_class)->constructor (type, n_construct_properties, construct_params);
 
610
 
 
611
        icon = GVC_STREAM_STATUS_ICON (object);
 
612
 
 
613
        gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON (icon),
 
614
                                            icon->priv->icon_names[0]);
 
615
 
 
616
        /* window */
 
617
        icon->priv->dock = gtk_window_new (GTK_WINDOW_POPUP);
 
618
        gtk_widget_set_name (icon->priv->dock, "gvc-stream-status-icon-popup-window");
 
619
        g_signal_connect (icon->priv->dock,
 
620
                          "button-press-event",
 
621
                          G_CALLBACK (on_dock_button_press),
 
622
                          icon);
 
623
        g_signal_connect (icon->priv->dock,
 
624
                          "key-release-event",
 
625
                          G_CALLBACK (on_dock_key_release),
 
626
                          icon);
 
627
        g_signal_connect (icon->priv->dock,
 
628
                          "scroll-event",
 
629
                          G_CALLBACK (on_dock_scroll_event),
 
630
                          icon);
 
631
 
 
632
        gtk_window_set_decorated (GTK_WINDOW (icon->priv->dock), FALSE);
 
633
 
 
634
        frame = gtk_frame_new (NULL);
 
635
        gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
 
636
        gtk_container_add (GTK_CONTAINER (icon->priv->dock), frame);
 
637
 
 
638
        box = gtk_vbox_new (FALSE, 6);
 
639
        gtk_container_set_border_width (GTK_CONTAINER (box), 2);
 
640
        gtk_container_add (GTK_CONTAINER (frame), box);
 
641
 
 
642
        icon->priv->bar = gvc_channel_bar_new ();
 
643
        gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (icon->priv->bar),
 
644
                                         GTK_ORIENTATION_VERTICAL);
 
645
 
 
646
        gtk_box_pack_start (GTK_BOX (box), icon->priv->bar, TRUE, FALSE, 0);
 
647
        g_signal_connect (icon->priv->bar,
 
648
                          "notify::is-muted",
 
649
                          G_CALLBACK (on_bar_is_muted_notify),
 
650
                          icon);
 
651
 
 
652
        adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (icon->priv->bar)));
 
653
        g_signal_connect (adj,
 
654
                          "value-changed",
 
655
                          G_CALLBACK (on_adjustment_value_changed),
 
656
                          icon);
 
657
 
 
658
        return object;
 
659
}
 
660
 
 
661
static void
 
662
gvc_stream_status_icon_dispose (GObject *object)
 
663
{
 
664
        GvcStreamStatusIcon *icon = GVC_STREAM_STATUS_ICON (object);
 
665
 
 
666
        if (icon->priv->dock != NULL) {
 
667
                gtk_widget_destroy (icon->priv->dock);
 
668
                icon->priv->dock = NULL;
 
669
        }
 
670
 
 
671
        if (icon->priv->mixer_stream != NULL) {
 
672
                g_object_unref (icon->priv->mixer_stream);
 
673
                icon->priv->mixer_stream = NULL;
 
674
        }
 
675
 
 
676
        G_OBJECT_CLASS (gvc_stream_status_icon_parent_class)->dispose (object);
 
677
}
 
678
 
 
679
static void
 
680
gvc_stream_status_icon_class_init (GvcStreamStatusIconClass *klass)
 
681
{
 
682
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
683
 
 
684
        object_class->constructor = gvc_stream_status_icon_constructor;
 
685
        object_class->finalize = gvc_stream_status_icon_finalize;
 
686
        object_class->dispose = gvc_stream_status_icon_dispose;
 
687
        object_class->set_property = gvc_stream_status_icon_set_property;
 
688
        object_class->get_property = gvc_stream_status_icon_get_property;
 
689
 
 
690
        g_object_class_install_property (object_class,
 
691
                                         PROP_MIXER_STREAM,
 
692
                                         g_param_spec_object ("mixer-stream",
 
693
                                                              "mixer stream",
 
694
                                                              "mixer stream",
 
695
                                                              GVC_TYPE_MIXER_STREAM,
 
696
                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
 
697
        g_object_class_install_property (object_class,
 
698
                                         PROP_DISPLAY_NAME,
 
699
                                         g_param_spec_string ("display-name",
 
700
                                                              "Display Name",
 
701
                                                              "Name to display for this stream",
 
702
                                                              NULL,
 
703
                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
 
704
        g_object_class_install_property (object_class,
 
705
                                         PROP_ICON_NAMES,
 
706
                                         g_param_spec_boxed ("icon-names",
 
707
                                                             "Icon Names",
 
708
                                                             "Name of icon to display for this stream",
 
709
                                                              G_TYPE_STRV,
 
710
                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
 
711
 
 
712
        g_type_class_add_private (klass, sizeof (GvcStreamStatusIconPrivate));
 
713
}
 
714
 
 
715
static void
 
716
on_status_icon_visible_notify (GvcStreamStatusIcon *icon)
 
717
{
 
718
        gboolean visible;
 
719
 
 
720
        g_object_get (icon, "visible", &visible, NULL);
 
721
        if (! visible) {
 
722
                if (icon->priv->dock != NULL) {
 
723
                        gtk_widget_hide (icon->priv->dock);
 
724
                }
 
725
        }
 
726
}
 
727
 
 
728
static void
 
729
gvc_stream_status_icon_init (GvcStreamStatusIcon *icon)
 
730
{
 
731
        icon->priv = GVC_STREAM_STATUS_ICON_GET_PRIVATE (icon);
 
732
 
 
733
        g_signal_connect (icon,
 
734
                          "activate",
 
735
                          G_CALLBACK (on_status_icon_activate),
 
736
                          icon);
 
737
        g_signal_connect (icon,
 
738
                          "popup-menu",
 
739
                          G_CALLBACK (on_status_icon_popup_menu),
 
740
                          icon);
 
741
        g_signal_connect (icon,
 
742
                          "scroll-event",
 
743
                          G_CALLBACK (on_status_icon_scroll_event),
 
744
                          icon);
 
745
        g_signal_connect (icon,
 
746
                          "notify::visible",
 
747
                          G_CALLBACK (on_status_icon_visible_notify),
 
748
                          NULL);
 
749
 
 
750
        icon->priv->thaw = FALSE;
 
751
}
 
752
 
 
753
static void
 
754
gvc_stream_status_icon_finalize (GObject *object)
 
755
{
 
756
        GvcStreamStatusIcon *stream_status_icon;
 
757
 
 
758
        g_return_if_fail (object != NULL);
 
759
        g_return_if_fail (GVC_IS_STREAM_STATUS_ICON (object));
 
760
 
 
761
        stream_status_icon = GVC_STREAM_STATUS_ICON (object);
 
762
 
 
763
        g_return_if_fail (stream_status_icon->priv != NULL);
 
764
 
 
765
        g_strfreev (stream_status_icon->priv->icon_names);
 
766
 
 
767
        G_OBJECT_CLASS (gvc_stream_status_icon_parent_class)->finalize (object);
 
768
}
 
769
 
 
770
GvcStreamStatusIcon *
 
771
gvc_stream_status_icon_new (GvcMixerStream *stream,
 
772
                            const char    **icon_names)
 
773
{
 
774
        GObject *icon;
 
775
        icon = g_object_new (GVC_TYPE_STREAM_STATUS_ICON,
 
776
                             "mixer-stream", stream,
 
777
                             "icon-names", icon_names,
 
778
                             NULL);
 
779
        return GVC_STREAM_STATUS_ICON (icon);
 
780
}