+ _menu->popup (1, t);
+}
+
+void
+PortMatrix::remove_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
+{
+ boost::shared_ptr<Bundle> sb = b.lock ();
+ if (!sb) {
+ return;
+ }
+
+ remove_channel (BundleChannel (sb, c));
+
+}
+
+void
+PortMatrix::rename_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
+{
+ boost::shared_ptr<Bundle> sb = b.lock ();
+ if (!sb) {
+ return;
+ }
+
+ rename_channel (BundleChannel (sb, c));
+}
+
+void
+PortMatrix::disassociate_all_on_bundle (boost::weak_ptr<Bundle> bundle, int dim)
+{
+ boost::shared_ptr<Bundle> sb = bundle.lock ();
+ if (!sb) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < sb->nchannels().n_total(); ++i) {
+ if (should_show (sb->channel_type(i))) {
+ disassociate_all_on_channel (bundle, i, dim);
+ }
+ }
+}
+
+void
+PortMatrix::disassociate_all_on_channel (boost::weak_ptr<Bundle> bundle, uint32_t channel, int dim)
+{
+ boost::shared_ptr<Bundle> sb = bundle.lock ();
+ if (!sb) {
+ return;
+ }
+
+ PortGroup::BundleList a = _ports[1-dim].bundles ();
+
+ for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
+ for (uint32_t j = 0; j < (*i)->bundle->nchannels().n_total(); ++j) {
+
+ if (!should_show ((*i)->bundle->channel_type(j))) {
+ continue;
+ }
+
+ BundleChannel c[2];
+ c[dim] = BundleChannel (sb, channel);
+ c[1-dim] = BundleChannel ((*i)->bundle, j);
+
+ if (get_state (c) == PortMatrixNode::ASSOCIATED) {
+ set_state (c, false);
+ }
+ }
+ }
+
+ _body->rebuild_and_draw_grid ();
+}
+
+void
+PortMatrix::setup_global_ports ()
+{
+ ENSURE_GUI_THREAD (*this, &PortMatrix::setup_global_ports)
+
+ for (int i = 0; i < 2; ++i) {
+ if (list_is_global (i)) {
+ setup_ports (i);
+ }
+ }
+}
+
+void
+PortMatrix::setup_global_ports_proxy (RouteSortOrderKey sk)
+{
+ if (sk == EditorSort) {
+ /* Avoid a deadlock by calling this in an idle handler: see IOSelector::io_changed_proxy
+ for a discussion.
+ */
+
+ Glib::signal_idle().connect_once (sigc::mem_fun (*this, &PortMatrix::setup_global_ports));
+ }
+}
+
+void
+PortMatrix::setup_all_ports ()
+{
+ if (_session->deletion_in_progress()) {
+ return;
+ }
+
+ ENSURE_GUI_THREAD (*this, &PortMatrix::setup_all_ports)
+
+ setup_ports (0);
+ setup_ports (1);
+}
+
+void
+PortMatrix::toggle_show_only_bundles ()
+{
+ if (_inhibit_toggle_show_only_bundles) {
+ return;
+ }
+
+ _show_only_bundles = !_show_only_bundles;
+
+ setup ();
+
+ /* The way in which hardware ports are grouped changes depending on the _show_only_bundles
+ setting, so we need to set things up again now.
+ */
+ setup_all_ports ();
+}
+
+pair<uint32_t, uint32_t>
+PortMatrix::max_size () const
+{
+ pair<uint32_t, uint32_t> m = _body->max_size ();
+
+ m.first += _vscroll.get_width () + _vbox.get_width () + 4;
+ m.second += _hscroll.get_height () + _hbox.get_height () + 4;
+
+ return m;
+}
+
+bool
+PortMatrix::on_scroll_event (GdkEventScroll* ev)
+{
+ double const h = _hscroll.get_value ();
+ double const v = _vscroll.get_value ();
+
+ switch (ev->direction) {
+ case GDK_SCROLL_UP:
+ _vscroll.set_value (v - PortMatrixComponent::grid_spacing ());
+ break;
+ case GDK_SCROLL_DOWN:
+ _vscroll.set_value (v + PortMatrixComponent::grid_spacing ());
+ break;
+ case GDK_SCROLL_LEFT:
+ _hscroll.set_value (h - PortMatrixComponent::grid_spacing ());
+ break;
+ case GDK_SCROLL_RIGHT:
+ _hscroll.set_value (h + PortMatrixComponent::grid_spacing ());
+ break;
+ }
+
+ return true;
+}
+
+boost::shared_ptr<IO>
+PortMatrix::io_from_bundle (boost::shared_ptr<Bundle> b) const
+{
+ boost::shared_ptr<IO> io = _ports[0].io_from_bundle (b);
+ if (!io) {
+ io = _ports[1].io_from_bundle (b);
+ }
+
+ return io;
+}
+
+bool
+PortMatrix::can_add_channels (boost::shared_ptr<Bundle> b) const
+{
+ return io_from_bundle (b);
+}
+
+void
+PortMatrix::add_channel (boost::shared_ptr<Bundle> b, DataType t)
+{
+ boost::shared_ptr<IO> io = io_from_bundle (b);
+
+ if (io) {
+ int const r = io->add_port ("", this, t);
+ if (r == -1) {
+ Gtk::MessageDialog msg (_("It is not possible to add a port here, as the first processor in the track or buss cannot "
+ "support the new configuration."
+ ));
+ msg.set_title (_("Cannot add port"));
+ msg.run ();
+ }
+ }
+}
+
+bool
+PortMatrix::can_remove_channels (boost::shared_ptr<Bundle> b) const
+{
+ return io_from_bundle (b);
+}
+
+void
+PortMatrix::remove_channel (ARDOUR::BundleChannel b)
+{
+ boost::shared_ptr<IO> io = io_from_bundle (b.bundle);
+
+ if (io) {
+ boost::shared_ptr<Port> p = io->nth (b.channel);
+ if (p) {
+ int const r = io->remove_port (p, this);
+ if (r == -1) {
+ ArdourDialog d (_("Port removal not allowed"));
+ Label l (_("This port cannot be removed, as the first plugin in the track or buss cannot accept the new number of inputs."));
+ d.get_vbox()->pack_start (l);
+ d.add_button (Stock::OK, RESPONSE_ACCEPT);
+ d.set_modal (true);
+ d.show_all ();
+ d.run ();
+ }
+ }
+ }
+}
+
+void
+PortMatrix::remove_all_channels (boost::weak_ptr<Bundle> w)
+{
+ boost::shared_ptr<Bundle> b = w.lock ();
+ if (!b) {
+ return;
+ }
+
+ /* Remove channels backwards so that we don't renumber channels
+ that we are about to remove.
+ */
+ for (int i = (b->nchannels().n_total() - 1); i >= 0; --i) {
+ if (should_show (b->channel_type(i))) {
+ remove_channel (ARDOUR::BundleChannel (b, i));
+ }
+ }
+}
+
+void
+PortMatrix::add_channel_proxy (boost::weak_ptr<Bundle> w, DataType t)
+{
+ boost::shared_ptr<Bundle> b = w.lock ();
+ if (!b) {
+ return;
+ }
+
+ add_channel (b, t);
+}
+
+void
+PortMatrix::setup_notebooks ()
+{
+ int const h_current_page = _hnotebook.get_current_page ();
+ int const v_current_page = _vnotebook.get_current_page ();
+
+ /* for some reason best known to GTK, erroneous switch_page signals seem to be generated
+ when adding or removing pages to or from notebooks, so ignore them */
+
+ _ignore_notebook_page_selected = true;
+
+ remove_notebook_pages (_hnotebook);
+ remove_notebook_pages (_vnotebook);
+
+ for (PortGroupList::List::const_iterator i = _ports[_row_index].begin(); i != _ports[_row_index].end(); ++i) {
+ HBox* dummy = manage (new HBox);
+ dummy->show ();
+ Label* label = manage (new Label ((*i)->name));
+ label->set_angle (_arrangement == LEFT_TO_BOTTOM ? 90 : -90);
+ label->set_use_markup ();
+ label->show ();
+ if (_arrangement == LEFT_TO_BOTTOM) {
+ _vnotebook.prepend_page (*dummy, *label);
+ } else {
+ /* Reverse the order of vertical tabs when they are on the right hand side
+ so that from top to bottom it is the same order as that from left to right
+ for the top tabs.
+ */
+ _vnotebook.append_page (*dummy, *label);
+ }
+ }
+
+ for (PortGroupList::List::const_iterator i = _ports[_column_index].begin(); i != _ports[_column_index].end(); ++i) {
+ HBox* dummy = manage (new HBox);
+ dummy->show ();
+ Label* label = manage (new Label ((*i)->name));
+ label->set_use_markup ();
+ label->show ();
+ _hnotebook.append_page (*dummy, *label);
+ }
+
+ _ignore_notebook_page_selected = false;
+
+ if (_arrangement == TOP_TO_RIGHT) {
+ _vnotebook.set_tab_pos (POS_RIGHT);
+ _hnotebook.set_tab_pos (POS_TOP);
+ } else {
+ _vnotebook.set_tab_pos (POS_LEFT);
+ _hnotebook.set_tab_pos (POS_BOTTOM);
+ }
+
+ if (h_current_page != -1 && _hnotebook.get_n_pages() > h_current_page) {
+ _hnotebook.set_current_page (h_current_page);
+ } else {
+ _hnotebook.set_current_page (0);
+ }
+
+ if (v_current_page != -1 && _vnotebook.get_n_pages() > v_current_page) {
+ _vnotebook.set_current_page (v_current_page);
+ } else {
+ _vnotebook.set_current_page (0);
+ }
+
+ if (_hnotebook.get_n_pages() <= 1) {
+ _hbox.hide ();
+ } else {
+ _hbox.show ();
+ }
+
+ if (_vnotebook.get_n_pages() <= 1) {
+ _vbox.hide ();
+ } else {
+ _vbox.show ();
+ }
+}
+
+void
+PortMatrix::remove_notebook_pages (Notebook& n)
+{
+ int const N = n.get_n_pages ();
+
+ for (int i = 0; i < N; ++i) {
+ n.remove_page ();
+ }
+}
+
+void
+PortMatrix::notebook_page_selected (GtkNotebookPage *, guint)
+{
+ if (_ignore_notebook_page_selected) {
+ return;
+ }
+
+ _body->setup ();