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;
269 bool need_separator = false;
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))
322 items.push_back (MenuElem (bc[dim].bundle->name().c_str(), *m));
323 need_separator = true;
328 if (need_separator) {
329 items.push_back (SeparatorElem ());
332 need_separator = false;
334 for (int dim = 0; dim < 2; ++dim) {
338 boost::weak_ptr<PortGroup> wp (pg[dim]);
340 if (pg[dim]->visible()) {
342 if (pg[dim]->name.empty()) {
343 snprintf (buf, sizeof (buf), _("Hide sources"));
345 snprintf (buf, sizeof (buf), _("Hide '%s' sources"), pg[dim]->name.c_str());
348 if (pg[dim]->name.empty()) {
349 snprintf (buf, sizeof (buf), _("Hide destinations"));
351 snprintf (buf, sizeof (buf), _("Hide '%s' destinations"), pg[dim]->name.c_str());
355 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::hide_group), wp)));
358 if (pg[dim]->name.empty()) {
359 snprintf (buf, sizeof (buf), _("Show sources"));
361 snprintf (buf, sizeof (buf), _("Show '%s' sources"), pg[dim]->name.c_str());
364 if (pg[dim]->name.empty()) {
365 snprintf (buf, sizeof (buf), _("Show destinations"));
367 snprintf (buf, sizeof (buf), _("Show '%s' destinations"), pg[dim]->name.c_str());
370 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::show_group), wp)));
373 need_separator = true;
377 if (need_separator) {
378 items.push_back (SeparatorElem ());
381 items.push_back (MenuElem (_("Rescan"), mem_fun (*this, &PortMatrix::setup_all_ports)));
382 items.push_back (CheckMenuElem (_("Show individual ports"), mem_fun (*this, &PortMatrix::toggle_show_only_bundles)));
383 CheckMenuItem* i = dynamic_cast<CheckMenuItem*> (&items.back());
384 _inhibit_toggle_show_only_bundles = true;
385 i->set_active (!_show_only_bundles);
386 _inhibit_toggle_show_only_bundles = false;
392 PortMatrix::remove_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
394 boost::shared_ptr<Bundle> sb = b.lock ();
399 remove_channel (BundleChannel (sb, c));
404 PortMatrix::rename_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
406 boost::shared_ptr<Bundle> sb = b.lock ();
411 rename_channel (BundleChannel (sb, c));
415 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<Bundle> bundle, uint32_t channel, int dim)
417 boost::shared_ptr<Bundle> sb = bundle.lock ();
422 PortGroup::BundleList a = _ports[1-dim].bundles ();
424 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
425 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
428 c[dim] = BundleChannel (sb, channel);
429 c[1-dim] = BundleChannel (i->bundle, j);
431 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
432 set_state (c, false);
437 _body->rebuild_and_draw_grid ();
441 PortMatrix::setup_global_ports ()
443 for (int i = 0; i < 2; ++i) {
444 if (list_is_global (i)) {
451 PortMatrix::setup_all_ports ()
458 PortMatrix::toggle_show_only_bundles ()
460 if (_inhibit_toggle_show_only_bundles) {
464 _show_only_bundles = !_show_only_bundles;
471 PortMatrix::hide_group (boost::weak_ptr<PortGroup> w)
473 boost::shared_ptr<PortGroup> g = w.lock ();
478 g->set_visible (false);
482 PortMatrix::show_group (boost::weak_ptr<PortGroup> w)
484 boost::shared_ptr<PortGroup> g = w.lock ();
489 g->set_visible (true);
492 pair<uint32_t, uint32_t>
493 PortMatrix::max_size () const
495 pair<uint32_t, uint32_t> m = _body->max_size ();
497 m.first += _vscroll.get_width ();
498 m.second += _hscroll.get_height ();
504 PortMatrix::setup_max_size ()
510 pair<uint32_t, uint32_t> const m = max_size ();
513 g.max_width = m.first;
514 g.max_height = m.second;
516 _parent->set_geometry_hints (*this, g, Gdk::HINT_MAX_SIZE);
520 PortMatrix::on_scroll_event (GdkEventScroll* ev)
522 double const h = _hscroll.get_value ();
523 double const v = _vscroll.get_value ();
525 switch (ev->direction) {
527 _vscroll.set_value (v - PortMatrixComponent::grid_spacing ());
529 case GDK_SCROLL_DOWN:
530 _vscroll.set_value (v + PortMatrixComponent::grid_spacing ());
532 case GDK_SCROLL_LEFT:
533 _hscroll.set_value (h - PortMatrixComponent::grid_spacing ());
535 case GDK_SCROLL_RIGHT:
536 _hscroll.set_value (h + PortMatrixComponent::grid_spacing ());
543 boost::shared_ptr<IO>
544 PortMatrix::io_from_bundle (boost::shared_ptr<Bundle> b) const
546 boost::shared_ptr<IO> io = _ports[0].io_from_bundle (b);
548 io = _ports[1].io_from_bundle (b);
555 PortMatrix::can_add_channel (boost::shared_ptr<Bundle> b) const
557 return io_from_bundle (b);
561 PortMatrix::add_channel (boost::shared_ptr<Bundle> b)
563 boost::shared_ptr<IO> io = io_from_bundle (b);
566 io->add_port ("", this, _type);
571 PortMatrix::can_remove_channels (boost::shared_ptr<Bundle> b) const
573 return io_from_bundle (b);
577 PortMatrix::remove_channel (ARDOUR::BundleChannel b)
579 boost::shared_ptr<IO> io = io_from_bundle (b.bundle);
582 Port* p = io->nth (b.channel);
584 io->remove_port (p, this);
590 PortMatrix::add_channel_proxy (boost::weak_ptr<Bundle> w)
592 boost::shared_ptr<Bundle> b = w.lock ();