Settings|Mixer: aggregateMenu position is now working.
[AdvancedVolumeMixer.git] / mixer.js
blobc6f6b873f103c9f7d9c246d96aaa3bb275efa2d4
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.hasHeadphones = false;
34     this._control = Volume.getMixerControl();
35     this._sinks = {};
36     this._outputs = {};
37     this._settings = Settings.gsettings;
39     log("AdvVolumeMixer init");
40     log(this._control.get_vol_max_norm());
41     log(this._settings.get_enum("position"));
43     this._control.connect(
44       "state-changed",
45       Lang.bind(this, this._onControlStateChanged)
46     );
47     this._control.connect(
48       "default-sink-changed",
49       Lang.bind(this, this._readOutput));
50     this._control.connect(
51       "default-source-changed",
52       Lang.bind(this, this._readInput)
53     );
54     this._control.connect(
55       "stream-added",
56       Lang.bind(this, this._streamAdded)
57     );
58     this._control.connect(
59       "stream-removed",
60       Lang.bind(this, this._streamRemoved)
61     );
63     this._output = null;
65     if (this._settings.get_enum("output-type") == 0) {
66       this._output = new Volume.OutputStreamSlider(this._control);
67     } else {
68       this._output = new Widget.AppOutputStreamSlider(this._control);
69     }
70     this._output.connect('stream-updated', Lang.bind(this, function() {
71       this.emit('icon-changed');
72     }));
74     this._input = new Volume.InputStreamSlider(this._control);
75     this._separator = new PopupMenu.PopupSeparatorMenuItem();
77     this.addMenuItem(this._output.item);
78     this.addMenuItem(this._input.item);
79     this.addMenuItem(this._separator);
81     this._onControlStateChanged();
82   },
84   scroll: function(event) {
85     this._output.scroll(event);
86   },
88   separatorLastItem: function(last) {
89     log("separatorLastItem");
90     log(last);
92     this._separator.destroy();
93     this._separator = new PopupMenu.PopupSeparatorMenuItem();
95     if (last) {
96       this.addMenuItem(this._separator, 999);
97     } else {
98       this.addMenuItem(this._separator, 2);
99     }
100   },
102   _streamAdded: function(control, id) {
103     if (id in this._sinks) {
104       return;
105     }
107     if (id in this._outputs) {
108       return;
109     }
111     let stream = control.lookup_stream_id(id);
113     if (stream["is-event-stream"]) {
114       // Do nothing
115     } else if (stream instanceof Gvc.MixerSinkInput) {
116       let s = new Widget.AppOutputStreamSlider(this._control);
117       s.stream = stream;
118       this._sinks[id] = s;
119       this.addMenuItem(s.item);
120     } else if (stream instanceof Gvc.MixerSink) {
121       let s = new Volume.OutputStreamSlider(this._control);
122       s.stream = stream;
123       this._outputs[id] = s;
124     }
125   },
127   _streamRemoved: function(control, id) {
128     if (id in this._sinks) {
129       this._sinks[id].item.destroy();
130       delete this._sinks[id];
131     } else if (id in this._outputs) {
132       this._outputs[id].item.destroy();
133       delete this._outputs[id];
134     }
135   },
137   _onControlStateChanged: function() {
138     if (this._control.get_state() == Gvc.MixerControlState.READY) {
139       this._readInput();
140       this._readOutput();
142       let streams = this._control.get_streams();
143       for (let i = 0; i < streams.length; i++) {
144         this._streamAdded(this._control, streams[i].id);
145       }
146     }
148     this.emit('icon-changed');
149   },
151   _readOutput: function() {
152     this._output.stream = this._control.get_default_sink();
153   },
155   _readInput: function() {
156     this._input.stream = this._control.get_default_source();
157   },
159   getIcon: function() {
160     return this._output.getIcon();
161   }