Extension/Mixer/Panel/Widget: Code clean-up.
[AdvancedVolumeMixer.git] / mixer.js
blobf009cb496b3502a0c6127ae514dd62c0b5a75524
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     log("AdvVolumeMixer init");
84   },
86   scroll: function(event) {
87     this._output.scroll(event);
88   },
90   outputHasHeadphones: function() {
91     return this._output._hasHeadphones;
92   },
94   separatorLastItem: function(last) {
95     log("separatorLastItem");
96     log(last);
98     this._separator.destroy();
99     this._separator = new PopupMenu.PopupSeparatorMenuItem();
101     if (last) {
102       this.addMenuItem(this._separator, 999);
103     } else {
104       this.addMenuItem(this._separator, 2);
105     }
106   },
108   _streamAdded: function(control, id) {
109     if (id in this._sinks) {
110       return;
111     }
113     if (id in this._outputs) {
114       return;
115     }
117     let stream = control.lookup_stream_id(id);
119     if (stream["is-event-stream"]) {
120       // Do nothing
121     } else if (stream instanceof Gvc.MixerSinkInput) {
122       let s = new Widget.AppOutputStreamSlider(this._control);
123       s.stream = stream;
124       this._sinks[id] = s;
125       this.addMenuItem(s.item);
126       s.item.actor.connect(
127         "button-press-event",
128         function (actor, event) {
129           if (event.get_button() == 2) {
130             actor.stream.change_is_muted(!actor.stream.is_muted);
131           }
132         }
133       );
134     } else if (stream instanceof Gvc.MixerSink) {
135       let s = new Widget.AppOutputStreamSlider(this._control);
136       s.stream = stream;
137       s.item.setOrnament(this._output.stream.id == s.stream.id);
138       this._outputs[id] = s;
139       this._output.item.menu.addMenuItem(s.item);
140       s.item.actor.connect(
141         "button-press-event",
142         function (actor, event) {
143           if (event.get_button() == 1) {
144             control.set_default_sink(actor.stream);
145           } else if (event.get_button() == 2) {
146             actor.stream.change_is_muted(!actor.stream.is_muted);
147           }
148         }
149       );
150     }
151   },
153   _streamRemoved: function(control, id) {
154     if (id in this._sinks) {
155       this._sinks[id].item.destroy();
156       delete this._sinks[id];
157     } else if (id in this._outputs) {
158       this._outputs[id].item.destroy();
159       delete this._outputs[id];
160     }
161   },
163   _onControlStateChanged: function() {
164     if (this._control.get_state() == Gvc.MixerControlState.READY) {
165       this._readInput();
166       this._readOutput();
168       let streams = this._control.get_streams();
169       for (let i = 0; i < streams.length; i++) {
170         this._streamAdded(this._control, streams[i].id);
171       }
172     }
174     this.emit('icon-changed');
175   },
177   _readOutput: function() {
178     this._output.stream = this._control.get_default_sink();
180     for (let output in this._outputs) {
181       this._outputs[output].item.setOrnament(this._output.stream.id == output);
182     }
183   },
185   _readInput: function() {
186     this._input.stream = this._control.get_default_source();
187   },
189   getIcon: function() {
190     return this._output.getIcon();
191   }