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 _ports[0].set_type (type);
53 _ports[1].set_type (type);
55 _row_visibility_box.pack_start (_row_visibility_label, Gtk::PACK_SHRINK);
56 _column_visibility_box.pack_start (_column_visibility_label, Gtk::PACK_SHRINK);
58 _hscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::hscroll_changed));
59 _vscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::vscroll_changed));
61 /* watch for routes being added or removed */
62 _session.RouteAdded.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrix::routes_changed)));
64 reconnect_to_routes ();
69 PortMatrix::~PortMatrix ()
73 for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
77 for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
84 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
86 PortMatrix::reconnect_to_routes ()
88 for (std::vector<sigc::connection>::iterator i = _route_connections.begin(); i != _route_connections.end(); ++i) {
92 boost::shared_ptr<ARDOUR::RouteList> routes = _session.get_routes ();
93 for (ARDOUR::RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
94 _route_connections.push_back (
95 (*i)->processors_changed.connect (sigc::mem_fun (*this, &PortMatrix::setup))
100 /** A route has been added to or removed from the session */
102 PortMatrix::routes_changed ()
104 reconnect_to_routes ();
108 /** Set up everything that changes about the matrix */
112 select_arrangement ();
119 /* we've set up before, so we need to clean up before re-setting-up */
120 /* XXX: we ought to be able to do this by just getting a list of children
121 from each container widget, but I couldn't make that work */
124 for (std::vector<Gtk::CheckButton*>::iterator i = _column_visibility_buttons.begin(); i != _column_visibility_buttons.end(); ++i) {
125 _column_visibility_box.remove (**i);
129 _column_visibility_buttons.clear ();
131 for (std::vector<Gtk::CheckButton*>::iterator i = _row_visibility_buttons.begin(); i != _row_visibility_buttons.end(); ++i) {
132 _row_visibility_box.remove (**i);
136 _row_visibility_buttons.clear ();
138 _scroller_table.remove (_vscroll);
139 _scroller_table.remove (*_body);
140 _scroller_table.remove (_hscroll);
142 _main_hbox.remove (_scroller_table);
143 if (_row_visibility_box_added) {
144 _main_hbox.remove (_row_visibility_box);
147 if (_column_visibility_box_added) {
148 remove (_column_visibility_box);
153 if (_column_index == 0) {
154 _column_visibility_label.set_text (_("Show Outputs"));
155 _row_visibility_label.set_text (_("Show Inputs"));
157 _column_visibility_label.set_text (_("Show Inputs"));
158 _row_visibility_label.set_text (_("Show Outputs"));
161 for (PortGroupList::List::const_iterator i = columns()->begin(); i != columns()->end(); ++i) {
162 Gtk::CheckButton* b = new Gtk::CheckButton ((*i)->name);
163 b->set_active ((*i)->visible());
164 boost::weak_ptr<PortGroup> w (*i);
165 b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &PortMatrix::visibility_toggled), w, b));
166 _column_visibility_buttons.push_back (b);
167 _column_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
170 for (PortGroupList::List::const_iterator i = rows()->begin(); i != rows()->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 _row_visibility_buttons.push_back (b);
176 _row_visibility_box.pack_start (*b, Gtk::PACK_SHRINK);
179 if (_arrangement == TOP_TO_RIGHT) {
181 _scroller_table.attach (_hscroll, 0, 1, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
182 _scroller_table.attach (*_body, 0, 1, 1, 2);
183 _scroller_table.attach (_vscroll, 1, 2, 1, 2, Gtk::SHRINK);
185 _main_hbox.pack_start (_scroller_table);
187 if (rows()->size() > 1) {
188 _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
189 _row_visibility_box_added = true;
191 _row_visibility_box_added = false;
194 if (columns()->size() > 1) {
195 pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
196 _column_visibility_box_added = true;
198 _column_visibility_box_added = false;
201 pack_start (_main_hbox);
204 _scroller_table.attach (_vscroll, 0, 1, 0, 1, Gtk::SHRINK);
205 _scroller_table.attach (*_body, 1, 2, 0, 1);
206 _scroller_table.attach (_hscroll, 1, 2, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
208 if (rows()->size() > 1) {
209 _main_hbox.pack_start (_row_visibility_box, Gtk::PACK_SHRINK);
210 _row_visibility_box_added = true;
212 _row_visibility_box_added = false;
215 _main_hbox.pack_start (_scroller_table);
217 pack_start (_main_hbox);
219 if (columns()->size() > 1) {
220 pack_start (_column_visibility_box, Gtk::PACK_SHRINK);
221 _column_visibility_box_added = true;
223 _column_visibility_box_added = false;
233 PortMatrix::set_type (ARDOUR::DataType t)
236 _ports[0].set_type (_type);
237 _ports[1].set_type (_type);
242 PortMatrix::hscroll_changed ()
244 _body->set_xoffset (_hscroll.get_adjustment()->get_value());
248 PortMatrix::vscroll_changed ()
250 _body->set_yoffset (_vscroll.get_adjustment()->get_value());
254 PortMatrix::setup_scrollbars ()
256 Gtk::Adjustment* a = _hscroll.get_adjustment ();
258 a->set_upper (_body->full_scroll_width());
259 a->set_page_size (_body->alloc_scroll_width());
260 a->set_step_increment (32);
261 a->set_page_increment (128);
263 a = _vscroll.get_adjustment ();
265 a->set_upper (_body->full_scroll_height());
266 a->set_page_size (_body->alloc_scroll_height());
267 a->set_step_increment (32);
268 a->set_page_increment (128);
271 /** Disassociate all of our ports from each other */
273 PortMatrix::disassociate_all ()
275 ARDOUR::BundleList a = _ports[0].bundles ();
276 ARDOUR::BundleList b = _ports[1].bundles ();
278 for (ARDOUR::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
279 for (uint32_t j = 0; j < (*i)->nchannels(); ++j) {
280 for (ARDOUR::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
281 for (uint32_t l = 0; l < (*k)->nchannels(); ++l) {
283 ARDOUR::BundleChannel c[2] = {
284 ARDOUR::BundleChannel (*i, j),
285 ARDOUR::BundleChannel (*k, l)
288 set_state (c, false);
295 _body->rebuild_and_draw_grid ();
298 /* Decide how to arrange the components of the matrix */
300 PortMatrix::select_arrangement ()
302 uint32_t const N[2] = {
303 _ports[0].total_visible_ports (),
304 _ports[1].total_visible_ports ()
307 /* The list with the most ports goes on left or right, so that the most port
308 names are printed horizontally and hence more readable. However we also
309 maintain notional `signal flow' vaguely from left to right. Subclasses
310 should choose where to put ports based on signal flowing from _ports[0]
317 _arrangement = LEFT_TO_BOTTOM;
323 _arrangement = TOP_TO_RIGHT;
327 /** @return columns list */
328 PortGroupList const *
329 PortMatrix::columns () const
331 return &_ports[_column_index];
334 /* @return rows list */
335 PortGroupList const *
336 PortMatrix::rows () const
338 return &_ports[_row_index];
341 /** A group visibility checkbutton has been toggled.
346 PortMatrix::visibility_toggled (boost::weak_ptr<PortGroup> w, Gtk::CheckButton* b)
348 boost::shared_ptr<PortGroup> g = w.lock ();
353 g->set_visible (b->get_active());
360 PortMatrix::popup_channel_context_menu (int dim, uint32_t N, uint32_t t)
364 _menu = new Gtk::Menu;
365 _menu->set_name ("ArdourContextMenu");
367 Gtk::Menu_Helpers::MenuList& items = _menu->items ();
369 ARDOUR::BundleChannel bc;
371 ARDOUR::BundleList const r = _ports[dim].bundles();
372 for (ARDOUR::BundleList::const_iterator i = r.begin(); i != r.end(); ++i) {
373 if (N < (*i)->nchannels ()) {
374 bc = ARDOUR::BundleChannel (*i, N);
377 N -= (*i)->nchannels ();
384 if (can_rename_channels (dim)) {
385 snprintf (buf, sizeof (buf), _("Rename '%s'..."), bc.bundle->channel_name (bc.channel).c_str());
386 boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
388 Gtk::Menu_Helpers::MenuElem (
390 sigc::bind (sigc::mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc.channel)
395 if (can_remove_channels (dim)) {
396 snprintf (buf, sizeof (buf), _("Remove '%s'"), bc.bundle->channel_name (bc.channel).c_str());
397 boost::weak_ptr<ARDOUR::Bundle> w (bc.bundle);
399 Gtk::Menu_Helpers::MenuElem (
401 sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_channel_proxy), w, bc.channel)
413 PortMatrix::remove_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
415 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
420 remove_channel (ARDOUR::BundleChannel (sb, c));
425 PortMatrix::rename_channel_proxy (boost::weak_ptr<ARDOUR::Bundle> b, uint32_t c)
427 boost::shared_ptr<ARDOUR::Bundle> sb = b.lock ();
432 rename_channel (ARDOUR::BundleChannel (sb, c));