Move duplicated AudioFileSource::Flags and SMFSource::Flags into Source.
[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           _min_height_divisor (1)
50 {
51         _body = new PortMatrixBody (this);
52
53         for (int i = 0; i < 2; ++i) {
54                 _ports[i].set_type (type);
55                 
56                 /* watch for the content of _ports[] changing */
57                 _ports[i].Changed.connect (sigc::mem_fun (*this, &PortMatrix::setup));
58         }
59
60         _row_visibility_box.pack_start (_row_visibility_label, Gtk::PACK_SHRINK);
61         _column_visibility_box.pack_start (_column_visibility_label, Gtk::PACK_SHRINK);
62
63         _hscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::hscroll_changed));
64         _vscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::vscroll_changed));
65
66         /* watch for routes being added or removed */
67         _session.RouteAdded.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrix::routes_changed)));
68
69         /* and also bundles */
70         _session.BundleAdded.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrix::setup_global_ports)));
71         
72         reconnect_to_routes ();
73
74         show_all ();
75 }
76
77 PortMatrix::~PortMatrix ()
78 {
79         delete _body;
80         
81         for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
82                 delete *i;
83         }
84
85         for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
86                 delete *i;
87         }
88
89         delete _menu;
90 }
91
92 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
93 void
94 PortMatrix::reconnect_to_routes ()
95 {
96         for (std::vector<sigc::connection>::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) {
97                 i->disconnect ();
98         }
99         _route_connections.clear ();
100
101         boost::shared_ptr<ARDOUR::RouteList> routes = _session.get_routes ();
102         for (ARDOUR::RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
103                 _route_connections.push_back (
104                         (*i)->processors_changed.connect (sigc::mem_fun (*this, &PortMatrix::setup_global_ports))
105                         );
106         }
107 }
108
109 /** A route has been added to or removed from the session */
110 void
111 PortMatrix::routes_changed ()
112 {
113         reconnect_to_routes ();
114         setup_global_ports ();
115 }
116
117 /** Set up everything that depends on the content of _ports[] */
118 void
119 PortMatrix::setup ()
120 {
121         select_arrangement ();
122         _body->setup ();
123         setup_scrollbars ();
124         queue_draw ();
125
126         if (_setup_once) {
127
128                 /* we've set up before, so we need to clean up before re-setting-up */
129                 /* XXX: we ought to be able to do this by just getting a list of children
130                    from each container widget, but I couldn't make that work */
131                
132                 for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
133                         _column_visibility_box.remove (**i);
134                         delete *i;
135                 }
136
137                 _column_visibility_buttons.clear ();
138
139                 for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
140                         _row_visibility_box.remove (**i);
141                         delete *i;
142                 }
143
144                 _row_visibility_buttons.clear ();
145                 
146                 _scroller_table.remove (_vscroll);
147                 _scroller_table.remove (*_body);
148                 _scroller_table.remove (_hscroll);
149
150                 _main_hbox.remove (_scroller_table);
151                 if (_row_visibility_box_added) {
152                         _main_hbox.remove (_row_visibility_box);
153                 }
154                 
155                 if (_column_visibility_box_added) {
156                         remove (_column_visibility_box);
157                 }
158                 remove (_main_hbox);
159         }
160
161         if (_column_index == 0) {
162                 _column_visibility_label.set_text (_("Show Outputs"));
163                 _row_visibility_label.set_text (_("Show Inputs"));
164         } else {
165                 _column_visibility_label.set_text (_("Show Inputs"));
166                 _row_visibility_label.set_text (_("Show Outputs"));
167         }
168
169         for (PortGroupList::List::const_iterator i = columns()->begin(); i != columns()->end(); ++i) {
170                 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
171                 b->set_active ((*i)->visible());
172                 boost::weak_ptr<PortGroup> w (*i);
173                 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
174                 _column_visibility_buttons.push_back (b);
175                 _column_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
176         }
177
178         for (PortGroupList::List::const_iterator i = rows()->begin(); i != rows()->end(); ++i) {
179                 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
180                 b->set_active ((*i)->visible());
181                 boost::weak_ptr<PortGroup> w (*i);
182                 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
183                 _row_visibility_buttons.push_back (b);
184                 _row_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
185         }
186
187         if (_arrangement == TOP_TO_RIGHT) {
188           
189                 _scroller_table.attach (_hscroll, 0, 1, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
190                 _scroller_table.attach (*_body, 0, 1, 1, 2);
191                 _scroller_table.attach (_vscroll, 1, 2, 1, 2, Gtk::SHRINK);
192
193                 _main_hbox.pack_start (_scroller_table);
194
195                 if (rows()->size() > 1) {
196                         _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
197                         _row_visibility_box_added = true;
198                 } else {
199                         _row_visibility_box_added = false;
200                 }
201
202                 if (columns()->size() > 1) {
203                         pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
204                         _column_visibility_box_added = true;
205                 } else {
206                         _column_visibility_box_added = false;
207                 }
208                 
209                 pack_start (_main_hbox);
210                 
211         } else {
212                 _scroller_table.attach (_vscroll, 0, 1, 0, 1, Gtk::SHRINK);
213                 _scroller_table.attach (*_body, 1, 2, 0, 1);
214                 _scroller_table.attach (_hscroll, 1, 2, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
215
216                 if (rows()->size() > 1) {
217                         _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
218                         _row_visibility_box_added = true;
219                 } else {
220                         _row_visibility_box_added = false;
221                 }
222                 
223                 _main_hbox.pack_start (_scroller_table);
224
225                 pack_start (_main_hbox);
226                 
227                 if (columns()->size() > 1) {
228                         pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
229                         _column_visibility_box_added = true;
230                 } else {
231                         _column_visibility_box_added = false;
232                 }
233         }
234
235         _setup_once = true;
236
237         show_all ();
238 }
239
240 void
241 PortMatrix::set_type (ARDOUR::DataType t)
242 {
243         _type = t;
244         _ports[0].set_type (_type);
245         _ports[1].set_type (_type);
246         
247         setup_all_ports ();
248 }
249
250 void
251 PortMatrix::hscroll_changed ()
252 {
253         _body->set_xoffset (_hscroll.get_adjustment()->get_value());
254 }
255
256 void
257 PortMatrix::vscroll_changed ()
258 {
259         _body->set_yoffset (_vscroll.get_adjustment()->get_value());
260 }
261
262 void
263 PortMatrix::setup_scrollbars ()
264 {
265         Gtk::Adjustment* a = _hscroll.get_adjustment ();
266         a->set_lower (0);
267         a->set_upper (_body->full_scroll_width());
268         a->set_page_size (_body->alloc_scroll_width());
269         a->set_step_increment (32);
270         a->set_page_increment (128);
271
272         a = _vscroll.get_adjustment ();
273         a->set_lower (0);
274         a->set_upper (_body->full_scroll_height());
275         a->set_page_size (_body->alloc_scroll_height());
276         a->set_step_increment (32);
277         a->set_page_increment (128);
278 }
279
280 /** Disassociate all of our ports from each other */
281 void
282 PortMatrix::disassociate_all ()
283 {
284         ARDOUR::BundleList a = _ports[0].bundles ();
285         ARDOUR::BundleList b = _ports[1].bundles ();
286
287         for (ARDOUR::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
288                 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
289                         for (ARDOUR::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
290                                 for (uint32_t l = 0; l < (*k)->nchannels(); ++l) {
291                                                 
292                                         ARDOUR::BundleChannel c[2] = {
293                                                 ARDOUR::BundleChannel (*i, j),
294                                                 ARDOUR::BundleChannel (*k, l)
295                                                         };
296
297                                         if (get_state (c) == ASSOCIATED) {
298                                                 set_state (c, false);
299                                         }
300
301                                 }
302                         }
303                 }
304         }
305
306         _body->rebuild_and_draw_grid ();
307 }
308
309 /* Decide how to arrange the components of the matrix */
310 void
311 PortMatrix::select_arrangement ()
312 {
313         uint32_t const N[2] = {
314                 _ports[0].total_visible_channels (),
315                 _ports[1].total_visible_channels ()
316         };
317
318         /* The list with the most channels goes on left or right, so that the most channel
319            names are printed horizontally and hence more readable.  However we also
320            maintain notional `signal flow' vaguely from left to right.  Subclasses
321            should choose where to put ports based on signal flowing from _ports[0]
322            to _ports[1] */
323         
324         if (N[0] > N[1]) {
325
326                 _row_index = 0;
327                 _column_index = 1;
328                 _arrangement = LEFT_TO_BOTTOM;
329
330         } else {
331
332                 _row_index = 1;
333                 _column_index = 0;
334                 _arrangement = TOP_TO_RIGHT;
335         }
336 }
337
338 /** @return columns list */
339 PortGroupList const *
340 PortMatrix::columns () const
341 {
342         return &_ports[_column_index];
343 }
344
345 /* @return rows list */
346 PortGroupList const *
347 PortMatrix::rows () const
348 {
349         return &_ports[_row_index];
350 }
351
352 /** A group visibility checkbutton has been toggled.
353  * @param w Group.
354  * @param b Button.
355  */
356 void
357 PortMatrix::visibility_toggled (boost::weak_ptr<PortGroup> w, Gtk::CheckButton* b)
358 {
359         boost::shared_ptr<PortGroup> g = w.lock ();
360         if (!g) {
361                 return;
362         }
363         
364         g->set_visible (b->get_active());
365         _body->setup ();
366         setup_scrollbars ();
367         queue_draw ();
368 }
369
370 void
371 PortMatrix::popup_channel_context_menu (int dim, uint32_t N, uint32_t t)
372 {
373         delete _menu;
374
375         _menu = new Gtk::Menu;
376         _menu->set_name ("ArdourContextMenu");
377         
378         Gtk::Menu_Helpers::MenuList& items = _menu->items ();
379
380         ARDOUR::BundleChannel bc;
381
382         ARDOUR::BundleList const r = _ports[dim].bundles();
383         for (ARDOUR::BundleList::const_iterator i = r.begin(); i != r.end(); ++i) {
384                 if (N < (*i)->nchannels ()) {
385                         bc = ARDOUR::BundleChannel (*i, N);
386                         break;
387                 } else {
388                         N -= (*i)->nchannels ();
389                 }
390         }
391
392         if (bc.bundle) {
393                 char buf [64];
394                 bool have_one = false;
395
396                 if (can_rename_channels (dim)) {
397                         snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc.bundle->channel_name (bc.channel).c_str());
398                         boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
399                         items.push_back (
400                                 Gtk::Menu_Helpers::MenuElem (
401                                         buf,
402                                         sigc::bind (sigc::mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc.channel)
403                                         )
404                                 );
405
406                         have_one = true;
407                 }
408
409                 if (can_remove_channels (dim)) {
410                         snprintf (buf, sizeof (buf), _("Remove '%s'"), bc.bundle->channel_name (bc.channel).c_str());
411                         boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
412                         items.push_back (
413                                 Gtk::Menu_Helpers::MenuElem (
414                                         buf,
415                                         sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc.channel)
416                                         )
417                                 );
418
419                         have_one = true;
420                 }
421
422                 if (have_one) {
423                         items.push_back (Gtk::Menu_Helpers::SeparatorElem ());
424                 }
425                 
426                 boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
427                 items.push_back (Gtk::Menu_Helpers::MenuElem (
428                                          _("Disassociate all"),
429                                          sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc.channel, dim)
430                                          )
431                         );
432                         
433                 _menu->popup (1, t);
434         }
435         
436 }
437
438
439 void
440 PortMatrix::remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
441 {
442         boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
443         if (!sb) {
444                 return;
445         }
446
447         remove_channel (ARDOUR::BundleChannel (sb, c));
448
449 }
450
451 void
452 PortMatrix::rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
453 {
454         boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
455         if (!sb) {
456                 return;
457         }
458
459         rename_channel (ARDOUR::BundleChannel (sb, c));
460 }
461
462 void
463 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<ARDOUR::Bundle> bundle, uint32_t channel, int dim)
464 {
465         boost::shared_ptr<ARDOUR::Bundle> sb = bundle.lock ();
466         if (!sb) {
467                 return;
468         }
469
470         ARDOUR::BundleList a = _ports[1-dim].bundles ();
471
472         for (ARDOUR::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
473                 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
474
475                         ARDOUR::BundleChannel c[2];
476                         c[dim] = ARDOUR::BundleChannel (sb, channel);
477                         c[1-dim] = ARDOUR::BundleChannel (*i, j);
478
479                         if (get_state (c) == ASSOCIATED) {
480                                 set_state (c, false);
481                         }
482                 }
483         }
484
485         _body->rebuild_and_draw_grid ();
486 }
487
488 void
489 PortMatrix::setup_global_ports ()
490 {
491         for (int i = 0; i < 2; ++i) {
492                 if (list_is_global (i)) {
493                         setup_ports (i);
494                 }
495         }
496 }
497
498 void
499 PortMatrix::setup_all_ports ()
500 {
501         setup_ports (0);
502         setup_ports (1);
503 }