Reduce header dependencies.
[ardour.git] / gtk2_ardour / port_matrix.cc
1 /*
2     Copyright (C) 2002-2009 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <iostream>
21 #include <gtkmm/scrolledwindow.h>
22 #include <gtkmm/adjustment.h>
23 #include <gtkmm/label.h>
24 #include <gtkmm/menu.h>
25 #include <gtkmm/menushell.h>
26 #include <gtkmm/menu_elems.h>
27 #include "ardour/bundle.h"
28 #include "ardour/types.h"
29 #include "ardour/session.h"
30 #include "ardour/route.h"
31 #include "port_matrix.h"
32 #include "port_matrix_body.h"
33 #include "i18n.h"
34
35 /** PortMatrix constructor.
36  *  @param session Our session.
37  *  @param type Port type that we are handling.
38  */
39 PortMatrix::PortMatrix (ARDOUR::Session& session, ARDOUR::DataType type)
40         : _session (session),
41           _type (type),
42           _column_visibility_box_added (false),
43           _row_visibility_box_added (false),
44           _menu (0),
45           _setup_once (false),
46           _arrangement (TOP_TO_RIGHT),
47           _row_index (0),
48           _column_index (1)
49 {
50         _body = new PortMatrixBody (this);
51         
52         _ports[0].set_type (type);
53         _ports[1].set_type (type);
54
55         _row_visibility_box.pack_start (_row_visibility_label, Gtk::PACK_SHRINK);
56         _column_visibility_box.pack_start (_column_visibility_label, Gtk::PACK_SHRINK);
57
58         _hscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::hscroll_changed));
59         _vscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::vscroll_changed));
60
61         /* watch for routes being added or removed */
62         _session.RouteAdded.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrix::routes_changed)));
63         
64         reconnect_to_routes ();
65
66         show_all ();
67 }
68
69 PortMatrix::~PortMatrix ()
70 {
71         delete _body;
72         
73         for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
74                 delete *i;
75         }
76
77         for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
78                 delete *i;
79         }
80
81         delete _menu;
82 }
83
84 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
85 void
86 PortMatrix::reconnect_to_routes ()
87 {
88         for (std::vector<sigc::connection>::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) {
89                 i->disconnect ();
90         }
91
92         boost::shared_ptr<ARDOUR::RouteList> routes = _session.get_routes ();
93         for (ARDOUR::RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
94                 _route_connections.push_back (
95                         (*i)->processors_changed.connect (sigc::mem_fun (*this, &PortMatrix::setup))
96                         );
97         }
98 }
99
100 /** A route has been added to or removed from the session */
101 void
102 PortMatrix::routes_changed ()
103 {
104         reconnect_to_routes ();
105         setup ();
106 }
107
108 /** Set up everything that changes about the matrix */
109 void
110 PortMatrix::setup ()
111 {
112         select_arrangement ();
113         _body->setup ();
114         setup_scrollbars ();
115         queue_draw ();
116
117         if (_setup_once) {
118
119                 /* we've set up before, so we need to clean up before re-setting-up */
120                 /* XXX: we ought to be able to do this by just getting a list of children
121                    from each container widget, but I couldn't make that work */
122
123                
124                 for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
125                         _column_visibility_box.remove (**i);
126                         delete *i;
127                 }
128
129                 _column_visibility_buttons.clear ();
130
131                 for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
132                         _row_visibility_box.remove (**i);
133                         delete *i;
134                 }
135
136                 _row_visibility_buttons.clear ();
137                 
138                 _scroller_table.remove (_vscroll);
139                 _scroller_table.remove (*_body);
140                 _scroller_table.remove (_hscroll);
141
142                 _main_hbox.remove (_scroller_table);
143                 if (_row_visibility_box_added) {
144                         _main_hbox.remove (_row_visibility_box);
145                 }
146                 
147                 if (_column_visibility_box_added) {
148                         remove (_column_visibility_box);
149                 }
150                 remove (_main_hbox);
151         }
152
153         if (_column_index == 0) {
154                 _column_visibility_label.set_text (_("Show Outputs"));
155                 _row_visibility_label.set_text (_("Show Inputs"));
156         } else {
157                 _column_visibility_label.set_text (_("Show Inputs"));
158                 _row_visibility_label.set_text (_("Show Outputs"));
159         }
160
161         for (PortGroupList::List::const_iterator i = columns()->begin(); i != columns()->end(); ++i) {
162                 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
163                 b->set_active ((*i)->visible());
164                 boost::weak_ptr<PortGroup> w (*i);
165                 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
166                 _column_visibility_buttons.push_back (b);
167                 _column_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
168         }
169
170         for (PortGroupList::List::const_iterator i = rows()->begin(); i != rows()->end(); ++i) {
171                 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
172                 b->set_active ((*i)->visible());
173                 boost::weak_ptr<PortGroup> w (*i);
174                 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
175                 _row_visibility_buttons.push_back (b);
176                 _row_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
177         }
178
179         if (_arrangement == TOP_TO_RIGHT) {
180           
181                 _scroller_table.attach (_hscroll, 0, 1, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
182                 _scroller_table.attach (*_body, 0, 1, 1, 2);
183                 _scroller_table.attach (_vscroll, 1, 2, 1, 2, Gtk::SHRINK);
184
185                 _main_hbox.pack_start (_scroller_table);
186
187                 if (rows()->size() > 1) {
188                         _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
189                         _row_visibility_box_added = true;
190                 } else {
191                         _row_visibility_box_added = false;
192                 }
193
194                 if (columns()->size() > 1) {
195                         pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
196                         _column_visibility_box_added = true;
197                 } else {
198                         _column_visibility_box_added = false;
199                 }
200                 
201                 pack_start (_main_hbox);
202                 
203         } else {
204                 _scroller_table.attach (_vscroll, 0, 1, 0, 1, Gtk::SHRINK);
205                 _scroller_table.attach (*_body, 1, 2, 0, 1);
206                 _scroller_table.attach (_hscroll, 1, 2, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
207
208                 if (rows()->size() > 1) {
209                         _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
210                         _row_visibility_box_added = true;
211                 } else {
212                         _row_visibility_box_added = false;
213                 }
214                 
215                 _main_hbox.pack_start (_scroller_table);
216
217                 pack_start (_main_hbox);
218                 
219                 if (columns()->size() > 1) {
220                         pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
221                         _column_visibility_box_added = true;
222                 } else {
223                         _column_visibility_box_added = false;
224                 }
225         }
226
227         _setup_once = true;
228
229         show_all ();
230 }
231
232 void
233 PortMatrix::set_type (ARDOUR::DataType t)
234 {
235         _type = t;
236         _ports[0].set_type (_type);
237         _ports[1].set_type (_type);
238         setup ();
239 }
240
241 void
242 PortMatrix::hscroll_changed ()
243 {
244         _body->set_xoffset (_hscroll.get_adjustment()->get_value());
245 }
246
247 void
248 PortMatrix::vscroll_changed ()
249 {
250         _body->set_yoffset (_vscroll.get_adjustment()->get_value());
251 }
252
253 void
254 PortMatrix::setup_scrollbars ()
255 {
256         Gtk::Adjustment* a = _hscroll.get_adjustment ();
257         a->set_lower (0);
258         a->set_upper (_body->full_scroll_width());
259         a->set_page_size (_body->alloc_scroll_width());
260         a->set_step_increment (32);
261         a->set_page_increment (128);
262
263         a = _vscroll.get_adjustment ();
264         a->set_lower (0);
265         a->set_upper (_body->full_scroll_height());
266         a->set_page_size (_body->alloc_scroll_height());
267         a->set_step_increment (32);
268         a->set_page_increment (128);
269 }
270
271 /** Disassociate all of our ports from each other */
272 void
273 PortMatrix::disassociate_all ()
274 {
275         ARDOUR::BundleList a = _ports[0].bundles ();
276         ARDOUR::BundleList b = _ports[1].bundles ();
277         
278         for (ARDOUR::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
279                 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
280                         for (ARDOUR::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
281                                 for (uint32_t l = 0; l < (*k)->nchannels(); ++l) {
282                                                 
283                                         ARDOUR::BundleChannel c[2] = {
284                                                 ARDOUR::BundleChannel (*i, j),
285                                                 ARDOUR::BundleChannel (*k, l)
286                                                         };
287                                         
288                                         set_state (c, false);
289
290                                 }
291                         }
292                 }
293         }
294
295         _body->rebuild_and_draw_grid ();
296 }
297
298 /* Decide how to arrange the components of the matrix */
299 void
300 PortMatrix::select_arrangement ()
301 {
302         uint32_t const N[2] = {
303                 _ports[0].total_visible_ports (),
304                 _ports[1].total_visible_ports ()
305         };
306
307         /* The list with the most ports goes on left or right, so that the most port
308            names are printed horizontally and hence more readable.  However we also
309            maintain notional `signal flow' vaguely from left to right.  Subclasses
310            should choose where to put ports based on signal flowing from _ports[0]
311            to _ports[1] */
312         
313         if (N[0] > N[1]) {
314
315                 _row_index = 0;
316                 _column_index = 1;
317                 _arrangement = LEFT_TO_BOTTOM;
318
319         } else {
320
321                 _row_index = 1;
322                 _column_index = 0;
323                 _arrangement = TOP_TO_RIGHT;
324         }
325 }
326
327 /** @return columns list */
328 PortGroupList const *
329 PortMatrix::columns () const
330 {
331         return &_ports[_column_index];
332 }
333
334 /* @return rows list */
335 PortGroupList const *
336 PortMatrix::rows () const
337 {
338         return &_ports[_row_index];
339 }
340
341 /** A group visibility checkbutton has been toggled.
342  * @param w Group.
343  * @param b Button.
344  */
345 void
346 PortMatrix::visibility_toggled (boost::weak_ptr<PortGroup> w, Gtk::CheckButton* b)
347 {
348         boost::shared_ptr<PortGroup> g = w.lock ();
349         if (!g) {
350                 return;
351         }
352         
353         g->set_visible (b->get_active());
354         _body->setup ();
355         setup_scrollbars ();
356         queue_draw ();
357 }
358
359 void
360 PortMatrix::popup_channel_context_menu (int dim, uint32_t N, uint32_t t)
361 {
362         delete _menu;
363
364         _menu = new Gtk::Menu;
365         _menu->set_name ("ArdourContextMenu");
366         
367         Gtk::Menu_Helpers::MenuList& items = _menu->items ();
368
369         ARDOUR::BundleChannel bc;
370
371         ARDOUR::BundleList const r = _ports[dim].bundles();
372         for (ARDOUR::BundleList::const_iterator i = r.begin(); i != r.end(); ++i) {
373                 if (N < (*i)->nchannels ()) {
374                         bc = ARDOUR::BundleChannel (*i, N);
375                         break;
376                 } else {
377                         N -= (*i)->nchannels ();
378                 }
379         }
380
381         if (bc.bundle) {
382                 char buf [64];
383
384                 if (can_rename_channels (dim)) {
385                         snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc.bundle->channel_name (bc.channel).c_str());
386                         boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
387                         items.push_back (
388                                 Gtk::Menu_Helpers::MenuElem (
389                                         buf,
390                                         sigc::bind (sigc::mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc.channel)
391                                         )
392                                 );
393                 }
394
395                 if (can_remove_channels (dim)) {
396                         snprintf (buf, sizeof (buf), _("Remove '%s'"), bc.bundle->channel_name (bc.channel).c_str());
397                         boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
398                         items.push_back (
399                                 Gtk::Menu_Helpers::MenuElem (
400                                         buf,
401                                         sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc.channel)
402                                         )
403                                 );
404                 }
405                         
406                 _menu->popup (1, t);
407         }
408         
409 }
410
411
412 void
413 PortMatrix::remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
414 {
415         boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
416         if (!sb) {
417                 return;
418         }
419
420         remove_channel (ARDOUR::BundleChannel (sb, c));
421
422 }
423
424 void
425 PortMatrix::rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
426 {
427         boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
428         if (!sb) {
429                 return;
430         }
431
432         rename_channel (ARDOUR::BundleChannel (sb, c));
433 }
434