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),
49 _min_height_divisor (1)
51 _body = new PortMatrixBody (this);
53 for (int i = 0; i < 2; ++i) {
54 _ports[i].set_type (type);
56 /* watch for the content of _ports[] changing */
57 _ports[i].Changed.connect (sigc::mem_fun (*this, &PortMatrix::setup));
60 _row_visibility_box.pack_start (_row_visibility_label, Gtk::PACK_SHRINK);
61 _column_visibility_box.pack_start (_column_visibility_label, Gtk::PACK_SHRINK);
63 _hscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::hscroll_changed));
64 _vscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::vscroll_changed));
66 /* watch for routes being added or removed */
67 _session.RouteAdded.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrix::routes_changed)));
69 /* and also bundles */
70 _session.BundleAdded.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrix::setup_global_ports)));
72 reconnect_to_routes ();
77 PortMatrix::~PortMatrix ()
81 for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
85 for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
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 (std::vector<sigc::connection>::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) {
99 _route_connections.clear ();
101 boost::shared_ptr<ARDOUR::RouteList> routes = _session.get_routes ();
102 for (ARDOUR::RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
103 _route_connections.push_back (
104 (*i)->processors_changed.connect (sigc::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 select_arrangement ();
128 /* we've set up before, so we need to clean up before re-setting-up */
129 /* XXX: we ought to be able to do this by just getting a list of children
130 from each container widget, but I couldn't make that work */
132 for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
133 _column_visibility_box.remove (**i);
137 _column_visibility_buttons.clear ();
139 for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
140 _row_visibility_box.remove (**i);
144 _row_visibility_buttons.clear ();
146 _scroller_table.remove (_vscroll);
147 _scroller_table.remove (*_body);
148 _scroller_table.remove (_hscroll);
150 _main_hbox.remove (_scroller_table);
151 if (_row_visibility_box_added) {
152 _main_hbox.remove (_row_visibility_box);
155 if (_column_visibility_box_added) {
156 remove (_column_visibility_box);
161 if (_column_index == 0) {
162 _column_visibility_label.set_text (_("Show Outputs"));
163 _row_visibility_label.set_text (_("Show Inputs"));
165 _column_visibility_label.set_text (_("Show Inputs"));
166 _row_visibility_label.set_text (_("Show Outputs"));
169 for (PortGroupList::List::const_iterator i = columns()->begin(); i != columns()->end(); ++i) {
170 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
171 b->set_active ((*i)->visible());
172 boost::weak_ptr<PortGroup> w (*i);
173 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
174 _column_visibility_buttons.push_back (b);
175 _column_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
178 for (PortGroupList::List::const_iterator i = rows()->begin(); i != rows()->end(); ++i) {
179 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
180 b->set_active ((*i)->visible());
181 boost::weak_ptr<PortGroup> w (*i);
182 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
183 _row_visibility_buttons.push_back (b);
184 _row_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
187 if (_arrangement == TOP_TO_RIGHT) {
189 _scroller_table.attach (_hscroll, 0, 1, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
190 _scroller_table.attach (*_body, 0, 1, 1, 2);
191 _scroller_table.attach (_vscroll, 1, 2, 1, 2, Gtk::SHRINK);
193 _main_hbox.pack_start (_scroller_table);
195 if (rows()->size() > 1) {
196 _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
197 _row_visibility_box_added = true;
199 _row_visibility_box_added = false;
202 if (columns()->size() > 1) {
203 pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
204 _column_visibility_box_added = true;
206 _column_visibility_box_added = false;
209 pack_start (_main_hbox);
212 _scroller_table.attach (_vscroll, 0, 1, 0, 1, Gtk::SHRINK);
213 _scroller_table.attach (*_body, 1, 2, 0, 1);
214 _scroller_table.attach (_hscroll, 1, 2, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
216 if (rows()->size() > 1) {
217 _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
218 _row_visibility_box_added = true;
220 _row_visibility_box_added = false;
223 _main_hbox.pack_start (_scroller_table);
225 pack_start (_main_hbox);
227 if (columns()->size() > 1) {
228 pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
229 _column_visibility_box_added = true;
231 _column_visibility_box_added = false;
241 PortMatrix::set_type (ARDOUR::DataType t)
244 _ports[0].set_type (_type);
245 _ports[1].set_type (_type);
251 PortMatrix::hscroll_changed ()
253 _body->set_xoffset (_hscroll.get_adjustment()->get_value());
257 PortMatrix::vscroll_changed ()
259 _body->set_yoffset (_vscroll.get_adjustment()->get_value());
263 PortMatrix::setup_scrollbars ()
265 Gtk::Adjustment* a = _hscroll.get_adjustment ();
267 a->set_upper (_body->full_scroll_width());
268 a->set_page_size (_body->alloc_scroll_width());
269 a->set_step_increment (32);
270 a->set_page_increment (128);
272 a = _vscroll.get_adjustment ();
274 a->set_upper (_body->full_scroll_height());
275 a->set_page_size (_body->alloc_scroll_height());
276 a->set_step_increment (32);
277 a->set_page_increment (128);
280 /** Disassociate all of our ports from each other */
282 PortMatrix::disassociate_all ()
284 ARDOUR::BundleList a = _ports[0].bundles ();
285 ARDOUR::BundleList b = _ports[1].bundles ();
287 for (ARDOUR::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
288 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
289 for (ARDOUR::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
290 for (uint32_t l = 0; l < (*k)->nchannels(); ++l) {
292 ARDOUR::BundleChannel c[2] = {
293 ARDOUR::BundleChannel (*i, j),
294 ARDOUR::BundleChannel (*k, l)
297 if (get_state (c) == ASSOCIATED) {
298 set_state (c, false);
306 _body->rebuild_and_draw_grid ();
309 /* Decide how to arrange the components of the matrix */
311 PortMatrix::select_arrangement ()
313 uint32_t const N[2] = {
314 _ports[0].total_visible_channels (),
315 _ports[1].total_visible_channels ()
318 /* The list with the most channels goes on left or right, so that the most channel
319 names are printed horizontally and hence more readable. However we also
320 maintain notional `signal flow' vaguely from left to right. Subclasses
321 should choose where to put ports based on signal flowing from _ports[0]
328 _arrangement = LEFT_TO_BOTTOM;
334 _arrangement = TOP_TO_RIGHT;
338 /** @return columns list */
339 PortGroupList const *
340 PortMatrix::columns () const
342 return &_ports[_column_index];
345 /* @return rows list */
346 PortGroupList const *
347 PortMatrix::rows () const
349 return &_ports[_row_index];
352 /** A group visibility checkbutton has been toggled.
357 PortMatrix::visibility_toggled (boost::weak_ptr<PortGroup> w, Gtk::CheckButton* b)
359 boost::shared_ptr<PortGroup> g = w.lock ();
364 g->set_visible (b->get_active());
371 PortMatrix::popup_channel_context_menu (int dim, uint32_t N, uint32_t t)
375 _menu = new Gtk::Menu;
376 _menu->set_name ("ArdourContextMenu");
378 Gtk::Menu_Helpers::MenuList& items = _menu->items ();
380 ARDOUR::BundleChannel bc;
382 ARDOUR::BundleList const r = _ports[dim].bundles();
383 for (ARDOUR::BundleList::const_iterator i = r.begin(); i != r.end(); ++i) {
384 if (N < (*i)->nchannels ()) {
385 bc = ARDOUR::BundleChannel (*i, N);
388 N -= (*i)->nchannels ();
394 bool have_one = false;
396 if (can_rename_channels (dim)) {
397 snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc.bundle->channel_name (bc.channel).c_str());
398 boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
400 Gtk::Menu_Helpers::MenuElem (
402 sigc::bind (sigc::mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc.channel)
409 if (can_remove_channels (dim)) {
410 snprintf (buf, sizeof (buf), _("Remove '%s'"), bc.bundle->channel_name (bc.channel).c_str());
411 boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
413 Gtk::Menu_Helpers::MenuElem (
415 sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc.channel)
423 items.push_back (Gtk::Menu_Helpers::SeparatorElem ());
426 boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
427 items.push_back (Gtk::Menu_Helpers::MenuElem (
428 _("Disassociate all"),
429 sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc.channel, dim)
440 PortMatrix::remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
442 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
447 remove_channel (ARDOUR::BundleChannel (sb, c));
452 PortMatrix::rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
454 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
459 rename_channel (ARDOUR::BundleChannel (sb, c));
463 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<ARDOUR::Bundle> bundle, uint32_t channel, int dim)
465 boost::shared_ptr<ARDOUR::Bundle> sb = bundle.lock ();
470 ARDOUR::BundleList a = _ports[1-dim].bundles ();
472 for (ARDOUR::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
473 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
475 ARDOUR::BundleChannel c[2];
476 c[dim] = ARDOUR::BundleChannel (sb, channel);
477 c[1-dim] = ARDOUR::BundleChannel (*i, j);
479 if (get_state (c) == ASSOCIATED) {
480 set_state (c, false);
485 _body->rebuild_and_draw_grid ();
489 PortMatrix::setup_global_ports ()
491 for (int i = 0; i < 2; ++i) {
492 if (list_is_global (i)) {
499 PortMatrix::setup_all_ports ()