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"
35 /** PortMatrix constructor.
36 * @param session Our session.
37 * @param type Port type that we are handling.
39 PortMatrix::PortMatrix (ARDOUR::Session& session, ARDOUR::DataType type)
43 _column_visibility_box_added (false),
44 _row_visibility_box_added (false),
47 _arrangement (TOP_TO_RIGHT),
50 _min_height_divisor (1),
51 _show_only_bundles (false)
53 _body = new PortMatrixBody (this);
55 for (int i = 0; i < 2; ++i) {
56 _ports[i].set_type (type);
58 /* watch for the content of _ports[] changing */
59 _ports[i].Changed.connect (sigc::mem_fun (*this, &PortMatrix::setup));
62 _row_visibility_box.pack_start (_row_visibility_label, Gtk::PACK_SHRINK);
63 _column_visibility_box.pack_start (_column_visibility_label, Gtk::PACK_SHRINK);
65 _hscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::hscroll_changed));
66 _vscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::vscroll_changed));
68 /* watch for routes being added or removed */
69 _session.RouteAdded.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrix::routes_changed)));
71 /* and also bundles */
72 _session.BundleAdded.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrix::setup_global_ports)));
74 reconnect_to_routes ();
79 PortMatrix::~PortMatrix ()
83 for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
87 for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
94 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
96 PortMatrix::reconnect_to_routes ()
98 for (std::vector<sigc::connection>::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) {
101 _route_connections.clear ();
103 boost::shared_ptr<ARDOUR::RouteList> routes = _session.get_routes ();
104 for (ARDOUR::RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
105 _route_connections.push_back (
106 (*i)->processors_changed.connect (sigc::mem_fun (*this, &PortMatrix::setup_global_ports))
111 /** A route has been added to or removed from the session */
113 PortMatrix::routes_changed ()
115 reconnect_to_routes ();
116 setup_global_ports ();
119 /** Set up everything that depends on the content of _ports[] */
123 select_arrangement ();
130 /* we've set up before, so we need to clean up before re-setting-up */
131 /* XXX: we ought to be able to do this by just getting a list of children
132 from each container widget, but I couldn't make that work */
134 for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
135 _column_visibility_box.remove (**i);
139 _column_visibility_buttons.clear ();
141 for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
142 _row_visibility_box.remove (**i);
146 _row_visibility_buttons.clear ();
148 _scroller_table.remove (_vscroll);
149 _scroller_table.remove (*_body);
150 _scroller_table.remove (_hscroll);
152 remove (_scroller_table);
153 if (_row_visibility_box_added) {
154 remove (_row_visibility_box);
157 if (_column_visibility_box_added) {
158 remove (_column_visibility_box);
162 if (_column_index == 0) {
163 _column_visibility_label.set_text (_("Show Outputs"));
164 _row_visibility_label.set_text (_("Show Inputs"));
166 _column_visibility_label.set_text (_("Show Inputs"));
167 _row_visibility_label.set_text (_("Show Outputs"));
170 for (PortGroupList::List::const_iterator i = columns()->begin(); i != columns()->end(); ++i) {
171 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
172 b->set_active ((*i)->visible());
173 boost::weak_ptr<PortGroup> w (*i);
174 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
175 _column_visibility_buttons.push_back (b);
176 _column_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
179 for (PortGroupList::List::const_iterator i = rows()->begin(); i != rows()->end(); ++i) {
180 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
181 b->set_active ((*i)->visible());
182 boost::weak_ptr<PortGroup> w (*i);
183 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
184 _row_visibility_buttons.push_back (b);
185 _row_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
188 if (_arrangement == TOP_TO_RIGHT) {
190 _scroller_table.attach (_hscroll, 0, 1, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
191 _scroller_table.attach (*_body, 0, 1, 1, 2);
192 _scroller_table.attach (_vscroll, 1, 2, 1, 2, Gtk::SHRINK);
194 attach (_scroller_table, 0, 1, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::FILL | Gtk::EXPAND);
196 if (rows()->size() > 1) {
197 attach (_row_visibility_box, 1, 2, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
198 _row_visibility_box_added = true;
200 _row_visibility_box_added = false;
203 if (columns()->size() > 1) {
204 attach (_column_visibility_box, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
205 _column_visibility_box_added = true;
207 _column_visibility_box_added = false;
211 _scroller_table.attach (_vscroll, 0, 1, 0, 1, Gtk::SHRINK);
212 _scroller_table.attach (*_body, 1, 2, 0, 1);
213 _scroller_table.attach (_hscroll, 1, 2, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
215 if (rows()->size() > 1) {
216 attach (_row_visibility_box, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
217 _row_visibility_box_added = true;
219 _row_visibility_box_added = false;
222 attach (_scroller_table, 1, 2, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL | Gtk::EXPAND);
224 if (columns()->size() > 1) {
225 attach (_column_visibility_box, 1, 2, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
226 _column_visibility_box_added = true;
228 _column_visibility_box_added = false;
238 PortMatrix::set_type (ARDOUR::DataType t)
241 _ports[0].set_type (_type);
242 _ports[1].set_type (_type);
248 PortMatrix::hscroll_changed ()
250 _body->set_xoffset (_hscroll.get_adjustment()->get_value());
254 PortMatrix::vscroll_changed ()
256 _body->set_yoffset (_vscroll.get_adjustment()->get_value());
260 PortMatrix::setup_scrollbars ()
262 Gtk::Adjustment* a = _hscroll.get_adjustment ();
264 a->set_upper (_body->full_scroll_width());
265 a->set_page_size (_body->alloc_scroll_width());
266 a->set_step_increment (32);
267 a->set_page_increment (128);
269 a = _vscroll.get_adjustment ();
271 a->set_upper (_body->full_scroll_height());
272 a->set_page_size (_body->alloc_scroll_height());
273 a->set_step_increment (32);
274 a->set_page_increment (128);
277 /** Disassociate all of our ports from each other */
279 PortMatrix::disassociate_all ()
281 ARDOUR::BundleList a = _ports[0].bundles ();
282 ARDOUR::BundleList b = _ports[1].bundles ();
284 for (ARDOUR::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
285 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
286 for (ARDOUR::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
287 for (uint32_t l = 0; l < (*k)->nchannels(); ++l) {
289 ARDOUR::BundleChannel c[2] = {
290 ARDOUR::BundleChannel (*i, j),
291 ARDOUR::BundleChannel (*k, l)
294 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
295 set_state (c, false);
303 _body->rebuild_and_draw_grid ();
306 /* Decide how to arrange the components of the matrix */
308 PortMatrix::select_arrangement ()
310 uint32_t const N[2] = {
311 _ports[0].total_visible_channels (),
312 _ports[1].total_visible_channels ()
315 /* The list with the most channels goes on left or right, so that the most channel
316 names are printed horizontally and hence more readable. However we also
317 maintain notional `signal flow' vaguely from left to right. Subclasses
318 should choose where to put ports based on signal flowing from _ports[0]
325 _arrangement = LEFT_TO_BOTTOM;
331 _arrangement = TOP_TO_RIGHT;
335 /** @return columns list */
336 PortGroupList const *
337 PortMatrix::columns () const
339 return &_ports[_column_index];
342 /* @return rows list */
343 PortGroupList const *
344 PortMatrix::rows () const
346 return &_ports[_row_index];
349 /** A group visibility checkbutton has been toggled.
354 PortMatrix::visibility_toggled (boost::weak_ptr<PortGroup> w, Gtk::CheckButton* b)
356 boost::shared_ptr<PortGroup> g = w.lock ();
361 g->set_visible (b->get_active());
368 PortMatrix::popup_channel_context_menu (int dim, uint32_t N, uint32_t t)
372 _menu = new Gtk::Menu;
373 _menu->set_name ("ArdourContextMenu");
375 Gtk::Menu_Helpers::MenuList& items = _menu->items ();
377 ARDOUR::BundleChannel bc;
379 ARDOUR::BundleList const r = _ports[dim].bundles();
380 for (ARDOUR::BundleList::const_iterator i = r.begin(); i != r.end(); ++i) {
381 if (N < (*i)->nchannels ()) {
382 bc = ARDOUR::BundleChannel (*i, N);
385 N -= (*i)->nchannels ();
391 bool have_one = false;
393 if (can_rename_channels (dim)) {
394 snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc.bundle->channel_name (bc.channel).c_str());
395 boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
397 Gtk::Menu_Helpers::MenuElem (
399 sigc::bind (sigc::mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc.channel)
406 if (can_remove_channels (dim)) {
407 snprintf (buf, sizeof (buf), _("Remove '%s'"), bc.bundle->channel_name (bc.channel).c_str());
408 boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
410 Gtk::Menu_Helpers::MenuElem (
412 sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc.channel)
420 items.push_back (Gtk::Menu_Helpers::SeparatorElem ());
423 boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
424 items.push_back (Gtk::Menu_Helpers::MenuElem (
425 _("Disassociate all"),
426 sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc.channel, dim)
437 PortMatrix::remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
439 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
444 remove_channel (ARDOUR::BundleChannel (sb, c));
449 PortMatrix::rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
451 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
456 rename_channel (ARDOUR::BundleChannel (sb, c));
460 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<ARDOUR::Bundle> bundle, uint32_t channel, int dim)
462 boost::shared_ptr<ARDOUR::Bundle> sb = bundle.lock ();
467 ARDOUR::BundleList a = _ports[1-dim].bundles ();
469 for (ARDOUR::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
470 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
472 ARDOUR::BundleChannel c[2];
473 c[dim] = ARDOUR::BundleChannel (sb, channel);
474 c[1-dim] = ARDOUR::BundleChannel (*i, j);
476 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
477 set_state (c, false);
482 _body->rebuild_and_draw_grid ();
486 PortMatrix::setup_global_ports ()
488 for (int i = 0; i < 2; ++i) {
489 if (list_is_global (i)) {
496 PortMatrix::setup_all_ports ()
503 PortMatrix::set_show_only_bundles (bool s)
505 _show_only_bundles = s;