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 ();
130 PortMatrix::set_type (ARDOUR::DataType t)
133 _ports[0].set_type (_type);
134 _ports[1].set_type (_type);
140 PortMatrix::hscroll_changed ()
142 _body->set_xoffset (_hscroll.get_adjustment()->get_value());
146 PortMatrix::vscroll_changed ()
148 _body->set_yoffset (_vscroll.get_adjustment()->get_value());
152 PortMatrix::setup_scrollbars ()
154 Adjustment* a = _hscroll.get_adjustment ();
156 a->set_upper (_body->full_scroll_width());
157 a->set_page_size (_body->alloc_scroll_width());
158 a->set_step_increment (32);
159 a->set_page_increment (128);
161 a = _vscroll.get_adjustment ();
163 a->set_upper (_body->full_scroll_height());
164 a->set_page_size (_body->alloc_scroll_height());
165 a->set_step_increment (32);
166 a->set_page_increment (128);
169 /** Disassociate all of our ports from each other */
171 PortMatrix::disassociate_all ()
173 PortGroup::BundleList a = _ports[0].bundles ();
174 PortGroup::BundleList b = _ports[1].bundles ();
176 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
177 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
178 for (PortGroup::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
179 for (uint32_t l = 0; l < k->bundle->nchannels(); ++l) {
181 ARDOUR::BundleChannel c[2] = {
182 ARDOUR::BundleChannel (i->bundle, j),
183 ARDOUR::BundleChannel (k->bundle, l)
186 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
187 set_state (c, false);
195 _body->rebuild_and_draw_grid ();
198 /* Decide how to arrange the components of the matrix */
200 PortMatrix::select_arrangement ()
202 uint32_t const N[2] = {
203 _ports[0].total_visible_channels (),
204 _ports[1].total_visible_channels ()
207 /* The list with the most channels goes on left or right, so that the most channel
208 names are printed horizontally and hence more readable. However we also
209 maintain notional `signal flow' vaguely from left to right. Subclasses
210 should choose where to put ports based on signal flowing from _ports[0]
217 _arrangement = LEFT_TO_BOTTOM;
223 _arrangement = TOP_TO_RIGHT;
227 /** @return columns list */
228 PortGroupList const *
229 PortMatrix::columns () const
231 return &_ports[_column_index];
234 /* @return rows list */
235 PortGroupList const *
236 PortMatrix::rows () const
238 return &_ports[_row_index];
242 PortMatrix::popup_menu (
243 pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel> column,
244 pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel> row,
248 using namespace Menu_Helpers;
253 _menu->set_name ("ArdourContextMenu");
255 MenuList& items = _menu->items ();
257 boost::shared_ptr<PortGroup> pg[2];
258 pg[_column_index] = column.first;
259 pg[_row_index] = row.first;
261 ARDOUR::BundleChannel bc[2];
262 bc[_column_index] = column.second;
263 bc[_row_index] = row.second;
267 for (int dim = 0; dim < 2; ++dim) {
271 boost::weak_ptr<PortGroup> wp (pg[dim]);
273 if (pg[dim]->visible()) {
275 snprintf (buf, sizeof (buf), _("Hide '%s' sources"), pg[dim]->name.c_str());
277 snprintf (buf, sizeof (buf), _("Hide '%s' destinations"), pg[dim]->name.c_str());
280 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::hide_group), wp)));
283 snprintf (buf, sizeof (buf), _("Show '%s' sources"), pg[dim]->name.c_str());
285 snprintf (buf, sizeof (buf), _("Show '%s' destinations"), pg[dim]->name.c_str());
287 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::show_group), wp)));
291 if (bc[dim].bundle) {
292 bool have_one = false;
294 if (can_rename_channels (dim)) {
295 snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
296 boost::weak_ptr<ARDOUR::Bundle> w (bc[dim].bundle);
300 bind (mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc[dim].channel)
307 if (can_remove_channels (dim)) {
308 snprintf (buf, sizeof (buf), _("Remove '%s'"), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
309 boost::weak_ptr<ARDOUR::Bundle> w (bc[dim].bundle);
313 bind (mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc[dim].channel)
320 boost::weak_ptr<ARDOUR::Bundle> w (bc[dim].bundle);
322 if (_show_only_bundles) {
323 snprintf (buf, sizeof (buf), _("Disassociate all from '%s'"), bc[dim].bundle->name().c_str());
326 buf, sizeof (buf), _("Disassociate all from '%s/%s'"),
327 bc[dim].bundle->name().c_str(), bc[dim].bundle->channel_name (bc[dim].channel).c_str()
332 MenuElem (buf, bind (mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc[dim].channel, dim))
338 items.push_back (SeparatorElem ());
340 items.push_back (MenuElem (_("Rescan"), mem_fun (*this, &PortMatrix::setup_all_ports)));
341 items.push_back (CheckMenuElem (_("Show individual ports"), mem_fun (*this, &PortMatrix::toggle_show_only_bundles)));
342 CheckMenuItem* i = dynamic_cast<CheckMenuItem*> (&items.back());
343 _inhibit_toggle_show_only_bundles = true;
344 i->set_active (!_show_only_bundles);
345 _inhibit_toggle_show_only_bundles = false;
352 PortMatrix::remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
354 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
359 remove_channel (ARDOUR::BundleChannel (sb, c));
364 PortMatrix::rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
366 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
371 rename_channel (ARDOUR::BundleChannel (sb, c));
375 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<ARDOUR::Bundle> bundle, uint32_t channel, int dim)
377 boost::shared_ptr<ARDOUR::Bundle> sb = bundle.lock ();
382 PortGroup::BundleList a = _ports[1-dim].bundles ();
384 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
385 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
387 ARDOUR::BundleChannel c[2];
388 c[dim] = ARDOUR::BundleChannel (sb, channel);
389 c[1-dim] = ARDOUR::BundleChannel (i->bundle, j);
391 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
392 set_state (c, false);
397 _body->rebuild_and_draw_grid ();
401 PortMatrix::setup_global_ports ()
403 for (int i = 0; i < 2; ++i) {
404 if (list_is_global (i)) {
411 PortMatrix::setup_all_ports ()
418 PortMatrix::toggle_show_only_bundles ()
420 if (_inhibit_toggle_show_only_bundles) {
424 _show_only_bundles = !_show_only_bundles;
431 PortMatrix::hide_group (boost::weak_ptr<PortGroup> w)
433 boost::shared_ptr<PortGroup> g = w.lock ();
438 g->set_visible (false);
442 PortMatrix::show_group (boost::weak_ptr<PortGroup> w)
444 boost::shared_ptr<PortGroup> g = w.lock ();
449 g->set_visible (true);
452 pair<uint32_t, uint32_t>
453 PortMatrix::max_size () const
455 pair<uint32_t, uint32_t> m = _body->max_size ();
457 m.first += _vscroll.get_width ();
458 m.second += _hscroll.get_height ();
464 PortMatrix::on_realize ()
466 Widget::on_realize ();
471 PortMatrix::on_unrealize ()
473 Widget::on_unrealize ();