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)
42 _column_visibility_box_added (false),
43 _row_visibility_box_added (false),
46 _arrangement (TOP_TO_RIGHT),
50 _body = new PortMatrixBody (this);
52 for (int i = 0; i < 2; ++i) {
53 _ports[i].set_type (type);
55 /* watch for the content of _ports[] changing */
56 _ports[i].Changed.connect (sigc::mem_fun (*this, &PortMatrix::setup));
59 _row_visibility_box.pack_start (_row_visibility_label, Gtk::PACK_SHRINK);
60 _column_visibility_box.pack_start (_column_visibility_label, Gtk::PACK_SHRINK);
62 _hscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::hscroll_changed));
63 _vscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::vscroll_changed));
65 /* watch for routes being added or removed */
66 _session.RouteAdded.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrix::routes_changed)));
68 /* and also bundles */
69 _session.BundleAdded.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrix::setup_global_ports)));
71 reconnect_to_routes ();
76 PortMatrix::~PortMatrix ()
80 for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
84 for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
91 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
93 PortMatrix::reconnect_to_routes ()
95 for (std::vector<sigc::connection>::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) {
98 _route_connections.clear ();
100 boost::shared_ptr<ARDOUR::RouteList> routes = _session.get_routes ();
101 for (ARDOUR::RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
102 _route_connections.push_back (
103 (*i)->processors_changed.connect (sigc::mem_fun (*this, &PortMatrix::setup_global_ports))
108 /** A route has been added to or removed from the session */
110 PortMatrix::routes_changed ()
112 reconnect_to_routes ();
113 setup_global_ports ();
116 /** Set up everything that depends on the content of _ports[] */
120 select_arrangement ();
127 /* we've set up before, so we need to clean up before re-setting-up */
128 /* XXX: we ought to be able to do this by just getting a list of children
129 from each container widget, but I couldn't make that work */
131 for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
132 _column_visibility_box.remove (**i);
136 _column_visibility_buttons.clear ();
138 for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
139 _row_visibility_box.remove (**i);
143 _row_visibility_buttons.clear ();
145 _scroller_table.remove (_vscroll);
146 _scroller_table.remove (*_body);
147 _scroller_table.remove (_hscroll);
149 _main_hbox.remove (_scroller_table);
150 if (_row_visibility_box_added) {
151 _main_hbox.remove (_row_visibility_box);
154 if (_column_visibility_box_added) {
155 remove (_column_visibility_box);
160 if (_column_index == 0) {
161 _column_visibility_label.set_text (_("Show Outputs"));
162 _row_visibility_label.set_text (_("Show Inputs"));
164 _column_visibility_label.set_text (_("Show Inputs"));
165 _row_visibility_label.set_text (_("Show Outputs"));
168 for (PortGroupList::List::const_iterator i = columns()->begin(); i != columns()->end(); ++i) {
169 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
170 b->set_active ((*i)->visible());
171 boost::weak_ptr<PortGroup> w (*i);
172 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
173 _column_visibility_buttons.push_back (b);
174 _column_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
177 for (PortGroupList::List::const_iterator i = rows()->begin(); i != rows()->end(); ++i) {
178 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
179 b->set_active ((*i)->visible());
180 boost::weak_ptr<PortGroup> w (*i);
181 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
182 _row_visibility_buttons.push_back (b);
183 _row_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
186 if (_arrangement == TOP_TO_RIGHT) {
188 _scroller_table.attach (_hscroll, 0, 1, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
189 _scroller_table.attach (*_body, 0, 1, 1, 2);
190 _scroller_table.attach (_vscroll, 1, 2, 1, 2, Gtk::SHRINK);
192 _main_hbox.pack_start (_scroller_table);
194 if (rows()->size() > 1) {
195 _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
196 _row_visibility_box_added = true;
198 _row_visibility_box_added = false;
201 if (columns()->size() > 1) {
202 pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
203 _column_visibility_box_added = true;
205 _column_visibility_box_added = false;
208 pack_start (_main_hbox);
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 _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
217 _row_visibility_box_added = true;
219 _row_visibility_box_added = false;
222 _main_hbox.pack_start (_scroller_table);
224 pack_start (_main_hbox);
226 if (columns()->size() > 1) {
227 pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
228 _column_visibility_box_added = true;
230 _column_visibility_box_added = false;
240 PortMatrix::set_type (ARDOUR::DataType t)
243 _ports[0].set_type (_type);
244 _ports[1].set_type (_type);
250 PortMatrix::hscroll_changed ()
252 _body->set_xoffset (_hscroll.get_adjustment()->get_value());
256 PortMatrix::vscroll_changed ()
258 _body->set_yoffset (_vscroll.get_adjustment()->get_value());
262 PortMatrix::setup_scrollbars ()
264 Gtk::Adjustment* a = _hscroll.get_adjustment ();
266 a->set_upper (_body->full_scroll_width());
267 a->set_page_size (_body->alloc_scroll_width());
268 a->set_step_increment (32);
269 a->set_page_increment (128);
271 a = _vscroll.get_adjustment ();
273 a->set_upper (_body->full_scroll_height());
274 a->set_page_size (_body->alloc_scroll_height());
275 a->set_step_increment (32);
276 a->set_page_increment (128);
279 /** Disassociate all of our ports from each other */
281 PortMatrix::disassociate_all ()
283 ARDOUR::BundleList a = _ports[0].bundles ();
284 ARDOUR::BundleList b = _ports[1].bundles ();
286 for (ARDOUR::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
287 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
288 for (ARDOUR::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
289 for (uint32_t l = 0; l < (*k)->nchannels(); ++l) {
291 ARDOUR::BundleChannel c[2] = {
292 ARDOUR::BundleChannel (*i, j),
293 ARDOUR::BundleChannel (*k, l)
296 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 set_state (c, false);
482 PortMatrix::setup_global_ports ()
484 for (int i = 0; i < 2; ++i) {
485 if (list_is_global (i)) {
493 PortMatrix::setup_all_ports ()