implement meterbridge & meter-strip
[ardour.git] / gtk2_ardour / meterbridge.cc
1 /*
2     Copyright (C) 2012 Paul Davis
3     Author: Robin Gareus
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., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #ifdef WAF_BUILD
22 #include "gtk2ardour-config.h"
23 #endif
24
25 #include <map>
26 #include <sigc++/bind.h>
27
28 #include <gtkmm/accelmap.h>
29
30 #include <glibmm/threads.h>
31
32 #include <gtkmm2ext/gtk_ui.h>
33 #include <gtkmm2ext/utils.h>
34 #include <gtkmm2ext/window_title.h>
35
36 #include "ardour/debug.h"
37 #include "ardour/midi_track.h"
38 #include "ardour/route_group.h"
39 #include "ardour/session.h"
40
41 #include "meterbridge.h"
42
43 #include "monitor_section.h"
44 #include "public_editor.h"
45 #include "ardour_ui.h"
46 #include "utils.h"
47 #include "route_sorter.h"
48 #include "actions.h"
49 #include "gui_thread.h"
50
51 #include "i18n.h"
52
53 using namespace ARDOUR;
54 using namespace PBD;
55 using namespace Gtk;
56 using namespace Glib;
57 using namespace Gtkmm2ext;
58 using namespace std;
59
60 using PBD::atoi;
61
62 Meterbridge* Meterbridge::_instance = 0;
63
64 Meterbridge*
65 Meterbridge::instance ()
66 {
67         if (!_instance) {
68                 _instance  = new Meterbridge;
69         }
70
71         return _instance;
72 }
73
74 Meterbridge::Meterbridge ()
75         : Window (Gtk::WINDOW_TOPLEVEL)
76         , VisibilityTracker (*((Gtk::Window*) this))
77         , _visible (false)
78 {
79         set_name ("Meter Bridge");
80
81         set_wmclass (X_("ardour_mixer"), PROGRAM_NAME);
82
83         signal_delete_event().connect (sigc::mem_fun (*this, &Meterbridge::hide_window));
84         signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
85
86         MeterStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Meterbridge::remove_strip, this, _1), gui_context());
87
88         global_hpacker.set_spacing(1);
89         scroller.add (global_hpacker);
90         scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_NEVER);
91         global_vpacker.pack_start (scroller, true, true);
92         add (global_vpacker);
93
94         global_hpacker.show();
95         global_vpacker.show();
96         scroller.show();
97 }
98
99 Meterbridge::~Meterbridge ()
100 {
101 }
102
103 void
104 Meterbridge::show_window ()
105 {
106         present();
107         if (!_visible) {
108                 set_window_pos_and_size ();
109         }
110         _visible = true;
111 }
112
113 void
114 Meterbridge::set_window_pos_and_size ()
115 {
116         resize (m_width, m_height);
117         move (m_root_x, m_root_y);
118 }
119
120 void
121 Meterbridge::get_window_pos_and_size ()
122 {
123         get_position(m_root_x, m_root_y);
124         get_size(m_width, m_height);
125 }
126
127 bool
128 Meterbridge::hide_window (GdkEventAny *ev)
129 {
130         get_window_pos_and_size();
131         _visible = false;
132         return just_hide_it(ev, static_cast<Gtk::Window *>(this));
133 }
134
135 bool
136 Meterbridge::on_key_press_event (GdkEventKey* ev)
137 {
138         if (gtk_window_propagate_key_event (GTK_WINDOW(gobj()), ev)) {
139                 return true;
140         }
141         return forward_key_press (ev);
142 }
143
144 bool
145 Meterbridge::on_key_release_event (GdkEventKey* ev)
146 {
147         if (gtk_window_propagate_key_event (GTK_WINDOW(gobj()), ev)) {
148                 return true;
149         }
150         /* don't forward releases */
151         return true;
152 }
153
154
155 // copy from gtk2_ardour/mixer_ui.cc 
156 struct SignalOrderRouteSorter {
157     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
158             if (a->is_master() || a->is_monitor()) {
159                     /* "a" is a special route (master, monitor, etc), and comes
160                      * last in the mixer ordering
161                      */
162                     return false;
163             } else if (b->is_master() || b->is_monitor()) {
164                     /* everything comes before b */
165                     return true;
166             }
167             return a->order_key (MixerSort) < b->order_key (MixerSort);
168     }
169 };
170
171 void
172 Meterbridge::set_session (Session* s)
173 {
174         SessionHandlePtr::set_session (s);
175
176         if (!_session) {
177                 return;
178         }
179
180         XMLNode* node = _session->instant_xml(X_("Meterbridge"));
181         if (node) {
182                 set_state (*node);
183         }
184
185         SignalOrderRouteSorter sorter;
186         boost::shared_ptr<RouteList> routes = _session->get_routes();
187
188         RouteList copy(*routes);
189         copy.sort(sorter);
190         add_strips(copy);
191
192         _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Meterbridge::add_strips, this, _1), gui_context());
193
194         _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Meterbridge::parameter_changed, this, _1), gui_context());
195
196         if (_visible) {
197                 show_window();
198                 ActionManager::check_toggleaction ("<Actions>/Common/toggle-meterbridge");
199         }
200         start_updating ();
201 }
202
203 void
204 Meterbridge::session_going_away ()
205 {
206         ENSURE_GUI_THREAD (*this, &Meterbridge::session_going_away);
207
208         for (list<MeterStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
209                 delete (*i);
210         }
211
212         strips.clear ();
213         stop_updating ();
214
215         SessionHandlePtr::session_going_away ();
216
217         _session = 0;
218 }
219
220 int
221 Meterbridge::set_state (const XMLNode& node)
222 {
223         const XMLProperty* prop;
224         XMLNode* geometry;
225
226         m_width = default_width;
227         m_height = default_height;
228         m_root_x = 1;
229         m_root_y = 1;
230
231         if ((geometry = find_named_node (node, "geometry")) != 0) {
232
233                 XMLProperty* prop;
234
235                 if ((prop = geometry->property("x_size")) == 0) {
236                         prop = geometry->property ("x-size");
237                 }
238                 if (prop) {
239                         m_width = atoi(prop->value());
240                 }
241                 if ((prop = geometry->property("y_size")) == 0) {
242                         prop = geometry->property ("y-size");
243                 }
244                 if (prop) {
245                         m_height = atoi(prop->value());
246                 }
247
248                 if ((prop = geometry->property ("x_pos")) == 0) {
249                         prop = geometry->property ("x-pos");
250                 }
251                 if (prop) {
252                         m_root_x = atoi (prop->value());
253
254                 }
255                 if ((prop = geometry->property ("y_pos")) == 0) {
256                         prop = geometry->property ("y-pos");
257                 }
258                 if (prop) {
259                         m_root_y = atoi (prop->value());
260                 }
261         }
262
263         set_window_pos_and_size ();
264
265         if ((prop = node.property ("show-meterbridge"))) {
266                 if (string_is_affirmative (prop->value())) {
267                        _visible = true;
268                 }
269         }
270
271         return 0;
272 }
273
274 XMLNode&
275 Meterbridge::get_state (void)
276 {
277         XMLNode* node = new XMLNode ("Meterbridge");
278
279         if (is_realized()) {
280                 Glib::RefPtr<Gdk::Window> win = get_window();
281
282                 get_window_pos_and_size ();
283
284                 XMLNode* geometry = new XMLNode ("geometry");
285                 char buf[32];
286                 snprintf(buf, sizeof(buf), "%d", m_width);
287                 geometry->add_property(X_("x_size"), string(buf));
288                 snprintf(buf, sizeof(buf), "%d", m_height);
289                 geometry->add_property(X_("y_size"), string(buf));
290                 snprintf(buf, sizeof(buf), "%d", m_root_x);
291                 geometry->add_property(X_("x_pos"), string(buf));
292                 snprintf(buf, sizeof(buf), "%d", m_root_y);
293                 geometry->add_property(X_("y_pos"), string(buf));
294                 node->add_child_nocopy (*geometry);
295         }
296
297         node->add_property ("show-meterbridge", _visible ? "yes" : "no");
298         return *node;
299 }
300
301
302 gint
303 Meterbridge::start_updating ()
304 {
305         fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &Meterbridge::fast_update_strips));
306         return 0;
307 }
308
309 gint
310 Meterbridge::stop_updating ()
311 {
312         fast_screen_update_connection.disconnect();
313         return 0;
314 }
315
316 void
317 Meterbridge::fast_update_strips ()
318 {
319         if (!is_mapped () || !_session) {
320                 return;
321         }
322         for (list<MeterStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
323                 (*i)->fast_update ();
324         }
325 }
326
327 void
328 Meterbridge::add_strips (RouteList& routes)
329 {
330         MeterStrip* strip;
331         for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
332                 boost::shared_ptr<Route> route = (*x);
333                 if (route->is_auditioner()) {
334                         continue;
335                 }
336                 if (route->is_monitor()) {
337                         continue;
338                 }
339
340                 strip = new MeterStrip (*this, _session, route);
341                 strips.push_back (strip);
342
343                 // TODO sort-routes, insert at proper position
344                 // order_key
345
346                 global_hpacker.pack_start (*strip, false, false);
347                 strip->show();
348         }
349 }
350
351 void
352 Meterbridge::remove_strip (MeterStrip* strip)
353 {
354         if (_session && _session->deletion_in_progress()) {
355                 return;
356         }
357
358         list<MeterStrip *>::iterator i;
359         if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
360                 strips.erase (i);
361         }
362 }
363
364 void
365 Meterbridge::parameter_changed (string const & p)
366 {
367 }