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 <gtkmm/window.h>
28 #include "ardour/bundle.h"
29 #include "ardour/types.h"
30 #include "ardour/session.h"
31 #include "ardour/route.h"
32 #include "port_matrix.h"
33 #include "port_matrix_body.h"
34 #include "port_matrix_component.h"
40 using namespace ARDOUR;
42 /** PortMatrix constructor.
43 * @param session Our session.
44 * @param type Port type that we are handling.
46 PortMatrix::PortMatrix (Window* parent, Session& session, DataType type)
52 _arrangement (TOP_TO_RIGHT),
55 _min_height_divisor (1),
56 _show_only_bundles (false),
57 _inhibit_toggle_show_only_bundles (false)
59 _body = new PortMatrixBody (this);
61 for (int i = 0; i < 2; ++i) {
62 _ports[i].set_type (type);
64 /* watch for the content of _ports[] changing */
65 _ports[i].Changed.connect (mem_fun (*this, &PortMatrix::setup));
68 _hscroll.signal_value_changed().connect (mem_fun (*this, &PortMatrix::hscroll_changed));
69 _vscroll.signal_value_changed().connect (mem_fun (*this, &PortMatrix::vscroll_changed));
71 /* watch for routes being added or removed */
72 _session.RouteAdded.connect (sigc::hide (mem_fun (*this, &PortMatrix::routes_changed)));
74 /* and also bundles */
75 _session.BundleAdded.connect (sigc::hide (mem_fun (*this, &PortMatrix::setup_global_ports)));
77 reconnect_to_routes ();
79 attach (*_body, 0, 1, 0, 1);
80 attach (_vscroll, 1, 2, 0, 1, SHRINK);
81 attach (_hscroll, 0, 1, 1, 2, FILL | EXPAND, SHRINK);
86 PortMatrix::~PortMatrix ()
92 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
94 PortMatrix::reconnect_to_routes ()
96 for (vector<connection>::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) {
99 _route_connections.clear ();
101 boost::shared_ptr<RouteList> routes = _session.get_routes ();
102 for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
103 _route_connections.push_back (
104 (*i)->processors_changed.connect (mem_fun (*this, &PortMatrix::setup_global_ports))
109 /** A route has been added to or removed from the session */
111 PortMatrix::routes_changed ()
113 reconnect_to_routes ();
114 setup_global_ports ();
117 /** Set up everything that depends on the content of _ports[] */
121 if ((get_flags () & Gtk::REALIZED) == 0) {
122 select_arrangement ();
133 PortMatrix::set_type (DataType t)
136 _ports[0].set_type (_type);
137 _ports[1].set_type (_type);
143 PortMatrix::hscroll_changed ()
145 _body->set_xoffset (_hscroll.get_adjustment()->get_value());
149 PortMatrix::vscroll_changed ()
151 _body->set_yoffset (_vscroll.get_adjustment()->get_value());
155 PortMatrix::setup_scrollbars ()
157 Adjustment* a = _hscroll.get_adjustment ();
159 a->set_upper (_body->full_scroll_width());
160 a->set_page_size (_body->alloc_scroll_width());
161 a->set_step_increment (32);
162 a->set_page_increment (128);
164 a = _vscroll.get_adjustment ();
166 a->set_upper (_body->full_scroll_height());
167 a->set_page_size (_body->alloc_scroll_height());
168 a->set_step_increment (32);
169 a->set_page_increment (128);
172 /** Disassociate all of our ports from each other */
174 PortMatrix::disassociate_all ()
176 PortGroup::BundleList a = _ports[0].bundles ();
177 PortGroup::BundleList b = _ports[1].bundles ();
179 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
180 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
181 for (PortGroup::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
182 for (uint32_t l = 0; l < k->bundle->nchannels(); ++l) {
184 BundleChannel c[2] = {
185 BundleChannel (i->bundle, j),
186 BundleChannel (k->bundle, l)
189 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
190 set_state (c, false);
198 _body->rebuild_and_draw_grid ();
201 /* Decide how to arrange the components of the matrix */
203 PortMatrix::select_arrangement ()
205 uint32_t const N[2] = {
206 _ports[0].total_visible_channels (),
207 _ports[1].total_visible_channels ()
210 /* The list with the most channels goes on left or right, so that the most channel
211 names are printed horizontally and hence more readable. However we also
212 maintain notional `signal flow' vaguely from left to right. Subclasses
213 should choose where to put ports based on signal flowing from _ports[0]
220 _arrangement = LEFT_TO_BOTTOM;
226 _arrangement = TOP_TO_RIGHT;
230 /** @return columns list */
231 PortGroupList const *
232 PortMatrix::columns () const
234 return &_ports[_column_index];
237 /* @return rows list */
238 PortGroupList const *
239 PortMatrix::rows () const
241 return &_ports[_row_index];
245 PortMatrix::popup_menu (
246 pair<boost::shared_ptr<PortGroup>, BundleChannel> column,
247 pair<boost::shared_ptr<PortGroup>, BundleChannel> row,
251 using namespace Menu_Helpers;
256 _menu->set_name ("ArdourContextMenu");
258 MenuList& items = _menu->items ();
260 boost::shared_ptr<PortGroup> pg[2];
261 pg[_column_index] = column.first;
262 pg[_row_index] = row.first;
265 bc[_column_index] = column.second;
266 bc[_row_index] = row.second;
271 for (int dim = 0; dim < 2; ++dim) {
273 if (bc[dim].bundle) {
275 Menu* m = manage (new Menu);
276 MenuList& sub = m->items ();
278 boost::weak_ptr<Bundle> w (bc[dim].bundle);
280 if (can_add_channel (bc[dim].bundle)) {
281 snprintf (buf, sizeof (buf), _("Add %s"), channel_noun().c_str());
282 sub.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::add_channel_proxy), w)));
286 if (can_rename_channels (bc[dim].bundle)) {
287 snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
291 bind (mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc[dim].channel)
296 sub.push_back (SeparatorElem ());
298 if (can_remove_channels (bc[dim].bundle)) {
299 snprintf (buf, sizeof (buf), _("Remove '%s'"), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
303 bind (mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc[dim].channel)
308 if (_show_only_bundles) {
309 snprintf (buf, sizeof (buf), _("%s all"), disassociation_verb().c_str());
312 buf, sizeof (buf), _("%s all from '%s'"),
313 disassociation_verb().c_str(),
314 bc[dim].bundle->channel_name (bc[dim].channel).c_str()
319 MenuElem (buf, bind (mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc[dim].channel, dim))
323 items.push_back (MenuElem (bc[dim].bundle->name().c_str(), *m));
327 items.push_back (SeparatorElem ());
329 for (int dim = 0; dim < 2; ++dim) {
333 boost::weak_ptr<PortGroup> wp (pg[dim]);
335 if (pg[dim]->visible()) {
337 snprintf (buf, sizeof (buf), _("Hide '%s' sources"), pg[dim]->name.c_str());
339 snprintf (buf, sizeof (buf), _("Hide '%s' destinations"), pg[dim]->name.c_str());
342 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::hide_group), wp)));
345 snprintf (buf, sizeof (buf), _("Show '%s' sources"), pg[dim]->name.c_str());
347 snprintf (buf, sizeof (buf), _("Show '%s' destinations"), pg[dim]->name.c_str());
349 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::show_group), wp)));
354 items.push_back (SeparatorElem ());
356 items.push_back (MenuElem (_("Rescan"), mem_fun (*this, &PortMatrix::setup_all_ports)));
357 items.push_back (CheckMenuElem (_("Show individual ports"), mem_fun (*this, &PortMatrix::toggle_show_only_bundles)));
358 CheckMenuItem* i = dynamic_cast<CheckMenuItem*> (&items.back());
359 _inhibit_toggle_show_only_bundles = true;
360 i->set_active (!_show_only_bundles);
361 _inhibit_toggle_show_only_bundles = false;
367 PortMatrix::remove_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
369 boost::shared_ptr<Bundle> sb = b.lock ();
374 remove_channel (BundleChannel (sb, c));
379 PortMatrix::rename_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
381 boost::shared_ptr<Bundle> sb = b.lock ();
386 rename_channel (BundleChannel (sb, c));
390 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<Bundle> bundle, uint32_t channel, int dim)
392 boost::shared_ptr<Bundle> sb = bundle.lock ();
397 PortGroup::BundleList a = _ports[1-dim].bundles ();
399 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
400 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
403 c[dim] = BundleChannel (sb, channel);
404 c[1-dim] = BundleChannel (i->bundle, j);
406 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
407 set_state (c, false);
412 _body->rebuild_and_draw_grid ();
416 PortMatrix::setup_global_ports ()
418 for (int i = 0; i < 2; ++i) {
419 if (list_is_global (i)) {
426 PortMatrix::setup_all_ports ()
433 PortMatrix::toggle_show_only_bundles ()
435 if (_inhibit_toggle_show_only_bundles) {
439 _show_only_bundles = !_show_only_bundles;
446 PortMatrix::hide_group (boost::weak_ptr<PortGroup> w)
448 boost::shared_ptr<PortGroup> g = w.lock ();
453 g->set_visible (false);
457 PortMatrix::show_group (boost::weak_ptr<PortGroup> w)
459 boost::shared_ptr<PortGroup> g = w.lock ();
464 g->set_visible (true);
467 pair<uint32_t, uint32_t>
468 PortMatrix::max_size () const
470 pair<uint32_t, uint32_t> m = _body->max_size ();
472 m.first += _vscroll.get_width ();
473 m.second += _hscroll.get_height ();
479 PortMatrix::setup_max_size ()
485 pair<uint32_t, uint32_t> const m = max_size ();
488 g.max_width = m.first;
489 g.max_height = m.second;
491 _parent->set_geometry_hints (*this, g, Gdk::HINT_MAX_SIZE);
495 PortMatrix::on_scroll_event (GdkEventScroll* ev)
497 double const h = _hscroll.get_value ();
498 double const v = _vscroll.get_value ();
500 switch (ev->direction) {
502 _vscroll.set_value (v - PortMatrixComponent::grid_spacing ());
504 case GDK_SCROLL_DOWN:
505 _vscroll.set_value (v + PortMatrixComponent::grid_spacing ());
507 case GDK_SCROLL_LEFT:
508 _hscroll.set_value (h - PortMatrixComponent::grid_spacing ());
510 case GDK_SCROLL_RIGHT:
511 _hscroll.set_value (h + PortMatrixComponent::grid_spacing ());
518 boost::shared_ptr<IO>
519 PortMatrix::io_from_bundle (boost::shared_ptr<Bundle> b) const
521 boost::shared_ptr<IO> io = _ports[0].io_from_bundle (b);
523 io = _ports[1].io_from_bundle (b);
530 PortMatrix::can_add_channel (boost::shared_ptr<Bundle> b) const
532 return io_from_bundle (b);
536 PortMatrix::add_channel (boost::shared_ptr<Bundle> b)
538 boost::shared_ptr<IO> io = io_from_bundle (b);
541 io->add_port ("", this, _type);
546 PortMatrix::can_remove_channels (boost::shared_ptr<Bundle> b) const
548 return io_from_bundle (b);
552 PortMatrix::remove_channel (ARDOUR::BundleChannel b)
554 boost::shared_ptr<IO> io = io_from_bundle (b.bundle);
557 Port* p = io->nth (b.channel);
559 io->remove_port (p, this);
565 PortMatrix::add_channel_proxy (boost::weak_ptr<Bundle> w)
567 boost::shared_ptr<Bundle> b = w.lock ();