X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fport_matrix.cc;h=81a62d52c808c81e6daf0e11ea3e89ff42723fc6;hb=a7f4f660956250cc98cc519321ad5b8e715f9d0f;hp=e04dd3b252db2d7343822c21a573a18c0f1b9b5e;hpb=954e1a6e795a5a53865f9278105579f00143cdb1;p=ardour.git diff --git a/gtk2_ardour/port_matrix.cc b/gtk2_ardour/port_matrix.cc index e04dd3b252..81a62d52c8 100644 --- a/gtk2_ardour/port_matrix.cc +++ b/gtk2_ardour/port_matrix.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2007 Paul Davis + Copyright (C) 2002-2009 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,175 +17,582 @@ */ +#include +#include +#include #include -#include #include -#include -#include #include -#include -#include -#include "ardour/data_type.h" -#include "i18n.h" +#include +#include +#include "ardour/bundle.h" +#include "ardour/types.h" +#include "ardour/session.h" +#include "ardour/route.h" #include "port_matrix.h" +#include "port_matrix_body.h" +#include "port_matrix_component.h" +#include "i18n.h" +using namespace std; +using namespace sigc; using namespace Gtk; - -PortMatrix::PortMatrix (ARDOUR::Session& session, ARDOUR::DataType type, bool offer_inputs, PortGroupList::Mask mask) - : _offer_inputs (offer_inputs), _port_group_list (session, type, offer_inputs, mask), _type (type), matrix (this) +using namespace ARDOUR; + +/** PortMatrix constructor. + * @param session Our session. + * @param type Port type that we are handling. + */ +PortMatrix::PortMatrix (Window* parent, Session& session, DataType type) + : Table (2, 2), + _session (session), + _parent (parent), + _type (type), + _menu (0), + _arrangement (TOP_TO_RIGHT), + _row_index (0), + _column_index (1), + _min_height_divisor (1), + _show_only_bundles (false), + _inhibit_toggle_show_only_bundles (false) { - _side_vbox_pad = 0; + _body = new PortMatrixBody (this); - _visibility_checkbutton_box.pack_start (*(manage (new Label (_("Connections displayed: ")))), false, false, 10); - pack_start (_visibility_checkbutton_box, false, false); + for (int i = 0; i < 2; ++i) { + _ports[i].set_type (type); + + /* watch for the content of _ports[] changing */ + _ports[i].Changed.connect (mem_fun (*this, &PortMatrix::setup)); + } - _scrolled_window.set_policy (POLICY_ALWAYS, POLICY_AUTOMATIC); - _scrolled_window.set_shadow_type (SHADOW_NONE); + _hscroll.signal_value_changed().connect (mem_fun (*this, &PortMatrix::hscroll_changed)); + _vscroll.signal_value_changed().connect (mem_fun (*this, &PortMatrix::vscroll_changed)); - _scrolled_window.add (matrix); + /* watch for routes being added or removed */ + _session.RouteAdded.connect (sigc::hide (mem_fun (*this, &PortMatrix::routes_changed))); - if (offer_inputs) { - _overall_hbox.pack_start (_side_vbox, false, false, 6); - _overall_hbox.pack_start (_scrolled_window, true, true); - } else { - _overall_hbox.pack_start (_scrolled_window, true, true, 6); - _overall_hbox.pack_start (_side_vbox, false, false); - } + /* and also bundles */ + _session.BundleAdded.connect (sigc::hide (mem_fun (*this, &PortMatrix::setup_global_ports))); + + reconnect_to_routes (); - pack_start (_overall_hbox); + attach (*_body, 0, 1, 0, 1); + attach (_vscroll, 1, 2, 0, 1, SHRINK); + attach (_hscroll, 0, 1, 1, 2, FILL | EXPAND, SHRINK); + + show_all (); } PortMatrix::~PortMatrix () { - clear (); + delete _body; + delete _menu; } -void -PortMatrix::set_ports (const std::list& ports) +/** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */ +void +PortMatrix::reconnect_to_routes () { - matrix.set_ports (ports); + for (vector::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) { + i->disconnect (); + } + _route_connections.clear (); + + boost::shared_ptr routes = _session.get_routes (); + for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { + _route_connections.push_back ( + (*i)->processors_changed.connect (mem_fun (*this, &PortMatrix::setup_global_ports)) + ); + } } -/** Clear out the things that change when the number of source or destination ports changes */ +/** A route has been added to or removed from the session */ void -PortMatrix::clear () +PortMatrix::routes_changed () { - /* remove lurking, invisible label and padding */ + reconnect_to_routes (); + setup_global_ports (); +} + +/** Set up everything that depends on the content of _ports[] */ +void +PortMatrix::setup () +{ + if ((get_flags () & Gtk::REALIZED) == 0) { + select_arrangement (); + } + + _body->setup (); + setup_scrollbars (); + queue_draw (); + + show_all (); +} + +void +PortMatrix::set_type (DataType t) +{ + _type = t; + _ports[0].set_type (_type); + _ports[1].set_type (_type); - _side_vbox.children().clear (); + setup_all_ports (); +} + +void +PortMatrix::hscroll_changed () +{ + _body->set_xoffset (_hscroll.get_adjustment()->get_value()); +} + +void +PortMatrix::vscroll_changed () +{ + _body->set_yoffset (_vscroll.get_adjustment()->get_value()); +} - delete _side_vbox_pad; - _side_vbox_pad = 0; +void +PortMatrix::setup_scrollbars () +{ + Adjustment* a = _hscroll.get_adjustment (); + a->set_lower (0); + a->set_upper (_body->full_scroll_width()); + a->set_page_size (_body->alloc_scroll_width()); + a->set_step_increment (32); + a->set_page_increment (128); + + a = _vscroll.get_adjustment (); + a->set_lower (0); + a->set_upper (_body->full_scroll_height()); + a->set_page_size (_body->alloc_scroll_height()); + a->set_step_increment (32); + a->set_page_increment (128); +} - for (std::vector::iterator i = _port_group_ui.begin(); i != _port_group_ui.end(); ++i) { - _visibility_checkbutton_box.remove ((*i)->get_visibility_checkbutton()); - delete *i; +/** Disassociate all of our ports from each other */ +void +PortMatrix::disassociate_all () +{ + PortGroup::BundleList a = _ports[0].bundles (); + PortGroup::BundleList b = _ports[1].bundles (); + + for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) { + for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) { + for (PortGroup::BundleList::iterator k = b.begin(); k != b.end(); ++k) { + for (uint32_t l = 0; l < k->bundle->nchannels(); ++l) { + + BundleChannel c[2] = { + BundleChannel (i->bundle, j), + BundleChannel (k->bundle, l) + }; + + if (get_state (c) == PortMatrixNode::ASSOCIATED) { + set_state (c, false); + } + + } + } + } } - _port_group_ui.clear (); + _body->rebuild_and_draw_grid (); } -/** Set up the dialogue */ +/* Decide how to arrange the components of the matrix */ void -PortMatrix::setup () +PortMatrix::select_arrangement () { - /* sort out the ports that we'll offer to connect to */ - _port_group_list.refresh (); + uint32_t const N[2] = { + _ports[0].total_visible_channels (), + _ports[1].total_visible_channels () + }; + + /* The list with the most channels goes on left or right, so that the most channel + names are printed horizontally and hence more readable. However we also + maintain notional `signal flow' vaguely from left to right. Subclasses + should choose where to put ports based on signal flowing from _ports[0] + to _ports[1] */ - clear (); + if (N[0] > N[1]) { - _side_vbox_pad = new Label (""); /* unmanaged, explicitly deleted */ + _row_index = 0; + _column_index = 1; + _arrangement = LEFT_TO_BOTTOM; - _side_vbox.pack_start (*_side_vbox_pad, false, false); - _side_vbox.pack_start (*manage (new Label (""))); + } else { - matrix.clear (); + _row_index = 1; + _column_index = 0; + _arrangement = TOP_TO_RIGHT; + } +} - /* Matrix and visibility checkbuttons */ - for (PortGroupList::iterator i = _port_group_list.begin(); i != _port_group_list.end(); ++i) { +/** @return columns list */ +PortGroupList const * +PortMatrix::columns () const +{ + return &_ports[_column_index]; +} - PortGroupUI* t = new PortGroupUI (*this, **i); - - _port_group_ui.push_back (t); +/* @return rows list */ +PortGroupList const * +PortMatrix::rows () const +{ + return &_ports[_row_index]; +} - matrix.add_group (**i); +void +PortMatrix::popup_menu ( + pair, BundleChannel> column, + pair, BundleChannel> row, + uint32_t t + ) +{ + using namespace Menu_Helpers; + + delete _menu; - _visibility_checkbutton_box.pack_start (t->get_visibility_checkbutton(), false, false); + _menu = new Menu; + _menu->set_name ("ArdourContextMenu"); + + MenuList& items = _menu->items (); + + boost::shared_ptr pg[2]; + pg[_column_index] = column.first; + pg[_row_index] = row.first; + + BundleChannel bc[2]; + bc[_column_index] = column.second; + bc[_row_index] = row.second; + + char buf [64]; + bool need_separator = false; + + for (int dim = 0; dim < 2; ++dim) { + + if (bc[dim].bundle) { + + Menu* m = manage (new Menu); + MenuList& sub = m->items (); + + boost::weak_ptr w (bc[dim].bundle); + + if (can_add_channel (bc[dim].bundle)) { + snprintf (buf, sizeof (buf), _("Add %s"), channel_noun().c_str()); + sub.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::add_channel_proxy), w))); + } + + + if (can_rename_channels (bc[dim].bundle)) { + snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc[dim].bundle->channel_name (bc[dim].channel).c_str()); + sub.push_back ( + MenuElem ( + buf, + bind (mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc[dim].channel) + ) + ); + } + + sub.push_back (SeparatorElem ()); + + if (can_remove_channels (bc[dim].bundle)) { + snprintf (buf, sizeof (buf), _("Remove '%s'"), bc[dim].bundle->channel_name (bc[dim].channel).c_str()); + sub.push_back ( + MenuElem ( + buf, + bind (mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc[dim].channel) + ) + ); + } + + if (_show_only_bundles) { + snprintf (buf, sizeof (buf), _("%s all"), disassociation_verb().c_str()); + } else { + snprintf ( + buf, sizeof (buf), _("%s all from '%s'"), + disassociation_verb().c_str(), + bc[dim].bundle->channel_name (bc[dim].channel).c_str() + ); + } + + sub.push_back ( + MenuElem (buf, bind (mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc[dim].channel, dim)) + ); + + items.push_back (MenuElem (bc[dim].bundle->name().c_str(), *m)); + need_separator = true; + } - CheckButton* chk = dynamic_cast(&t->get_visibility_checkbutton()); + } - if (chk) { - chk->signal_toggled().connect (sigc::mem_fun (*this, &PortMatrix::reset_visibility)); + if (need_separator) { + items.push_back (SeparatorElem ()); + } + + need_separator = false; + + for (int dim = 0; dim < 2; ++dim) { + + if (pg[dim]) { + + boost::weak_ptr wp (pg[dim]); + + if (pg[dim]->visible()) { + if (dim == 0) { + if (pg[dim]->name.empty()) { + snprintf (buf, sizeof (buf), _("Hide sources")); + } else { + snprintf (buf, sizeof (buf), _("Hide '%s' sources"), pg[dim]->name.c_str()); + } + } else { + if (pg[dim]->name.empty()) { + snprintf (buf, sizeof (buf), _("Hide destinations")); + } else { + snprintf (buf, sizeof (buf), _("Hide '%s' destinations"), pg[dim]->name.c_str()); + } + } + + items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::hide_group), wp))); + } else { + if (dim == 0) { + if (pg[dim]->name.empty()) { + snprintf (buf, sizeof (buf), _("Show sources")); + } else { + snprintf (buf, sizeof (buf), _("Show '%s' sources"), pg[dim]->name.c_str()); + } + } else { + if (pg[dim]->name.empty()) { + snprintf (buf, sizeof (buf), _("Show destinations")); + } else { + snprintf (buf, sizeof (buf), _("Show '%s' destinations"), pg[dim]->name.c_str()); + } + } + items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::show_group), wp))); + } + + need_separator = true; } } - show_all (); + if (need_separator) { + items.push_back (SeparatorElem ()); + } - reset_visibility (); + items.push_back (MenuElem (_("Rescan"), mem_fun (*this, &PortMatrix::setup_all_ports))); + items.push_back (CheckMenuElem (_("Show individual ports"), mem_fun (*this, &PortMatrix::toggle_show_only_bundles))); + CheckMenuItem* i = dynamic_cast (&items.back()); + _inhibit_toggle_show_only_bundles = true; + i->set_active (!_show_only_bundles); + _inhibit_toggle_show_only_bundles = false; + + _menu->popup (1, t); } void -PortMatrix::reset_visibility () +PortMatrix::remove_channel_proxy (boost::weak_ptr b, uint32_t c) { - for (std::vector::iterator i = _port_group_ui.begin(); i != _port_group_ui.end(); ++i) { + boost::shared_ptr sb = b.lock (); + if (!sb) { + return; + } - (*i)->setup_visibility (); - - if ((*i)->port_group().visible) { - matrix.show_group ((*i)->port_group()); - } else { - matrix.hide_group ((*i)->port_group()); - } + remove_channel (BundleChannel (sb, c)); + +} + +void +PortMatrix::rename_channel_proxy (boost::weak_ptr b, uint32_t c) +{ + boost::shared_ptr sb = b.lock (); + if (!sb) { + return; } + + rename_channel (BundleChannel (sb, c)); } +void +PortMatrix::disassociate_all_on_channel (boost::weak_ptr bundle, uint32_t channel, int dim) +{ + boost::shared_ptr sb = bundle.lock (); + if (!sb) { + return; + } -/** Handle a button press on a row label */ -bool -PortMatrix::row_label_button_pressed (GdkEventButton* e, int r) + 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(); ++j) { + + 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 () { - if (e->type != GDK_BUTTON_PRESS || e->button != 3) { - return false; + for (int i = 0; i < 2; ++i) { + if (list_is_global (i)) { + setup_ports (i); + } } +} - Menu* menu = manage (new Menu); - Menu_Helpers::MenuList& items = menu->items (); - menu->set_name ("ArdourContextMenu"); +void +PortMatrix::setup_all_ports () +{ + setup_ports (0); + setup_ports (1); +} - bool const can_add = maximum_rows () > n_rows (); - bool const can_remove = minimum_rows () < n_rows (); - std::string const name = row_name (r); +void +PortMatrix::toggle_show_only_bundles () +{ + if (_inhibit_toggle_show_only_bundles) { + return; + } - items.push_back ( - Menu_Helpers::MenuElem (string_compose(_("Add %1"), row_descriptor()), sigc::mem_fun (*this, &PortMatrix::add_row)) - ); + _show_only_bundles = !_show_only_bundles; + _body->setup (); + setup_scrollbars (); + queue_draw (); +} + +void +PortMatrix::hide_group (boost::weak_ptr w) +{ + boost::shared_ptr g = w.lock (); + if (!g) { + return; + } + + g->set_visible (false); +} - items.back().set_sensitive (can_add); +void +PortMatrix::show_group (boost::weak_ptr w) +{ + boost::shared_ptr g = w.lock (); + if (!g) { + return; + } - items.push_back ( - Menu_Helpers::MenuElem (string_compose(_("Remove %1 \"%2\""), row_descriptor(), name), sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_row), r)) - ); + g->set_visible (true); +} - items.back().set_sensitive (can_remove); +pair +PortMatrix::max_size () const +{ + pair m = _body->max_size (); + + m.first += _vscroll.get_width (); + m.second += _hscroll.get_height (); + + return m; +} - menu->popup (e->button, e->time); +void +PortMatrix::setup_max_size () +{ + if (!_parent) { + return; + } + + pair const m = max_size (); + + GdkGeometry g; + g.max_width = m.first; + g.max_height = m.second; + + _parent->set_geometry_hints (*this, g, Gdk::HINT_MAX_SIZE); +} + +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 +PortMatrix::io_from_bundle (boost::shared_ptr b) const +{ + boost::shared_ptr io = _ports[0].io_from_bundle (b); + if (!io) { + io = _ports[1].io_from_bundle (b); + } + + return io; +} + +bool +PortMatrix::can_add_channel (boost::shared_ptr b) const +{ + return io_from_bundle (b); +} + void -PortMatrix::set_type (ARDOUR::DataType t) +PortMatrix::add_channel (boost::shared_ptr b) { - _type = t; - _port_group_list.set_type (t); - setup (); + boost::shared_ptr io = io_from_bundle (b); + + if (io) { + io->add_port ("", this, _type); + } +} + +bool +PortMatrix::can_remove_channels (boost::shared_ptr b) const +{ + return io_from_bundle (b); } void -PortMatrix::set_offer_inputs (bool i) +PortMatrix::remove_channel (ARDOUR::BundleChannel b) { - _offer_inputs = i; - _port_group_list.set_offer_inputs (i); - setup (); + boost::shared_ptr io = io_from_bundle (b.bundle); + + if (io) { + Port* p = io->nth (b.channel); + if (p) { + io->remove_port (p, this); + } + } } +void +PortMatrix::add_channel_proxy (boost::weak_ptr w) +{ + boost::shared_ptr b = w.lock (); + if (!b) { + return; + } + + add_channel (b); +}