1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* Copyright (C) 2008 William Jon McCann
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.
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.
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.
28
#include <glib/gi18n-lib.h>
29
#include <gdk/gdkkeysyms.h>
31
#include <pulse/pulseaudio.h>
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"
47
#define SCALE_SIZE 128
49
#define GVC_MIXER_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_DIALOG, GvcMixerDialogPrivate))
51
struct GvcMixerDialogPrivate
53
GvcMixerControl *mixer_control;
56
GtkWidget *output_bar;
58
GtkWidget *input_level_bar;
59
GtkWidget *effects_bar;
60
GtkWidget *output_stream_box;
61
GtkWidget *sound_effects_box;
63
GtkWidget *hw_treeview;
64
GtkWidget *hw_settings_box;
65
GtkWidget *hw_profile_combo;
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;
84
gdouble last_input_peak;
104
HW_PROFILE_HUMAN_COLUMN,
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);
119
static void bar_set_stream (GvcMixerDialog *dialog,
121
GvcMixerStream *stream);
123
static void on_adjustment_value_changed (GtkAdjustment *adjustment,
124
GvcMixerDialog *dialog);
126
G_DEFINE_TYPE (GvcMixerDialog, gvc_mixer_dialog, GTK_TYPE_VBOX)
129
update_default_input (GvcMixerDialog *dialog)
135
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview));
136
ret = gtk_tree_model_get_iter_first (model, &iter);
138
g_debug ("No default input selected or available");
145
GvcMixerStream *stream;
147
gtk_tree_model_get (model, &iter,
149
ACTIVE_COLUMN, &toggled,
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);
159
if (stream == gvc_mixer_control_get_default_source (dialog->priv->mixer_control)) {
163
gtk_list_store_set (GTK_LIST_STORE (model),
165
ACTIVE_COLUMN, is_default,
168
GtkTreeSelection *selection;
170
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->input_treeview));
171
gtk_tree_selection_select_iter (selection, &iter);
173
} while (gtk_tree_model_iter_next (model, &iter));
177
update_description (GvcMixerDialog *dialog,
180
GvcMixerStream *stream)
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));
191
g_assert_not_reached ();
192
gtk_tree_model_get_iter_first (model, &iter);
194
id = gvc_mixer_stream_get_id (stream);
198
gtk_tree_model_get (model, &iter,
199
ID_COLUMN, ¤t_id,
201
if (id != current_id)
204
gtk_list_store_set (GTK_LIST_STORE (model),
209
} while (gtk_tree_model_iter_next (model, &iter));
213
port_selection_changed (GvcComboBox *combo_box,
215
GvcMixerDialog *dialog)
217
GvcMixerStream *stream;
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");
224
if (gvc_mixer_stream_change_port (stream, port) == FALSE) {
225
g_warning ("Could not change port for stream");
230
update_output_settings (GvcMixerDialog *dialog)
232
GvcMixerStream *stream;
233
const GvcChannelMap *map;
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;
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;
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;
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;
259
stream = gvc_mixer_control_get_default_sink (dialog->priv->mixer_control);
260
if (stream == NULL) {
261
g_warning ("Default sink stream not found");
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));
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));
276
map = gvc_mixer_stream_get_channel_map (stream);
278
g_warning ("Default sink stream has no channel map");
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,
288
gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box),
289
dialog->priv->output_balance_bar,
291
gtk_widget_show (dialog->priv->output_balance_bar);
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,
300
gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box),
301
dialog->priv->output_fade_bar,
303
gtk_widget_show (dialog->priv->output_fade_bar);
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,
313
gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box),
314
dialog->priv->output_lfe_bar,
316
gtk_widget_show (dialog->priv->output_lfe_bar);
319
ports = gvc_mixer_stream_get_ports (stream);
321
const GvcMixerStreamPort *port;
322
port = gvc_mixer_stream_get_port (stream);
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),
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);
332
gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box),
333
dialog->priv->output_port_combo,
336
if (dialog->priv->size_group != NULL) {
337
gvc_combo_box_set_size_group (GVC_COMBO_BOX (dialog->priv->output_port_combo),
338
dialog->priv->size_group, FALSE);
341
gtk_widget_show (dialog->priv->output_port_combo);
344
/* FIXME: We could make this into a "No settings" label instead */
345
gtk_widget_set_sensitive (dialog->priv->output_balance_bar, gvc_channel_map_can_balance (map));
349
update_default_output (GvcMixerDialog *dialog)
354
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview));
355
gtk_tree_model_get_iter_first (model, &iter);
360
GvcMixerStream *stream;
362
gtk_tree_model_get (model, &iter,
364
ACTIVE_COLUMN, &toggled,
367
stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
368
if (stream == NULL) {
369
g_warning ("Unable to find stream for id: %u", id);
374
if (stream == gvc_mixer_control_get_default_sink (dialog->priv->mixer_control)) {
378
gtk_list_store_set (GTK_LIST_STORE (model),
380
ACTIVE_COLUMN, is_default,
383
GtkTreeSelection *selection;
385
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->output_treeview));
386
gtk_tree_selection_select_iter (selection, &iter);
388
} while (gtk_tree_model_iter_next (model, &iter));
392
on_mixer_control_default_sink_changed (GvcMixerControl *control,
394
GvcMixerDialog *dialog)
396
GvcMixerStream *stream;
398
g_debug ("GvcMixerDialog: default sink changed: %u", id);
400
if (id == PA_INVALID_INDEX)
403
stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control,
405
bar_set_stream (dialog, dialog->priv->output_bar, stream);
407
update_output_settings (dialog);
409
update_default_output (dialog);
413
#define DECAY_STEP .15
416
update_input_peak (GvcMixerDialog *dialog,
421
if (dialog->priv->last_input_peak >= DECAY_STEP) {
422
if (v < dialog->priv->last_input_peak - DECAY_STEP) {
423
v = dialog->priv->last_input_peak - DECAY_STEP;
427
dialog->priv->last_input_peak = v;
429
adj = gvc_level_bar_get_peak_adjustment (GVC_LEVEL_BAR (dialog->priv->input_level_bar));
431
gtk_adjustment_set_value (adj, v);
433
gtk_adjustment_set_value (adj, 0.0);
438
update_input_meter (GvcMixerDialog *dialog,
439
uint32_t source_index,
440
uint32_t sink_input_idx,
443
update_input_peak (dialog, v);
447
on_monitor_suspended_callback (pa_stream *s,
450
GvcMixerDialog *dialog;
454
if (pa_stream_is_suspended (s)) {
455
g_debug ("Stream suspended");
456
update_input_meter (dialog,
457
pa_stream_get_device_index (s),
464
on_monitor_read_callback (pa_stream *s,
468
GvcMixerDialog *dialog;
474
if (pa_stream_peek (s, &data, &length) < 0) {
475
g_warning ("Failed to read data from stream");
480
assert (length % sizeof (float) == 0);
482
v = ((const float *) data)[length / sizeof (float) -1];
493
update_input_meter (dialog,
494
pa_stream_get_device_index (s),
495
pa_stream_get_monitor_stream (s),
500
create_monitor_stream_for_source (GvcMixerDialog *dialog,
501
GvcMixerStream *stream)
509
pa_proplist *proplist;
510
gboolean has_monitor;
512
if (stream == NULL) {
515
has_monitor = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (stream), "has-monitor"));
516
if (has_monitor != FALSE) {
520
g_debug ("Create monitor for %u",
521
gvc_mixer_stream_get_index (stream));
523
context = gvc_mixer_control_get_pa_context (dialog->priv->mixer_control);
525
if (pa_context_get_server_protocol_version (context) < 13) {
530
ss.format = PA_SAMPLE_FLOAT32;
533
memset (&attr, 0, sizeof (attr));
534
attr.fragsize = sizeof (float);
535
attr.maxlength = (uint32_t) -1;
537
snprintf (t, sizeof (t), "%u", gvc_mixer_stream_get_index (stream));
539
proplist = pa_proplist_new ();
540
pa_proplist_sets (proplist, PA_PROP_APPLICATION_ID, "org.gnome.VolumeControl");
541
s = pa_stream_new_with_proplist (context, _("Peak detect"), &ss, NULL, proplist);
542
pa_proplist_free (proplist);
544
g_warning ("Failed to create monitoring stream");
548
pa_stream_set_read_callback (s, on_monitor_read_callback, dialog);
549
pa_stream_set_suspended_callback (s, on_monitor_suspended_callback, dialog);
551
res = pa_stream_connect_record (s,
554
(pa_stream_flags_t) (PA_STREAM_DONT_MOVE
555
|PA_STREAM_PEAK_DETECT
556
|PA_STREAM_ADJUST_LATENCY));
558
g_warning ("Failed to connect monitoring stream");
561
g_object_set_data (G_OBJECT (stream), "has-monitor", GINT_TO_POINTER (TRUE));
562
g_object_set_data (G_OBJECT (dialog->priv->input_level_bar), "pa_stream", s);
563
g_object_set_data (G_OBJECT (dialog->priv->input_level_bar), "stream", stream);
568
stop_monitor_stream_for_source (GvcMixerDialog *dialog)
573
GvcMixerStream *stream;
575
s = g_object_get_data (G_OBJECT (dialog->priv->input_level_bar), "pa_stream");
578
stream = g_object_get_data (G_OBJECT (dialog->priv->input_level_bar), "stream");
579
g_assert (stream != NULL);
581
g_debug ("Stopping monitor for %u", pa_stream_get_index (s));
583
context = gvc_mixer_control_get_pa_context (dialog->priv->mixer_control);
585
if (pa_context_get_server_protocol_version (context) < 13) {
589
res = pa_stream_disconnect (s);
591
g_object_set_data (G_OBJECT (stream), "has-monitor", GINT_TO_POINTER (FALSE));
592
g_object_set_data (G_OBJECT (dialog->priv->input_level_bar), "pa_stream", NULL);
593
g_object_set_data (G_OBJECT (dialog->priv->input_level_bar), "stream", NULL);
597
update_input_settings (GvcMixerDialog *dialog)
600
GvcMixerStream *stream;
603
g_debug ("Updating input settings");
605
stop_monitor_stream_for_source (dialog);
607
if (dialog->priv->input_port_combo != NULL) {
608
gtk_container_remove (GTK_CONTAINER (dialog->priv->input_settings_box),
609
dialog->priv->input_port_combo);
610
dialog->priv->input_port_combo = NULL;
613
stream = gvc_mixer_control_get_default_source (dialog->priv->mixer_control);
614
if (stream == NULL) {
615
g_debug ("Default source stream not found");
619
gvc_channel_bar_set_base_volume (GVC_CHANNEL_BAR (dialog->priv->input_bar),
620
gvc_mixer_stream_get_base_volume (stream));
621
gvc_channel_bar_set_is_amplified (GVC_CHANNEL_BAR (dialog->priv->input_bar),
622
gvc_mixer_stream_get_can_decibel (stream));
624
/* Update the adjustment in case the previous bar wasn't decibel
625
* capable, and we clipped it */
626
adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (dialog->priv->input_bar)));
627
gtk_adjustment_set_value (adj,
628
gvc_mixer_stream_get_volume (stream));
630
ports = gvc_mixer_stream_get_ports (stream);
632
const GvcMixerStreamPort *port;
633
port = gvc_mixer_stream_get_port (stream);
635
dialog->priv->input_port_combo = gvc_combo_box_new (_("Co_nnector:"));
636
gvc_combo_box_set_ports (GVC_COMBO_BOX (dialog->priv->input_port_combo),
638
gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->input_port_combo), port->port);
639
g_object_set_data (G_OBJECT (dialog->priv->input_port_combo), "stream", stream);
640
g_signal_connect (G_OBJECT (dialog->priv->input_port_combo), "changed",
641
G_CALLBACK (port_selection_changed), dialog);
643
if (dialog->priv->size_group != NULL) {
644
gvc_combo_box_set_size_group (GVC_COMBO_BOX (dialog->priv->input_port_combo),
645
dialog->priv->size_group, FALSE);
647
gtk_box_pack_start (GTK_BOX (dialog->priv->input_settings_box),
648
dialog->priv->input_port_combo,
650
gtk_widget_show (dialog->priv->input_port_combo);
653
create_monitor_stream_for_source (dialog, stream);
657
on_mixer_control_default_source_changed (GvcMixerControl *control,
659
GvcMixerDialog *dialog)
661
GvcMixerStream *stream;
664
g_debug ("GvcMixerDialog: default source changed: %u", id);
666
if (id == PA_INVALID_INDEX)
669
stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
671
/* Disconnect the adj, otherwise it might change if is_amplified changes */
672
adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (dialog->priv->input_bar)));
673
g_signal_handlers_disconnect_by_func(adj, on_adjustment_value_changed, dialog);
675
bar_set_stream (dialog, dialog->priv->input_bar, stream);
676
update_input_settings (dialog);
678
g_signal_connect (adj,
680
G_CALLBACK (on_adjustment_value_changed),
683
update_default_input (dialog);
687
gvc_mixer_dialog_set_mixer_control (GvcMixerDialog *dialog,
688
GvcMixerControl *control)
690
g_return_if_fail (GVC_MIXER_DIALOG (dialog));
691
g_return_if_fail (GVC_IS_MIXER_CONTROL (control));
693
g_object_ref (control);
695
if (dialog->priv->mixer_control != NULL) {
696
g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
697
G_CALLBACK (on_mixer_control_default_sink_changed),
699
g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
700
G_CALLBACK (on_mixer_control_default_source_changed),
702
g_object_unref (dialog->priv->mixer_control);
705
dialog->priv->mixer_control = control;
707
g_signal_connect (dialog->priv->mixer_control,
708
"default-sink-changed",
709
G_CALLBACK (on_mixer_control_default_sink_changed),
711
g_signal_connect (dialog->priv->mixer_control,
712
"default-source-changed",
713
G_CALLBACK (on_mixer_control_default_source_changed),
716
g_object_notify (G_OBJECT (dialog), "mixer-control");
719
static GvcMixerControl *
720
gvc_mixer_dialog_get_mixer_control (GvcMixerDialog *dialog)
722
g_return_val_if_fail (GVC_IS_MIXER_DIALOG (dialog), NULL);
724
return dialog->priv->mixer_control;
728
gvc_mixer_dialog_set_property (GObject *object,
733
GvcMixerDialog *self = GVC_MIXER_DIALOG (object);
736
case PROP_MIXER_CONTROL:
737
gvc_mixer_dialog_set_mixer_control (self, g_value_get_object (value));
740
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
746
gvc_mixer_dialog_get_property (GObject *object,
751
GvcMixerDialog *self = GVC_MIXER_DIALOG (object);
754
case PROP_MIXER_CONTROL:
755
g_value_set_object (value, gvc_mixer_dialog_get_mixer_control (self));
758
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
764
on_adjustment_value_changed (GtkAdjustment *adjustment,
765
GvcMixerDialog *dialog)
767
GvcMixerStream *stream;
769
stream = g_object_get_data (G_OBJECT (adjustment), "gvc-mixer-dialog-stream");
770
if (stream != NULL) {
772
gdouble volume, rounded;
775
volume = gtk_adjustment_get_value (adjustment);
776
rounded = round (volume);
778
bar = g_object_get_data (G_OBJECT (adjustment), "gvc-mixer-dialog-bar");
779
g_object_get (bar, "name", &name, NULL);
780
g_debug ("Setting stream volume %lf (rounded: %lf) for bar '%s'", volume, rounded, name);
783
/* FIXME would need to do that in the balance bar really... */
784
/* Make sure we do not unmute muted streams, there's a button for that */
786
gvc_mixer_stream_set_is_muted (stream, TRUE);
787
/* Only push the volume if it's actually changed */
788
if (gvc_mixer_stream_set_volume(stream, (pa_volume_t) rounded) != FALSE)
789
gvc_mixer_stream_push_volume (stream);
794
on_bar_is_muted_notify (GObject *object,
796
GvcMixerDialog *dialog)
799
GvcMixerStream *stream;
801
is_muted = gvc_channel_bar_get_is_muted (GVC_CHANNEL_BAR (object));
803
stream = g_object_get_data (object, "gvc-mixer-dialog-stream");
804
if (stream != NULL) {
805
gvc_mixer_stream_change_is_muted (stream, is_muted);
808
g_object_get (object, "name", &name, NULL);
809
g_warning ("Unable to find stream for bar '%s'", name);
815
lookup_bar_for_stream (GvcMixerDialog *dialog,
816
GvcMixerStream *stream)
820
bar = g_hash_table_lookup (dialog->priv->bars, GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)));
826
lookup_combo_box_for_stream (GvcMixerDialog *dialog,
827
GvcMixerStream *stream)
829
GvcMixerStream *combo_stream;
832
id = gvc_mixer_stream_get_id (stream);
834
if (dialog->priv->output_port_combo != NULL) {
835
combo_stream = g_object_get_data (G_OBJECT (dialog->priv->output_port_combo),
837
if (combo_stream != NULL) {
838
if (id == gvc_mixer_stream_get_id (combo_stream))
839
return dialog->priv->output_port_combo;
843
if (dialog->priv->input_port_combo != NULL) {
844
combo_stream = g_object_get_data (G_OBJECT (dialog->priv->input_port_combo),
846
if (combo_stream != NULL) {
847
if (id == gvc_mixer_stream_get_id (combo_stream))
848
return dialog->priv->input_port_combo;
856
on_stream_description_notify (GvcMixerStream *stream,
858
GvcMixerDialog *dialog)
860
update_description (dialog, NAME_COLUMN,
861
gvc_mixer_stream_get_description (stream),
866
on_stream_port_notify (GObject *object,
868
GvcMixerDialog *dialog)
870
GvcComboBox *combo_box;
873
combo_box = GVC_COMBO_BOX (lookup_combo_box_for_stream (dialog, GVC_MIXER_STREAM (object)));
874
if (combo_box == NULL)
877
g_signal_handlers_block_by_func (G_OBJECT (combo_box),
878
port_selection_changed,
881
g_object_get (object, "port", &port, NULL);
882
gvc_combo_box_set_active (GVC_COMBO_BOX (combo_box), port);
884
g_signal_handlers_unblock_by_func (G_OBJECT (combo_box),
885
port_selection_changed,
890
on_stream_volume_notify (GObject *object,
892
GvcMixerDialog *dialog)
894
GvcMixerStream *stream;
898
stream = GVC_MIXER_STREAM (object);
900
bar = lookup_bar_for_stream (dialog, stream);
903
g_warning ("Unable to find bar for stream %s in on_stream_volume_notify()",
904
gvc_mixer_stream_get_name (stream));
908
adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
910
g_signal_handlers_block_by_func (adj,
911
on_adjustment_value_changed,
914
gtk_adjustment_set_value (adj,
915
gvc_mixer_stream_get_volume (stream));
917
g_signal_handlers_unblock_by_func (adj,
918
on_adjustment_value_changed,
923
on_stream_is_muted_notify (GObject *object,
925
GvcMixerDialog *dialog)
927
GvcMixerStream *stream;
931
stream = GVC_MIXER_STREAM (object);
932
bar = lookup_bar_for_stream (dialog, stream);
935
g_warning ("Unable to find bar for stream %s in on_stream_is_muted_notify()",
936
gvc_mixer_stream_get_name (stream));
940
is_muted = gvc_mixer_stream_get_is_muted (stream);
941
gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (bar),
944
if (stream == gvc_mixer_control_get_default_sink (dialog->priv->mixer_control)) {
945
gtk_widget_set_sensitive (dialog->priv->applications_box,
952
save_bar_for_stream (GvcMixerDialog *dialog,
953
GvcMixerStream *stream,
956
g_hash_table_insert (dialog->priv->bars,
957
GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)),
962
create_bar (GvcMixerDialog *dialog,
963
gboolean add_to_size_group,
968
bar = gvc_channel_bar_new ();
969
gtk_widget_set_sensitive (bar, FALSE);
970
if (add_to_size_group && dialog->priv->size_group != NULL) {
971
gvc_channel_bar_set_size_group (GVC_CHANNEL_BAR (bar),
972
dialog->priv->size_group,
975
gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (bar),
976
GTK_ORIENTATION_HORIZONTAL);
977
gvc_channel_bar_set_show_mute (GVC_CHANNEL_BAR (bar),
979
g_signal_connect (bar,
981
G_CALLBACK (on_bar_is_muted_notify),
987
create_app_bar (GvcMixerDialog *dialog,
989
const char *icon_name)
993
bar = create_bar (dialog, FALSE, FALSE);
994
gvc_channel_bar_set_ellipsize (GVC_CHANNEL_BAR (bar), TRUE);
995
gvc_channel_bar_set_icon_name (GVC_CHANNEL_BAR (bar), icon_name);
996
if (name == NULL || strchr (name, '_') == NULL) {
997
gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar), name);
999
char **tokens, *escaped;
1001
tokens = g_strsplit (name, "_", -1);
1002
escaped = g_strjoinv ("__", tokens);
1003
g_strfreev (tokens);
1004
gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar), escaped);
1012
bar_set_stream (GvcMixerDialog *dialog,
1014
GvcMixerStream *stream)
1017
GvcMixerStream *old_stream;
1019
g_assert (bar != NULL);
1021
old_stream = g_object_get_data (G_OBJECT (bar), "gvc-mixer-dialog-stream");
1022
if (old_stream != NULL) {
1025
g_object_get (bar, "name", &name, NULL);
1026
g_debug ("Disconnecting old stream '%s' from bar '%s'",
1027
gvc_mixer_stream_get_name (old_stream), name);
1030
g_signal_handlers_disconnect_by_func (old_stream, on_stream_is_muted_notify, dialog);
1031
g_signal_handlers_disconnect_by_func (old_stream, on_stream_volume_notify, dialog);
1032
g_signal_handlers_disconnect_by_func (old_stream, on_stream_port_notify, dialog);
1033
g_hash_table_remove (dialog->priv->bars, GUINT_TO_POINTER (gvc_mixer_stream_get_id (old_stream)));
1036
gtk_widget_set_sensitive (bar, (stream != NULL));
1038
adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
1040
g_signal_handlers_disconnect_by_func (adj, on_adjustment_value_changed, dialog);
1042
g_object_set_data (G_OBJECT (bar), "gvc-mixer-dialog-stream", stream);
1043
g_object_set_data (G_OBJECT (adj), "gvc-mixer-dialog-stream", stream);
1044
g_object_set_data (G_OBJECT (adj), "gvc-mixer-dialog-bar", bar);
1046
if (stream != NULL) {
1049
is_muted = gvc_mixer_stream_get_is_muted (stream);
1050
gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (bar), is_muted);
1052
save_bar_for_stream (dialog, stream, bar);
1054
gtk_adjustment_set_value (adj,
1055
gvc_mixer_stream_get_volume (stream));
1057
g_signal_connect (stream,
1059
G_CALLBACK (on_stream_is_muted_notify),
1061
g_signal_connect (stream,
1063
G_CALLBACK (on_stream_volume_notify),
1065
g_signal_connect (stream,
1067
G_CALLBACK (on_stream_port_notify),
1069
g_signal_connect (adj,
1071
G_CALLBACK (on_adjustment_value_changed),
1077
add_stream (GvcMixerDialog *dialog,
1078
GvcMixerStream *stream)
1082
gboolean is_default;
1086
g_assert (stream != NULL);
1088
if (gvc_mixer_stream_is_event_stream (stream) != FALSE)
1093
id = gvc_mixer_stream_get_application_id (stream);
1095
if (stream == gvc_mixer_control_get_default_sink (dialog->priv->mixer_control)) {
1096
bar = dialog->priv->output_bar;
1097
is_muted = gvc_mixer_stream_get_is_muted (stream);
1099
gtk_widget_set_sensitive (dialog->priv->applications_box,
1101
adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
1102
g_signal_handlers_disconnect_by_func(adj, on_adjustment_value_changed, dialog);
1103
update_output_settings (dialog);
1104
} else if (stream == gvc_mixer_control_get_default_source (dialog->priv->mixer_control)) {
1105
bar = dialog->priv->input_bar;
1106
adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
1107
g_signal_handlers_disconnect_by_func(adj, on_adjustment_value_changed, dialog);
1108
update_input_settings (dialog);
1110
} else if (stream == gvc_mixer_control_get_event_sink_input (dialog->priv->mixer_control)) {
1111
bar = dialog->priv->effects_bar;
1112
g_debug ("Adding effects stream");
1113
} else if (! GVC_IS_MIXER_SOURCE (stream)
1114
&& !GVC_IS_MIXER_SINK (stream)
1115
&& !gvc_mixer_stream_is_virtual (stream)
1116
&& g_strcmp0 (id, "org.gnome.VolumeControl") != 0
1117
&& g_strcmp0 (id, "org.PulseAudio.pavucontrol") != 0) {
1120
name = gvc_mixer_stream_get_name (stream);
1121
bar = create_app_bar (dialog, name,
1122
gvc_mixer_stream_get_icon_name (stream));
1124
gtk_box_pack_start (GTK_BOX (dialog->priv->applications_box), bar, FALSE, FALSE, 12);
1125
dialog->priv->num_apps++;
1126
gtk_widget_hide (dialog->priv->no_apps_label);
1129
if (GVC_IS_MIXER_SOURCE (stream)) {
1130
GtkTreeModel *model;
1134
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview));
1135
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1137
icon = gvc_mixer_stream_get_gicon (stream);
1138
gtk_list_store_set (GTK_LIST_STORE (model),
1140
NAME_COLUMN, gvc_mixer_stream_get_description (stream),
1142
ACTIVE_COLUMN, is_default,
1144
ID_COLUMN, gvc_mixer_stream_get_id (stream),
1147
g_object_unref (icon);
1150
GtkTreeSelection *selection;
1152
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->input_treeview));
1153
gtk_tree_selection_select_iter (selection, &iter);
1156
g_signal_connect (stream,
1157
"notify::description",
1158
G_CALLBACK (on_stream_description_notify),
1160
} else if (GVC_IS_MIXER_SINK (stream)) {
1161
GtkTreeModel *model;
1163
const GvcChannelMap *map;
1166
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview));
1167
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1169
map = gvc_mixer_stream_get_channel_map (stream);
1170
icon = gvc_mixer_stream_get_gicon (stream);
1171
gtk_list_store_set (GTK_LIST_STORE (model),
1173
NAME_COLUMN, gvc_mixer_stream_get_description (stream),
1175
ACTIVE_COLUMN, is_default,
1177
ID_COLUMN, gvc_mixer_stream_get_id (stream),
1178
SPEAKERS_COLUMN, gvc_channel_map_get_mapping (map),
1181
g_object_unref (icon);
1184
GtkTreeSelection *selection;
1186
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->output_treeview));
1187
gtk_tree_selection_select_iter (selection, &iter);
1190
g_signal_connect (stream,
1191
"notify::description",
1192
G_CALLBACK (on_stream_description_notify),
1197
bar_set_stream (dialog, bar, stream);
1198
gtk_widget_show (bar);
1203
on_control_stream_added (GvcMixerControl *control,
1205
GvcMixerDialog *dialog)
1207
GvcMixerStream *stream;
1210
bar = g_hash_table_lookup (dialog->priv->bars, GUINT_TO_POINTER (id));
1212
g_debug ("GvcMixerDialog: Stream %u already added", id);
1216
stream = gvc_mixer_control_lookup_stream_id (control, id);
1217
if (stream != NULL) {
1218
add_stream (dialog, stream);
1223
find_item_by_id (GtkTreeModel *model,
1228
gboolean found_item;
1232
if (!gtk_tree_model_get_iter_first (model, iter)) {
1239
gtk_tree_model_get (model, iter,
1245
} while (!found_item && gtk_tree_model_iter_next (model, iter));
1251
remove_stream (GvcMixerDialog *dialog,
1257
GtkTreeModel *model;
1259
/* remove bars for applications and reset fixed bars */
1260
bar = g_hash_table_lookup (dialog->priv->bars, GUINT_TO_POINTER (id));
1261
if (bar == dialog->priv->output_bar
1262
|| bar == dialog->priv->input_bar
1263
|| bar == dialog->priv->effects_bar) {
1265
g_object_get (bar, "name", &name, NULL);
1266
g_debug ("Removing stream for bar '%s'", name);
1268
bar_set_stream (dialog, bar, NULL);
1269
} else if (bar != NULL) {
1270
g_hash_table_remove (dialog->priv->bars, GUINT_TO_POINTER (id));
1271
gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (bar)),
1273
dialog->priv->num_apps--;
1274
if (dialog->priv->num_apps == 0) {
1275
gtk_widget_show (dialog->priv->no_apps_label);
1279
/* remove from any models */
1280
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview));
1281
found = find_item_by_id (GTK_TREE_MODEL (model), id, ID_COLUMN, &iter);
1283
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1285
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview));
1286
found = find_item_by_id (GTK_TREE_MODEL (model), id, ID_COLUMN, &iter);
1288
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1293
on_control_stream_removed (GvcMixerControl *control,
1295
GvcMixerDialog *dialog)
1297
remove_stream (dialog, id);
1301
add_card (GvcMixerDialog *dialog,
1304
GtkTreeModel *model;
1306
GtkTreeSelection *selection;
1307
GvcMixerCardProfile *profile;
1311
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview));
1312
index = gvc_mixer_card_get_index (card);
1313
if (find_item_by_id (GTK_TREE_MODEL (model), index, HW_ID_COLUMN, &iter) == FALSE)
1314
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1315
profile = gvc_mixer_card_get_profile (card);
1316
g_assert (profile != NULL);
1317
icon = g_themed_icon_new_with_default_fallbacks (gvc_mixer_card_get_icon_name (card));
1318
//FIXME we need the status (default for a profile?) here
1319
gtk_list_store_set (GTK_LIST_STORE (model),
1321
HW_NAME_COLUMN, gvc_mixer_card_get_name (card),
1322
HW_ID_COLUMN, index,
1323
HW_ICON_COLUMN, icon,
1324
HW_PROFILE_COLUMN, profile->profile,
1325
HW_PROFILE_HUMAN_COLUMN, profile->human_profile,
1326
HW_STATUS_COLUMN, profile->status,
1327
HW_SENSITIVE_COLUMN, g_strcmp0 (profile->profile, "off") != 0,
1330
g_object_unref (icon);
1332
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->hw_treeview));
1333
if (gtk_tree_selection_get_selected (selection, NULL, NULL) == FALSE) {
1334
gtk_tree_selection_select_iter (selection, &iter);
1335
} else if (dialog->priv->hw_profile_combo != NULL) {
1336
GvcMixerCard *selected;
1338
/* Set the current profile if it changed for the selected card */
1339
selected = g_object_get_data (G_OBJECT (dialog->priv->hw_profile_combo), "card");
1340
if (gvc_mixer_card_get_index (selected) == gvc_mixer_card_get_index (card)) {
1341
gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->hw_profile_combo),
1343
g_object_set (G_OBJECT (dialog->priv->hw_profile_combo),
1344
"show-button", profile->n_sinks == 1,
1351
on_control_card_added (GvcMixerControl *control,
1353
GvcMixerDialog *dialog)
1357
card = gvc_mixer_control_lookup_card_id (control, id);
1359
add_card (dialog, card);
1364
remove_card (GvcMixerDialog *dialog,
1369
GtkTreeModel *model;
1371
/* remove from any models */
1372
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview));
1373
found = find_item_by_id (GTK_TREE_MODEL (model), id, HW_ID_COLUMN, &iter);
1375
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1379
on_control_card_removed (GvcMixerControl *control,
1381
GvcMixerDialog *dialog)
1383
remove_card (dialog, id);
1387
_gtk_label_make_bold (GtkLabel *label)
1389
PangoFontDescription *font_desc;
1391
font_desc = pango_font_description_new ();
1393
pango_font_description_set_weight (font_desc,
1396
/* This will only affect the weight of the font, the rest is
1397
* from the current state of the widget, which comes from the
1398
* theme or user prefs, since the font desc only has the
1399
* weight flag turned on.
1401
gtk_widget_modify_font (GTK_WIDGET (label), font_desc);
1403
pango_font_description_free (font_desc);
1407
on_input_selection_changed (GtkTreeSelection *selection,
1408
GvcMixerDialog *dialog)
1410
GtkTreeModel *model;
1415
if (gtk_tree_selection_get_selected (selection, &model, &iter) == FALSE) {
1416
g_debug ("Could not get default input from selection");
1420
gtk_tree_model_get (model, &iter,
1422
ACTIVE_COLUMN, &toggled,
1427
GvcMixerStream *stream;
1429
g_debug ("Default input selected: %u", id);
1430
stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
1431
if (stream == NULL) {
1432
g_warning ("Unable to find stream for id: %u", id);
1436
gvc_mixer_control_set_default_source (dialog->priv->mixer_control, stream);
1441
on_output_selection_changed (GtkTreeSelection *selection,
1442
GvcMixerDialog *dialog)
1444
GtkTreeModel *model;
1449
if (gtk_tree_selection_get_selected (selection, &model, &iter) == FALSE) {
1450
g_debug ("Could not get default output from selection");
1454
gtk_tree_model_get (model, &iter,
1456
ACTIVE_COLUMN, &toggled,
1461
GvcMixerStream *stream;
1463
g_debug ("Default output selected: %u", id);
1464
stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
1465
if (stream == NULL) {
1466
g_warning ("Unable to find stream for id: %u", id);
1470
gvc_mixer_control_set_default_sink (dialog->priv->mixer_control, stream);
1475
name_to_text (GtkTreeViewColumn *column,
1476
GtkCellRenderer *cell,
1477
GtkTreeModel *model,
1481
char *name, *mapping;
1483
gtk_tree_model_get(model, iter,
1485
SPEAKERS_COLUMN, &mapping,
1488
if (mapping == NULL) {
1489
g_object_set (cell, "text", name, NULL);
1493
str = g_strdup_printf ("%s\n<i>%s</i>",
1495
g_object_set (cell, "markup", str, NULL);
1504
create_stream_treeview (GvcMixerDialog *dialog,
1505
GCallback on_selection_changed)
1507
GtkWidget *treeview;
1508
GtkListStore *store;
1509
GtkCellRenderer *renderer;
1510
GtkTreeViewColumn *column;
1511
GtkTreeSelection *selection;
1513
treeview = gtk_tree_view_new ();
1514
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
1516
store = gtk_list_store_new (NUM_COLUMNS,
1523
gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
1524
GTK_TREE_MODEL (store));
1526
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
1527
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
1529
column = gtk_tree_view_column_new ();
1530
gtk_tree_view_column_set_title (column, _("Name"));
1531
renderer = gtk_cell_renderer_pixbuf_new ();
1532
gtk_tree_view_column_pack_start (column, renderer, FALSE);
1533
g_object_set (G_OBJECT (renderer), "stock-size", GTK_ICON_SIZE_LARGE_TOOLBAR, NULL);
1534
gtk_tree_view_column_set_attributes (column, renderer,
1535
"gicon", ICON_COLUMN,
1538
renderer = gtk_cell_renderer_text_new ();
1539
gtk_tree_view_column_pack_start (column, renderer, TRUE);
1540
gtk_tree_view_column_set_cell_data_func (column, renderer,
1541
name_to_text, NULL, NULL);
1542
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1544
g_signal_connect (G_OBJECT (selection), "changed",
1545
on_selection_changed, dialog);
1547
renderer = gtk_cell_renderer_text_new ();
1548
column = gtk_tree_view_column_new_with_attributes (_("Device"),
1550
"text", DEVICE_COLUMN,
1552
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1558
on_profile_changed (GvcComboBox *widget,
1559
const char *profile,
1564
card = g_object_get_data (G_OBJECT (widget), "card");
1566
g_warning ("Could not find card for combobox");
1570
g_debug ("Profile changed to %s for card %s", profile,
1571
gvc_mixer_card_get_name (card));
1573
gvc_mixer_card_change_profile (card, profile);
1577
on_test_speakers_clicked (GvcComboBox *widget,
1580
GvcMixerDialog *dialog = GVC_MIXER_DIALOG (user_data);
1582
GvcMixerCardProfile *profile;
1583
GtkWidget *d, *speaker_test, *container;
1586
card = g_object_get_data (G_OBJECT (widget), "card");
1588
g_warning ("Could not find card for combobox");
1591
profile = gvc_mixer_card_get_profile (card);
1593
g_debug ("XXX Start speaker testing for profile '%s', card %s XXX",
1594
profile->profile, gvc_mixer_card_get_name (card));
1596
title = g_strdup_printf (_("Speaker Testing for %s"), gvc_mixer_card_get_name (card));
1597
d = gtk_dialog_new_with_buttons (title,
1598
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (widget))),
1599
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1600
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1602
/* FIXME: this should not be necessary */
1603
gtk_window_set_has_resize_grip (GTK_WINDOW (d), FALSE);
1606
speaker_test = gvc_speaker_test_new (dialog->priv->mixer_control,
1608
gtk_widget_show (speaker_test);
1609
container = gtk_dialog_get_content_area (GTK_DIALOG (d));
1610
gtk_container_add (GTK_CONTAINER (container), speaker_test);
1612
gtk_dialog_run (GTK_DIALOG (d));
1613
gtk_widget_destroy (d);
1617
on_card_selection_changed (GtkTreeSelection *selection,
1620
GvcMixerDialog *dialog = GVC_MIXER_DIALOG (user_data);
1621
GtkTreeModel *model;
1623
const GList *profiles;
1626
GvcMixerCardProfile *current_profile;
1628
g_debug ("Card selection changed");
1630
if (dialog->priv->hw_profile_combo != NULL) {
1631
gtk_container_remove (GTK_CONTAINER (dialog->priv->hw_settings_box),
1632
dialog->priv->hw_profile_combo);
1633
dialog->priv->hw_profile_combo = NULL;
1636
if (gtk_tree_selection_get_selected (selection,
1642
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview));
1643
gtk_tree_model_get (model, &iter,
1646
card = gvc_mixer_control_lookup_card_id (dialog->priv->mixer_control, id);
1648
g_warning ("Unable to find card for id: %u", id);
1652
current_profile = gvc_mixer_card_get_profile (card);
1653
profiles = gvc_mixer_card_get_profiles (card);
1654
dialog->priv->hw_profile_combo = gvc_combo_box_new (_("_Profile:"));
1655
g_object_set (G_OBJECT (dialog->priv->hw_profile_combo), "button-label", _("_Test Speakers"), NULL);
1656
gvc_combo_box_set_profiles (GVC_COMBO_BOX (dialog->priv->hw_profile_combo), profiles);
1657
gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->hw_profile_combo), current_profile->profile);
1659
gtk_box_pack_start (GTK_BOX (dialog->priv->hw_settings_box),
1660
dialog->priv->hw_profile_combo,
1662
g_object_set (G_OBJECT (dialog->priv->hw_profile_combo),
1663
"show-button", current_profile->n_sinks == 1,
1665
gtk_widget_show (dialog->priv->hw_profile_combo);
1667
g_object_set_data (G_OBJECT (dialog->priv->hw_profile_combo), "card", card);
1668
g_signal_connect (G_OBJECT (dialog->priv->hw_profile_combo), "changed",
1669
G_CALLBACK (on_profile_changed), dialog);
1670
g_signal_connect (G_OBJECT (dialog->priv->hw_profile_combo), "button-clicked",
1671
G_CALLBACK (on_test_speakers_clicked), dialog);
1675
card_to_text (GtkTreeViewColumn *column,
1676
GtkCellRenderer *cell,
1677
GtkTreeModel *model,
1681
char *name, *status, *profile, *str;
1684
gtk_tree_model_get(model, iter,
1685
HW_NAME_COLUMN, &name,
1686
HW_STATUS_COLUMN, &status,
1687
HW_PROFILE_HUMAN_COLUMN, &profile,
1688
HW_SENSITIVE_COLUMN, &sensitive,
1691
str = g_strdup_printf ("%s\n<i>%s</i>\n<i>%s</i>",
1692
name, status, profile);
1695
"sensitive", sensitive,
1705
create_cards_treeview (GvcMixerDialog *dialog,
1706
GCallback on_changed)
1708
GtkWidget *treeview;
1709
GtkListStore *store;
1710
GtkCellRenderer *renderer;
1711
GtkTreeViewColumn *column;
1712
GtkTreeSelection *selection;
1714
treeview = gtk_tree_view_new ();
1715
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
1717
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
1718
g_signal_connect (G_OBJECT (selection), "changed",
1719
on_changed, dialog);
1721
store = gtk_list_store_new (HW_NUM_COLUMNS,
1729
gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
1730
GTK_TREE_MODEL (store));
1732
renderer = gtk_cell_renderer_pixbuf_new ();
1733
g_object_set (G_OBJECT (renderer), "stock-size", GTK_ICON_SIZE_DIALOG, NULL);
1734
column = gtk_tree_view_column_new_with_attributes (NULL,
1736
"gicon", HW_ICON_COLUMN,
1737
"sensitive", HW_SENSITIVE_COLUMN,
1739
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1741
gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (treeview), -1,
1742
_("Name"), gtk_cell_renderer_text_new (),
1743
card_to_text, NULL, NULL);
1749
gvc_mixer_dialog_constructor (GType type,
1750
guint n_construct_properties,
1751
GObjectConstructParam *construct_params)
1754
GvcMixerDialog *self;
1755
GtkWidget *main_vbox;
1757
GtkWidget *alignment;
1764
GvcMixerStream *stream;
1766
GtkTreeSelection *selection;
1768
object = G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->constructor (type, n_construct_properties, construct_params);
1770
self = GVC_MIXER_DIALOG (object);
1772
main_vbox = GTK_WIDGET (self);
1773
gtk_box_set_spacing (GTK_BOX (main_vbox), 2);
1775
gtk_container_set_border_width (GTK_CONTAINER (self), 6);
1777
self->priv->output_stream_box = gtk_hbox_new (FALSE, 12);
1778
alignment = gtk_alignment_new (0, 0, 1, 1);
1779
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 12, 0, 0, 0);
1780
gtk_container_add (GTK_CONTAINER (alignment), self->priv->output_stream_box);
1781
gtk_box_pack_start (GTK_BOX (main_vbox),
1784
self->priv->output_bar = create_bar (self, TRUE, TRUE);
1785
gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->output_bar),
1786
_("_Output volume:"));
1787
gtk_widget_set_sensitive (self->priv->output_bar, FALSE);
1788
gtk_box_pack_start (GTK_BOX (self->priv->output_stream_box),
1789
self->priv->output_bar, TRUE, TRUE, 12);
1791
self->priv->notebook = gtk_notebook_new ();
1792
gtk_box_pack_start (GTK_BOX (main_vbox),
1793
self->priv->notebook,
1795
gtk_container_set_border_width (GTK_CONTAINER (self->priv->notebook), 5);
1798
self->priv->sound_effects_box = gtk_vbox_new (FALSE, 6);
1799
gtk_container_set_border_width (GTK_CONTAINER (self->priv->sound_effects_box), 12);
1800
label = gtk_label_new (_("Sound Effects"));
1801
gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
1802
self->priv->sound_effects_box,
1805
self->priv->effects_bar = create_bar (self, TRUE, TRUE);
1806
gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->effects_bar),
1807
_("_Alert volume:"));
1808
gtk_widget_set_sensitive (self->priv->effects_bar, FALSE);
1809
gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box),
1810
self->priv->effects_bar, FALSE, FALSE, 0);
1812
self->priv->sound_theme_chooser = gvc_sound_theme_chooser_new ();
1813
gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box),
1814
self->priv->sound_theme_chooser,
1818
self->priv->hw_box = gtk_vbox_new (FALSE, 12);
1819
gtk_container_set_border_width (GTK_CONTAINER (self->priv->hw_box), 12);
1820
label = gtk_label_new (_("Hardware"));
1821
gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
1825
box = gtk_frame_new (_("C_hoose a device to configure:"));
1826
label = gtk_frame_get_label_widget (GTK_FRAME (box));
1827
_gtk_label_make_bold (GTK_LABEL (label));
1828
gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
1829
gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
1830
gtk_box_pack_start (GTK_BOX (self->priv->hw_box), box, TRUE, TRUE, 0);
1832
alignment = gtk_alignment_new (0, 0, 1, 1);
1833
gtk_container_add (GTK_CONTAINER (box), alignment);
1834
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
1836
self->priv->hw_treeview = create_cards_treeview (self,
1837
G_CALLBACK (on_card_selection_changed));
1838
gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->hw_treeview);
1840
box = gtk_scrolled_window_new (NULL, NULL);
1841
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box),
1843
GTK_POLICY_AUTOMATIC);
1844
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box),
1846
gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (box), 150);
1847
gtk_container_add (GTK_CONTAINER (box), self->priv->hw_treeview);
1848
gtk_container_add (GTK_CONTAINER (alignment), box);
1850
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->hw_treeview));
1851
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
1853
box = gtk_frame_new (_("Settings for the selected device:"));
1854
label = gtk_frame_get_label_widget (GTK_FRAME (box));
1855
_gtk_label_make_bold (GTK_LABEL (label));
1856
gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
1857
gtk_box_pack_start (GTK_BOX (self->priv->hw_box), box, FALSE, TRUE, 12);
1858
self->priv->hw_settings_box = gtk_vbox_new (FALSE, 12);
1859
gtk_container_add (GTK_CONTAINER (box), self->priv->hw_settings_box);
1862
self->priv->input_box = gtk_vbox_new (FALSE, 12);
1863
gtk_container_set_border_width (GTK_CONTAINER (self->priv->input_box), 12);
1864
label = gtk_label_new (_("Input"));
1865
gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
1866
self->priv->input_box,
1869
self->priv->input_bar = create_bar (self, TRUE, TRUE);
1870
gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->input_bar),
1871
_("_Input volume:"));
1872
gvc_channel_bar_set_low_icon_name (GVC_CHANNEL_BAR (self->priv->input_bar),
1873
"audio-input-microphone-low-symbolic");
1874
gvc_channel_bar_set_high_icon_name (GVC_CHANNEL_BAR (self->priv->input_bar),
1875
"audio-input-microphone-high-symbolic");
1876
gtk_widget_set_sensitive (self->priv->input_bar, FALSE);
1877
alignment = gtk_alignment_new (0, 0, 1, 1);
1878
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
1879
gtk_container_add (GTK_CONTAINER (alignment), self->priv->input_bar);
1880
gtk_box_pack_start (GTK_BOX (self->priv->input_box),
1884
box = gtk_hbox_new (FALSE, 6);
1885
gtk_box_pack_start (GTK_BOX (self->priv->input_box),
1889
sbox = gtk_hbox_new (FALSE, 6);
1890
gtk_box_pack_start (GTK_BOX (box),
1894
label = gtk_label_new (_("Input level:"));
1895
gtk_box_pack_start (GTK_BOX (sbox),
1898
if (self->priv->size_group != NULL)
1899
gtk_size_group_add_widget (self->priv->size_group, sbox);
1901
self->priv->input_level_bar = gvc_level_bar_new ();
1902
gvc_level_bar_set_orientation (GVC_LEVEL_BAR (self->priv->input_level_bar),
1903
GTK_ORIENTATION_HORIZONTAL);
1904
gvc_level_bar_set_scale (GVC_LEVEL_BAR (self->priv->input_level_bar),
1905
GVC_LEVEL_SCALE_LINEAR);
1906
gtk_box_pack_start (GTK_BOX (box),
1907
self->priv->input_level_bar,
1910
ebox = gtk_hbox_new (FALSE, 6);
1911
gtk_box_pack_start (GTK_BOX (box),
1914
if (self->priv->size_group != NULL)
1915
gtk_size_group_add_widget (self->priv->size_group, ebox);
1917
self->priv->input_settings_box = gtk_hbox_new (FALSE, 6);
1918
gtk_box_pack_start (GTK_BOX (self->priv->input_box),
1919
self->priv->input_settings_box,
1922
box = gtk_frame_new (_("C_hoose a device for sound input:"));
1923
label = gtk_frame_get_label_widget (GTK_FRAME (box));
1924
_gtk_label_make_bold (GTK_LABEL (label));
1925
gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
1926
gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
1927
gtk_box_pack_start (GTK_BOX (self->priv->input_box), box, TRUE, TRUE, 0);
1929
alignment = gtk_alignment_new (0, 0, 1, 1);
1930
gtk_container_add (GTK_CONTAINER (box), alignment);
1931
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
1933
self->priv->input_treeview = create_stream_treeview (self,
1934
G_CALLBACK (on_input_selection_changed));
1935
gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->input_treeview);
1937
box = gtk_scrolled_window_new (NULL, NULL);
1938
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box),
1940
GTK_POLICY_AUTOMATIC);
1941
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box),
1943
gtk_container_add (GTK_CONTAINER (box), self->priv->input_treeview);
1944
gtk_container_add (GTK_CONTAINER (alignment), box);
1947
self->priv->output_box = gtk_vbox_new (FALSE, 12);
1948
gtk_container_set_border_width (GTK_CONTAINER (self->priv->output_box), 12);
1949
label = gtk_label_new (_("Output"));
1950
gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
1951
self->priv->output_box,
1954
box = gtk_frame_new (_("C_hoose a device for sound output:"));
1955
label = gtk_frame_get_label_widget (GTK_FRAME (box));
1956
_gtk_label_make_bold (GTK_LABEL (label));
1957
gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
1958
gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
1959
gtk_box_pack_start (GTK_BOX (self->priv->output_box), box, TRUE, TRUE, 0);
1961
alignment = gtk_alignment_new (0, 0, 1, 1);
1962
gtk_container_add (GTK_CONTAINER (box), alignment);
1963
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
1965
self->priv->output_treeview = create_stream_treeview (self,
1966
G_CALLBACK (on_output_selection_changed));
1967
gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->output_treeview);
1969
box = gtk_scrolled_window_new (NULL, NULL);
1970
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box),
1972
GTK_POLICY_AUTOMATIC);
1973
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box),
1975
gtk_container_add (GTK_CONTAINER (box), self->priv->output_treeview);
1976
gtk_container_add (GTK_CONTAINER (alignment), box);
1978
box = gtk_frame_new (_("Settings for the selected device:"));
1979
label = gtk_frame_get_label_widget (GTK_FRAME (box));
1980
_gtk_label_make_bold (GTK_LABEL (label));
1981
gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
1982
gtk_box_pack_start (GTK_BOX (self->priv->output_box), box, FALSE, FALSE, 12);
1983
self->priv->output_settings_box = gtk_vbox_new (FALSE, 0);
1984
gtk_container_add (GTK_CONTAINER (box), self->priv->output_settings_box);
1987
self->priv->applications_box = gtk_vbox_new (FALSE, 12);
1988
gtk_container_set_border_width (GTK_CONTAINER (self->priv->applications_box), 12);
1989
label = gtk_label_new (_("Applications"));
1990
gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
1991
self->priv->applications_box,
1993
self->priv->no_apps_label = gtk_label_new (_("No application is currently playing or recording audio."));
1994
gtk_box_pack_start (GTK_BOX (self->priv->applications_box),
1995
self->priv->no_apps_label,
1998
g_signal_connect (self->priv->mixer_control,
2000
G_CALLBACK (on_control_stream_added),
2002
g_signal_connect (self->priv->mixer_control,
2004
G_CALLBACK (on_control_stream_removed),
2006
g_signal_connect (self->priv->mixer_control,
2008
G_CALLBACK (on_control_card_added),
2010
g_signal_connect (self->priv->mixer_control,
2012
G_CALLBACK (on_control_card_removed),
2015
gtk_widget_show_all (main_vbox);
2017
streams = gvc_mixer_control_get_streams (self->priv->mixer_control);
2018
for (l = streams; l != NULL; l = l->next) {
2020
add_stream (self, stream);
2022
g_slist_free (streams);
2024
cards = gvc_mixer_control_get_cards (self->priv->mixer_control);
2025
for (l = cards; l != NULL; l = l->next) {
2027
add_card (self, card);
2029
g_slist_free (cards);
2035
gvc_mixer_dialog_dispose (GObject *object)
2037
GvcMixerDialog *dialog = GVC_MIXER_DIALOG (object);
2039
if (dialog->priv->mixer_control != NULL) {
2040
g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
2041
on_control_stream_added,
2043
g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
2044
on_control_stream_removed,
2046
g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
2047
on_control_card_added,
2049
g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
2050
on_control_card_removed,
2053
g_object_unref (dialog->priv->mixer_control);
2054
dialog->priv->mixer_control = NULL;
2057
if (dialog->priv->bars != NULL) {
2058
g_hash_table_destroy (dialog->priv->bars);
2059
dialog->priv->bars = NULL;
2062
G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->dispose (object);
2066
gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass)
2068
GObjectClass *object_class = G_OBJECT_CLASS (klass);
2070
object_class->constructor = gvc_mixer_dialog_constructor;
2071
object_class->dispose = gvc_mixer_dialog_dispose;
2072
object_class->finalize = gvc_mixer_dialog_finalize;
2073
object_class->set_property = gvc_mixer_dialog_set_property;
2074
object_class->get_property = gvc_mixer_dialog_get_property;
2076
g_object_class_install_property (object_class,
2078
g_param_spec_object ("mixer-control",
2081
GVC_TYPE_MIXER_CONTROL,
2082
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
2084
g_type_class_add_private (klass, sizeof (GvcMixerDialogPrivate));
2089
gvc_mixer_dialog_init (GvcMixerDialog *dialog)
2091
dialog->priv = GVC_MIXER_DIALOG_GET_PRIVATE (dialog);
2092
dialog->priv->bars = g_hash_table_new (NULL, NULL);
2093
dialog->priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
2097
gvc_mixer_dialog_finalize (GObject *object)
2099
GvcMixerDialog *mixer_dialog;
2101
g_return_if_fail (object != NULL);
2102
g_return_if_fail (GVC_IS_MIXER_DIALOG (object));
2104
mixer_dialog = GVC_MIXER_DIALOG (object);
2106
g_return_if_fail (mixer_dialog->priv != NULL);
2107
G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->finalize (object);
2111
gvc_mixer_dialog_new (GvcMixerControl *control)
2114
dialog = g_object_new (GVC_TYPE_MIXER_DIALOG,
2115
"mixer-control", control,
2117
return GVC_MIXER_DIALOG (dialog);
2129
gvc_mixer_dialog_set_page (GvcMixerDialog *self,
2134
g_return_val_if_fail (self != NULL, FALSE);
2138
else if (g_str_equal (page, "effects"))
2140
else if (g_str_equal (page, "hardware"))
2141
num = PAGE_HARDWARE;
2142
else if (g_str_equal (page, "input"))
2144
else if (g_str_equal (page, "output"))
2146
else if (g_str_equal (page, "applications"))
2147
num = PAGE_APPLICATIONS;
2151
gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook), num);