Make editor route list rec button respect group settings.
[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 <gtkmm/window.h>
28 #include "ardour/bundle.h"
29 #include "ardour/types.h"
30 #include "ardour/session.h"
31 #include "ardour/route.h"
32 #include "port_matrix.h"
33 #include "port_matrix_body.h"
34 #include "port_matrix_component.h"
35 #include "i18n.h"
36
37 using namespace std;
38 using namespace sigc;
39 using namespace Gtk;
40 using namespace ARDOUR;
41
42 /** PortMatrix constructor.
43  *  @param session Our session.
44  *  @param type Port type that we are handling.
45  */
46 PortMatrix::PortMatrix (Window* parent, Session& session, DataType type)
47         : Table (2, 2),
48           _session (session),
49           _parent (parent),
50           _type (type),
51           _menu (0),
52           _arrangement (TOP_TO_RIGHT),
53           _row_index (0),
54           _column_index (1),
55           _min_height_divisor (1),
56           _show_only_bundles (false),
57           _inhibit_toggle_show_only_bundles (false)
58 {
59         _body = new PortMatrixBody (this);
60
61         for (int i = 0; i < 2; ++i) {
62                 _ports[i].set_type (type);
63                 
64                 /* watch for the content of _ports[] changing */
65                 _ports[i].Changed.connect (mem_fun (*this, &PortMatrix::setup));
66         }
67
68         _hscroll.signal_value_changed().connect (mem_fun (*this, &PortMatrix::hscroll_changed));
69         _vscroll.signal_value_changed().connect (mem_fun (*this, &PortMatrix::vscroll_changed));
70
71         /* watch for routes being added or removed */
72         _session.RouteAdded.connect (sigc::hide (mem_fun (*this, &PortMatrix::routes_changed)));
73
74         /* and also bundles */
75         _session.BundleAdded.connect (sigc::hide (mem_fun (*this, &PortMatrix::setup_global_ports)));
76         
77         reconnect_to_routes ();
78
79         attach (*_body, 0, 1, 0, 1);
80         attach (_vscroll, 1, 2, 0, 1, SHRINK);
81         attach (_hscroll, 0, 1, 1, 2, FILL | EXPAND, SHRINK);
82         
83         show_all ();
84 }
85
86 PortMatrix::~PortMatrix ()
87 {
88         delete _body;
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 (vector<connection>::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) {
97                 i->disconnect ();
98         }
99         _route_connections.clear ();
100
101         boost::shared_ptr<RouteList> routes = _session.get_routes ();
102         for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
103                 _route_connections.push_back (
104                         (*i)->processors_changed.connect (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         if ((get_flags () & Gtk::REALIZED) == 0) {
122                 select_arrangement ();
123         }
124
125         _body->setup ();
126         setup_scrollbars ();
127         queue_draw ();
128
129         show_all ();
130 }
131
132 void
133 PortMatrix::set_type (DataType t)
134 {
135         _type = t;
136         _ports[0].set_type (_type);
137         _ports[1].set_type (_type);
138         
139         setup_all_ports ();
140 }
141
142 void
143 PortMatrix::hscroll_changed ()
144 {
145         _body->set_xoffset (_hscroll.get_adjustment()->get_value());
146 }
147
148 void
149 PortMatrix::vscroll_changed ()
150 {
151         _body->set_yoffset (_vscroll.get_adjustment()->get_value());
152 }
153
154 void
155 PortMatrix::setup_scrollbars ()
156 {
157         Adjustment* a = _hscroll.get_adjustment ();
158         a->set_lower (0);
159         a->set_upper (_body->full_scroll_width());
160         a->set_page_size (_body->alloc_scroll_width());
161         a->set_step_increment (32);
162         a->set_page_increment (128);
163
164         a = _vscroll.get_adjustment ();
165         a->set_lower (0);
166         a->set_upper (_body->full_scroll_height());
167         a->set_page_size (_body->alloc_scroll_height());
168         a->set_step_increment (32);
169         a->set_page_increment (128);
170 }
171
172 /** Disassociate all of our ports from each other */
173 void
174 PortMatrix::disassociate_all ()
175 {
176         PortGroup::BundleList a = _ports[0].bundles ();
177         PortGroup::BundleList b = _ports[1].bundles ();
178
179         for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
180                 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
181                         for (PortGroup::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
182                                 for (uint32_t l = 0; l < k->bundle->nchannels(); ++l) {
183                                                 
184                                         BundleChannel c[2] = {
185                                                 BundleChannel (i->bundle, j),
186                                                 BundleChannel (k->bundle, l)
187                                                         };
188
189                                         if (get_state (c) == PortMatrixNode::ASSOCIATED) {
190                                                 set_state (c, false);
191                                         }
192
193                                 }
194                         }
195                 }
196         }
197
198         _body->rebuild_and_draw_grid ();
199 }
200
201 /* Decide how to arrange the components of the matrix */
202 void
203 PortMatrix::select_arrangement ()
204 {
205         uint32_t const N[2] = {
206                 _ports[0].total_visible_channels (),
207                 _ports[1].total_visible_channels ()
208         };
209
210         /* The list with the most channels goes on left or right, so that the most channel
211            names are printed horizontally and hence more readable.  However we also
212            maintain notional `signal flow' vaguely from left to right.  Subclasses
213            should choose where to put ports based on signal flowing from _ports[0]
214            to _ports[1] */
215         
216         if (N[0] > N[1]) {
217
218                 _row_index = 0;
219                 _column_index = 1;
220                 _arrangement = LEFT_TO_BOTTOM;
221
222         } else {
223
224                 _row_index = 1;
225                 _column_index = 0;
226                 _arrangement = TOP_TO_RIGHT;
227         }
228 }
229
230 /** @return columns list */
231 PortGroupList const *
232 PortMatrix::columns () const
233 {
234         return &_ports[_column_index];
235 }
236
237 /* @return rows list */
238 PortGroupList const *
239 PortMatrix::rows () const
240 {
241         return &_ports[_row_index];
242 }
243
244 void
245 PortMatrix::popup_menu (
246         pair<boost::shared_ptr<PortGroup>, BundleChannel> column,
247         pair<boost::shared_ptr<PortGroup>, BundleChannel> row,
248         uint32_t t
249         )
250 {
251         using namespace Menu_Helpers;
252         
253         delete _menu;
254
255         _menu = new Menu;
256         _menu->set_name ("ArdourContextMenu");
257         
258         MenuList& items = _menu->items ();
259
260         boost::shared_ptr<PortGroup> pg[2];
261         pg[_column_index] = column.first;
262         pg[_row_index] = row.first;
263
264         BundleChannel bc[2];
265         bc[_column_index] = column.second;
266         bc[_row_index] = row.second;
267
268         char buf [64];
269
270
271         for (int dim = 0; dim < 2; ++dim) {
272
273                 if (bc[dim].bundle) {
274
275                         Menu* m = manage (new Menu);
276                         MenuList& sub = m->items ();
277
278                         boost::weak_ptr<Bundle> w (bc[dim].bundle);
279
280                         if (can_add_channel (bc[dim].bundle)) {
281                                 snprintf (buf, sizeof (buf), _("Add %s"), channel_noun().c_str());
282                                 sub.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::add_channel_proxy), w)));
283                         }
284                         
285                         
286                         if (can_rename_channels (bc[dim].bundle)) {
287                                 snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
288                                 sub.push_back (
289                                         MenuElem (
290                                                 buf,
291                                                 bind (mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc[dim].channel)
292                                                 )
293                                         );
294                         }
295
296                         sub.push_back (SeparatorElem ());
297                         
298                         if (can_remove_channels (bc[dim].bundle)) {
299                                 snprintf (buf, sizeof (buf), _("Remove '%s'"), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
300                                 sub.push_back (
301                                         MenuElem (
302                                                 buf,
303                                                 bind (mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc[dim].channel)
304                                                 )
305                                         );
306                         }                       
307
308                         if (_show_only_bundles) {
309                                 snprintf (buf, sizeof (buf), _("%s all"), disassociation_verb().c_str());
310                         } else {
311                                 snprintf (
312                                         buf, sizeof (buf), _("%s all from '%s'"),
313                                         disassociation_verb().c_str(),
314                                         bc[dim].bundle->channel_name (bc[dim].channel).c_str()
315                                         );
316                         }
317                         
318                         sub.push_back (
319                                 MenuElem (buf, bind (mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc[dim].channel, dim))
320                                 );
321
322                         
323                         items.push_back (MenuElem (bc[dim].bundle->name().c_str(), *m));
324                 }
325         }
326
327         items.push_back (SeparatorElem ());
328
329         for (int dim = 0; dim < 2; ++dim) {
330
331                 if (pg[dim]) {
332
333                         boost::weak_ptr<PortGroup> wp (pg[dim]);
334                         
335                         if (pg[dim]->visible()) {
336                                 if (dim == 0) {
337                                         snprintf (buf, sizeof (buf), _("Hide '%s' sources"), pg[dim]->name.c_str());
338                                 } else {
339                                         snprintf (buf, sizeof (buf), _("Hide '%s' destinations"), pg[dim]->name.c_str());
340                                 }
341
342                                 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::hide_group), wp)));
343                         } else {
344                                 if (dim == 0) {
345                                         snprintf (buf, sizeof (buf), _("Show '%s' sources"), pg[dim]->name.c_str());
346                                 } else {
347                                         snprintf (buf, sizeof (buf), _("Show '%s' destinations"), pg[dim]->name.c_str());
348                                 }
349                                 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::show_group), wp)));
350                         }
351                 }
352         }
353
354         items.push_back (SeparatorElem ());
355
356         items.push_back (MenuElem (_("Rescan"), mem_fun (*this, &PortMatrix::setup_all_ports)));
357         items.push_back (CheckMenuElem (_("Show individual ports"), mem_fun (*this, &PortMatrix::toggle_show_only_bundles)));
358         CheckMenuItem* i = dynamic_cast<CheckMenuItem*> (&items.back());
359         _inhibit_toggle_show_only_bundles = true;
360         i->set_active (!_show_only_bundles);
361         _inhibit_toggle_show_only_bundles = false;
362         
363         _menu->popup (1, t);
364 }
365
366 void
367 PortMatrix::remove_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
368 {
369         boost::shared_ptr<Bundle> sb = b.lock ();
370         if (!sb) {
371                 return;
372         }
373
374         remove_channel (BundleChannel (sb, c));
375
376 }
377
378 void
379 PortMatrix::rename_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
380 {
381         boost::shared_ptr<Bundle> sb = b.lock ();
382         if (!sb) {
383                 return;
384         }
385
386         rename_channel (BundleChannel (sb, c));
387 }
388
389 void
390 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<Bundle> bundle, uint32_t channel, int dim)
391 {
392         boost::shared_ptr<Bundle> sb = bundle.lock ();
393         if (!sb) {
394                 return;
395         }
396
397         PortGroup::BundleList a = _ports[1-dim].bundles ();
398
399         for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
400                 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
401
402                         BundleChannel c[2];
403                         c[dim] = BundleChannel (sb, channel);
404                         c[1-dim] = BundleChannel (i->bundle, j);
405
406                         if (get_state (c) == PortMatrixNode::ASSOCIATED) {
407                                 set_state (c, false);
408                         }
409                 }
410         }
411
412         _body->rebuild_and_draw_grid ();
413 }
414
415 void
416 PortMatrix::setup_global_ports ()
417 {
418         for (int i = 0; i < 2; ++i) {
419                 if (list_is_global (i)) {
420                         setup_ports (i);
421                 }
422         }
423 }
424
425 void
426 PortMatrix::setup_all_ports ()
427 {
428         setup_ports (0);
429         setup_ports (1);
430 }
431
432 void
433 PortMatrix::toggle_show_only_bundles ()
434 {
435         if (_inhibit_toggle_show_only_bundles) {
436                 return;
437         }
438         
439         _show_only_bundles = !_show_only_bundles;
440         _body->setup ();
441         setup_scrollbars ();
442         queue_draw ();
443 }
444
445 void
446 PortMatrix::hide_group (boost::weak_ptr<PortGroup> w)
447 {
448         boost::shared_ptr<PortGroup> g = w.lock ();
449         if (!g) {
450                 return;
451         }
452
453         g->set_visible (false);
454 }
455
456 void
457 PortMatrix::show_group (boost::weak_ptr<PortGroup> w)
458 {
459         boost::shared_ptr<PortGroup> g = w.lock ();
460         if (!g) {
461                 return;
462         }
463
464         g->set_visible (true);
465 }
466
467 pair<uint32_t, uint32_t>
468 PortMatrix::max_size () const
469 {
470         pair<uint32_t, uint32_t> m = _body->max_size ();
471
472         m.first += _vscroll.get_width ();
473         m.second += _hscroll.get_height ();
474
475         return m;
476 }
477
478 void
479 PortMatrix::setup_max_size ()
480 {
481         if (!_parent) {
482                 return;
483         }
484
485         pair<uint32_t, uint32_t> const m = max_size ();
486         
487         GdkGeometry g;
488         g.max_width = m.first;
489         g.max_height = m.second;
490
491         _parent->set_geometry_hints (*this, g, Gdk::HINT_MAX_SIZE);
492 }
493
494 bool
495 PortMatrix::on_scroll_event (GdkEventScroll* ev)
496 {
497         double const h = _hscroll.get_value ();
498         double const v = _vscroll.get_value ();
499         
500         switch (ev->direction) {
501         case GDK_SCROLL_UP:
502                 _vscroll.set_value (v - PortMatrixComponent::grid_spacing ());
503                 break;
504         case GDK_SCROLL_DOWN:
505                 _vscroll.set_value (v + PortMatrixComponent::grid_spacing ());
506                 break;
507         case GDK_SCROLL_LEFT:
508                 _hscroll.set_value (h - PortMatrixComponent::grid_spacing ());
509                 break;
510         case GDK_SCROLL_RIGHT:
511                 _hscroll.set_value (h + PortMatrixComponent::grid_spacing ());
512                 break;
513         }
514
515         return true;
516 }
517
518 boost::shared_ptr<IO>
519 PortMatrix::io_from_bundle (boost::shared_ptr<Bundle> b) const
520 {
521         boost::shared_ptr<IO> io = _ports[0].io_from_bundle (b);
522         if (!io) {
523                 io = _ports[1].io_from_bundle (b);
524         }
525
526         return io;
527 }
528
529 bool
530 PortMatrix::can_add_channel (boost::shared_ptr<Bundle> b) const
531 {
532         return io_from_bundle (b);
533 }
534
535 void
536 PortMatrix::add_channel (boost::shared_ptr<Bundle> b)
537 {
538         boost::shared_ptr<IO> io = io_from_bundle (b);
539
540         if (io) {
541                 io->add_port ("", this, _type);
542         }
543 }
544
545 bool
546 PortMatrix::can_remove_channels (boost::shared_ptr<Bundle> b) const
547 {
548         return io_from_bundle (b);
549 }
550
551 void
552 PortMatrix::remove_channel (ARDOUR::BundleChannel b)
553 {
554         boost::shared_ptr<IO> io = io_from_bundle (b.bundle);
555
556         if (io) {
557                 Port* p = io->nth (b.channel);
558                 if (p) {
559                         io->remove_port (p, this);
560                 }
561         }
562 }
563
564 void
565 PortMatrix::add_channel_proxy (boost::weak_ptr<Bundle> w)
566 {
567         boost::shared_ptr<Bundle> b = w.lock ();
568         if (!b) {
569                 return;
570         }
571
572         add_channel (b);
573 }