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"
34 /** PortMatrix constructor.
35 * @param session Our session.
36 * @param type Port type that we are handling.
38 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 _ports[0].set_type (type);
51 _ports[1].set_type (type);
53 _row_visibility_box.pack_start (_row_visibility_label, Gtk::PACK_SHRINK);
54 _column_visibility_box.pack_start (_column_visibility_label, Gtk::PACK_SHRINK);
56 _hscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::hscroll_changed));
57 _vscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::vscroll_changed));
59 /* watch for routes being added or removed */
60 _session.RouteAdded.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrix::routes_changed)));
62 reconnect_to_routes ();
67 PortMatrix::~PortMatrix ()
69 for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
73 for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
80 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
82 PortMatrix::reconnect_to_routes ()
84 for (std::vector<sigc::connection>::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) {
88 boost::shared_ptr<ARDOUR::RouteList> routes = _session.get_routes ();
89 for (ARDOUR::RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
90 _route_connections.push_back (
91 (*i)->processors_changed.connect (sigc::mem_fun (*this, &PortMatrix::setup))
96 /** A route has been added to or removed from the session */
98 PortMatrix::routes_changed ()
100 reconnect_to_routes ();
104 /** Set up everything that changes about the matrix */
108 select_arrangement ();
115 /* we've set up before, so we need to clean up before re-setting-up */
116 /* XXX: we ought to be able to do this by just getting a list of children
117 from each container widget, but I couldn't make that work */
120 for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
121 _column_visibility_box.remove (**i);
125 _column_visibility_buttons.clear ();
127 for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
128 _row_visibility_box.remove (**i);
132 _row_visibility_buttons.clear ();
134 _scroller_table.remove (_vscroll);
135 _scroller_table.remove (_body);
136 _scroller_table.remove (_hscroll);
138 _main_hbox.remove (_scroller_table);
139 if (_row_visibility_box_added) {
140 _main_hbox.remove (_row_visibility_box);
143 if (_column_visibility_box_added) {
144 remove (_column_visibility_box);
149 if (_column_index == 0) {
150 _column_visibility_label.set_text (_("Show Outputs"));
151 _row_visibility_label.set_text (_("Show Inputs"));
153 _column_visibility_label.set_text (_("Show Inputs"));
154 _row_visibility_label.set_text (_("Show Outputs"));
157 for (PortGroupList::List::const_iterator i = columns()->begin(); i != columns()->end(); ++i) {
158 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
159 b->set_active ((*i)->visible());
160 boost::weak_ptr<PortGroup> w (*i);
161 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
162 _column_visibility_buttons.push_back (b);
163 _column_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
166 for (PortGroupList::List::const_iterator i = rows()->begin(); i != rows()->end(); ++i) {
167 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
168 b->set_active ((*i)->visible());
169 boost::weak_ptr<PortGroup> w (*i);
170 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
171 _row_visibility_buttons.push_back (b);
172 _row_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
175 if (_arrangement == TOP_TO_RIGHT) {
177 _scroller_table.attach (_hscroll, 0, 1, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
178 _scroller_table.attach (_body, 0, 1, 1, 2);
179 _scroller_table.attach (_vscroll, 1, 2, 1, 2, Gtk::SHRINK);
181 _main_hbox.pack_start (_scroller_table);
183 if (rows()->size() > 1) {
184 _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
185 _row_visibility_box_added = true;
187 _row_visibility_box_added = false;
190 if (columns()->size() > 1) {
191 pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
192 _column_visibility_box_added = true;
194 _column_visibility_box_added = false;
197 pack_start (_main_hbox);
200 _scroller_table.attach (_vscroll, 0, 1, 0, 1, Gtk::SHRINK);
201 _scroller_table.attach (_body, 1, 2, 0, 1);
202 _scroller_table.attach (_hscroll, 1, 2, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
204 if (rows()->size() > 1) {
205 _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
206 _row_visibility_box_added = true;
208 _row_visibility_box_added = false;
211 _main_hbox.pack_start (_scroller_table);
213 pack_start (_main_hbox);
215 if (columns()->size() > 1) {
216 pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
217 _column_visibility_box_added = true;
219 _column_visibility_box_added = false;
229 PortMatrix::set_type (ARDOUR::DataType t)
232 _ports[0].set_type (_type);
233 _ports[1].set_type (_type);
238 PortMatrix::hscroll_changed ()
240 _body.set_xoffset (_hscroll.get_adjustment()->get_value());
244 PortMatrix::vscroll_changed ()
246 _body.set_yoffset (_vscroll.get_adjustment()->get_value());
250 PortMatrix::setup_scrollbars ()
252 Gtk::Adjustment* a = _hscroll.get_adjustment ();
254 a->set_upper (_body.full_scroll_width());
255 a->set_page_size (_body.alloc_scroll_width());
256 a->set_step_increment (32);
257 a->set_page_increment (128);
259 a = _vscroll.get_adjustment ();
261 a->set_upper (_body.full_scroll_height());
262 a->set_page_size (_body.alloc_scroll_height());
263 a->set_step_increment (32);
264 a->set_page_increment (128);
267 /** Disassociate all of our ports from each other */
269 PortMatrix::disassociate_all ()
271 ARDOUR::BundleList a = _ports[0].bundles ();
272 ARDOUR::BundleList b = _ports[1].bundles ();
274 for (ARDOUR::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
275 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
276 for (ARDOUR::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
277 for (uint32_t l = 0; l < (*k)->nchannels(); ++l) {
279 ARDOUR::BundleChannel c[2] = {
280 ARDOUR::BundleChannel (*i, j),
281 ARDOUR::BundleChannel (*k, l)
284 set_state (c, false);
291 _body.rebuild_and_draw_grid ();
294 /* Decide how to arrange the components of the matrix */
296 PortMatrix::select_arrangement ()
298 uint32_t const N[2] = {
299 _ports[0].total_visible_ports (),
300 _ports[1].total_visible_ports ()
303 /* The list with the most ports goes on left or right, so that the most port
304 names are printed horizontally and hence more readable. However we also
305 maintain notional `signal flow' vaguely from left to right. Subclasses
306 should choose where to put ports based on signal flowing from _ports[0]
313 _arrangement = LEFT_TO_BOTTOM;
319 _arrangement = TOP_TO_RIGHT;
323 /** @return columns list */
324 PortGroupList const *
325 PortMatrix::columns () const
327 return &_ports[_column_index];
330 /* @return rows list */
331 PortGroupList const *
332 PortMatrix::rows () const
334 return &_ports[_row_index];
337 /** A group visibility checkbutton has been toggled.
342 PortMatrix::visibility_toggled (boost::weak_ptr<PortGroup> w, Gtk::CheckButton* b)
344 boost::shared_ptr<PortGroup> g = w.lock ();
349 g->set_visible (b->get_active());
356 PortMatrix::popup_channel_context_menu (int dim, uint32_t N, uint32_t t)
360 _menu = new Gtk::Menu;
361 _menu->set_name ("ArdourContextMenu");
363 Gtk::Menu_Helpers::MenuList& items = _menu->items ();
365 ARDOUR::BundleChannel bc;
367 ARDOUR::BundleList const r = _ports[dim].bundles();
368 for (ARDOUR::BundleList::const_iterator i = r.begin(); i != r.end(); ++i) {
369 if (N < (*i)->nchannels ()) {
370 bc = ARDOUR::BundleChannel (*i, N);
373 N -= (*i)->nchannels ();
380 if (can_rename_channels (dim)) {
381 snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc.bundle->channel_name (bc.channel).c_str());
382 boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
384 Gtk::Menu_Helpers::MenuElem (
386 sigc::bind (sigc::mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc.channel)
391 if (can_remove_channels (dim)) {
392 snprintf (buf, sizeof (buf), _("Remove '%s'"), bc.bundle->channel_name (bc.channel).c_str());
393 boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
395 Gtk::Menu_Helpers::MenuElem (
397 sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc.channel)
409 PortMatrix::remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
411 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
416 remove_channel (ARDOUR::BundleChannel (sb, c));
421 PortMatrix::rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
423 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
428 rename_channel (ARDOUR::BundleChannel (sb, c));