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"
37 #include "gui_thread.h"
42 using namespace ARDOUR;
44 /** PortMatrix constructor.
45 * @param session Our session.
46 * @param type Port type that we are handling.
48 PortMatrix::PortMatrix (Window* parent, Session& session, DataType type)
54 _arrangement (TOP_TO_RIGHT),
57 _min_height_divisor (1),
58 _show_only_bundles (false),
59 _inhibit_toggle_show_only_bundles (false)
61 _body = new PortMatrixBody (this);
63 for (int i = 0; i < 2; ++i) {
64 _ports[i].set_type (type);
66 /* watch for the content of _ports[] changing */
67 _ports[i].Changed.connect (mem_fun (*this, &PortMatrix::setup));
69 /* and for bundles in _ports[] changing */
70 _ports[i].BundleChanged.connect (mem_fun (*this, &PortMatrix::bundle_changed));
73 _hscroll.signal_value_changed().connect (mem_fun (*this, &PortMatrix::hscroll_changed));
74 _vscroll.signal_value_changed().connect (mem_fun (*this, &PortMatrix::vscroll_changed));
76 /* watch for routes being added or removed */
77 _session.RouteAdded.connect (sigc::hide (mem_fun (*this, &PortMatrix::routes_changed)));
79 /* and also bundles */
80 _session.BundleAdded.connect (sigc::hide (mem_fun (*this, &PortMatrix::setup_global_ports)));
83 _session.engine().PortRegisteredOrUnregistered.connect (mem_fun (*this, &PortMatrix::setup_all_ports));
85 reconnect_to_routes ();
87 attach (*_body, 0, 1, 0, 1);
88 attach (_vscroll, 1, 2, 0, 1, SHRINK);
89 attach (_hscroll, 0, 1, 1, 2, FILL | EXPAND, SHRINK);
94 PortMatrix::~PortMatrix ()
100 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
102 PortMatrix::reconnect_to_routes ()
104 for (vector<connection>::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) {
107 _route_connections.clear ();
109 boost::shared_ptr<RouteList> routes = _session.get_routes ();
110 for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
111 _route_connections.push_back (
112 (*i)->processors_changed.connect (mem_fun (*this, &PortMatrix::setup_global_ports))
117 /** A route has been added to or removed from the session */
119 PortMatrix::routes_changed ()
121 reconnect_to_routes ();
122 setup_global_ports ();
125 /** Set up everything that depends on the content of _ports[] */
129 if ((get_flags () & Gtk::REALIZED) == 0) {
130 select_arrangement ();
141 PortMatrix::set_type (DataType t)
144 _ports[0].set_type (_type);
145 _ports[1].set_type (_type);
151 PortMatrix::hscroll_changed ()
153 _body->set_xoffset (_hscroll.get_adjustment()->get_value());
157 PortMatrix::vscroll_changed ()
159 _body->set_yoffset (_vscroll.get_adjustment()->get_value());
163 PortMatrix::setup_scrollbars ()
165 Adjustment* a = _hscroll.get_adjustment ();
167 a->set_upper (_body->full_scroll_width());
168 a->set_page_size (_body->alloc_scroll_width());
169 a->set_step_increment (32);
170 a->set_page_increment (128);
172 a = _vscroll.get_adjustment ();
174 a->set_upper (_body->full_scroll_height());
175 a->set_page_size (_body->alloc_scroll_height());
176 a->set_step_increment (32);
177 a->set_page_increment (128);
180 /** Disassociate all of our ports from each other */
182 PortMatrix::disassociate_all ()
184 PortGroup::BundleList a = _ports[0].bundles ();
185 PortGroup::BundleList b = _ports[1].bundles ();
187 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
188 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
189 for (PortGroup::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
190 for (uint32_t l = 0; l < k->bundle->nchannels(); ++l) {
192 BundleChannel c[2] = {
193 BundleChannel (i->bundle, j),
194 BundleChannel (k->bundle, l)
197 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
198 set_state (c, false);
206 _body->rebuild_and_draw_grid ();
209 /* Decide how to arrange the components of the matrix */
211 PortMatrix::select_arrangement ()
213 uint32_t const N[2] = {
214 _ports[0].total_visible_channels (),
215 _ports[1].total_visible_channels ()
218 /* The list with the most channels goes on left or right, so that the most channel
219 names are printed horizontally and hence more readable. However we also
220 maintain notional `signal flow' vaguely from left to right. Subclasses
221 should choose where to put ports based on signal flowing from _ports[0]
228 _arrangement = LEFT_TO_BOTTOM;
234 _arrangement = TOP_TO_RIGHT;
238 /** @return columns list */
239 PortGroupList const *
240 PortMatrix::columns () const
242 return &_ports[_column_index];
245 /* @return rows list */
246 PortGroupList const *
247 PortMatrix::rows () const
249 return &_ports[_row_index];
253 PortMatrix::popup_menu (
254 pair<boost::shared_ptr<PortGroup>, BundleChannel> column,
255 pair<boost::shared_ptr<PortGroup>, BundleChannel> row,
259 using namespace Menu_Helpers;
264 _menu->set_name ("ArdourContextMenu");
266 MenuList& items = _menu->items ();
268 boost::shared_ptr<PortGroup> pg[2];
269 pg[_column_index] = column.first;
270 pg[_row_index] = row.first;
273 bc[_column_index] = column.second;
274 bc[_row_index] = row.second;
277 bool need_separator = false;
279 for (int dim = 0; dim < 2; ++dim) {
281 if (bc[dim].bundle) {
283 Menu* m = manage (new Menu);
284 MenuList& sub = m->items ();
286 boost::weak_ptr<Bundle> w (bc[dim].bundle);
288 bool can_add_or_rename = false;
290 if (can_add_channel (bc[dim].bundle)) {
291 snprintf (buf, sizeof (buf), _("Add %s"), channel_noun().c_str());
292 sub.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::add_channel_proxy), w)));
293 can_add_or_rename = true;
297 if (can_rename_channels (bc[dim].bundle)) {
298 snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
302 bind (mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc[dim].channel)
305 can_add_or_rename = true;
308 if (can_add_or_rename) {
309 sub.push_back (SeparatorElem ());
312 if (can_remove_channels (bc[dim].bundle)) {
313 snprintf (buf, sizeof (buf), _("Remove '%s'"), bc[dim].bundle->channel_name (bc[dim].channel).c_str());
317 bind (mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc[dim].channel)
322 if (_show_only_bundles || bc[dim].bundle->nchannels() <= 1) {
323 snprintf (buf, sizeof (buf), _("%s all"), disassociation_verb().c_str());
326 buf, sizeof (buf), _("%s all from '%s'"),
327 disassociation_verb().c_str(),
328 bc[dim].bundle->channel_name (bc[dim].channel).c_str()
333 MenuElem (buf, bind (mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc[dim].channel, dim))
336 items.push_back (MenuElem (bc[dim].bundle->name().c_str(), *m));
337 need_separator = true;
342 if (need_separator) {
343 items.push_back (SeparatorElem ());
346 need_separator = false;
348 for (int dim = 0; dim < 2; ++dim) {
352 boost::weak_ptr<PortGroup> wp (pg[dim]);
354 if (pg[dim]->visible()) {
356 if (pg[dim]->name.empty()) {
357 snprintf (buf, sizeof (buf), _("Hide sources"));
359 snprintf (buf, sizeof (buf), _("Hide '%s' sources"), pg[dim]->name.c_str());
362 if (pg[dim]->name.empty()) {
363 snprintf (buf, sizeof (buf), _("Hide destinations"));
365 snprintf (buf, sizeof (buf), _("Hide '%s' destinations"), pg[dim]->name.c_str());
369 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::hide_group), wp)));
372 if (pg[dim]->name.empty()) {
373 snprintf (buf, sizeof (buf), _("Show sources"));
375 snprintf (buf, sizeof (buf), _("Show '%s' sources"), pg[dim]->name.c_str());
378 if (pg[dim]->name.empty()) {
379 snprintf (buf, sizeof (buf), _("Show destinations"));
381 snprintf (buf, sizeof (buf), _("Show '%s' destinations"), pg[dim]->name.c_str());
384 items.push_back (MenuElem (buf, bind (mem_fun (*this, &PortMatrix::show_group), wp)));
387 need_separator = true;
391 if (need_separator) {
392 items.push_back (SeparatorElem ());
395 items.push_back (MenuElem (_("Rescan"), mem_fun (*this, &PortMatrix::setup_all_ports)));
396 items.push_back (CheckMenuElem (_("Show individual ports"), mem_fun (*this, &PortMatrix::toggle_show_only_bundles)));
397 CheckMenuItem* i = dynamic_cast<CheckMenuItem*> (&items.back());
398 _inhibit_toggle_show_only_bundles = true;
399 i->set_active (!_show_only_bundles);
400 _inhibit_toggle_show_only_bundles = false;
406 PortMatrix::remove_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
408 boost::shared_ptr<Bundle> sb = b.lock ();
413 remove_channel (BundleChannel (sb, c));
418 PortMatrix::rename_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
420 boost::shared_ptr<Bundle> sb = b.lock ();
425 rename_channel (BundleChannel (sb, c));
429 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<Bundle> bundle, uint32_t channel, int dim)
431 boost::shared_ptr<Bundle> sb = bundle.lock ();
436 PortGroup::BundleList a = _ports[1-dim].bundles ();
438 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
439 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
442 c[dim] = BundleChannel (sb, channel);
443 c[1-dim] = BundleChannel (i->bundle, j);
445 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
446 set_state (c, false);
451 _body->rebuild_and_draw_grid ();
455 PortMatrix::setup_global_ports ()
457 for (int i = 0; i < 2; ++i) {
458 if (list_is_global (i)) {
465 PortMatrix::setup_all_ports ()
467 ENSURE_GUI_THREAD (mem_fun (*this, &PortMatrix::setup_all_ports));
474 PortMatrix::toggle_show_only_bundles ()
476 if (_inhibit_toggle_show_only_bundles) {
480 _show_only_bundles = !_show_only_bundles;
487 PortMatrix::hide_group (boost::weak_ptr<PortGroup> w)
489 boost::shared_ptr<PortGroup> g = w.lock ();
494 g->set_visible (false);
498 PortMatrix::show_group (boost::weak_ptr<PortGroup> w)
500 boost::shared_ptr<PortGroup> g = w.lock ();
505 g->set_visible (true);
508 pair<uint32_t, uint32_t>
509 PortMatrix::max_size () const
511 pair<uint32_t, uint32_t> m = _body->max_size ();
513 m.first += _vscroll.get_width ();
514 m.second += _hscroll.get_height ();
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 ();
601 PortMatrix::bundle_changed (ARDOUR::Bundle::Change c)
603 if (c != Bundle::NameChanged) {