2 Copyright (C) 2002-2009 Paul Davis
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.
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.
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.
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 <gtkmm/stock.h>
29 #include "ardour/bundle.h"
30 #include "ardour/types.h"
31 #include "ardour/session.h"
32 #include "ardour/route.h"
33 #include "ardour/audioengine.h"
34 #include "port_matrix.h"
35 #include "port_matrix_body.h"
36 #include "port_matrix_component.h"
37 #include "ardour_dialog.h"
39 #include "gui_thread.h"
44 using namespace ARDOUR;
46 /** PortMatrix constructor.
47 * @param session Our session.
48 * @param type Port type that we are handling.
50 PortMatrix::PortMatrix (Window* parent, Session* session, DataType type)
55 , _arrangement (TOP_TO_RIGHT)
58 , _min_height_divisor (1)
59 , _show_only_bundles (false)
60 , _inhibit_toggle_show_only_bundles (false)
61 , _ignore_notebook_page_selected (false)
63 set_session (session);
65 _body = new PortMatrixBody (this);
66 _body->DimensionsChanged.connect (sigc::mem_fun (*this, &PortMatrix::body_dimensions_changed));
68 _hbox.pack_end (_hspacer, true, true);
69 _hbox.pack_end (_hnotebook, false, false);
70 _hbox.pack_end (_hlabel, false, false);
72 _vnotebook.signal_switch_page().connect (sigc::mem_fun (*this, &PortMatrix::notebook_page_selected));
73 _vnotebook.property_tab_border() = 4;
74 _vnotebook.set_name (X_("PortMatrixLabel"));
75 _hnotebook.signal_switch_page().connect (sigc::mem_fun (*this, &PortMatrix::notebook_page_selected));
76 _hnotebook.property_tab_border() = 4;
77 _hnotebook.set_name (X_("PortMatrixLabel"));
79 _vlabel.set_use_markup ();
80 _vlabel.set_alignment (1, 1);
81 _vlabel.set_padding (4, 16);
82 _vlabel.set_name (X_("PortMatrixLabel"));
83 _hlabel.set_use_markup ();
84 _hlabel.set_alignment (1, 0.5);
85 _hlabel.set_padding (16, 4);
86 _hlabel.set_name (X_("PortMatrixLabel"));
88 set_row_spacing (0, 8);
89 set_col_spacing (0, 8);
90 set_row_spacing (2, 8);
91 set_col_spacing (2, 8);
106 PortMatrix::~PortMatrix ()
112 /** Perform initial and once-only setup. This must be called by
113 * subclasses after they have set up _ports[] to at least some
114 * reasonable extent. Two-part initialisation is necessary because
115 * setting up _ports is largely done by virtual functions in
122 select_arrangement ();
124 /* Signal handling is kind of split into three parts:
126 * 1. When _ports[] changes, we call setup(). This essentially sorts out our visual
127 * representation of the information in _ports[].
129 * 2. When certain other things change, we need to get our subclass to clear and
130 * re-fill _ports[], which in turn causes appropriate signals to be raised to
131 * hook into part (1).
133 * 3. Assorted other signals.
137 /* Part 1: the basic _ports[] change -> reset visuals */
139 for (int i = 0; i < 2; ++i) {
140 /* watch for the content of _ports[] changing */
141 _ports[i].Changed.connect (_changed_connections, invalidator (*this), boost::bind (&PortMatrix::setup, this), gui_context());
143 /* and for bundles in _ports[] changing */
144 _ports[i].BundleChanged.connect (_bundle_changed_connections, invalidator (*this), boost::bind (&PortMatrix::setup, this), gui_context());
147 /* Part 2: notice when things have changed that require our subclass to clear and refill _ports[] */
149 /* watch for routes being added or removed */
150 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&PortMatrix::routes_changed, this), gui_context());
152 /* and also bundles */
153 _session->BundleAdded.connect (_session_connections, invalidator (*this), boost::bind (&PortMatrix::setup_global_ports, this), gui_context());
156 _session->engine().PortRegisteredOrUnregistered.connect (_session_connections, invalidator (*this), boost::bind (&PortMatrix::setup_global_ports, this), gui_context());
158 /* watch for route order keys changing, which changes the order of things in our global ports list(s) */
159 _session->RouteOrderKeyChanged.connect (_session_connections, invalidator (*this), boost::bind (&PortMatrix::setup_global_ports_proxy, this), gui_context());
161 /* Part 3: other stuff */
163 _session->engine().PortConnectedOrDisconnected.connect (_session_connections, invalidator (*this), boost::bind (&PortMatrix::port_connected_or_disconnected, this), gui_context ());
165 _hscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::hscroll_changed));
166 _vscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::vscroll_changed));
168 reconnect_to_routes ();
173 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
175 PortMatrix::reconnect_to_routes ()
177 _route_connections.drop_connections ();
179 boost::shared_ptr<RouteList> routes = _session->get_routes ();
180 for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
181 (*i)->processors_changed.connect (_route_connections, invalidator (*this), ui_bind (&PortMatrix::route_processors_changed, this, _1), gui_context());
186 PortMatrix::route_processors_changed (RouteProcessorChange c)
188 if (c.type == RouteProcessorChange::MeterPointChange) {
189 /* this change has no impact on the port matrix */
193 setup_global_ports ();
196 /** A route has been added to or removed from the session */
198 PortMatrix::routes_changed ()
200 reconnect_to_routes ();
201 setup_global_ports ();
204 /** Set up everything that depends on the content of _ports[] */
208 /* this needs to be done first, as the visible_ports() method uses the
209 notebook state to decide which ports are being shown */
219 PortMatrix::set_type (DataType t)
225 PortMatrix::hscroll_changed ()
227 _body->set_xoffset (_hscroll.get_adjustment()->get_value());
231 PortMatrix::vscroll_changed ()
233 _body->set_yoffset (_vscroll.get_adjustment()->get_value());
237 PortMatrix::setup_scrollbars ()
239 Adjustment* a = _hscroll.get_adjustment ();
241 a->set_upper (_body->full_scroll_width());
242 a->set_page_size (_body->alloc_scroll_width());
243 a->set_step_increment (32);
244 a->set_page_increment (128);
246 a = _vscroll.get_adjustment ();
248 a->set_upper (_body->full_scroll_height());
249 a->set_page_size (_body->alloc_scroll_height());
250 a->set_step_increment (32);
251 a->set_page_increment (128);
254 /** Disassociate all of our ports from each other */
256 PortMatrix::disassociate_all ()
258 PortGroup::BundleList a = _ports[0].bundles ();
259 PortGroup::BundleList b = _ports[1].bundles ();
261 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
262 for (uint32_t j = 0; j < (*i)->bundle->nchannels().n_total(); ++j) {
263 for (PortGroup::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
264 for (uint32_t l = 0; l < (*k)->bundle->nchannels().n_total(); ++l) {
266 if (!should_show ((*i)->bundle->channel_type(j)) || !should_show ((*k)->bundle->channel_type(l))) {
270 BundleChannel c[2] = {
271 BundleChannel ((*i)->bundle, j),
272 BundleChannel ((*k)->bundle, l)
275 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
276 set_state (c, false);
284 _body->rebuild_and_draw_grid ();
287 /* Decide how to arrange the components of the matrix */
289 PortMatrix::select_arrangement ()
291 uint32_t const N[2] = {
292 count_of_our_type (_ports[0].total_channels()),
293 count_of_our_type (_ports[1].total_channels())
296 /* XXX: shirley there's an easier way than this */
298 if (_vspacer.get_parent()) {
299 _vbox.remove (_vspacer);
302 if (_vnotebook.get_parent()) {
303 _vbox.remove (_vnotebook);
306 if (_vlabel.get_parent()) {
307 _vbox.remove (_vlabel);
310 /* The list with the most channels goes on left or right, so that the most channel
311 names are printed horizontally and hence more readable. However we also
312 maintain notional `signal flow' vaguely from left to right. Subclasses
313 should choose where to put ports based on signal flowing from _ports[0]
320 _arrangement = LEFT_TO_BOTTOM;
321 _vlabel.set_label (_("<b>Sources</b>"));
322 _hlabel.set_label (_("<b>Destinations</b>"));
323 _vlabel.set_angle (90);
325 _vbox.pack_end (_vlabel, false, false);
326 _vbox.pack_end (_vnotebook, false, false);
327 _vbox.pack_end (_vspacer, true, true);
329 attach (*_body, 2, 3, 1, 2, FILL | EXPAND, FILL | EXPAND);
330 attach (_vscroll, 3, 4, 1, 2, SHRINK);
331 attach (_hscroll, 2, 3, 3, 4, FILL | EXPAND, SHRINK);
332 attach (_vbox, 1, 2, 1, 2, SHRINK);
333 attach (_hbox, 2, 3, 2, 3, FILL | EXPAND, SHRINK);
339 _arrangement = TOP_TO_RIGHT;
340 _hlabel.set_label (_("<b>Sources</b>"));
341 _vlabel.set_label (_("<b>Destinations</b>"));
342 _vlabel.set_angle (-90);
344 _vbox.pack_end (_vspacer, true, true);
345 _vbox.pack_end (_vnotebook, false, false);
346 _vbox.pack_end (_vlabel, false, false);
348 attach (*_body, 1, 2, 2, 3, FILL | EXPAND, FILL | EXPAND);
349 attach (_vscroll, 3, 4, 2, 3, SHRINK);
350 attach (_hscroll, 1, 2, 3, 4, FILL | EXPAND, SHRINK);
351 attach (_vbox, 2, 3, 2, 3, SHRINK);
352 attach (_hbox, 1, 2, 1, 2, FILL | EXPAND, SHRINK);
356 /** @return columns list */
357 PortGroupList const *
358 PortMatrix::columns () const
360 return &_ports[_column_index];
363 boost::shared_ptr<const PortGroup>
364 PortMatrix::visible_columns () const
366 return visible_ports (_column_index);
369 /* @return rows list */
370 PortGroupList const *
371 PortMatrix::rows () const
373 return &_ports[_row_index];
376 boost::shared_ptr<const PortGroup>
377 PortMatrix::visible_rows () const
379 return visible_ports (_row_index);
383 PortMatrix::popup_menu (BundleChannel column, BundleChannel row, uint32_t t)
385 using namespace Menu_Helpers;
390 _menu->set_name ("ArdourContextMenu");
392 MenuList& items = _menu->items ();
395 bc[_column_index] = column;
396 bc[_row_index] = row;
399 bool need_separator = false;
401 for (int dim = 0; dim < 2; ++dim) {
403 if (bc[dim].bundle) {
405 Menu* m = manage (new Menu);
406 MenuList& sub = m->items ();
408 boost::weak_ptr<Bundle> w (bc[dim].bundle);
410 bool can_add_or_rename = false;
412 /* Start off with options for the `natural' port type */
413 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
414 if (should_show (*i)) {
415 snprintf (buf, sizeof (buf), _("Add %s %s"), (*i).to_i18n_string(), channel_noun().c_str());
416 sub.push_back (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::add_channel_proxy), w, *i)));
417 can_add_or_rename = true;
421 /* Now add other ones */
422 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
423 if (!should_show (*i)) {
424 snprintf (buf, sizeof (buf), _("Add %s %s"), (*i).to_i18n_string(), channel_noun().c_str());
425 sub.push_back (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::add_channel_proxy), w, *i)));
426 can_add_or_rename = true;
430 if (can_rename_channels (bc[dim].bundle)) {
432 buf, sizeof (buf), _("Rename '%s'..."),
433 escape_underscores (bc[dim].bundle->channel_name (bc[dim].channel)).c_str()
438 sigc::bind (sigc::mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc[dim].channel)
441 can_add_or_rename = true;
444 if (can_add_or_rename) {
445 sub.push_back (SeparatorElem ());
448 if (can_remove_channels (bc[dim].bundle)) {
449 if (bc[dim].channel != -1) {
450 add_remove_option (sub, w, bc[dim].channel);
453 snprintf (buf, sizeof (buf), _("Remove all"));
455 MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_all_channels), w))
458 for (uint32_t i = 0; i < bc[dim].bundle->nchannels().n_total(); ++i) {
459 if (should_show (bc[dim].bundle->channel_type(i))) {
460 add_remove_option (sub, w, i);
466 if (_show_only_bundles || count_of_our_type (bc[dim].bundle->nchannels()) <= 1) {
467 snprintf (buf, sizeof (buf), _("%s all"), disassociation_verb().c_str());
469 MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc[dim].channel, dim))
474 if (bc[dim].channel != -1) {
475 add_disassociate_option (sub, w, dim, bc[dim].channel);
477 snprintf (buf, sizeof (buf), _("%s all"), disassociation_verb().c_str());
479 MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_bundle), w, dim))
482 for (uint32_t i = 0; i < bc[dim].bundle->nchannels().n_total(); ++i) {
483 if (should_show (bc[dim].bundle->channel_type(i))) {
484 add_disassociate_option (sub, w, dim, i);
490 items.push_back (MenuElem (escape_underscores (bc[dim].bundle->name()).c_str(), *m));
491 need_separator = true;
496 if (need_separator) {
497 items.push_back (SeparatorElem ());
500 items.push_back (MenuElem (_("Rescan"), sigc::mem_fun (*this, &PortMatrix::setup_all_ports)));
501 items.push_back (CheckMenuElem (_("Show individual ports"), sigc::mem_fun (*this, &PortMatrix::toggle_show_only_bundles)));
502 CheckMenuItem* i = dynamic_cast<CheckMenuItem*> (&items.back());
503 _inhibit_toggle_show_only_bundles = true;
504 i->set_active (!_show_only_bundles);
505 _inhibit_toggle_show_only_bundles = false;
511 PortMatrix::remove_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
513 boost::shared_ptr<Bundle> sb = b.lock ();
518 remove_channel (BundleChannel (sb, c));
523 PortMatrix::rename_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
525 boost::shared_ptr<Bundle> sb = b.lock ();
530 rename_channel (BundleChannel (sb, c));
534 PortMatrix::disassociate_all_on_bundle (boost::weak_ptr<Bundle> bundle, int dim)
536 boost::shared_ptr<Bundle> sb = bundle.lock ();
541 for (uint32_t i = 0; i < sb->nchannels().n_total(); ++i) {
542 if (should_show (sb->channel_type(i))) {
543 disassociate_all_on_channel (bundle, i, dim);
549 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<Bundle> bundle, uint32_t channel, int dim)
551 boost::shared_ptr<Bundle> sb = bundle.lock ();
556 PortGroup::BundleList a = _ports[1-dim].bundles ();
558 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
559 for (uint32_t j = 0; j < (*i)->bundle->nchannels().n_total(); ++j) {
561 if (should_show ((*i)->bundle->channel_type(j))) {
566 c[dim] = BundleChannel (sb, channel);
567 c[1-dim] = BundleChannel ((*i)->bundle, j);
569 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
570 set_state (c, false);
575 _body->rebuild_and_draw_grid ();
579 PortMatrix::setup_global_ports ()
581 ENSURE_GUI_THREAD (*this, &PortMatrix::setup_global_ports)
583 for (int i = 0; i < 2; ++i) {
584 if (list_is_global (i)) {
591 PortMatrix::setup_global_ports_proxy ()
593 /* Avoid a deadlock by calling this in an idle handler: see IOSelector::io_changed_proxy
597 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &PortMatrix::setup_global_ports));
601 PortMatrix::setup_all_ports ()
603 if (_session->deletion_in_progress()) {
607 ENSURE_GUI_THREAD (*this, &PortMatrix::setup_all_ports)
614 PortMatrix::toggle_show_only_bundles ()
616 if (_inhibit_toggle_show_only_bundles) {
620 _show_only_bundles = !_show_only_bundles;
625 pair<uint32_t, uint32_t>
626 PortMatrix::max_size () const
628 pair<uint32_t, uint32_t> m = _body->max_size ();
630 m.first += _vscroll.get_width () + _vbox.get_width () + 4;
631 m.second += _hscroll.get_height () + _hbox.get_height () + 4;
637 PortMatrix::on_scroll_event (GdkEventScroll* ev)
639 double const h = _hscroll.get_value ();
640 double const v = _vscroll.get_value ();
642 switch (ev->direction) {
644 _vscroll.set_value (v - PortMatrixComponent::grid_spacing ());
646 case GDK_SCROLL_DOWN:
647 _vscroll.set_value (v + PortMatrixComponent::grid_spacing ());
649 case GDK_SCROLL_LEFT:
650 _hscroll.set_value (h - PortMatrixComponent::grid_spacing ());
652 case GDK_SCROLL_RIGHT:
653 _hscroll.set_value (h + PortMatrixComponent::grid_spacing ());
660 boost::shared_ptr<IO>
661 PortMatrix::io_from_bundle (boost::shared_ptr<Bundle> b) const
663 boost::shared_ptr<IO> io = _ports[0].io_from_bundle (b);
665 io = _ports[1].io_from_bundle (b);
672 PortMatrix::can_add_channel (boost::shared_ptr<Bundle> b) const
674 return io_from_bundle (b);
678 PortMatrix::add_channel (boost::shared_ptr<Bundle> b, DataType t)
680 boost::shared_ptr<IO> io = io_from_bundle (b);
683 io->add_port ("", this, t);
688 PortMatrix::can_remove_channels (boost::shared_ptr<Bundle> b) const
690 return io_from_bundle (b);
694 PortMatrix::remove_channel (ARDOUR::BundleChannel b)
696 boost::shared_ptr<IO> io = io_from_bundle (b.bundle);
699 Port* p = io->nth (b.channel);
701 int const r = io->remove_port (p, this);
703 ArdourDialog d (_("Port removal not allowed"));
704 Label l (_("This port cannot be removed, as the first plugin in the track or buss cannot accept the new number of inputs."));
705 d.get_vbox()->pack_start (l);
706 d.add_button (Stock::OK, RESPONSE_ACCEPT);
716 PortMatrix::remove_all_channels (boost::weak_ptr<Bundle> w)
718 boost::shared_ptr<Bundle> b = w.lock ();
723 for (uint32_t i = 0; i < b->nchannels().n_total(); ++i) {
724 if (should_show (b->channel_type(i))) {
725 remove_channel (ARDOUR::BundleChannel (b, i));
731 PortMatrix::add_channel_proxy (boost::weak_ptr<Bundle> w, DataType t)
733 boost::shared_ptr<Bundle> b = w.lock ();
742 PortMatrix::setup_notebooks ()
744 int const h_current_page = _hnotebook.get_current_page ();
745 int const v_current_page = _vnotebook.get_current_page ();
747 /* for some reason best known to GTK, erroneous switch_page signals seem to be generated
748 when adding or removing pages to or from notebooks, so ignore them */
750 _ignore_notebook_page_selected = true;
752 remove_notebook_pages (_hnotebook);
753 remove_notebook_pages (_vnotebook);
755 for (PortGroupList::List::const_iterator i = _ports[_row_index].begin(); i != _ports[_row_index].end(); ++i) {
756 HBox* dummy = manage (new HBox);
758 Label* label = manage (new Label ((*i)->name));
759 label->set_angle (_arrangement == LEFT_TO_BOTTOM ? 90 : -90);
761 _vnotebook.prepend_page (*dummy, *label);
764 for (PortGroupList::List::const_iterator i = _ports[_column_index].begin(); i != _ports[_column_index].end(); ++i) {
765 HBox* dummy = manage (new HBox);
767 _hnotebook.append_page (*dummy, (*i)->name);
770 _ignore_notebook_page_selected = false;
772 if (_arrangement == TOP_TO_RIGHT) {
773 _vnotebook.set_tab_pos (POS_RIGHT);
774 _hnotebook.set_tab_pos (POS_TOP);
776 _vnotebook.set_tab_pos (POS_LEFT);
777 _hnotebook.set_tab_pos (POS_BOTTOM);
780 if (h_current_page != -1 && _hnotebook.get_n_pages() > h_current_page) {
781 _hnotebook.set_current_page (h_current_page);
783 _hnotebook.set_current_page (0);
786 if (v_current_page != -1 && _vnotebook.get_n_pages() > v_current_page) {
787 _vnotebook.set_current_page (v_current_page);
789 _vnotebook.set_current_page (0);
792 if (_hnotebook.get_n_pages() <= 1) {
798 if (_vnotebook.get_n_pages() <= 1) {
806 PortMatrix::remove_notebook_pages (Notebook& n)
808 int const N = n.get_n_pages ();
810 for (int i = 0; i < N; ++i) {
816 PortMatrix::notebook_page_selected (GtkNotebookPage *, guint)
818 if (_ignore_notebook_page_selected) {
828 PortMatrix::session_going_away ()
834 PortMatrix::body_dimensions_changed ()
836 _hspacer.set_size_request (_body->column_labels_border_x (), -1);
837 if (_arrangement == TOP_TO_RIGHT) {
838 _vspacer.set_size_request (-1, _body->column_labels_height ());
846 _parent->get_size (curr_width, curr_height);
848 pair<uint32_t, uint32_t> m = max_size ();
850 /* Don't shrink the window */
851 m.first = max (int (m.first), curr_width);
852 m.second = max (int (m.second), curr_height);
854 resize_window_to_proportion_of_monitor (_parent, m.first, m.second);
858 boost::shared_ptr<const PortGroup>
859 PortMatrix::visible_ports (int d) const
861 PortGroupList const & p = _ports[d];
862 PortGroupList::List::const_iterator j = p.begin ();
865 if (d == _row_index) {
866 n = p.size() - _vnotebook.get_current_page () - 1;
868 n = _hnotebook.get_current_page ();
872 while (i != int (n) && j != p.end ()) {
878 return boost::shared_ptr<const PortGroup> ();
885 PortMatrix::add_remove_option (Menu_Helpers::MenuList& m, boost::weak_ptr<Bundle> w, int c)
887 using namespace Menu_Helpers;
889 boost::shared_ptr<Bundle> b = w.lock ();
895 snprintf (buf, sizeof (buf), _("Remove '%s'"), escape_underscores (b->channel_name (c)).c_str());
896 m.push_back (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_channel_proxy), w, c)));
900 PortMatrix::add_disassociate_option (Menu_Helpers::MenuList& m, boost::weak_ptr<Bundle> w, int d, int c)
902 using namespace Menu_Helpers;
904 boost::shared_ptr<Bundle> b = w.lock ();
910 snprintf (buf, sizeof (buf), _("%s all from '%s'"), disassociation_verb().c_str(), escape_underscores (b->channel_name (c)).c_str());
911 m.push_back (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, c, d)));
915 PortMatrix::port_connected_or_disconnected ()
917 _body->rebuild_and_draw_grid ();
921 PortMatrix::channel_noun () const
926 /** @return true if this matrix should show bundles / ports of type \t */
928 PortMatrix::should_show (DataType t) const
930 return (_type == DataType::NIL || t == _type);
934 PortMatrix::count_of_our_type (ChanCount c) const
936 if (_type == DataType::NIL) {
940 return c.get (_type);
943 PortMatrixNode::State
944 PortMatrix::get_association (PortMatrixNode node) const
946 if (show_only_bundles ()) {
948 bool have_off_diagonal_association = false;
949 bool have_diagonal_association = false;
950 bool have_diagonal_not_association = false;
952 for (uint32_t i = 0; i < node.row.bundle->nchannels().n_total(); ++i) {
954 for (uint32_t j = 0; j < node.column.bundle->nchannels().n_total(); ++j) {
956 if (!should_show (node.row.bundle->channel_type(i)) || !should_show (node.column.bundle->channel_type(j))) {
960 ARDOUR::BundleChannel c[2];
961 c[column_index()] = ARDOUR::BundleChannel (node.row.bundle, i);
962 c[row_index()] = ARDOUR::BundleChannel (node.column.bundle, j);
964 PortMatrixNode::State const s = get_state (c);
967 case PortMatrixNode::ASSOCIATED:
969 have_diagonal_association = true;
971 have_off_diagonal_association = true;
975 case PortMatrixNode::NOT_ASSOCIATED:
977 have_diagonal_not_association = true;
987 if (have_diagonal_association && !have_off_diagonal_association && !have_diagonal_not_association) {
988 return PortMatrixNode::ASSOCIATED;
989 } else if (!have_diagonal_association && !have_off_diagonal_association) {
990 return PortMatrixNode::NOT_ASSOCIATED;
993 return PortMatrixNode::PARTIAL;
997 ARDOUR::BundleChannel c[2];
998 c[column_index()] = node.column;
999 c[row_index()] = node.row;
1000 return get_state (c);
1005 return PortMatrixNode::NOT_ASSOCIATED;