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 "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"
39 /** PortMatrix constructor.
40 * @param session Our session.
41 * @param type Port type that we are handling.
43 PortMatrix::PortMatrix (ARDOUR::Session& session, ARDOUR::DataType type)
48 _arrangement (TOP_TO_RIGHT),
51 _min_height_divisor (1),
52 _show_only_bundles (false),
53 _inhibit_toggle_show_only_bundles (false),
56 _body = new PortMatrixBody (this);
58 for (int i = 0; i < 2; ++i) {
59 _ports[i].set_type (type);
61 /* watch for the content of _ports[] changing */
62 _ports[i].Changed.connect (mem_fun (*this, &PortMatrix::setup));
65 _hscroll.signal_value_changed().connect (mem_fun (*this, &PortMatrix::hscroll_changed));
66 _vscroll.signal_value_changed().connect (mem_fun (*this, &PortMatrix::vscroll_changed));
68 /* watch for routes being added or removed */
69 _session.RouteAdded.connect (sigc::hide (mem_fun (*this, &PortMatrix::routes_changed)));
71 /* and also bundles */
72 _session.BundleAdded.connect (sigc::hide (mem_fun (*this, &PortMatrix::setup_global_ports)));
74 reconnect_to_routes ();
76 attach (*_body, 0, 1, 0, 1);
77 attach (_vscroll, 1, 2, 0, 1, SHRINK);
78 attach (_hscroll, 0, 1, 1, 2, FILL | EXPAND, SHRINK);
83 PortMatrix::~PortMatrix ()
89 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
91 PortMatrix::reconnect_to_routes ()
93 for (vector<connection>::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) {
96 _route_connections.clear ();
98 boost::shared_ptr<ARDOUR::RouteList> routes = _session.get_routes ();
99 for (ARDOUR::RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
100 _route_connections.push_back (
101 (*i)->processors_changed.connect (mem_fun (*this, &PortMatrix::setup_global_ports))
106 /** A route has been added to or removed from the session */
108 PortMatrix::routes_changed ()
110 reconnect_to_routes ();
111 setup_global_ports ();
114 /** Set up everything that depends on the content of _ports[] */
119 select_arrangement ();
126 _first_setup = false;
132 PortMatrix::set_type (ARDOUR::DataType t)
135 _ports[0].set_type (_type);
136 _ports[1].set_type (_type);
142 PortMatrix::hscroll_changed ()
144 _body->set_xoffset (_hscroll.get_adjustment()->get_value());
148 PortMatrix::vscroll_changed ()
150 _body->set_yoffset (_vscroll.get_adjustment()->get_value());
154 PortMatrix::setup_scrollbars ()
156 Adjustment* a = _hscroll.get_adjustment ();
158 a->set_upper (_body->full_scroll_width());
159 a->set_page_size (_body->alloc_scroll_width());
160 a->set_step_increment (32);
161 a->set_page_increment (128);
163 a = _vscroll.get_adjustment ();
165 a->set_upper (_body->full_scroll_height());
166 a->set_page_size (_body->alloc_scroll_height());
167 a->set_step_increment (32);
168 a->set_page_increment (128);
171 /** Disassociate all of our ports from each other */
173 PortMatrix::disassociate_all ()
175 PortGroup::BundleList a = _ports[0].bundles ();
176 PortGroup::BundleList b = _ports[1].bundles ();
178 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
179 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
180 for (PortGroup::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
181 for (uint32_t l = 0; l < k->bundle->nchannels(); ++l) {
183 ARDOUR::BundleChannel c[2] = {
184 ARDOUR::BundleChannel (i->bundle, j),
185 ARDOUR::BundleChannel (k->bundle, l)
188 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
189 set_state (c, false);
197 _body->rebuild_and_draw_grid ();
200 /* Decide how to arrange the components of the matrix */
202 PortMatrix::select_arrangement ()
204 uint32_t const N[2] = {
205 _ports[0].total_visible_channels (),
206 _ports[1].total_visible_channels ()
209 /* The list with the most channels goes on left or right, so that the most channel
210 names are printed horizontally and hence more readable. However we also
211 maintain notional `signal flow' vaguely from left to right. Subclasses
212 should choose where to put ports based on signal flowing from _ports[0]
219 _arrangement = LEFT_TO_BOTTOM;
225 _arrangement = TOP_TO_RIGHT;
229 /** @return columns list */
230 PortGroupList const *
231 PortMatrix::columns () const
233 return &_ports[_column_index];
236 /* @return rows list */
237 PortGroupList const *
238 PortMatrix::rows () const
240 return &_ports[_row_index];
244 PortMatrix::popup_menu (
245 pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel> column,
246 pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel> row,
250 using namespace Menu_Helpers;
255 _menu->set_name ("ArdourContextMenu");
257 MenuList& items = _menu->items ();
259 boost::shared_ptr<PortGroup> pg[2];
260 pg[_column_index] = column.first;
261 pg[_row_index] = row.first;
263 ARDOUR::BundleChannel bc[2];
264 bc[_column_index] = column.second;
265 bc[_row_index] = row.second;
269 for (int dim = 0; dim < 2; ++dim) {
273 boost::weak_ptr<PortGroup> wp (pg[dim]);
275 if (pg[dim]->visible()) {
277 snprintf (buf, sizeof (buf), _("Hide '%s' sources"), pg[dim]->name.c_str());
279 snprintf (buf, sizeof (buf), _("Hide '%s' destinations"), pg[dim]->name.c_str());
282 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::hide_group), wp)));
285 snprintf (buf, sizeof (buf), _("Show '%s' sources"), pg[dim]->name.c_str());
287 snprintf (buf, sizeof (buf), _("Show '%s' destinations"), pg[dim]->name.c_str());
289 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::show_group), wp)));
293 if (bc[dim].bundle) {
294 bool have_one = false;
296 if (can_rename_channels (dim)) {
297 snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
298 boost::weak_ptr<ARDOUR::Bundle> w (bc[dim].bundle);
302 bind (mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc[dim].channel)
309 if (can_remove_channels (dim)) {
310 snprintf (buf, sizeof (buf), _("Remove '%s'"), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
311 boost::weak_ptr<ARDOUR::Bundle> w (bc[dim].bundle);
315 bind (mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc[dim].channel)
322 boost::weak_ptr<ARDOUR::Bundle> w (bc[dim].bundle);
324 if (_show_only_bundles) {
325 snprintf (buf, sizeof (buf), _("Disassociate all from '%s'"), bc[dim].bundle->name().c_str());
328 buf, sizeof (buf), _("Disassociate all from '%s/%s'"),
329 bc[dim].bundle->name().c_str(), bc[dim].bundle->channel_name (bc[dim].channel).c_str()
334 MenuElem (buf, bind (mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc[dim].channel, dim))
340 items.push_back (SeparatorElem ());
342 items.push_back (MenuElem (_("Rescan"), mem_fun (*this, &PortMatrix::setup_all_ports)));
343 items.push_back (CheckMenuElem (_("Show individual ports"), mem_fun (*this, &PortMatrix::toggle_show_only_bundles)));
344 CheckMenuItem* i = dynamic_cast<CheckMenuItem*> (&items.back());
345 _inhibit_toggle_show_only_bundles = true;
346 i->set_active (!_show_only_bundles);
347 _inhibit_toggle_show_only_bundles = false;
354 PortMatrix::remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
356 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
361 remove_channel (ARDOUR::BundleChannel (sb, c));
366 PortMatrix::rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
368 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
373 rename_channel (ARDOUR::BundleChannel (sb, c));
377 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<ARDOUR::Bundle> bundle, uint32_t channel, int dim)
379 boost::shared_ptr<ARDOUR::Bundle> sb = bundle.lock ();
384 PortGroup::BundleList a = _ports[1-dim].bundles ();
386 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
387 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
389 ARDOUR::BundleChannel c[2];
390 c[dim] = ARDOUR::BundleChannel (sb, channel);
391 c[1-dim] = ARDOUR::BundleChannel (i->bundle, j);
393 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
394 set_state (c, false);
399 _body->rebuild_and_draw_grid ();
403 PortMatrix::setup_global_ports ()
405 for (int i = 0; i < 2; ++i) {
406 if (list_is_global (i)) {
413 PortMatrix::setup_all_ports ()
420 PortMatrix::toggle_show_only_bundles ()
422 if (_inhibit_toggle_show_only_bundles) {
426 _show_only_bundles = !_show_only_bundles;
433 PortMatrix::hide_group (boost::weak_ptr<PortGroup> w)
435 boost::shared_ptr<PortGroup> g = w.lock ();
440 g->set_visible (false);
444 PortMatrix::show_group (boost::weak_ptr<PortGroup> w)
446 boost::shared_ptr<PortGroup> g = w.lock ();
451 g->set_visible (true);
454 pair<uint32_t, uint32_t>
455 PortMatrix::max_size () const
457 pair<uint32_t, uint32_t> m = _body->max_size ();
459 m.first += _vscroll.get_width ();
460 m.second += _hscroll.get_height ();