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"
33 #include "port_matrix_component.h"
40 /** PortMatrix constructor.
41 * @param session Our session.
42 * @param type Port type that we are handling.
44 PortMatrix::PortMatrix (ARDOUR::Session& session, ARDOUR::DataType type)
49 _arrangement (TOP_TO_RIGHT),
52 _min_height_divisor (1),
53 _show_only_bundles (false),
54 _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[] */
118 if ((get_flags () & Gtk::REALIZED) == 0) {
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 std::string const n = add_channel_name ();
269 snprintf (buf, sizeof (buf), _("Add %s to '%s'"), channel_noun().c_str(), n.c_str());
270 items.push_back (MenuElem (buf, mem_fun (*this, &PortMatrix::add_channel)));
273 for (int dim = 0; dim < 2; ++dim) {
277 boost::weak_ptr<PortGroup> wp (pg[dim]);
279 if (pg[dim]->visible()) {
281 snprintf (buf, sizeof (buf), _("Hide '%s' sources"), pg[dim]->name.c_str());
283 snprintf (buf, sizeof (buf), _("Hide '%s' destinations"), pg[dim]->name.c_str());
286 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::hide_group), wp)));
289 snprintf (buf, sizeof (buf), _("Show '%s' sources"), pg[dim]->name.c_str());
291 snprintf (buf, sizeof (buf), _("Show '%s' destinations"), pg[dim]->name.c_str());
293 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::show_group), wp)));
297 if (bc[dim].bundle) {
298 boost::weak_ptr<ARDOUR::Bundle> w (bc[dim].bundle);
300 if (can_remove_channels (dim)) {
301 snprintf (buf, sizeof (buf), _("Remove '%s'"), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
305 bind (mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc[dim].channel)
310 if (can_rename_channels (dim)) {
311 snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
315 bind (mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc[dim].channel)
320 if (_show_only_bundles) {
321 snprintf (buf, sizeof (buf), _("%s all from '%s'"), disassociation_verb().c_str(), bc[dim].bundle->name().c_str());
324 buf, sizeof (buf), _("%s all from '%s/%s'"),
325 disassociation_verb().c_str(),
326 bc[dim].bundle->name().c_str(),
327 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;
351 PortMatrix::remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
353 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
358 remove_channel (ARDOUR::BundleChannel (sb, c));
363 PortMatrix::rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
365 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
370 rename_channel (ARDOUR::BundleChannel (sb, c));
374 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<ARDOUR::Bundle> bundle, uint32_t channel, int dim)
376 boost::shared_ptr<ARDOUR::Bundle> sb = bundle.lock ();
381 PortGroup::BundleList a = _ports[1-dim].bundles ();
383 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
384 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
386 ARDOUR::BundleChannel c[2];
387 c[dim] = ARDOUR::BundleChannel (sb, channel);
388 c[1-dim] = ARDOUR::BundleChannel (i->bundle, j);
390 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
391 set_state (c, false);
396 _body->rebuild_and_draw_grid ();
400 PortMatrix::setup_global_ports ()
402 for (int i = 0; i < 2; ++i) {
403 if (list_is_global (i)) {
410 PortMatrix::setup_all_ports ()
417 PortMatrix::toggle_show_only_bundles ()
419 if (_inhibit_toggle_show_only_bundles) {
423 _show_only_bundles = !_show_only_bundles;
430 PortMatrix::hide_group (boost::weak_ptr<PortGroup> w)
432 boost::shared_ptr<PortGroup> g = w.lock ();
437 g->set_visible (false);
441 PortMatrix::show_group (boost::weak_ptr<PortGroup> w)
443 boost::shared_ptr<PortGroup> g = w.lock ();
448 g->set_visible (true);
451 pair<uint32_t, uint32_t>
452 PortMatrix::max_size () const
454 pair<uint32_t, uint32_t> m = _body->max_size ();
456 m.first += _vscroll.get_width ();
457 m.second += _hscroll.get_height ();
463 PortMatrix::setup_max_size ()
469 PortMatrix::on_scroll_event (GdkEventScroll* ev)
471 double const h = _hscroll.get_value ();
472 double const v = _vscroll.get_value ();
474 switch (ev->direction) {
476 _vscroll.set_value (v - PortMatrixComponent::grid_spacing ());
478 case GDK_SCROLL_DOWN:
479 _vscroll.set_value (v + PortMatrixComponent::grid_spacing ());
481 case GDK_SCROLL_LEFT:
482 _hscroll.set_value (h - PortMatrixComponent::grid_spacing ());
484 case GDK_SCROLL_RIGHT:
485 _hscroll.set_value (h + PortMatrixComponent::grid_spacing ());