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 "ardour/audioengine.h"
33 #include "port_matrix.h"
34 #include "port_matrix_body.h"
35 #include "port_matrix_component.h"
41 using namespace ARDOUR;
43 /** PortMatrix constructor.
44 * @param session Our session.
45 * @param type Port type that we are handling.
47 PortMatrix::PortMatrix (Window* parent, Session& session, DataType type)
53 _arrangement (TOP_TO_RIGHT),
56 _min_height_divisor (1),
57 _show_only_bundles (false),
58 _inhibit_toggle_show_only_bundles (false)
60 _body = new PortMatrixBody (this);
62 for (int i = 0; i < 2; ++i) {
63 _ports[i].set_type (type);
65 /* watch for the content of _ports[] changing */
66 _ports[i].Changed.connect (mem_fun (*this, &PortMatrix::setup));
69 _hscroll.signal_value_changed().connect (mem_fun (*this, &PortMatrix::hscroll_changed));
70 _vscroll.signal_value_changed().connect (mem_fun (*this, &PortMatrix::vscroll_changed));
72 /* watch for routes being added or removed */
73 _session.RouteAdded.connect (sigc::hide (mem_fun (*this, &PortMatrix::routes_changed)));
75 /* and also bundles */
76 _session.BundleAdded.connect (sigc::hide (mem_fun (*this, &PortMatrix::setup_global_ports)));
79 _session.engine().PortRegisteredOrUnregistered.connect (mem_fun (*this, &PortMatrix::setup_all_ports));
81 reconnect_to_routes ();
83 attach (*_body, 0, 1, 0, 1);
84 attach (_vscroll, 1, 2, 0, 1, SHRINK);
85 attach (_hscroll, 0, 1, 1, 2, FILL | EXPAND, SHRINK);
90 PortMatrix::~PortMatrix ()
96 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
98 PortMatrix::reconnect_to_routes ()
100 for (vector<connection>::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) {
103 _route_connections.clear ();
105 boost::shared_ptr<RouteList> routes = _session.get_routes ();
106 for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
107 _route_connections.push_back (
108 (*i)->processors_changed.connect (mem_fun (*this, &PortMatrix::setup_global_ports))
113 /** A route has been added to or removed from the session */
115 PortMatrix::routes_changed ()
117 reconnect_to_routes ();
118 setup_global_ports ();
121 /** Set up everything that depends on the content of _ports[] */
125 if ((get_flags () & Gtk::REALIZED) == 0) {
126 select_arrangement ();
137 PortMatrix::set_type (DataType t)
140 _ports[0].set_type (_type);
141 _ports[1].set_type (_type);
147 PortMatrix::hscroll_changed ()
149 _body->set_xoffset (_hscroll.get_adjustment()->get_value());
153 PortMatrix::vscroll_changed ()
155 _body->set_yoffset (_vscroll.get_adjustment()->get_value());
159 PortMatrix::setup_scrollbars ()
161 Adjustment* a = _hscroll.get_adjustment ();
163 a->set_upper (_body->full_scroll_width());
164 a->set_page_size (_body->alloc_scroll_width());
165 a->set_step_increment (32);
166 a->set_page_increment (128);
168 a = _vscroll.get_adjustment ();
170 a->set_upper (_body->full_scroll_height());
171 a->set_page_size (_body->alloc_scroll_height());
172 a->set_step_increment (32);
173 a->set_page_increment (128);
176 /** Disassociate all of our ports from each other */
178 PortMatrix::disassociate_all ()
180 PortGroup::BundleList a = _ports[0].bundles ();
181 PortGroup::BundleList b = _ports[1].bundles ();
183 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
184 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
185 for (PortGroup::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
186 for (uint32_t l = 0; l < k->bundle->nchannels(); ++l) {
188 BundleChannel c[2] = {
189 BundleChannel (i->bundle, j),
190 BundleChannel (k->bundle, l)
193 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
194 set_state (c, false);
202 _body->rebuild_and_draw_grid ();
205 /* Decide how to arrange the components of the matrix */
207 PortMatrix::select_arrangement ()
209 uint32_t const N[2] = {
210 _ports[0].total_visible_channels (),
211 _ports[1].total_visible_channels ()
214 /* The list with the most channels goes on left or right, so that the most channel
215 names are printed horizontally and hence more readable. However we also
216 maintain notional `signal flow' vaguely from left to right. Subclasses
217 should choose where to put ports based on signal flowing from _ports[0]
224 _arrangement = LEFT_TO_BOTTOM;
230 _arrangement = TOP_TO_RIGHT;
234 /** @return columns list */
235 PortGroupList const *
236 PortMatrix::columns () const
238 return &_ports[_column_index];
241 /* @return rows list */
242 PortGroupList const *
243 PortMatrix::rows () const
245 return &_ports[_row_index];
249 PortMatrix::popup_menu (
250 pair<boost::shared_ptr<PortGroup>, BundleChannel> column,
251 pair<boost::shared_ptr<PortGroup>, BundleChannel> row,
255 using namespace Menu_Helpers;
260 _menu->set_name ("ArdourContextMenu");
262 MenuList& items = _menu->items ();
264 boost::shared_ptr<PortGroup> pg[2];
265 pg[_column_index] = column.first;
266 pg[_row_index] = row.first;
269 bc[_column_index] = column.second;
270 bc[_row_index] = row.second;
273 bool need_separator = false;
275 for (int dim = 0; dim < 2; ++dim) {
277 if (bc[dim].bundle) {
279 Menu* m = manage (new Menu);
280 MenuList& sub = m->items ();
282 boost::weak_ptr<Bundle> w (bc[dim].bundle);
284 if (can_add_channel (bc[dim].bundle)) {
285 snprintf (buf, sizeof (buf), _("Add %s"), channel_noun().c_str());
286 sub.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::add_channel_proxy), w)));
290 if (can_rename_channels (bc[dim].bundle)) {
291 snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
295 bind (mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc[dim].channel)
300 sub.push_back (SeparatorElem ());
302 if (can_remove_channels (bc[dim].bundle)) {
303 snprintf (buf, sizeof (buf), _("Remove '%s'"), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
307 bind (mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc[dim].channel)
312 if (_show_only_bundles) {
313 snprintf (buf, sizeof (buf), _("%s all"), disassociation_verb().c_str());
316 buf, sizeof (buf), _("%s all from '%s'"),
317 disassociation_verb().c_str(),
318 bc[dim].bundle->channel_name (bc[dim].channel).c_str()
323 MenuElem (buf, bind (mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc[dim].channel, dim))
326 items.push_back (MenuElem (bc[dim].bundle->name().c_str(), *m));
327 need_separator = true;
332 if (need_separator) {
333 items.push_back (SeparatorElem ());
336 need_separator = false;
338 for (int dim = 0; dim < 2; ++dim) {
342 boost::weak_ptr<PortGroup> wp (pg[dim]);
344 if (pg[dim]->visible()) {
346 if (pg[dim]->name.empty()) {
347 snprintf (buf, sizeof (buf), _("Hide sources"));
349 snprintf (buf, sizeof (buf), _("Hide '%s' sources"), pg[dim]->name.c_str());
352 if (pg[dim]->name.empty()) {
353 snprintf (buf, sizeof (buf), _("Hide destinations"));
355 snprintf (buf, sizeof (buf), _("Hide '%s' destinations"), pg[dim]->name.c_str());
359 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::hide_group), wp)));
362 if (pg[dim]->name.empty()) {
363 snprintf (buf, sizeof (buf), _("Show sources"));
365 snprintf (buf, sizeof (buf), _("Show '%s' sources"), pg[dim]->name.c_str());
368 if (pg[dim]->name.empty()) {
369 snprintf (buf, sizeof (buf), _("Show destinations"));
371 snprintf (buf, sizeof (buf), _("Show '%s' destinations"), pg[dim]->name.c_str());
374 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::show_group), wp)));
377 need_separator = true;
381 if (need_separator) {
382 items.push_back (SeparatorElem ());
385 items.push_back (MenuElem (_("Rescan"), mem_fun (*this, &PortMatrix::setup_all_ports)));
386 items.push_back (CheckMenuElem (_("Show individual ports"), mem_fun (*this, &PortMatrix::toggle_show_only_bundles)));
387 CheckMenuItem* i = dynamic_cast<CheckMenuItem*> (&items.back());
388 _inhibit_toggle_show_only_bundles = true;
389 i->set_active (!_show_only_bundles);
390 _inhibit_toggle_show_only_bundles = false;
396 PortMatrix::remove_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
398 boost::shared_ptr<Bundle> sb = b.lock ();
403 remove_channel (BundleChannel (sb, c));
408 PortMatrix::rename_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
410 boost::shared_ptr<Bundle> sb = b.lock ();
415 rename_channel (BundleChannel (sb, c));
419 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<Bundle> bundle, uint32_t channel, int dim)
421 boost::shared_ptr<Bundle> sb = bundle.lock ();
426 PortGroup::BundleList a = _ports[1-dim].bundles ();
428 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
429 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
432 c[dim] = BundleChannel (sb, channel);
433 c[1-dim] = BundleChannel (i->bundle, j);
435 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
436 set_state (c, false);
441 _body->rebuild_and_draw_grid ();
445 PortMatrix::setup_global_ports ()
447 for (int i = 0; i < 2; ++i) {
448 if (list_is_global (i)) {
455 PortMatrix::setup_all_ports ()
462 PortMatrix::toggle_show_only_bundles ()
464 if (_inhibit_toggle_show_only_bundles) {
468 _show_only_bundles = !_show_only_bundles;
475 PortMatrix::hide_group (boost::weak_ptr<PortGroup> w)
477 boost::shared_ptr<PortGroup> g = w.lock ();
482 g->set_visible (false);
486 PortMatrix::show_group (boost::weak_ptr<PortGroup> w)
488 boost::shared_ptr<PortGroup> g = w.lock ();
493 g->set_visible (true);
496 pair<uint32_t, uint32_t>
497 PortMatrix::max_size () const
499 pair<uint32_t, uint32_t> m = _body->max_size ();
501 m.first += _vscroll.get_width ();
502 m.second += _hscroll.get_height ();
508 PortMatrix::on_scroll_event (GdkEventScroll* ev)
510 double const h = _hscroll.get_value ();
511 double const v = _vscroll.get_value ();
513 switch (ev->direction) {
515 _vscroll.set_value (v - PortMatrixComponent::grid_spacing ());
517 case GDK_SCROLL_DOWN:
518 _vscroll.set_value (v + PortMatrixComponent::grid_spacing ());
520 case GDK_SCROLL_LEFT:
521 _hscroll.set_value (h - PortMatrixComponent::grid_spacing ());
523 case GDK_SCROLL_RIGHT:
524 _hscroll.set_value (h + PortMatrixComponent::grid_spacing ());
531 boost::shared_ptr<IO>
532 PortMatrix::io_from_bundle (boost::shared_ptr<Bundle> b) const
534 boost::shared_ptr<IO> io = _ports[0].io_from_bundle (b);
536 io = _ports[1].io_from_bundle (b);
543 PortMatrix::can_add_channel (boost::shared_ptr<Bundle> b) const
545 return io_from_bundle (b);
549 PortMatrix::add_channel (boost::shared_ptr<Bundle> b)
551 boost::shared_ptr<IO> io = io_from_bundle (b);
554 io->add_port ("", this, _type);
559 PortMatrix::can_remove_channels (boost::shared_ptr<Bundle> b) const
561 return io_from_bundle (b);
565 PortMatrix::remove_channel (ARDOUR::BundleChannel b)
567 boost::shared_ptr<IO> io = io_from_bundle (b.bundle);
570 Port* p = io->nth (b.channel);
572 io->remove_port (p, this);
578 PortMatrix::add_channel_proxy (boost::weak_ptr<Bundle> w)
580 boost::shared_ptr<Bundle> b = w.lock ();