Version 14
[AdvancedVolumeMixer.git] / mixer.js
blobcb83a566db524529f22423754b0ec8716e9f0537
1 // extension.js
2 // vi: et sw=2
3 //
4 // Advanced Volume Mixer
5 // Control programs' volume from gnome volume mixer applet.
6 //
7 // Author: Harry Karvonen <harry.karvonen@gmail.com>
8 //
10 const Clutter = imports.gi.Clutter;
11 const Lang = imports.lang;
12 const Gvc = imports.gi.Gvc;
13 const St = imports.gi.St;
15 const Main = imports.ui.main;
16 const PopupMenu = imports.ui.popupMenu;
18 const Volume = imports.ui.status.volume;
20 const AVM = imports.misc.extensionUtils.getCurrentExtension();
21 const Widget = AVM.imports.widget;
22 const Settings = AVM.imports.settings;
23 const Panel = AVM.imports.panel;
25 const AdvancedVolumeMixer = new Lang.Class({
26   Name: "AdvancedVolumeMixer",
27   Extends: PopupMenu.PopupMenuSection,
29   _init: function() {
30     this.parent();
32     this._control = Volume.getMixerControl();
33     this._sinks = {};
34     this._outputs = {};
35     this._settings = Settings.gsettings;
37     this._control.connect(
38       "state-changed",
39       Lang.bind(this, this._onControlStateChanged)
40     );
41     this._control.connect(
42       "default-sink-changed",
43       Lang.bind(this, this._readOutput));
44     this._control.connect(
45       "default-source-changed",
46       Lang.bind(this, this._readInput)
47     );
48     this._control.connect(
49       "stream-added",
50       Lang.bind(this, this._streamAdded)
51     );
52     this._control.connect(
53       "stream-removed",
54       Lang.bind(this, this._streamRemoved)
55     );
57     this._output = new Widget.AdvOutputStreamSlider(
58       this._control,
59       this._settings.get_enum("output-type") == 0
60     );
62     this._output.connect('stream-updated', Lang.bind(this, function() {
63       this.emit('icon-changed');
64     }));
65     this._output.item.actor.connect(
66       'button-press-event',
67       Lang.bind(this, function(actor, event) {
68         if (event.get_button() == 2) {
69           actor.stream.change_is_muted(!actor.stream.is_muted);
70           return true;
71         }
72       }
73     ));
75     this._input = new Volume.InputStreamSlider(this._control);
76     this._separator = new PopupMenu.PopupSeparatorMenuItem();
78     this.addMenuItem(this._output.item);
79     this.addMenuItem(this._input.item);
80     this.addMenuItem(this._separator);
82     this._onControlStateChanged();
83   },
85   scroll: function(event) {
86     this._output.scroll(event);
87   },
89   outputHasHeadphones: function() {
90     return this._output._hasHeadphones;
91   },
93   separatorLastItem: function(last) {
94     if (this._separator) {
95       this._separator.destroy();
96     }
98     if (last) {
99       this._separator = null;
100     } else {
101       this._separator = new PopupMenu.PopupSeparatorMenuItem();
102       this.addMenuItem(this._separator, 2);
103     }
104   },
106   _streamAdded: function(control, id) {
107     if (id in this._sinks) {
108       return;
109     }
111     if (id in this._outputs) {
112       return;
113     }
115     let stream = control.lookup_stream_id(id);
117     if (stream["is-event-stream"]) {
118       // Do nothing
119     } else if (stream instanceof Gvc.MixerSinkInput) {
120       let s = new Widget.AppOutputStreamSlider(this._control);
121       s.stream = stream;
122       this._sinks[id] = s;
123       this.addMenuItem(s.item);
124       s.item.actor.connect(
125         "button-press-event",
126         function (actor, event) {
127           if (event.get_button() == 2) {
128             actor.stream.change_is_muted(!actor.stream.is_muted);
129           }
130         }
131       );
132     } else if (stream instanceof Gvc.MixerSink) {
133       let s = new Widget.AppOutputStreamSlider(this._control, false, function (st) { return st.get_description(); });
134       s.stream = stream;
135       s.item.setOrnament(this._output.stream.id == s.stream.id);
136       this._outputs[id] = s;
137       this._output.item.menu.addMenuItem(s.item);
138       s.item.actor.connect(
139         "button-press-event",
140         function (actor, event) {
141           if (event.get_button() == 1) {
142             control.set_default_sink(actor.stream);
143           } else if (event.get_button() == 2) {
144             actor.stream.change_is_muted(!actor.stream.is_muted);
145           }
146         }
147       );
148     }
149   },
151   _streamRemoved: function(control, id) {
152     if (id in this._sinks) {
153       this._sinks[id].item.destroy();
154       delete this._sinks[id];
155     } else if (id in this._outputs) {
156       this._outputs[id].item.destroy();
157       delete this._outputs[id];
158     }
159   },
161   _onControlStateChanged: function() {
162     if (this._control.get_state() == Gvc.MixerControlState.READY) {
163       this._readInput();
164       this._readOutput();
166       let streams = this._control.get_streams();
167       for (let i = 0; i < streams.length; i++) {
168         this._streamAdded(this._control, streams[i].id);
169       }
170     }
172     this.emit('icon-changed');
173   },
175   _readOutput: function() {
176     this._output.stream = this._control.get_default_sink();
178     for (let output in this._outputs) {
179       this._outputs[output].item.setOrnament(this._output.stream.id == output);
180     }
181   },
183   _readInput: function() {
184     this._input.stream = this._control.get_default_source();
185   },
187   getIcon: function() {
188     return this._output.getIcon();
189   }