~kroq-gar78/ubuntu/precise/gnome-control-center/fix-885947

« back to all changes in this revision

Viewing changes to panels/sound/gvc-mixer-dialog.c

  • Committer: Bazaar Package Importer
  • Author(s): Rodrigo Moya
  • Date: 2011-05-17 10:47:27 UTC
  • mfrom: (0.1.11 experimental) (1.1.45 upstream)
  • Revision ID: james.westby@ubuntu.com-20110517104727-lqel6m8vhfw5jby1
Tags: 1:3.0.1.1-1ubuntu1
* Rebase on Debian, remaining Ubuntu changes:
* debian/control:
  - Build-Depend on hardening-wrapper, dpkg-dev and dh-autoreconf
  - Add dependency on ubuntu-system-service
  - Remove dependency on gnome-icon-theme-symbolic
  - Move dependency on apg, gnome-icon-theme-symbolic and accountsservice to
    be a Recommends: until we get them in main
* debian/rules:
  - Use autoreconf
  - Add binary-post-install rule for gnome-control-center-data
  - Run dh-autoreconf
* debian/gnome-control-center.dirs:
* debian/gnome-control-center.links:
  - Add a link to the control center shell for indicators
* debian/patches/00_disable-nm.patch:
  - Temporary patch to disable building with NetworkManager until we get
    the new one in the archive
* debian/patches/01_git_remove_gettext_calls.patch:
  - Remove calls to AM_GNU_GETTEXT, IT_PROG_INTLTOOL should be enough
* debian/patches/01_git_kill_warning.patch:
  - Kill warning
* debian/patches/50_ubuntu_systemwide_prefs.patch:
  - Ubuntu specific proxy preferences
* debian/patches/51_ubuntu_system_keyboard.patch:
  - Implement the global keyboard spec at https://wiki.ubuntu.com/DefaultKeyboardSettings

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-lib.h>
 
29
#include <gdk/gdkkeysyms.h>
 
30
#include <gtk/gtk.h>
 
31
#include <pulse/pulseaudio.h>
 
32
 
 
33
#include "gvc-channel-bar.h"
 
34
#include "gvc-balance-bar.h"
 
35
#include "gvc-combo-box.h"
 
36
#include "gvc-mixer-control.h"
 
37
#include "gvc-mixer-card.h"
 
38
#include "gvc-mixer-sink.h"
 
39
#include "gvc-mixer-source.h"
 
40
#include "gvc-mixer-source-output.h"
 
41
#include "gvc-mixer-dialog.h"
 
42
#include "gvc-sound-theme-chooser.h"
 
43
#include "gvc-level-bar.h"
 
44
#include "gvc-speaker-test.h"
 
45
#include "gvc-mixer-control-private.h"
 
46
 
 
47
#define SCALE_SIZE 128
 
48
 
 
49
#define GVC_MIXER_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_DIALOG, GvcMixerDialogPrivate))
 
50
 
 
51
struct GvcMixerDialogPrivate
 
52
{
 
53
        GvcMixerControl *mixer_control;
 
54
        GHashTable      *bars;
 
55
        GtkWidget       *notebook;
 
56
        GtkWidget       *output_bar;
 
57
        GtkWidget       *input_bar;
 
58
        GtkWidget       *input_level_bar;
 
59
        GtkWidget       *effects_bar;
 
60
        GtkWidget       *output_stream_box;
 
61
        GtkWidget       *sound_effects_box;
 
62
        GtkWidget       *hw_box;
 
63
        GtkWidget       *hw_treeview;
 
64
        GtkWidget       *hw_settings_box;
 
65
        GtkWidget       *hw_profile_combo;
 
66
        GtkWidget       *input_box;
 
67
        GtkWidget       *output_box;
 
68
        GtkWidget       *applications_box;
 
69
        GtkWidget       *no_apps_label;
 
70
        GtkWidget       *output_treeview;
 
71
        GtkWidget       *output_settings_box;
 
72
        GtkWidget       *output_balance_bar;
 
73
        GtkWidget       *output_fade_bar;
 
74
        GtkWidget       *output_lfe_bar;
 
75
        GtkWidget       *output_port_combo;
 
76
        GtkWidget       *input_treeview;
 
77
        GtkWidget       *input_port_combo;
 
78
        GtkWidget       *input_settings_box;
 
79
        GtkWidget       *sound_theme_chooser;
 
80
        GtkWidget       *click_feedback_button;
 
81
        GtkWidget       *audible_bell_button;
 
82
        GtkSizeGroup    *size_group;
 
83
 
 
84
        gdouble          last_input_peak;
 
85
        guint            num_apps;
 
86
};
 
87
 
 
88
enum {
 
89
        NAME_COLUMN,
 
90
        DEVICE_COLUMN,
 
91
        ACTIVE_COLUMN,
 
92
        ID_COLUMN,
 
93
        SPEAKERS_COLUMN,
 
94
        ICON_COLUMN,
 
95
        NUM_COLUMNS
 
96
};
 
97
 
 
98
enum {
 
99
        HW_ID_COLUMN,
 
100
        HW_ICON_COLUMN,
 
101
        HW_NAME_COLUMN,
 
102
        HW_STATUS_COLUMN,
 
103
        HW_PROFILE_COLUMN,
 
104
        HW_PROFILE_HUMAN_COLUMN,
 
105
        HW_SENSITIVE_COLUMN,
 
106
        HW_NUM_COLUMNS
 
107
};
 
108
 
 
109
enum
 
110
{
 
111
        PROP_0,
 
112
        PROP_MIXER_CONTROL
 
113
};
 
114
 
 
115
static void     gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass);
 
116
static void     gvc_mixer_dialog_init       (GvcMixerDialog      *mixer_dialog);
 
117
static void     gvc_mixer_dialog_finalize   (GObject             *object);
 
118
 
 
119
static void     bar_set_stream              (GvcMixerDialog      *dialog,
 
120
                                             GtkWidget           *bar,
 
121
                                             GvcMixerStream      *stream);
 
122
 
 
123
static void     on_adjustment_value_changed (GtkAdjustment  *adjustment,
 
124
                                             GvcMixerDialog *dialog);
 
125
 
 
126
G_DEFINE_TYPE (GvcMixerDialog, gvc_mixer_dialog, GTK_TYPE_VBOX)
 
127
 
 
128
static void
 
129
update_default_input (GvcMixerDialog *dialog)
 
130
{
 
131
        GtkTreeModel *model;
 
132
        GtkTreeIter   iter;
 
133
        gboolean      ret;
 
134
 
 
135
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview));
 
136
        ret = gtk_tree_model_get_iter_first (model, &iter);
 
137
        if (ret == FALSE) {
 
138
                g_debug ("No default input selected or available");
 
139
                return;
 
140
        }
 
141
        do {
 
142
                gboolean        toggled;
 
143
                gboolean        is_default;
 
144
                guint           id;
 
145
                GvcMixerStream *stream;
 
146
 
 
147
                gtk_tree_model_get (model, &iter,
 
148
                                    ID_COLUMN, &id,
 
149
                                    ACTIVE_COLUMN, &toggled,
 
150
                                    -1);
 
151
 
 
152
                stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
 
153
                if (stream == NULL) {
 
154
                        g_warning ("Unable to find stream for id: %u", id);
 
155
                        continue;
 
156
                }
 
157
 
 
158
                is_default = FALSE;
 
159
                if (stream == gvc_mixer_control_get_default_source (dialog->priv->mixer_control)) {
 
160
                        is_default = TRUE;
 
161
                }
 
162
 
 
163
                gtk_list_store_set (GTK_LIST_STORE (model),
 
164
                                    &iter,
 
165
                                    ACTIVE_COLUMN, is_default,
 
166
                                    -1);
 
167
                if (is_default) {
 
168
                        GtkTreeSelection *selection;
 
169
 
 
170
                        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->input_treeview));
 
171
                        gtk_tree_selection_select_iter (selection, &iter);
 
172
                }
 
173
        } while (gtk_tree_model_iter_next (model, &iter));
 
174
}
 
175
 
 
176
static void
 
177
update_description (GvcMixerDialog *dialog,
 
178
                    guint column,
 
179
                    const char *value,
 
180
                    GvcMixerStream *stream)
 
181
{
 
182
        GtkTreeModel *model;
 
183
        GtkTreeIter   iter;
 
184
        guint         id;
 
185
 
 
186
        if (GVC_IS_MIXER_SOURCE (stream))
 
187
                model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview));
 
188
        else if (GVC_IS_MIXER_SINK (stream))
 
189
                model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview));
 
190
        else
 
191
                g_assert_not_reached ();
 
192
        gtk_tree_model_get_iter_first (model, &iter);
 
193
 
 
194
        id = gvc_mixer_stream_get_id (stream);
 
195
        do {
 
196
                guint       current_id;
 
197
 
 
198
                gtk_tree_model_get (model, &iter,
 
199
                                    ID_COLUMN, &current_id,
 
200
                                    -1);
 
201
                if (id != current_id)
 
202
                        continue;
 
203
 
 
204
                gtk_list_store_set (GTK_LIST_STORE (model),
 
205
                                    &iter,
 
206
                                    column, value,
 
207
                                    -1);
 
208
                break;
 
209
        } while (gtk_tree_model_iter_next (model, &iter));
 
210
}
 
211
 
 
212
static void
 
213
port_selection_changed (GvcComboBox *combo_box,
 
214
                        const char  *port,
 
215
                        GvcMixerDialog *dialog)
 
216
{
 
217
        GvcMixerStream *stream;
 
218
 
 
219
        stream = g_object_get_data (G_OBJECT (combo_box), "stream");
 
220
        if (stream == NULL) {
 
221
                g_warning ("Could not find stream for port combo box");
 
222
                return;
 
223
        }
 
224
        if (gvc_mixer_stream_change_port (stream, port) == FALSE) {
 
225
                g_warning ("Could not change port for stream");
 
226
        }
 
227
}
 
228
 
 
229
static void
 
230
update_output_settings (GvcMixerDialog *dialog)
 
231
{
 
232
        GvcMixerStream      *stream;
 
233
        const GvcChannelMap *map;
 
234
        const GList         *ports;
 
235
        GtkAdjustment       *adj;
 
236
 
 
237
        g_debug ("Updating output settings");
 
238
        if (dialog->priv->output_balance_bar != NULL) {
 
239
                gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box),
 
240
                                      dialog->priv->output_balance_bar);
 
241
                dialog->priv->output_balance_bar = NULL;
 
242
        }
 
243
        if (dialog->priv->output_fade_bar != NULL) {
 
244
                gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box),
 
245
                                      dialog->priv->output_fade_bar);
 
246
                dialog->priv->output_fade_bar = NULL;
 
247
        }
 
248
        if (dialog->priv->output_lfe_bar != NULL) {
 
249
                gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box),
 
250
                                      dialog->priv->output_lfe_bar);
 
251
                dialog->priv->output_lfe_bar = NULL;
 
252
        }
 
253
        if (dialog->priv->output_port_combo != NULL) {
 
254
                gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box),
 
255
                                      dialog->priv->output_port_combo);
 
256
                dialog->priv->output_port_combo = NULL;
 
257
        }
 
258
 
 
259
        stream = gvc_mixer_control_get_default_sink (dialog->priv->mixer_control);
 
260
        if (stream == NULL) {
 
261
                g_warning ("Default sink stream not found");
 
262
                return;
 
263
        }
 
264
 
 
265
        gvc_channel_bar_set_base_volume (GVC_CHANNEL_BAR (dialog->priv->output_bar),
 
266
                                         gvc_mixer_stream_get_base_volume (stream));
 
267
        gvc_channel_bar_set_is_amplified (GVC_CHANNEL_BAR (dialog->priv->output_bar),
 
268
                                          gvc_mixer_stream_get_can_decibel (stream));
 
269
 
 
270
        /* Update the adjustment in case the previous bar wasn't decibel
 
271
         * capable, and we clipped it */
 
272
        adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (dialog->priv->output_bar)));
 
273
        gtk_adjustment_set_value (adj,
 
274
                                  gvc_mixer_stream_get_volume (stream));
 
275
 
 
276
        map = gvc_mixer_stream_get_channel_map (stream);
 
277
        if (map == NULL) {
 
278
                g_warning ("Default sink stream has no channel map");
 
279
                return;
 
280
        }
 
281
 
 
282
        dialog->priv->output_balance_bar = gvc_balance_bar_new (map, BALANCE_TYPE_RL);
 
283
        if (dialog->priv->size_group != NULL) {
 
284
                gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_balance_bar),
 
285
                                                dialog->priv->size_group,
 
286
                                                TRUE);
 
287
        }
 
288
        gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box),
 
289
                            dialog->priv->output_balance_bar,
 
290
                            FALSE, FALSE, 6);
 
291
        gtk_widget_show (dialog->priv->output_balance_bar);
 
292
 
 
293
        if (gvc_channel_map_can_fade (map)) {
 
294
                dialog->priv->output_fade_bar = gvc_balance_bar_new (map, BALANCE_TYPE_FR);
 
295
                if (dialog->priv->size_group != NULL) {
 
296
                        gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_fade_bar),
 
297
                                                        dialog->priv->size_group,
 
298
                                                        TRUE);
 
299
                }
 
300
                gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box),
 
301
                                    dialog->priv->output_fade_bar,
 
302
                                    FALSE, FALSE, 6);
 
303
                gtk_widget_show (dialog->priv->output_fade_bar);
 
304
        }
 
305
 
 
306
        if (gvc_channel_map_has_lfe (map)) {
 
307
                dialog->priv->output_lfe_bar = gvc_balance_bar_new (map, BALANCE_TYPE_LFE);
 
308
                if (dialog->priv->size_group != NULL) {
 
309
                        gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_lfe_bar),
 
310
                                                        dialog->priv->size_group,
 
311
                                                        TRUE);
 
312
                }
 
313
                gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box),
 
314
                                    dialog->priv->output_lfe_bar,
 
315
                                    FALSE, FALSE, 6);
 
316
                gtk_widget_show (dialog->priv->output_lfe_bar);
 
317
        }
 
318
 
 
319
        ports = gvc_mixer_stream_get_ports (stream);
 
320
        if (ports != NULL) {
 
321
                const GvcMixerStreamPort *port;
 
322
                port = gvc_mixer_stream_get_port (stream);
 
323
 
 
324
                dialog->priv->output_port_combo = gvc_combo_box_new (_("Co_nnector:"));
 
325
                gvc_combo_box_set_ports (GVC_COMBO_BOX (dialog->priv->output_port_combo),
 
326
                                         ports);
 
327
                gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->output_port_combo), port->port);
 
328
                g_object_set_data (G_OBJECT (dialog->priv->output_port_combo), "stream", stream);
 
329
                g_signal_connect (G_OBJECT (dialog->priv->output_port_combo), "changed",
 
330
                                  G_CALLBACK (port_selection_changed), dialog);
 
331
 
 
332
                gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box),
 
333
                                    dialog->priv->output_port_combo,
 
334
                                    TRUE, FALSE, 6);
 
335
 
 
336
                gvc_combo_box_set_size_group (GVC_COMBO_BOX (dialog->priv->output_port_combo), dialog->priv->size_group, FALSE);
 
337
 
 
338
                gtk_widget_show (dialog->priv->output_port_combo);
 
339
        }
 
340
 
 
341
        /* FIXME: We could make this into a "No settings" label instead */
 
342
        gtk_widget_set_sensitive (dialog->priv->output_balance_bar, gvc_channel_map_can_balance (map));
 
343
}
 
344
 
 
345
static void
 
346
update_default_output (GvcMixerDialog *dialog)
 
347
{
 
348
        GtkTreeModel *model;
 
349
        GtkTreeIter   iter;
 
350
 
 
351
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview));
 
352
        gtk_tree_model_get_iter_first (model, &iter);
 
353
        do {
 
354
                gboolean        toggled;
 
355
                gboolean        is_default;
 
356
                guint           id;
 
357
                GvcMixerStream *stream;
 
358
 
 
359
                gtk_tree_model_get (model, &iter,
 
360
                                    ID_COLUMN, &id,
 
361
                                    ACTIVE_COLUMN, &toggled,
 
362
                                    -1);
 
363
 
 
364
                stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
 
365
                if (stream == NULL) {
 
366
                        g_warning ("Unable to find stream for id: %u", id);
 
367
                        continue;
 
368
                }
 
369
 
 
370
                is_default = FALSE;
 
371
                if (stream == gvc_mixer_control_get_default_sink (dialog->priv->mixer_control)) {
 
372
                        is_default = TRUE;
 
373
                }
 
374
 
 
375
                gtk_list_store_set (GTK_LIST_STORE (model),
 
376
                                    &iter,
 
377
                                    ACTIVE_COLUMN, is_default,
 
378
                                    -1);
 
379
                if (is_default) {
 
380
                        GtkTreeSelection *selection;
 
381
 
 
382
                        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->output_treeview));
 
383
                        gtk_tree_selection_select_iter (selection, &iter);
 
384
                }
 
385
        } while (gtk_tree_model_iter_next (model, &iter));
 
386
}
 
387
 
 
388
static void
 
389
on_mixer_control_default_sink_changed (GvcMixerControl *control,
 
390
                                       guint            id,
 
391
                                       GvcMixerDialog  *dialog)
 
392
{
 
393
        GvcMixerStream *stream;
 
394
 
 
395
        g_debug ("GvcMixerDialog: default sink changed: %u", id);
 
396
 
 
397
        if (id == PA_INVALID_INDEX)
 
398
                stream = NULL;
 
399
        else
 
400
                stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control,
 
401
                                                             id);
 
402
        bar_set_stream (dialog, dialog->priv->output_bar, stream);
 
403
 
 
404
        update_output_settings (dialog);
 
405
 
 
406
        update_default_output (dialog);
 
407
}
 
408
 
 
409
 
 
410
#define DECAY_STEP .15
 
411
 
 
412
static void
 
413
update_input_peak (GvcMixerDialog *dialog,
 
414
                   gdouble         v)
 
415
{
 
416
        GtkAdjustment *adj;
 
417
 
 
418
        if (dialog->priv->last_input_peak >= DECAY_STEP) {
 
419
                if (v < dialog->priv->last_input_peak - DECAY_STEP) {
 
420
                        v = dialog->priv->last_input_peak - DECAY_STEP;
 
421
                }
 
422
        }
 
423
 
 
424
        dialog->priv->last_input_peak = v;
 
425
 
 
426
        adj = gvc_level_bar_get_peak_adjustment (GVC_LEVEL_BAR (dialog->priv->input_level_bar));
 
427
        if (v >= 0) {
 
428
                gtk_adjustment_set_value (adj, v);
 
429
        } else {
 
430
                gtk_adjustment_set_value (adj, 0.0);
 
431
        }
 
432
}
 
433
 
 
434
static void
 
435
update_input_meter (GvcMixerDialog *dialog,
 
436
                    uint32_t        source_index,
 
437
                    uint32_t        sink_input_idx,
 
438
                    double          v)
 
439
{
 
440
        update_input_peak (dialog, v);
 
441
}
 
442
 
 
443
static void
 
444
on_monitor_suspended_callback (pa_stream *s,
 
445
                               void      *userdata)
 
446
{
 
447
        GvcMixerDialog *dialog;
 
448
 
 
449
        dialog = userdata;
 
450
 
 
451
        if (pa_stream_is_suspended (s)) {
 
452
                g_debug ("Stream suspended");
 
453
                update_input_meter (dialog,
 
454
                                    pa_stream_get_device_index (s),
 
455
                                    PA_INVALID_INDEX,
 
456
                                    -1);
 
457
        }
 
458
}
 
459
 
 
460
static void
 
461
on_monitor_read_callback (pa_stream *s,
 
462
                          size_t     length,
 
463
                          void      *userdata)
 
464
{
 
465
        GvcMixerDialog *dialog;
 
466
        const void     *data;
 
467
        double          v;
 
468
 
 
469
        dialog = userdata;
 
470
 
 
471
        if (pa_stream_peek (s, &data, &length) < 0) {
 
472
                g_warning ("Failed to read data from stream");
 
473
                return;
 
474
        }
 
475
 
 
476
        assert (length > 0);
 
477
        assert (length % sizeof (float) == 0);
 
478
 
 
479
        v = ((const float *) data)[length / sizeof (float) -1];
 
480
 
 
481
        pa_stream_drop (s);
 
482
 
 
483
        if (v < 0) {
 
484
                v = 0;
 
485
        }
 
486
        if (v > 1) {
 
487
                v = 1;
 
488
        }
 
489
 
 
490
        update_input_meter (dialog,
 
491
                            pa_stream_get_device_index (s),
 
492
                            pa_stream_get_monitor_stream (s),
 
493
                            v);
 
494
}
 
495
 
 
496
static void
 
497
create_monitor_stream_for_source (GvcMixerDialog *dialog,
 
498
                                  GvcMixerStream *stream)
 
499
{
 
500
        pa_stream     *s;
 
501
        char           t[16];
 
502
        pa_buffer_attr attr;
 
503
        pa_sample_spec ss;
 
504
        pa_context    *context;
 
505
        int            res;
 
506
        pa_proplist   *proplist;
 
507
        gboolean       has_monitor;
 
508
 
 
509
        if (stream == NULL) {
 
510
                return;
 
511
        }
 
512
        has_monitor = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (stream), "has-monitor"));
 
513
        if (has_monitor != FALSE) {
 
514
                return;
 
515
        }
 
516
 
 
517
        g_debug ("Create monitor for %u",
 
518
                 gvc_mixer_stream_get_index (stream));
 
519
 
 
520
        context = gvc_mixer_control_get_pa_context (dialog->priv->mixer_control);
 
521
 
 
522
        if (pa_context_get_server_protocol_version (context) < 13) {
 
523
                return;
 
524
        }
 
525
 
 
526
        ss.channels = 1;
 
527
        ss.format = PA_SAMPLE_FLOAT32;
 
528
        ss.rate = 25;
 
529
 
 
530
        memset (&attr, 0, sizeof (attr));
 
531
        attr.fragsize = sizeof (float);
 
532
        attr.maxlength = (uint32_t) -1;
 
533
 
 
534
        snprintf (t, sizeof (t), "%u", gvc_mixer_stream_get_index (stream));
 
535
 
 
536
        proplist = pa_proplist_new ();
 
537
        pa_proplist_sets (proplist, PA_PROP_APPLICATION_ID, "org.gnome.VolumeControl");
 
538
        s = pa_stream_new_with_proplist (context, _("Peak detect"), &ss, NULL, proplist);
 
539
        pa_proplist_free (proplist);
 
540
        if (s == NULL) {
 
541
                g_warning ("Failed to create monitoring stream");
 
542
                return;
 
543
        }
 
544
 
 
545
        pa_stream_set_read_callback (s, on_monitor_read_callback, dialog);
 
546
        pa_stream_set_suspended_callback (s, on_monitor_suspended_callback, dialog);
 
547
 
 
548
        res = pa_stream_connect_record (s,
 
549
                                        t,
 
550
                                        &attr,
 
551
                                        (pa_stream_flags_t) (PA_STREAM_DONT_MOVE
 
552
                                                             |PA_STREAM_PEAK_DETECT
 
553
                                                             |PA_STREAM_ADJUST_LATENCY));
 
554
        if (res < 0) {
 
555
                g_warning ("Failed to connect monitoring stream");
 
556
                pa_stream_unref (s);
 
557
        } else {
 
558
                g_object_set_data (G_OBJECT (stream), "has-monitor", GINT_TO_POINTER (TRUE));
 
559
                g_object_set_data (G_OBJECT (dialog->priv->input_level_bar), "pa_stream", s);
 
560
                g_object_set_data (G_OBJECT (dialog->priv->input_level_bar), "stream", stream);
 
561
        }
 
562
}
 
563
 
 
564
static void
 
565
stop_monitor_stream_for_source (GvcMixerDialog *dialog)
 
566
{
 
567
        pa_stream      *s;
 
568
        pa_context     *context;
 
569
        int             res;
 
570
        GvcMixerStream *stream;
 
571
 
 
572
        s = g_object_get_data (G_OBJECT (dialog->priv->input_level_bar), "pa_stream");
 
573
        if (s == NULL)
 
574
                return;
 
575
        stream = g_object_get_data (G_OBJECT (dialog->priv->input_level_bar), "stream");
 
576
        g_assert (stream != NULL);
 
577
 
 
578
        g_debug ("Stopping monitor for %u", pa_stream_get_index (s));
 
579
 
 
580
        context = gvc_mixer_control_get_pa_context (dialog->priv->mixer_control);
 
581
 
 
582
        if (pa_context_get_server_protocol_version (context) < 13) {
 
583
                return;
 
584
        }
 
585
 
 
586
        res = pa_stream_disconnect (s);
 
587
        if (res == 0)
 
588
                g_object_set_data (G_OBJECT (stream), "has-monitor", GINT_TO_POINTER (FALSE));
 
589
        g_object_set_data (G_OBJECT (dialog->priv->input_level_bar), "pa_stream", NULL);
 
590
        g_object_set_data (G_OBJECT (dialog->priv->input_level_bar), "stream", NULL);
 
591
}
 
592
 
 
593
static void
 
594
update_input_settings (GvcMixerDialog *dialog)
 
595
{
 
596
        const GList    *ports;
 
597
        GvcMixerStream *stream;
 
598
        GtkAdjustment  *adj;
 
599
 
 
600
        g_debug ("Updating input settings");
 
601
 
 
602
        stop_monitor_stream_for_source (dialog);
 
603
 
 
604
        if (dialog->priv->input_port_combo != NULL) {
 
605
                gtk_container_remove (GTK_CONTAINER (dialog->priv->input_settings_box),
 
606
                                      dialog->priv->input_port_combo);
 
607
                dialog->priv->input_port_combo = NULL;
 
608
        }
 
609
 
 
610
        stream = gvc_mixer_control_get_default_source (dialog->priv->mixer_control);
 
611
        if (stream == NULL) {
 
612
                g_debug ("Default source stream not found");
 
613
                return;
 
614
        }
 
615
 
 
616
        gvc_channel_bar_set_base_volume (GVC_CHANNEL_BAR (dialog->priv->input_bar),
 
617
                                         gvc_mixer_stream_get_base_volume (stream));
 
618
        gvc_channel_bar_set_is_amplified (GVC_CHANNEL_BAR (dialog->priv->input_bar),
 
619
                                          gvc_mixer_stream_get_can_decibel (stream));
 
620
 
 
621
        /* Update the adjustment in case the previous bar wasn't decibel
 
622
         * capable, and we clipped it */
 
623
        adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (dialog->priv->input_bar)));
 
624
        gtk_adjustment_set_value (adj,
 
625
                                  gvc_mixer_stream_get_volume (stream));
 
626
 
 
627
        ports = gvc_mixer_stream_get_ports (stream);
 
628
        if (ports != NULL) {
 
629
                const GvcMixerStreamPort *port;
 
630
                port = gvc_mixer_stream_get_port (stream);
 
631
 
 
632
                dialog->priv->input_port_combo = gvc_combo_box_new (_("Co_nnector:"));
 
633
                gvc_combo_box_set_ports (GVC_COMBO_BOX (dialog->priv->input_port_combo),
 
634
                                         ports);
 
635
                gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->input_port_combo), port->port);
 
636
                g_object_set_data (G_OBJECT (dialog->priv->input_port_combo), "stream", stream);
 
637
                g_signal_connect (G_OBJECT (dialog->priv->input_port_combo), "changed",
 
638
                                  G_CALLBACK (port_selection_changed), dialog);
 
639
 
 
640
                gvc_combo_box_set_size_group (GVC_COMBO_BOX (dialog->priv->input_port_combo), dialog->priv->size_group, FALSE);
 
641
                gtk_box_pack_start (GTK_BOX (dialog->priv->input_settings_box),
 
642
                                    dialog->priv->input_port_combo,
 
643
                                    TRUE, TRUE, 0);
 
644
                gtk_widget_show (dialog->priv->input_port_combo);
 
645
        }
 
646
 
 
647
        create_monitor_stream_for_source (dialog, stream);
 
648
}
 
649
 
 
650
static void
 
651
on_mixer_control_default_source_changed (GvcMixerControl *control,
 
652
                                         guint            id,
 
653
                                         GvcMixerDialog  *dialog)
 
654
{
 
655
        GvcMixerStream *stream;
 
656
        GtkAdjustment *adj;
 
657
 
 
658
        g_debug ("GvcMixerDialog: default source changed: %u", id);
 
659
 
 
660
        if (id == PA_INVALID_INDEX)
 
661
                stream = NULL;
 
662
        else
 
663
                stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
 
664
 
 
665
        /* Disconnect the adj, otherwise it might change if is_amplified changes */
 
666
        adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (dialog->priv->input_bar)));
 
667
        g_signal_handlers_disconnect_by_func(adj, on_adjustment_value_changed, dialog);
 
668
 
 
669
        bar_set_stream (dialog, dialog->priv->input_bar, stream);
 
670
        update_input_settings (dialog);
 
671
 
 
672
        g_signal_connect (adj,
 
673
                          "value-changed",
 
674
                          G_CALLBACK (on_adjustment_value_changed),
 
675
                          dialog);
 
676
 
 
677
        update_default_input (dialog);
 
678
}
 
679
 
 
680
static void
 
681
gvc_mixer_dialog_set_mixer_control (GvcMixerDialog  *dialog,
 
682
                                    GvcMixerControl *control)
 
683
{
 
684
        g_return_if_fail (GVC_MIXER_DIALOG (dialog));
 
685
        g_return_if_fail (GVC_IS_MIXER_CONTROL (control));
 
686
 
 
687
        g_object_ref (control);
 
688
 
 
689
        if (dialog->priv->mixer_control != NULL) {
 
690
                g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
 
691
                                                      G_CALLBACK (on_mixer_control_default_sink_changed),
 
692
                                                      dialog);
 
693
                g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
 
694
                                                      G_CALLBACK (on_mixer_control_default_source_changed),
 
695
                                                      dialog);
 
696
                g_object_unref (dialog->priv->mixer_control);
 
697
        }
 
698
 
 
699
        dialog->priv->mixer_control = control;
 
700
 
 
701
        g_signal_connect (dialog->priv->mixer_control,
 
702
                          "default-sink-changed",
 
703
                          G_CALLBACK (on_mixer_control_default_sink_changed),
 
704
                          dialog);
 
705
        g_signal_connect (dialog->priv->mixer_control,
 
706
                          "default-source-changed",
 
707
                          G_CALLBACK (on_mixer_control_default_source_changed),
 
708
                          dialog);
 
709
 
 
710
        g_object_notify (G_OBJECT (dialog), "mixer-control");
 
711
}
 
712
 
 
713
static GvcMixerControl *
 
714
gvc_mixer_dialog_get_mixer_control (GvcMixerDialog *dialog)
 
715
{
 
716
        g_return_val_if_fail (GVC_IS_MIXER_DIALOG (dialog), NULL);
 
717
 
 
718
        return dialog->priv->mixer_control;
 
719
}
 
720
 
 
721
static void
 
722
gvc_mixer_dialog_set_property (GObject       *object,
 
723
                               guint          prop_id,
 
724
                               const GValue  *value,
 
725
                               GParamSpec    *pspec)
 
726
{
 
727
        GvcMixerDialog *self = GVC_MIXER_DIALOG (object);
 
728
 
 
729
        switch (prop_id) {
 
730
        case PROP_MIXER_CONTROL:
 
731
                gvc_mixer_dialog_set_mixer_control (self, g_value_get_object (value));
 
732
                break;
 
733
        default:
 
734
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
735
                break;
 
736
        }
 
737
}
 
738
 
 
739
static void
 
740
gvc_mixer_dialog_get_property (GObject     *object,
 
741
                               guint        prop_id,
 
742
                               GValue      *value,
 
743
                               GParamSpec  *pspec)
 
744
{
 
745
        GvcMixerDialog *self = GVC_MIXER_DIALOG (object);
 
746
 
 
747
        switch (prop_id) {
 
748
        case PROP_MIXER_CONTROL:
 
749
                g_value_set_object (value, gvc_mixer_dialog_get_mixer_control (self));
 
750
                break;
 
751
        default:
 
752
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
753
                break;
 
754
        }
 
755
}
 
756
 
 
757
static void
 
758
on_adjustment_value_changed (GtkAdjustment  *adjustment,
 
759
                             GvcMixerDialog *dialog)
 
760
{
 
761
        GvcMixerStream *stream;
 
762
 
 
763
        stream = g_object_get_data (G_OBJECT (adjustment), "gvc-mixer-dialog-stream");
 
764
        if (stream != NULL) {
 
765
                GObject *bar;
 
766
                gdouble volume, rounded;
 
767
                char *name;
 
768
 
 
769
                volume = gtk_adjustment_get_value (adjustment);
 
770
                rounded = round (volume);
 
771
 
 
772
                bar = g_object_get_data (G_OBJECT (adjustment), "gvc-mixer-dialog-bar");
 
773
                g_object_get (bar, "name", &name, NULL);
 
774
                g_debug ("Setting stream volume %lf (rounded: %lf) for bar '%s'", volume, rounded, name);
 
775
                g_free (name);
 
776
 
 
777
                /* FIXME would need to do that in the balance bar really... */
 
778
                /* Make sure we do not unmute muted streams, there's a button for that */
 
779
                if (volume == 0.0)
 
780
                        gvc_mixer_stream_set_is_muted (stream, TRUE);
 
781
                /* Only push the volume if it's actually changed */
 
782
                if (gvc_mixer_stream_set_volume(stream, (pa_volume_t) rounded) != FALSE)
 
783
                        gvc_mixer_stream_push_volume (stream);
 
784
        }
 
785
}
 
786
 
 
787
static void
 
788
on_bar_is_muted_notify (GObject        *object,
 
789
                        GParamSpec     *pspec,
 
790
                        GvcMixerDialog *dialog)
 
791
{
 
792
        gboolean        is_muted;
 
793
        GvcMixerStream *stream;
 
794
 
 
795
        is_muted = gvc_channel_bar_get_is_muted (GVC_CHANNEL_BAR (object));
 
796
 
 
797
        stream = g_object_get_data (object, "gvc-mixer-dialog-stream");
 
798
        if (stream != NULL) {
 
799
                gvc_mixer_stream_change_is_muted (stream, is_muted);
 
800
        } else {
 
801
                char *name;
 
802
                g_object_get (object, "name", &name, NULL);
 
803
                g_warning ("Unable to find stream for bar '%s'", name);
 
804
                g_free (name);
 
805
        }
 
806
}
 
807
 
 
808
static GtkWidget *
 
809
lookup_bar_for_stream (GvcMixerDialog *dialog,
 
810
                       GvcMixerStream *stream)
 
811
{
 
812
        GtkWidget *bar;
 
813
 
 
814
        bar = g_hash_table_lookup (dialog->priv->bars, GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)));
 
815
 
 
816
        return bar;
 
817
}
 
818
 
 
819
static GtkWidget *
 
820
lookup_combo_box_for_stream (GvcMixerDialog *dialog,
 
821
                             GvcMixerStream *stream)
 
822
{
 
823
        GvcMixerStream *combo_stream;
 
824
        guint id;
 
825
 
 
826
        id = gvc_mixer_stream_get_id (stream);
 
827
 
 
828
        if (dialog->priv->output_port_combo != NULL) {
 
829
                combo_stream = g_object_get_data (G_OBJECT (dialog->priv->output_port_combo),
 
830
                                                  "stream");
 
831
                if (combo_stream != NULL) {
 
832
                        if (id == gvc_mixer_stream_get_id (combo_stream))
 
833
                                return dialog->priv->output_port_combo;
 
834
                }
 
835
        }
 
836
 
 
837
        if (dialog->priv->input_port_combo != NULL) {
 
838
                combo_stream = g_object_get_data (G_OBJECT (dialog->priv->input_port_combo),
 
839
                                                  "stream");
 
840
                if (combo_stream != NULL) {
 
841
                        if (id == gvc_mixer_stream_get_id (combo_stream))
 
842
                                return dialog->priv->input_port_combo;
 
843
                }
 
844
        }
 
845
 
 
846
        return NULL;
 
847
}
 
848
 
 
849
static void
 
850
on_stream_description_notify (GvcMixerStream *stream,
 
851
                              GParamSpec     *pspec,
 
852
                              GvcMixerDialog *dialog)
 
853
{
 
854
        update_description (dialog, NAME_COLUMN,
 
855
                            gvc_mixer_stream_get_description (stream),
 
856
                            stream);
 
857
}
 
858
 
 
859
static void
 
860
on_stream_port_notify (GObject        *object,
 
861
                       GParamSpec     *pspec,
 
862
                       GvcMixerDialog *dialog)
 
863
{
 
864
        GvcComboBox *combo_box;
 
865
        char *port;
 
866
 
 
867
        combo_box = GVC_COMBO_BOX (lookup_combo_box_for_stream (dialog, GVC_MIXER_STREAM (object)));
 
868
        if (combo_box == NULL)
 
869
                return;
 
870
 
 
871
        g_signal_handlers_block_by_func (G_OBJECT (combo_box),
 
872
                                         port_selection_changed,
 
873
                                         dialog);
 
874
 
 
875
        g_object_get (object, "port", &port, NULL);
 
876
        gvc_combo_box_set_active (GVC_COMBO_BOX (combo_box), port);
 
877
 
 
878
        g_signal_handlers_unblock_by_func (G_OBJECT (combo_box),
 
879
                                         port_selection_changed,
 
880
                                         dialog);
 
881
}
 
882
 
 
883
static void
 
884
on_stream_volume_notify (GObject        *object,
 
885
                         GParamSpec     *pspec,
 
886
                         GvcMixerDialog *dialog)
 
887
{
 
888
        GvcMixerStream *stream;
 
889
        GtkWidget      *bar;
 
890
        GtkAdjustment  *adj;
 
891
 
 
892
        stream = GVC_MIXER_STREAM (object);
 
893
 
 
894
        bar = lookup_bar_for_stream (dialog, stream);
 
895
 
 
896
        if (bar == NULL) {
 
897
                g_warning ("Unable to find bar for stream %s in on_stream_volume_notify()",
 
898
                           gvc_mixer_stream_get_name (stream));
 
899
                return;
 
900
        }
 
901
 
 
902
        adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
 
903
 
 
904
        g_signal_handlers_block_by_func (adj,
 
905
                                         on_adjustment_value_changed,
 
906
                                         dialog);
 
907
 
 
908
        gtk_adjustment_set_value (adj,
 
909
                                  gvc_mixer_stream_get_volume (stream));
 
910
 
 
911
        g_signal_handlers_unblock_by_func (adj,
 
912
                                           on_adjustment_value_changed,
 
913
                                           dialog);
 
914
}
 
915
 
 
916
static void
 
917
on_stream_is_muted_notify (GObject        *object,
 
918
                           GParamSpec     *pspec,
 
919
                           GvcMixerDialog *dialog)
 
920
{
 
921
        GvcMixerStream *stream;
 
922
        GtkWidget      *bar;
 
923
        gboolean        is_muted;
 
924
 
 
925
        stream = GVC_MIXER_STREAM (object);
 
926
        bar = lookup_bar_for_stream (dialog, stream);
 
927
 
 
928
        if (bar == NULL) {
 
929
                g_warning ("Unable to find bar for stream %s in on_stream_is_muted_notify()",
 
930
                           gvc_mixer_stream_get_name (stream));
 
931
                return;
 
932
        }
 
933
 
 
934
        is_muted = gvc_mixer_stream_get_is_muted (stream);
 
935
        gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (bar),
 
936
                                      is_muted);
 
937
 
 
938
        if (stream == gvc_mixer_control_get_default_sink (dialog->priv->mixer_control)) {
 
939
                gtk_widget_set_sensitive (dialog->priv->applications_box,
 
940
                                          !is_muted);
 
941
        }
 
942
 
 
943
}
 
944
 
 
945
static void
 
946
save_bar_for_stream (GvcMixerDialog *dialog,
 
947
                     GvcMixerStream *stream,
 
948
                     GtkWidget      *bar)
 
949
{
 
950
        g_hash_table_insert (dialog->priv->bars,
 
951
                             GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)),
 
952
                             bar);
 
953
}
 
954
 
 
955
static GtkWidget *
 
956
create_bar (GvcMixerDialog *dialog,
 
957
            gboolean        symmetric)
 
958
{
 
959
        GtkWidget *bar;
 
960
 
 
961
        bar = gvc_channel_bar_new ();
 
962
        gtk_widget_set_sensitive (bar, FALSE);
 
963
        gvc_channel_bar_set_size_group (GVC_CHANNEL_BAR (bar),
 
964
                                        dialog->priv->size_group,
 
965
                                        symmetric);
 
966
        gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (bar),
 
967
                                         GTK_ORIENTATION_HORIZONTAL);
 
968
        gvc_channel_bar_set_show_mute (GVC_CHANNEL_BAR (bar),
 
969
                                       TRUE);
 
970
        g_signal_connect (bar,
 
971
                          "notify::is-muted",
 
972
                          G_CALLBACK (on_bar_is_muted_notify),
 
973
                          dialog);
 
974
        return bar;
 
975
}
 
976
 
 
977
static GtkWidget *
 
978
create_app_bar (GvcMixerDialog *dialog,
 
979
                const char     *name,
 
980
                const char     *icon_name)
 
981
{
 
982
        GtkWidget *bar;
 
983
 
 
984
        bar = create_bar (dialog, FALSE);
 
985
        gvc_channel_bar_set_ellipsize (GVC_CHANNEL_BAR (bar), TRUE);
 
986
        gvc_channel_bar_set_icon_name (GVC_CHANNEL_BAR (bar), icon_name);
 
987
        if (name == NULL || strchr (name, '_') == NULL) {
 
988
                gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar), name);
 
989
        } else {
 
990
                char **tokens, *escaped;
 
991
 
 
992
                tokens = g_strsplit (name, "_", -1);
 
993
                escaped = g_strjoinv ("__", tokens);
 
994
                g_strfreev (tokens);
 
995
                gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar), escaped);
 
996
                g_free (escaped);
 
997
        }
 
998
 
 
999
        return bar;
 
1000
}
 
1001
 
 
1002
static void
 
1003
bar_set_stream (GvcMixerDialog *dialog,
 
1004
                GtkWidget      *bar,
 
1005
                GvcMixerStream *stream)
 
1006
{
 
1007
        GtkAdjustment  *adj;
 
1008
        GvcMixerStream *old_stream;
 
1009
 
 
1010
        g_assert (bar != NULL);
 
1011
 
 
1012
        old_stream = g_object_get_data (G_OBJECT (bar), "gvc-mixer-dialog-stream");
 
1013
        if (old_stream != NULL) {
 
1014
                char *name;
 
1015
 
 
1016
                g_object_get (bar, "name", &name, NULL);
 
1017
                g_debug ("Disconnecting old stream '%s' from bar '%s'",
 
1018
                         gvc_mixer_stream_get_name (old_stream), name);
 
1019
                g_free (name);
 
1020
 
 
1021
                g_signal_handlers_disconnect_by_func (old_stream, on_stream_is_muted_notify, dialog);
 
1022
                g_signal_handlers_disconnect_by_func (old_stream, on_stream_volume_notify, dialog);
 
1023
                g_signal_handlers_disconnect_by_func (old_stream, on_stream_port_notify, dialog);
 
1024
                g_hash_table_remove (dialog->priv->bars, GUINT_TO_POINTER (gvc_mixer_stream_get_id (old_stream)));
 
1025
        }
 
1026
 
 
1027
        gtk_widget_set_sensitive (bar, (stream != NULL));
 
1028
 
 
1029
        adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
 
1030
 
 
1031
        g_signal_handlers_disconnect_by_func (adj, on_adjustment_value_changed, dialog);
 
1032
 
 
1033
        g_object_set_data (G_OBJECT (bar), "gvc-mixer-dialog-stream", stream);
 
1034
        g_object_set_data (G_OBJECT (adj), "gvc-mixer-dialog-stream", stream);
 
1035
        g_object_set_data (G_OBJECT (adj), "gvc-mixer-dialog-bar", bar);
 
1036
 
 
1037
        if (stream != NULL) {
 
1038
                gboolean is_muted;
 
1039
 
 
1040
                is_muted = gvc_mixer_stream_get_is_muted (stream);
 
1041
                gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (bar), is_muted);
 
1042
 
 
1043
                save_bar_for_stream (dialog, stream, bar);
 
1044
 
 
1045
                gtk_adjustment_set_value (adj,
 
1046
                                          gvc_mixer_stream_get_volume (stream));
 
1047
 
 
1048
                g_signal_connect (stream,
 
1049
                                  "notify::is-muted",
 
1050
                                  G_CALLBACK (on_stream_is_muted_notify),
 
1051
                                  dialog);
 
1052
                g_signal_connect (stream,
 
1053
                                  "notify::volume",
 
1054
                                  G_CALLBACK (on_stream_volume_notify),
 
1055
                                  dialog);
 
1056
                g_signal_connect (stream,
 
1057
                                  "notify::port",
 
1058
                                  G_CALLBACK (on_stream_port_notify),
 
1059
                                  dialog);
 
1060
                g_signal_connect (adj,
 
1061
                                  "value-changed",
 
1062
                                  G_CALLBACK (on_adjustment_value_changed),
 
1063
                                  dialog);
 
1064
        }
 
1065
}
 
1066
 
 
1067
static void
 
1068
add_stream (GvcMixerDialog *dialog,
 
1069
            GvcMixerStream *stream)
 
1070
{
 
1071
        GtkWidget     *bar;
 
1072
        gboolean       is_muted;
 
1073
        gboolean       is_default;
 
1074
        GtkAdjustment *adj;
 
1075
        const char    *id;
 
1076
 
 
1077
        g_assert (stream != NULL);
 
1078
 
 
1079
        if (gvc_mixer_stream_is_event_stream (stream) != FALSE)
 
1080
                return;
 
1081
 
 
1082
        bar = NULL;
 
1083
        is_default = FALSE;
 
1084
        id = gvc_mixer_stream_get_application_id (stream);
 
1085
 
 
1086
        if (stream == gvc_mixer_control_get_default_sink (dialog->priv->mixer_control)) {
 
1087
                bar = dialog->priv->output_bar;
 
1088
                is_muted = gvc_mixer_stream_get_is_muted (stream);
 
1089
                is_default = TRUE;
 
1090
                gtk_widget_set_sensitive (dialog->priv->applications_box,
 
1091
                                          !is_muted);
 
1092
                adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
 
1093
                g_signal_handlers_disconnect_by_func(adj, on_adjustment_value_changed, dialog);
 
1094
                update_output_settings (dialog);
 
1095
        } else if (stream == gvc_mixer_control_get_default_source (dialog->priv->mixer_control)) {
 
1096
                bar = dialog->priv->input_bar;
 
1097
                adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
 
1098
                g_signal_handlers_disconnect_by_func(adj, on_adjustment_value_changed, dialog);
 
1099
                update_input_settings (dialog);
 
1100
                is_default = TRUE;
 
1101
        } else if (stream == gvc_mixer_control_get_event_sink_input (dialog->priv->mixer_control)) {
 
1102
                bar = dialog->priv->effects_bar;
 
1103
                g_debug ("Adding effects stream");
 
1104
        } else if (! GVC_IS_MIXER_SOURCE (stream)
 
1105
                   && !GVC_IS_MIXER_SINK (stream)
 
1106
                   && !gvc_mixer_stream_is_virtual (stream)
 
1107
                   && g_strcmp0 (id, "org.gnome.VolumeControl") != 0
 
1108
                   && g_strcmp0 (id, "org.PulseAudio.pavucontrol") != 0) {
 
1109
                const char *name;
 
1110
 
 
1111
                name = gvc_mixer_stream_get_name (stream);
 
1112
                bar = create_app_bar (dialog, name,
 
1113
                                      gvc_mixer_stream_get_icon_name (stream));
 
1114
 
 
1115
                gtk_box_pack_start (GTK_BOX (dialog->priv->applications_box), bar, FALSE, FALSE, 12);
 
1116
                dialog->priv->num_apps++;
 
1117
                gtk_widget_hide (dialog->priv->no_apps_label);
 
1118
        }
 
1119
 
 
1120
        if (GVC_IS_MIXER_SOURCE (stream)) {
 
1121
                GtkTreeModel *model;
 
1122
                GtkTreeIter   iter;
 
1123
                GIcon        *icon;
 
1124
 
 
1125
                model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview));
 
1126
                gtk_list_store_append (GTK_LIST_STORE (model), &iter);
 
1127
 
 
1128
                icon = gvc_mixer_stream_get_gicon (stream);
 
1129
                gtk_list_store_set (GTK_LIST_STORE (model),
 
1130
                                    &iter,
 
1131
                                    NAME_COLUMN, gvc_mixer_stream_get_description (stream),
 
1132
                                    DEVICE_COLUMN, "",
 
1133
                                    ACTIVE_COLUMN, is_default,
 
1134
                                    ICON_COLUMN, icon,
 
1135
                                    ID_COLUMN, gvc_mixer_stream_get_id (stream),
 
1136
                                    -1);
 
1137
                if (icon != NULL)
 
1138
                        g_object_unref (icon);
 
1139
 
 
1140
                if (is_default) {
 
1141
                        GtkTreeSelection *selection;
 
1142
 
 
1143
                        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->input_treeview));
 
1144
                        gtk_tree_selection_select_iter (selection, &iter);
 
1145
                }
 
1146
 
 
1147
                g_signal_connect (stream,
 
1148
                                  "notify::description",
 
1149
                                  G_CALLBACK (on_stream_description_notify),
 
1150
                                  dialog);
 
1151
        } else if (GVC_IS_MIXER_SINK (stream)) {
 
1152
                GtkTreeModel        *model;
 
1153
                GtkTreeIter          iter;
 
1154
                const GvcChannelMap *map;
 
1155
                GIcon               *icon;
 
1156
 
 
1157
                model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview));
 
1158
                gtk_list_store_append (GTK_LIST_STORE (model), &iter);
 
1159
 
 
1160
                map = gvc_mixer_stream_get_channel_map (stream);
 
1161
                icon = gvc_mixer_stream_get_gicon (stream);
 
1162
                gtk_list_store_set (GTK_LIST_STORE (model),
 
1163
                                    &iter,
 
1164
                                    NAME_COLUMN, gvc_mixer_stream_get_description (stream),
 
1165
                                    DEVICE_COLUMN, "",
 
1166
                                    ACTIVE_COLUMN, is_default,
 
1167
                                    ICON_COLUMN, icon,
 
1168
                                    ID_COLUMN, gvc_mixer_stream_get_id (stream),
 
1169
                                    SPEAKERS_COLUMN, gvc_channel_map_get_mapping (map),
 
1170
                                    -1);
 
1171
                if (icon != NULL)
 
1172
                        g_object_unref (icon);
 
1173
 
 
1174
                if (is_default) {
 
1175
                        GtkTreeSelection *selection;
 
1176
 
 
1177
                        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->output_treeview));
 
1178
                        gtk_tree_selection_select_iter (selection, &iter);
 
1179
                }
 
1180
 
 
1181
                g_signal_connect (stream,
 
1182
                                  "notify::description",
 
1183
                                  G_CALLBACK (on_stream_description_notify),
 
1184
                                  dialog);
 
1185
        }
 
1186
 
 
1187
        if (bar != NULL) {
 
1188
                bar_set_stream (dialog, bar, stream);
 
1189
                gtk_widget_show (bar);
 
1190
        }
 
1191
}
 
1192
 
 
1193
static void
 
1194
on_control_stream_added (GvcMixerControl *control,
 
1195
                         guint            id,
 
1196
                         GvcMixerDialog  *dialog)
 
1197
{
 
1198
        GvcMixerStream *stream;
 
1199
        GtkWidget      *bar;
 
1200
 
 
1201
        bar = g_hash_table_lookup (dialog->priv->bars, GUINT_TO_POINTER (id));
 
1202
        if (bar != NULL) {
 
1203
                g_debug ("GvcMixerDialog: Stream %u already added", id);
 
1204
                return;
 
1205
        }
 
1206
 
 
1207
        stream = gvc_mixer_control_lookup_stream_id (control, id);
 
1208
        if (stream != NULL) {
 
1209
                add_stream (dialog, stream);
 
1210
        }
 
1211
}
 
1212
 
 
1213
static gboolean
 
1214
find_item_by_id (GtkTreeModel *model,
 
1215
                   guint         id,
 
1216
                   guint         column,
 
1217
                   GtkTreeIter  *iter)
 
1218
{
 
1219
        gboolean found_item;
 
1220
 
 
1221
        found_item = FALSE;
 
1222
 
 
1223
        if (!gtk_tree_model_get_iter_first (model, iter)) {
 
1224
                return FALSE;
 
1225
        }
 
1226
 
 
1227
        do {
 
1228
                guint t_id;
 
1229
 
 
1230
                gtk_tree_model_get (model, iter,
 
1231
                                    column, &t_id, -1);
 
1232
 
 
1233
                if (id == t_id) {
 
1234
                        found_item = TRUE;
 
1235
                }
 
1236
        } while (!found_item && gtk_tree_model_iter_next (model, iter));
 
1237
 
 
1238
        return found_item;
 
1239
}
 
1240
 
 
1241
static void
 
1242
remove_stream (GvcMixerDialog  *dialog,
 
1243
               guint            id)
 
1244
{
 
1245
        GtkWidget    *bar;
 
1246
        gboolean      found;
 
1247
        GtkTreeIter   iter;
 
1248
        GtkTreeModel *model;
 
1249
 
 
1250
        /* remove bars for applications and reset fixed bars */
 
1251
        bar = g_hash_table_lookup (dialog->priv->bars, GUINT_TO_POINTER (id));
 
1252
        if (bar == dialog->priv->output_bar
 
1253
            || bar == dialog->priv->input_bar
 
1254
            || bar == dialog->priv->effects_bar) {
 
1255
                char *name;
 
1256
                g_object_get (bar, "name", &name, NULL);
 
1257
                g_debug ("Removing stream for bar '%s'", name);
 
1258
                g_free (name);
 
1259
                bar_set_stream (dialog, bar, NULL);
 
1260
        } else if (bar != NULL) {
 
1261
                g_hash_table_remove (dialog->priv->bars, GUINT_TO_POINTER (id));
 
1262
                gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (bar)),
 
1263
                                      bar);
 
1264
                dialog->priv->num_apps--;
 
1265
                if (dialog->priv->num_apps == 0) {
 
1266
                        gtk_widget_show (dialog->priv->no_apps_label);
 
1267
                }
 
1268
        }
 
1269
 
 
1270
        /* remove from any models */
 
1271
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview));
 
1272
        found = find_item_by_id (GTK_TREE_MODEL (model), id, ID_COLUMN, &iter);
 
1273
        if (found) {
 
1274
                gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
 
1275
        }
 
1276
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview));
 
1277
        found = find_item_by_id (GTK_TREE_MODEL (model), id, ID_COLUMN, &iter);
 
1278
        if (found) {
 
1279
                gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
 
1280
        }
 
1281
}
 
1282
 
 
1283
static void
 
1284
on_control_stream_removed (GvcMixerControl *control,
 
1285
                           guint            id,
 
1286
                           GvcMixerDialog  *dialog)
 
1287
{
 
1288
        remove_stream (dialog, id);
 
1289
}
 
1290
 
 
1291
static void
 
1292
add_card (GvcMixerDialog *dialog,
 
1293
          GvcMixerCard   *card)
 
1294
{
 
1295
        GtkTreeModel        *model;
 
1296
        GtkTreeIter          iter;
 
1297
        GtkTreeSelection    *selection;
 
1298
        GvcMixerCardProfile *profile;
 
1299
        GIcon               *icon;
 
1300
        guint                index;
 
1301
 
 
1302
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview));
 
1303
        index = gvc_mixer_card_get_index (card);
 
1304
        if (find_item_by_id (GTK_TREE_MODEL (model), index, HW_ID_COLUMN, &iter) == FALSE)
 
1305
                gtk_list_store_append (GTK_LIST_STORE (model), &iter);
 
1306
        profile = gvc_mixer_card_get_profile (card);
 
1307
        g_assert (profile != NULL);
 
1308
        icon = g_themed_icon_new_with_default_fallbacks (gvc_mixer_card_get_icon_name (card));
 
1309
        //FIXME we need the status (default for a profile?) here
 
1310
        gtk_list_store_set (GTK_LIST_STORE (model),
 
1311
                            &iter,
 
1312
                            HW_NAME_COLUMN, gvc_mixer_card_get_name (card),
 
1313
                            HW_ID_COLUMN, index,
 
1314
                            HW_ICON_COLUMN, icon,
 
1315
                            HW_PROFILE_COLUMN, profile->profile,
 
1316
                            HW_PROFILE_HUMAN_COLUMN, profile->human_profile,
 
1317
                            HW_STATUS_COLUMN, profile->status,
 
1318
                            HW_SENSITIVE_COLUMN, g_strcmp0 (profile->profile, "off") != 0,
 
1319
                            -1);
 
1320
        if (icon != NULL)
 
1321
                g_object_unref (icon);
 
1322
 
 
1323
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->hw_treeview));
 
1324
        if (gtk_tree_selection_get_selected (selection, NULL, NULL) == FALSE) {
 
1325
                gtk_tree_selection_select_iter (selection, &iter);
 
1326
        } else if (dialog->priv->hw_profile_combo != NULL) {
 
1327
                GvcMixerCard *selected;
 
1328
 
 
1329
                /* Set the current profile if it changed for the selected card */
 
1330
                selected = g_object_get_data (G_OBJECT (dialog->priv->hw_profile_combo), "card");
 
1331
                if (gvc_mixer_card_get_index (selected) == gvc_mixer_card_get_index (card)) {
 
1332
                        gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->hw_profile_combo),
 
1333
                                                  profile->profile);
 
1334
                        g_object_set (G_OBJECT (dialog->priv->hw_profile_combo),
 
1335
                                      "show-button", profile->n_sinks == 1,
 
1336
                                      NULL);
 
1337
                }
 
1338
        }
 
1339
}
 
1340
 
 
1341
static void
 
1342
on_control_card_added (GvcMixerControl *control,
 
1343
                       guint            id,
 
1344
                       GvcMixerDialog  *dialog)
 
1345
{
 
1346
        GvcMixerCard *card;
 
1347
 
 
1348
        card = gvc_mixer_control_lookup_card_id (control, id);
 
1349
        if (card != NULL) {
 
1350
                add_card (dialog, card);
 
1351
        }
 
1352
}
 
1353
 
 
1354
static void
 
1355
remove_card (GvcMixerDialog  *dialog,
 
1356
             guint            id)
 
1357
{
 
1358
        gboolean      found;
 
1359
        GtkTreeIter   iter;
 
1360
        GtkTreeModel *model;
 
1361
 
 
1362
        /* remove from any models */
 
1363
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview));
 
1364
        found = find_item_by_id (GTK_TREE_MODEL (model), id, HW_ID_COLUMN, &iter);
 
1365
        if (found) {
 
1366
                gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
 
1367
        }
 
1368
}
 
1369
static void
 
1370
on_control_card_removed (GvcMixerControl *control,
 
1371
                         guint            id,
 
1372
                         GvcMixerDialog  *dialog)
 
1373
{
 
1374
        remove_card (dialog, id);
 
1375
}
 
1376
 
 
1377
static void
 
1378
_gtk_label_make_bold (GtkLabel *label)
 
1379
{
 
1380
        PangoFontDescription *font_desc;
 
1381
 
 
1382
        font_desc = pango_font_description_new ();
 
1383
 
 
1384
        pango_font_description_set_weight (font_desc,
 
1385
                                           PANGO_WEIGHT_BOLD);
 
1386
 
 
1387
        /* This will only affect the weight of the font, the rest is
 
1388
         * from the current state of the widget, which comes from the
 
1389
         * theme or user prefs, since the font desc only has the
 
1390
         * weight flag turned on.
 
1391
         */
 
1392
        gtk_widget_modify_font (GTK_WIDGET (label), font_desc);
 
1393
 
 
1394
        pango_font_description_free (font_desc);
 
1395
}
 
1396
 
 
1397
static void
 
1398
on_input_selection_changed (GtkTreeSelection *selection,
 
1399
                            GvcMixerDialog   *dialog)
 
1400
{
 
1401
        GtkTreeModel *model;
 
1402
        GtkTreeIter   iter;
 
1403
        gboolean      toggled;
 
1404
        guint         id;
 
1405
 
 
1406
        if (gtk_tree_selection_get_selected (selection, &model, &iter) == FALSE) {
 
1407
                g_debug ("Could not get default input from selection");
 
1408
                return;
 
1409
        }
 
1410
 
 
1411
        gtk_tree_model_get (model, &iter,
 
1412
                            ID_COLUMN, &id,
 
1413
                            ACTIVE_COLUMN, &toggled,
 
1414
                            -1);
 
1415
 
 
1416
        toggled ^= 1;
 
1417
        if (toggled) {
 
1418
                GvcMixerStream *stream;
 
1419
 
 
1420
                g_debug ("Default input selected: %u", id);
 
1421
                stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
 
1422
                if (stream == NULL) {
 
1423
                        g_warning ("Unable to find stream for id: %u", id);
 
1424
                        return;
 
1425
                }
 
1426
 
 
1427
                gvc_mixer_control_set_default_source (dialog->priv->mixer_control, stream);
 
1428
        }
 
1429
}
 
1430
 
 
1431
static void
 
1432
on_output_selection_changed (GtkTreeSelection *selection,
 
1433
                             GvcMixerDialog   *dialog)
 
1434
{
 
1435
        GtkTreeModel *model;
 
1436
        GtkTreeIter   iter;
 
1437
        gboolean      toggled;
 
1438
        guint         id;
 
1439
 
 
1440
        if (gtk_tree_selection_get_selected (selection, &model, &iter) == FALSE) {
 
1441
                g_debug ("Could not get default output from selection");
 
1442
                return;
 
1443
        }
 
1444
 
 
1445
        gtk_tree_model_get (model, &iter,
 
1446
                            ID_COLUMN, &id,
 
1447
                            ACTIVE_COLUMN, &toggled,
 
1448
                            -1);
 
1449
 
 
1450
        toggled ^= 1;
 
1451
        if (toggled) {
 
1452
                GvcMixerStream *stream;
 
1453
 
 
1454
                g_debug ("Default output selected: %u", id);
 
1455
                stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
 
1456
                if (stream == NULL) {
 
1457
                        g_warning ("Unable to find stream for id: %u", id);
 
1458
                        return;
 
1459
                }
 
1460
 
 
1461
                gvc_mixer_control_set_default_sink (dialog->priv->mixer_control, stream);
 
1462
        }
 
1463
}
 
1464
 
 
1465
static void
 
1466
name_to_text (GtkTreeViewColumn *column,
 
1467
              GtkCellRenderer *cell,
 
1468
              GtkTreeModel *model,
 
1469
              GtkTreeIter *iter,
 
1470
              gpointer user_data)
 
1471
{
 
1472
        char *name, *mapping;
 
1473
 
 
1474
        gtk_tree_model_get(model, iter,
 
1475
                           NAME_COLUMN, &name,
 
1476
                           SPEAKERS_COLUMN, &mapping,
 
1477
                           -1);
 
1478
 
 
1479
        if (mapping == NULL) {
 
1480
                g_object_set (cell, "text", name, NULL);
 
1481
        } else {
 
1482
                char *str;
 
1483
 
 
1484
                str = g_strdup_printf ("%s\n<i>%s</i>",
 
1485
                                       name, mapping);
 
1486
                g_object_set (cell, "markup", str, NULL);
 
1487
                g_free (str);
 
1488
        }
 
1489
 
 
1490
        g_free (name);
 
1491
        g_free (mapping);
 
1492
}
 
1493
 
 
1494
static GtkWidget *
 
1495
create_stream_treeview (GvcMixerDialog *dialog,
 
1496
                        GCallback       on_selection_changed)
 
1497
{
 
1498
        GtkWidget         *treeview;
 
1499
        GtkListStore      *store;
 
1500
        GtkCellRenderer   *renderer;
 
1501
        GtkTreeViewColumn *column;
 
1502
        GtkTreeSelection  *selection;
 
1503
 
 
1504
        treeview = gtk_tree_view_new ();
 
1505
        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
 
1506
 
 
1507
        store = gtk_list_store_new (NUM_COLUMNS,
 
1508
                                    G_TYPE_STRING,
 
1509
                                    G_TYPE_STRING,
 
1510
                                    G_TYPE_BOOLEAN,
 
1511
                                    G_TYPE_UINT,
 
1512
                                    G_TYPE_STRING,
 
1513
                                    G_TYPE_ICON);
 
1514
        gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
 
1515
                                 GTK_TREE_MODEL (store));
 
1516
 
 
1517
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
 
1518
        gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
 
1519
 
 
1520
        column = gtk_tree_view_column_new ();
 
1521
        gtk_tree_view_column_set_title (column, _("Name"));
 
1522
        renderer = gtk_cell_renderer_pixbuf_new ();
 
1523
        gtk_tree_view_column_pack_start (column, renderer, FALSE);
 
1524
        g_object_set (G_OBJECT (renderer), "stock-size", GTK_ICON_SIZE_LARGE_TOOLBAR, NULL);
 
1525
        gtk_tree_view_column_set_attributes (column, renderer,
 
1526
                                             "gicon", ICON_COLUMN,
 
1527
                                             NULL);
 
1528
 
 
1529
        renderer = gtk_cell_renderer_text_new ();
 
1530
        gtk_tree_view_column_pack_start (column, renderer, TRUE);
 
1531
        gtk_tree_view_column_set_cell_data_func (column, renderer,
 
1532
                                                 name_to_text, NULL, NULL);
 
1533
        gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
 
1534
 
 
1535
        g_signal_connect (G_OBJECT (selection), "changed",
 
1536
                          on_selection_changed, dialog);
 
1537
#if 0
 
1538
        renderer = gtk_cell_renderer_text_new ();
 
1539
        column = gtk_tree_view_column_new_with_attributes (_("Device"),
 
1540
                                                           renderer,
 
1541
                                                           "text", DEVICE_COLUMN,
 
1542
                                                           NULL);
 
1543
        gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
 
1544
#endif
 
1545
        return treeview;
 
1546
}
 
1547
 
 
1548
static void
 
1549
on_profile_changed (GvcComboBox *widget,
 
1550
                    const char  *profile,
 
1551
                    gpointer     user_data)
 
1552
{
 
1553
        GvcMixerCard        *card;
 
1554
 
 
1555
        card = g_object_get_data (G_OBJECT (widget), "card");
 
1556
        if (card == NULL) {
 
1557
                g_warning ("Could not find card for combobox");
 
1558
                return;
 
1559
        }
 
1560
 
 
1561
        g_debug ("Profile changed to %s for card %s", profile,
 
1562
                 gvc_mixer_card_get_name (card));
 
1563
 
 
1564
        gvc_mixer_card_change_profile (card, profile);
 
1565
}
 
1566
 
 
1567
static void
 
1568
on_test_speakers_clicked (GvcComboBox *widget,
 
1569
                          gpointer     user_data)
 
1570
{
 
1571
        GvcMixerDialog      *dialog = GVC_MIXER_DIALOG (user_data);
 
1572
        GvcMixerCard        *card;
 
1573
        GvcMixerCardProfile *profile;
 
1574
        GtkWidget           *d, *speaker_test, *container;
 
1575
        char                *title;
 
1576
 
 
1577
        card = g_object_get_data (G_OBJECT (widget), "card");
 
1578
        if (card == NULL) {
 
1579
                g_warning ("Could not find card for combobox");
 
1580
                return;
 
1581
        }
 
1582
        profile = gvc_mixer_card_get_profile (card);
 
1583
 
 
1584
        g_debug ("XXX Start speaker testing for profile '%s', card %s XXX",
 
1585
                 profile->profile, gvc_mixer_card_get_name (card));
 
1586
 
 
1587
        title = g_strdup_printf (_("Speaker Testing for %s"), gvc_mixer_card_get_name (card));
 
1588
        d = gtk_dialog_new_with_buttons (title,
 
1589
                                         GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (widget))),
 
1590
                                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
 
1591
                                         GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
 
1592
                                         NULL);
 
1593
        /* FIXME: this should not be necessary */
 
1594
        gtk_window_set_has_resize_grip (GTK_WINDOW (d), FALSE);
 
1595
 
 
1596
        g_free (title);
 
1597
        speaker_test = gvc_speaker_test_new (dialog->priv->mixer_control,
 
1598
                                             card);
 
1599
        gtk_widget_show (speaker_test);
 
1600
        container = gtk_dialog_get_content_area (GTK_DIALOG (d));
 
1601
        gtk_container_add (GTK_CONTAINER (container), speaker_test);
 
1602
 
 
1603
        gtk_dialog_run (GTK_DIALOG (d));
 
1604
        gtk_widget_destroy (d);
 
1605
}
 
1606
 
 
1607
static void
 
1608
on_card_selection_changed (GtkTreeSelection *selection,
 
1609
                           gpointer          user_data)
 
1610
{
 
1611
        GvcMixerDialog      *dialog = GVC_MIXER_DIALOG (user_data);
 
1612
        GtkTreeModel        *model;
 
1613
        GtkTreeIter          iter;
 
1614
        const GList         *profiles;
 
1615
        guint                id;
 
1616
        GvcMixerCard        *card;
 
1617
        GvcMixerCardProfile *current_profile;
 
1618
 
 
1619
        g_debug ("Card selection changed");
 
1620
 
 
1621
        if (dialog->priv->hw_profile_combo != NULL) {
 
1622
                gtk_container_remove (GTK_CONTAINER (dialog->priv->hw_settings_box),
 
1623
                                      dialog->priv->hw_profile_combo);
 
1624
                dialog->priv->hw_profile_combo = NULL;
 
1625
        }
 
1626
 
 
1627
        if (gtk_tree_selection_get_selected (selection,
 
1628
                                             NULL,
 
1629
                                             &iter) == FALSE) {
 
1630
                return;
 
1631
        }
 
1632
 
 
1633
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview));
 
1634
        gtk_tree_model_get (model, &iter,
 
1635
                            HW_ID_COLUMN, &id,
 
1636
                            -1);
 
1637
        card = gvc_mixer_control_lookup_card_id (dialog->priv->mixer_control, id);
 
1638
        if (card == NULL) {
 
1639
                g_warning ("Unable to find card for id: %u", id);
 
1640
                return;
 
1641
        }
 
1642
 
 
1643
        current_profile = gvc_mixer_card_get_profile (card);
 
1644
        profiles = gvc_mixer_card_get_profiles (card);
 
1645
        dialog->priv->hw_profile_combo = gvc_combo_box_new (_("_Profile:"));
 
1646
        g_object_set (G_OBJECT (dialog->priv->hw_profile_combo), "button-label", _("Test Speakers"), NULL);
 
1647
        gvc_combo_box_set_profiles (GVC_COMBO_BOX (dialog->priv->hw_profile_combo), profiles);
 
1648
        gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->hw_profile_combo), current_profile->profile);
 
1649
 
 
1650
        gtk_box_pack_start (GTK_BOX (dialog->priv->hw_settings_box),
 
1651
                            dialog->priv->hw_profile_combo,
 
1652
                            TRUE, TRUE, 6);
 
1653
        g_object_set (G_OBJECT (dialog->priv->hw_profile_combo),
 
1654
                      "show-button", current_profile->n_sinks == 1,
 
1655
                      NULL);
 
1656
        gtk_widget_show (dialog->priv->hw_profile_combo);
 
1657
 
 
1658
        g_object_set_data (G_OBJECT (dialog->priv->hw_profile_combo), "card", card);
 
1659
        g_signal_connect (G_OBJECT (dialog->priv->hw_profile_combo), "changed",
 
1660
                          G_CALLBACK (on_profile_changed), dialog);
 
1661
        g_signal_connect (G_OBJECT (dialog->priv->hw_profile_combo), "button-clicked",
 
1662
                          G_CALLBACK (on_test_speakers_clicked), dialog);
 
1663
}
 
1664
 
 
1665
static void
 
1666
card_to_text (GtkTreeViewColumn *column,
 
1667
              GtkCellRenderer *cell,
 
1668
              GtkTreeModel *model,
 
1669
              GtkTreeIter *iter,
 
1670
              gpointer user_data)
 
1671
{
 
1672
        char *name, *status, *profile, *str;
 
1673
        gboolean sensitive;
 
1674
 
 
1675
        gtk_tree_model_get(model, iter,
 
1676
                           HW_NAME_COLUMN, &name,
 
1677
                           HW_STATUS_COLUMN, &status,
 
1678
                           HW_PROFILE_HUMAN_COLUMN, &profile,
 
1679
                           HW_SENSITIVE_COLUMN, &sensitive,
 
1680
                           -1);
 
1681
 
 
1682
        str = g_strdup_printf ("%s\n<i>%s</i>\n<i>%s</i>",
 
1683
                               name, status, profile);
 
1684
        g_object_set (cell,
 
1685
                      "markup", str,
 
1686
                      "sensitive", sensitive,
 
1687
                      NULL);
 
1688
        g_free (str);
 
1689
 
 
1690
        g_free (name);
 
1691
        g_free (status);
 
1692
        g_free (profile);
 
1693
}
 
1694
 
 
1695
static GtkWidget *
 
1696
create_cards_treeview (GvcMixerDialog *dialog,
 
1697
                       GCallback       on_changed)
 
1698
{
 
1699
        GtkWidget         *treeview;
 
1700
        GtkListStore      *store;
 
1701
        GtkCellRenderer   *renderer;
 
1702
        GtkTreeViewColumn *column;
 
1703
        GtkTreeSelection  *selection;
 
1704
 
 
1705
        treeview = gtk_tree_view_new ();
 
1706
        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
 
1707
 
 
1708
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
 
1709
        g_signal_connect (G_OBJECT (selection), "changed",
 
1710
                          on_changed, dialog);
 
1711
 
 
1712
        store = gtk_list_store_new (HW_NUM_COLUMNS,
 
1713
                                    G_TYPE_UINT,
 
1714
                                    G_TYPE_ICON,
 
1715
                                    G_TYPE_STRING,
 
1716
                                    G_TYPE_STRING,
 
1717
                                    G_TYPE_STRING,
 
1718
                                    G_TYPE_STRING,
 
1719
                                    G_TYPE_BOOLEAN);
 
1720
        gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
 
1721
                                 GTK_TREE_MODEL (store));
 
1722
 
 
1723
        renderer = gtk_cell_renderer_pixbuf_new ();
 
1724
        g_object_set (G_OBJECT (renderer), "stock-size", GTK_ICON_SIZE_DIALOG, NULL);
 
1725
        column = gtk_tree_view_column_new_with_attributes (NULL,
 
1726
                                                           renderer,
 
1727
                                                           "gicon", HW_ICON_COLUMN,
 
1728
                                                           "sensitive", HW_SENSITIVE_COLUMN,
 
1729
                                                           NULL);
 
1730
        gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
 
1731
 
 
1732
        gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (treeview), -1,
 
1733
                                                    _("Name"), gtk_cell_renderer_text_new (),
 
1734
                                                    card_to_text, NULL, NULL);
 
1735
 
 
1736
        return treeview;
 
1737
}
 
1738
 
 
1739
static GObject *
 
1740
gvc_mixer_dialog_constructor (GType                  type,
 
1741
                              guint                  n_construct_properties,
 
1742
                              GObjectConstructParam *construct_params)
 
1743
{
 
1744
        GObject          *object;
 
1745
        GvcMixerDialog   *self;
 
1746
        GtkWidget        *main_vbox;
 
1747
        GtkWidget        *label;
 
1748
        GtkWidget        *alignment;
 
1749
        GtkWidget        *box;
 
1750
        GtkWidget        *sbox;
 
1751
        GtkWidget        *ebox;
 
1752
        GSList           *streams;
 
1753
        GSList           *cards;
 
1754
        GSList           *l;
 
1755
        GvcMixerStream   *stream;
 
1756
        GvcMixerCard     *card;
 
1757
        GtkTreeSelection *selection;
 
1758
 
 
1759
        object = G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->constructor (type, n_construct_properties, construct_params);
 
1760
 
 
1761
        self = GVC_MIXER_DIALOG (object);
 
1762
 
 
1763
        main_vbox = GTK_WIDGET (self);
 
1764
        gtk_box_set_spacing (GTK_BOX (main_vbox), 2);
 
1765
 
 
1766
        gtk_container_set_border_width (GTK_CONTAINER (self), 6);
 
1767
 
 
1768
        self->priv->output_stream_box = gtk_hbox_new (FALSE, 12);
 
1769
        alignment = gtk_alignment_new (0, 0, 1, 1);
 
1770
        gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 12, 0, 0, 0);
 
1771
        gtk_container_add (GTK_CONTAINER (alignment), self->priv->output_stream_box);
 
1772
        gtk_box_pack_start (GTK_BOX (main_vbox),
 
1773
                            alignment,
 
1774
                            FALSE, FALSE, 0);
 
1775
        self->priv->output_bar = create_bar (self, TRUE);
 
1776
        gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->output_bar),
 
1777
                                  _("_Output volume: "));
 
1778
        gtk_widget_set_sensitive (self->priv->output_bar, FALSE);
 
1779
        gtk_box_pack_start (GTK_BOX (self->priv->output_stream_box),
 
1780
                            self->priv->output_bar, TRUE, TRUE, 12);
 
1781
 
 
1782
        self->priv->notebook = gtk_notebook_new ();
 
1783
        gtk_box_pack_start (GTK_BOX (main_vbox),
 
1784
                            self->priv->notebook,
 
1785
                            TRUE, TRUE, 0);
 
1786
        gtk_container_set_border_width (GTK_CONTAINER (self->priv->notebook), 5);
 
1787
 
 
1788
        /* Effects page */
 
1789
        self->priv->sound_effects_box = gtk_vbox_new (FALSE, 6);
 
1790
        gtk_container_set_border_width (GTK_CONTAINER (self->priv->sound_effects_box), 12);
 
1791
        label = gtk_label_new (_("Sound Effects"));
 
1792
        gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
 
1793
                                  self->priv->sound_effects_box,
 
1794
                                  label);
 
1795
 
 
1796
        self->priv->effects_bar = create_bar (self, TRUE);
 
1797
        gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->effects_bar),
 
1798
                                  _("_Alert volume: "));
 
1799
        gtk_widget_set_sensitive (self->priv->effects_bar, FALSE);
 
1800
        gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box),
 
1801
                            self->priv->effects_bar, FALSE, FALSE, 0);
 
1802
 
 
1803
        self->priv->sound_theme_chooser = gvc_sound_theme_chooser_new ();
 
1804
        gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box),
 
1805
                            self->priv->sound_theme_chooser,
 
1806
                            TRUE, TRUE, 6);
 
1807
 
 
1808
        /* Hardware page */
 
1809
        self->priv->hw_box = gtk_vbox_new (FALSE, 12);
 
1810
        gtk_container_set_border_width (GTK_CONTAINER (self->priv->hw_box), 12);
 
1811
        label = gtk_label_new (_("Hardware"));
 
1812
        gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
 
1813
                                  self->priv->hw_box,
 
1814
                                  label);
 
1815
 
 
1816
        box = gtk_frame_new (_("C_hoose a device to configure:"));
 
1817
        label = gtk_frame_get_label_widget (GTK_FRAME (box));
 
1818
        _gtk_label_make_bold (GTK_LABEL (label));
 
1819
        gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
 
1820
        gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
 
1821
        gtk_box_pack_start (GTK_BOX (self->priv->hw_box), box, TRUE, TRUE, 0);
 
1822
 
 
1823
        alignment = gtk_alignment_new (0, 0, 1, 1);
 
1824
        gtk_container_add (GTK_CONTAINER (box), alignment);
 
1825
        gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
 
1826
 
 
1827
        self->priv->hw_treeview = create_cards_treeview (self,
 
1828
                                                         G_CALLBACK (on_card_selection_changed));
 
1829
        gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->hw_treeview);
 
1830
 
 
1831
        box = gtk_scrolled_window_new (NULL, NULL);
 
1832
        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box),
 
1833
                                        GTK_POLICY_NEVER,
 
1834
                                        GTK_POLICY_AUTOMATIC);
 
1835
        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box),
 
1836
                                             GTK_SHADOW_IN);
 
1837
        gtk_container_add (GTK_CONTAINER (box), self->priv->hw_treeview);
 
1838
        gtk_container_add (GTK_CONTAINER (alignment), box);
 
1839
 
 
1840
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->hw_treeview));
 
1841
        gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
 
1842
 
 
1843
        box = gtk_frame_new (_("Settings for the selected device:"));
 
1844
        label = gtk_frame_get_label_widget (GTK_FRAME (box));
 
1845
        _gtk_label_make_bold (GTK_LABEL (label));
 
1846
        gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
 
1847
        gtk_box_pack_start (GTK_BOX (self->priv->hw_box), box, FALSE, TRUE, 12);
 
1848
        self->priv->hw_settings_box = gtk_vbox_new (FALSE, 12);
 
1849
        gtk_container_add (GTK_CONTAINER (box), self->priv->hw_settings_box);
 
1850
 
 
1851
        /* Input page */
 
1852
        self->priv->input_box = gtk_vbox_new (FALSE, 12);
 
1853
        gtk_container_set_border_width (GTK_CONTAINER (self->priv->input_box), 12);
 
1854
        label = gtk_label_new (_("Input"));
 
1855
        gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
 
1856
                                  self->priv->input_box,
 
1857
                                  label);
 
1858
 
 
1859
        self->priv->input_bar = create_bar (self, TRUE);
 
1860
        gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->input_bar),
 
1861
                                  _("_Input volume: "));
 
1862
        gvc_channel_bar_set_low_icon_name (GVC_CHANNEL_BAR (self->priv->input_bar),
 
1863
                                           "audio-input-microphone-low-symbolic");
 
1864
        gvc_channel_bar_set_high_icon_name (GVC_CHANNEL_BAR (self->priv->input_bar),
 
1865
                                            "audio-input-microphone-high-symbolic");
 
1866
        gtk_widget_set_sensitive (self->priv->input_bar, FALSE);
 
1867
        alignment = gtk_alignment_new (0, 0, 1, 1);
 
1868
        gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
 
1869
        gtk_container_add (GTK_CONTAINER (alignment), self->priv->input_bar);
 
1870
        gtk_box_pack_start (GTK_BOX (self->priv->input_box),
 
1871
                            alignment,
 
1872
                            FALSE, FALSE, 0);
 
1873
 
 
1874
        box = gtk_hbox_new (FALSE, 6);
 
1875
        gtk_box_pack_start (GTK_BOX (self->priv->input_box),
 
1876
                            box,
 
1877
                            FALSE, FALSE, 6);
 
1878
 
 
1879
        sbox = gtk_hbox_new (FALSE, 6);
 
1880
        gtk_box_pack_start (GTK_BOX (box),
 
1881
                            sbox,
 
1882
                            FALSE, FALSE, 0);
 
1883
 
 
1884
        label = gtk_label_new (_("Input level:"));
 
1885
        gtk_box_pack_start (GTK_BOX (sbox),
 
1886
                            label,
 
1887
                            FALSE, FALSE, 0);
 
1888
        gtk_size_group_add_widget (self->priv->size_group, sbox);
 
1889
 
 
1890
        self->priv->input_level_bar = gvc_level_bar_new ();
 
1891
        gvc_level_bar_set_orientation (GVC_LEVEL_BAR (self->priv->input_level_bar),
 
1892
                                       GTK_ORIENTATION_HORIZONTAL);
 
1893
        gvc_level_bar_set_scale (GVC_LEVEL_BAR (self->priv->input_level_bar),
 
1894
                                 GVC_LEVEL_SCALE_LINEAR);
 
1895
        gtk_box_pack_start (GTK_BOX (box),
 
1896
                            self->priv->input_level_bar,
 
1897
                            TRUE, TRUE, 6);
 
1898
 
 
1899
        ebox = gtk_hbox_new (FALSE, 6);
 
1900
        gtk_box_pack_start (GTK_BOX (box),
 
1901
                            ebox,
 
1902
                            FALSE, FALSE, 0);
 
1903
        gtk_size_group_add_widget (self->priv->size_group, ebox);
 
1904
 
 
1905
        self->priv->input_settings_box = gtk_hbox_new (FALSE, 6);
 
1906
        gtk_box_pack_start (GTK_BOX (self->priv->input_box),
 
1907
                            self->priv->input_settings_box,
 
1908
                            FALSE, FALSE, 0);
 
1909
 
 
1910
        box = gtk_frame_new (_("C_hoose a device for sound input:"));
 
1911
        label = gtk_frame_get_label_widget (GTK_FRAME (box));
 
1912
        _gtk_label_make_bold (GTK_LABEL (label));
 
1913
        gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
 
1914
        gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
 
1915
        gtk_box_pack_start (GTK_BOX (self->priv->input_box), box, TRUE, TRUE, 0);
 
1916
 
 
1917
        alignment = gtk_alignment_new (0, 0, 1, 1);
 
1918
        gtk_container_add (GTK_CONTAINER (box), alignment);
 
1919
        gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
 
1920
 
 
1921
        self->priv->input_treeview = create_stream_treeview (self,
 
1922
                                                             G_CALLBACK (on_input_selection_changed));
 
1923
        gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->input_treeview);
 
1924
 
 
1925
        box = gtk_scrolled_window_new (NULL, NULL);
 
1926
        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box),
 
1927
                                        GTK_POLICY_NEVER,
 
1928
                                        GTK_POLICY_AUTOMATIC);
 
1929
        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box),
 
1930
                                             GTK_SHADOW_IN);
 
1931
        gtk_container_add (GTK_CONTAINER (box), self->priv->input_treeview);
 
1932
        gtk_container_add (GTK_CONTAINER (alignment), box);
 
1933
 
 
1934
        /* Output page */
 
1935
        self->priv->output_box = gtk_vbox_new (FALSE, 12);
 
1936
        gtk_container_set_border_width (GTK_CONTAINER (self->priv->output_box), 12);
 
1937
        label = gtk_label_new (_("Output"));
 
1938
        gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
 
1939
                                  self->priv->output_box,
 
1940
                                  label);
 
1941
 
 
1942
        box = gtk_frame_new (_("C_hoose a device for sound output:"));
 
1943
        label = gtk_frame_get_label_widget (GTK_FRAME (box));
 
1944
        _gtk_label_make_bold (GTK_LABEL (label));
 
1945
        gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
 
1946
        gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
 
1947
        gtk_box_pack_start (GTK_BOX (self->priv->output_box), box, TRUE, TRUE, 0);
 
1948
 
 
1949
        alignment = gtk_alignment_new (0, 0, 1, 1);
 
1950
        gtk_container_add (GTK_CONTAINER (box), alignment);
 
1951
        gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
 
1952
 
 
1953
        self->priv->output_treeview = create_stream_treeview (self,
 
1954
                                                              G_CALLBACK (on_output_selection_changed));
 
1955
        gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->output_treeview);
 
1956
 
 
1957
        box = gtk_scrolled_window_new (NULL, NULL);
 
1958
        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box),
 
1959
                                        GTK_POLICY_NEVER,
 
1960
                                        GTK_POLICY_AUTOMATIC);
 
1961
        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box),
 
1962
                                             GTK_SHADOW_IN);
 
1963
        gtk_container_add (GTK_CONTAINER (box), self->priv->output_treeview);
 
1964
        gtk_container_add (GTK_CONTAINER (alignment), box);
 
1965
 
 
1966
        box = gtk_frame_new (_("Settings for the selected device:"));
 
1967
        label = gtk_frame_get_label_widget (GTK_FRAME (box));
 
1968
        _gtk_label_make_bold (GTK_LABEL (label));
 
1969
        gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
 
1970
        gtk_box_pack_start (GTK_BOX (self->priv->output_box), box, FALSE, FALSE, 12);
 
1971
        self->priv->output_settings_box = gtk_vbox_new (FALSE, 0);
 
1972
        gtk_container_add (GTK_CONTAINER (box), self->priv->output_settings_box);
 
1973
 
 
1974
        /* Applications */
 
1975
        self->priv->applications_box = gtk_vbox_new (FALSE, 12);
 
1976
        gtk_container_set_border_width (GTK_CONTAINER (self->priv->applications_box), 12);
 
1977
        label = gtk_label_new (_("Applications"));
 
1978
        gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
 
1979
                                  self->priv->applications_box,
 
1980
                                  label);
 
1981
        self->priv->no_apps_label = gtk_label_new (_("No application is currently playing or recording audio."));
 
1982
        gtk_box_pack_start (GTK_BOX (self->priv->applications_box),
 
1983
                            self->priv->no_apps_label,
 
1984
                            TRUE, TRUE, 0);
 
1985
 
 
1986
        g_signal_connect (self->priv->mixer_control,
 
1987
                          "stream-added",
 
1988
                          G_CALLBACK (on_control_stream_added),
 
1989
                          self);
 
1990
        g_signal_connect (self->priv->mixer_control,
 
1991
                          "stream-removed",
 
1992
                          G_CALLBACK (on_control_stream_removed),
 
1993
                          self);
 
1994
        g_signal_connect (self->priv->mixer_control,
 
1995
                          "card-added",
 
1996
                          G_CALLBACK (on_control_card_added),
 
1997
                          self);
 
1998
        g_signal_connect (self->priv->mixer_control,
 
1999
                          "card-removed",
 
2000
                          G_CALLBACK (on_control_card_removed),
 
2001
                          self);
 
2002
 
 
2003
        gtk_widget_show_all (main_vbox);
 
2004
 
 
2005
        streams = gvc_mixer_control_get_streams (self->priv->mixer_control);
 
2006
        for (l = streams; l != NULL; l = l->next) {
 
2007
                stream = l->data;
 
2008
                add_stream (self, stream);
 
2009
        }
 
2010
        g_slist_free (streams);
 
2011
 
 
2012
        cards = gvc_mixer_control_get_cards (self->priv->mixer_control);
 
2013
        for (l = cards; l != NULL; l = l->next) {
 
2014
                card = l->data;
 
2015
                add_card (self, card);
 
2016
        }
 
2017
        g_slist_free (cards);
 
2018
 
 
2019
        return object;
 
2020
}
 
2021
 
 
2022
static void
 
2023
gvc_mixer_dialog_dispose (GObject *object)
 
2024
{
 
2025
        GvcMixerDialog *dialog = GVC_MIXER_DIALOG (object);
 
2026
 
 
2027
        if (dialog->priv->mixer_control != NULL) {
 
2028
                g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
 
2029
                                                      on_control_stream_added,
 
2030
                                                      dialog);
 
2031
                g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
 
2032
                                                      on_control_stream_removed,
 
2033
                                                      dialog);
 
2034
                g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
 
2035
                                                      on_control_card_added,
 
2036
                                                      dialog);
 
2037
                g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
 
2038
                                                      on_control_card_removed,
 
2039
                                                      dialog);
 
2040
 
 
2041
                g_object_unref (dialog->priv->mixer_control);
 
2042
                dialog->priv->mixer_control = NULL;
 
2043
        }
 
2044
 
 
2045
        if (dialog->priv->bars != NULL) {
 
2046
                g_hash_table_destroy (dialog->priv->bars);
 
2047
                dialog->priv->bars = NULL;
 
2048
        }
 
2049
 
 
2050
        G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->dispose (object);
 
2051
}
 
2052
 
 
2053
static void
 
2054
gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass)
 
2055
{
 
2056
        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
 
2057
 
 
2058
        object_class->constructor = gvc_mixer_dialog_constructor;
 
2059
        object_class->dispose = gvc_mixer_dialog_dispose;
 
2060
        object_class->finalize = gvc_mixer_dialog_finalize;
 
2061
        object_class->set_property = gvc_mixer_dialog_set_property;
 
2062
        object_class->get_property = gvc_mixer_dialog_get_property;
 
2063
 
 
2064
        g_object_class_install_property (object_class,
 
2065
                                         PROP_MIXER_CONTROL,
 
2066
                                         g_param_spec_object ("mixer-control",
 
2067
                                                              "mixer control",
 
2068
                                                              "mixer control",
 
2069
                                                              GVC_TYPE_MIXER_CONTROL,
 
2070
                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
 
2071
 
 
2072
        g_type_class_add_private (klass, sizeof (GvcMixerDialogPrivate));
 
2073
}
 
2074
 
 
2075
 
 
2076
static void
 
2077
gvc_mixer_dialog_init (GvcMixerDialog *dialog)
 
2078
{
 
2079
        dialog->priv = GVC_MIXER_DIALOG_GET_PRIVATE (dialog);
 
2080
        dialog->priv->bars = g_hash_table_new (NULL, NULL);
 
2081
        dialog->priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
 
2082
}
 
2083
 
 
2084
static void
 
2085
gvc_mixer_dialog_finalize (GObject *object)
 
2086
{
 
2087
        GvcMixerDialog *mixer_dialog;
 
2088
 
 
2089
        g_return_if_fail (object != NULL);
 
2090
        g_return_if_fail (GVC_IS_MIXER_DIALOG (object));
 
2091
 
 
2092
        mixer_dialog = GVC_MIXER_DIALOG (object);
 
2093
 
 
2094
        g_return_if_fail (mixer_dialog->priv != NULL);
 
2095
        G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->finalize (object);
 
2096
}
 
2097
 
 
2098
GvcMixerDialog *
 
2099
gvc_mixer_dialog_new (GvcMixerControl *control)
 
2100
{
 
2101
        GObject *dialog;
 
2102
        dialog = g_object_new (GVC_TYPE_MIXER_DIALOG,
 
2103
                               "mixer-control", control,
 
2104
                               NULL);
 
2105
        return GVC_MIXER_DIALOG (dialog);
 
2106
}
 
2107
 
 
2108
enum {
 
2109
        PAGE_EVENTS,
 
2110
        PAGE_HARDWARE,
 
2111
        PAGE_INPUT,
 
2112
        PAGE_OUTPUT,
 
2113
        PAGE_APPLICATIONS
 
2114
};
 
2115
 
 
2116
gboolean
 
2117
gvc_mixer_dialog_set_page (GvcMixerDialog *self,
 
2118
                           const char     *page)
 
2119
{
 
2120
        guint num;
 
2121
 
 
2122
        g_return_val_if_fail (self != NULL, FALSE);
 
2123
 
 
2124
        if (page == NULL)
 
2125
                num = 0;
 
2126
        else if (g_str_equal (page, "effects"))
 
2127
                num = PAGE_EVENTS;
 
2128
        else if (g_str_equal (page, "hardware"))
 
2129
                num = PAGE_HARDWARE;
 
2130
        else if (g_str_equal (page, "input"))
 
2131
                num = PAGE_INPUT;
 
2132
        else if (g_str_equal (page, "output"))
 
2133
                num = PAGE_OUTPUT;
 
2134
        else if (g_str_equal (page, "applications"))
 
2135
                num = PAGE_APPLICATIONS;
 
2136
        else
 
2137
                num = 0;
 
2138
 
 
2139
        gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook), num);
 
2140
 
 
2141
        return TRUE;
 
2142
}