fix math bug with numthreads computation
[ardour2.git] / gtk2_ardour / port_matrix.cc
blob73e937e8405b2aead4ac7512de8ba0d95440ccbe
1 /*
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.
20 #include <iostream>
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 <gtkmm/window.h>
28 #include "ardour/bundle.h"
29 #include "ardour/types.h"
30 #include "ardour/session.h"
31 #include "ardour/route.h"
32 #include "ardour/audioengine.h"
33 #include "port_matrix.h"
34 #include "port_matrix_body.h"
35 #include "port_matrix_component.h"
36 #include "i18n.h"
37 #include "gui_thread.h"
38 #include "utils.h"
40 using namespace std;
41 using namespace Gtk;
42 using namespace ARDOUR;
44 /** PortMatrix constructor.
45 * @param session Our session.
46 * @param type Port type that we are handling.
48 PortMatrix::PortMatrix (Window* parent, Session* session, DataType type)
49 : Table (3, 3)
50 , _parent (parent)
51 , _type (type)
52 , _menu (0)
53 , _arrangement (TOP_TO_RIGHT)
54 , _row_index (0)
55 , _column_index (1)
56 , _min_height_divisor (1)
57 , _show_only_bundles (false)
58 , _inhibit_toggle_show_only_bundles (false)
59 , _ignore_notebook_page_selected (false)
61 set_session (session);
63 _body = new PortMatrixBody (this);
64 _body->DimensionsChanged.connect (sigc::mem_fun (*this, &PortMatrix::body_dimensions_changed));
66 _vbox.pack_start (_vspacer, false, false);
67 _vbox.pack_start (_vnotebook, false, false);
68 _vbox.pack_start (_vlabel, true, true);
69 _hbox.pack_start (_hspacer, false, false);
70 _hbox.pack_start (_hnotebook, false, false);
71 _hbox.pack_start (_hlabel, true, true);
73 _vnotebook.signal_switch_page().connect (sigc::mem_fun (*this, &PortMatrix::notebook_page_selected));
74 _vnotebook.property_tab_border() = 4;
75 _vnotebook.set_name (X_("PortMatrixLabel"));
76 _hnotebook.signal_switch_page().connect (sigc::mem_fun (*this, &PortMatrix::notebook_page_selected));
77 _hnotebook.property_tab_border() = 4;
78 _hnotebook.set_name (X_("PortMatrixLabel"));
80 for (int i = 0; i < 2; ++i) {
81 _ports[i].set_type (type);
84 _vlabel.set_use_markup ();
85 _vlabel.set_alignment (1, 1);
86 _vlabel.set_padding (4, 16);
87 _vlabel.set_name (X_("PortMatrixLabel"));
88 _hlabel.set_use_markup ();
89 _hlabel.set_alignment (1, 0.5);
90 _hlabel.set_padding (16, 4);
91 _hlabel.set_name (X_("PortMatrixLabel"));
93 _body->show ();
94 _vbox.show ();
95 _hbox.show ();
96 _vscroll.show ();
97 _hscroll.show ();
98 _vlabel.show ();
99 _hlabel.show ();
100 _hspacer.show ();
101 _vspacer.show ();
102 _vnotebook.show ();
103 _hnotebook.show ();
106 PortMatrix::~PortMatrix ()
108 delete _body;
109 delete _menu;
112 /** Perform initial and once-only setup. This must be called by
113 * subclasses after they have set up _ports[] to at least some
114 * reasonable extent. Two-part initialisation is necessary because
115 * setting up _ports is largely done by virtual functions in
116 * subclasses.
119 void
120 PortMatrix::init ()
122 select_arrangement ();
124 /* Signal handling is kind of split into three parts:
126 * 1. When _ports[] changes, we call setup(). This essentially sorts out our visual
127 * representation of the information in _ports[].
129 * 2. When certain other things change, we need to get our subclass to clear and
130 * re-fill _ports[], which in turn causes appropriate signals to be raised to
131 * hook into part (1).
133 * 3. Assorted other signals.
137 /* Part 1: the basic _ports[] change -> reset visuals */
139 for (int i = 0; i < 2; ++i) {
140 /* watch for the content of _ports[] changing */
141 _ports[i].Changed.connect (_changed_connections, invalidator (*this), boost::bind (&PortMatrix::setup, this), gui_context());
143 /* and for bundles in _ports[] changing */
144 _ports[i].BundleChanged.connect (_bundle_changed_connections, invalidator (*this), boost::bind (&PortMatrix::setup, this), gui_context());
147 /* Part 2: notice when things have changed that require our subclass to clear and refill _ports[] */
149 /* watch for routes being added or removed */
150 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&PortMatrix::routes_changed, this), gui_context());
152 /* and also bundles */
153 _session->BundleAdded.connect (_session_connections, invalidator (*this), boost::bind (&PortMatrix::setup_global_ports, this), gui_context());
155 /* and also ports */
156 _session->engine().PortRegisteredOrUnregistered.connect (_session_connections, invalidator (*this), boost::bind (&PortMatrix::setup_global_ports, this), gui_context());
159 /* Part 3: other stuff */
161 _session->engine().PortConnectedOrDisconnected.connect (_session_connections, invalidator (*this), boost::bind (&PortMatrix::port_connected_or_disconnected, this), gui_context ());
163 _hscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::hscroll_changed));
164 _vscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::vscroll_changed));
166 reconnect_to_routes ();
168 setup ();
171 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
172 void
173 PortMatrix::reconnect_to_routes ()
175 _route_connections.drop_connections ();
177 boost::shared_ptr<RouteList> routes = _session->get_routes ();
178 for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
179 (*i)->processors_changed.connect (_route_connections, invalidator (*this), ui_bind (&PortMatrix::route_processors_changed, this, _1), gui_context());
183 void
184 PortMatrix::route_processors_changed (RouteProcessorChange c)
186 if (c.type == RouteProcessorChange::MeterPointChange) {
187 /* this change has no impact on the port matrix */
188 return;
191 setup_global_ports ();
194 /** A route has been added to or removed from the session */
195 void
196 PortMatrix::routes_changed ()
198 reconnect_to_routes ();
199 setup_global_ports ();
202 /** Set up everything that depends on the content of _ports[] */
203 void
204 PortMatrix::setup ()
206 /* this needs to be done first, as the visible_ports() method uses the
207 notebook state to decide which ports are being shown */
209 setup_notebooks ();
211 _body->setup ();
212 setup_scrollbars ();
213 queue_draw ();
216 void
217 PortMatrix::set_type (DataType t)
219 _type = t;
220 _ports[0].set_type (_type);
221 _ports[1].set_type (_type);
223 setup_all_ports ();
226 void
227 PortMatrix::hscroll_changed ()
229 _body->set_xoffset (_hscroll.get_adjustment()->get_value());
232 void
233 PortMatrix::vscroll_changed ()
235 _body->set_yoffset (_vscroll.get_adjustment()->get_value());
238 void
239 PortMatrix::setup_scrollbars ()
241 Adjustment* a = _hscroll.get_adjustment ();
242 a->set_lower (0);
243 a->set_upper (_body->full_scroll_width());
244 a->set_page_size (_body->alloc_scroll_width());
245 a->set_step_increment (32);
246 a->set_page_increment (128);
248 a = _vscroll.get_adjustment ();
249 a->set_lower (0);
250 a->set_upper (_body->full_scroll_height());
251 a->set_page_size (_body->alloc_scroll_height());
252 a->set_step_increment (32);
253 a->set_page_increment (128);
256 /** Disassociate all of our ports from each other */
257 void
258 PortMatrix::disassociate_all ()
260 PortGroup::BundleList a = _ports[0].bundles ();
261 PortGroup::BundleList b = _ports[1].bundles ();
263 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
264 for (uint32_t j = 0; j < (*i)->bundle->nchannels(); ++j) {
265 for (PortGroup::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
266 for (uint32_t l = 0; l < (*k)->bundle->nchannels(); ++l) {
268 BundleChannel c[2] = {
269 BundleChannel ((*i)->bundle, j),
270 BundleChannel ((*k)->bundle, l)
273 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
274 set_state (c, false);
282 _body->rebuild_and_draw_grid ();
285 /* Decide how to arrange the components of the matrix */
286 void
287 PortMatrix::select_arrangement ()
289 uint32_t const N[2] = {
290 _ports[0].total_channels (),
291 _ports[1].total_channels ()
294 /* The list with the most channels goes on left or right, so that the most channel
295 names are printed horizontally and hence more readable. However we also
296 maintain notional `signal flow' vaguely from left to right. Subclasses
297 should choose where to put ports based on signal flowing from _ports[0]
298 to _ports[1] */
300 if (N[0] > N[1]) {
302 _row_index = 0;
303 _column_index = 1;
304 _arrangement = LEFT_TO_BOTTOM;
305 _vlabel.set_label (_("<b>Sources</b>"));
306 _hlabel.set_label (_("<b>Destinations</b>"));
307 _vlabel.set_angle (90);
309 attach (*_body, 1, 2, 0, 1);
310 attach (_vscroll, 2, 3, 0, 1, SHRINK);
311 attach (_hscroll, 1, 2, 2, 3, FILL | EXPAND, SHRINK);
312 attach (_vbox, 0, 1, 0, 1, SHRINK);
313 attach (_hbox, 1, 2, 1, 2, FILL | EXPAND, SHRINK);
315 set_col_spacing (0, 4);
316 set_row_spacing (0, 4);
318 } else {
320 _row_index = 1;
321 _column_index = 0;
322 _arrangement = TOP_TO_RIGHT;
323 _hlabel.set_label (_("<b>Sources</b>"));
324 _vlabel.set_label (_("<b>Destinations</b>"));
325 _vlabel.set_angle (-90);
327 attach (*_body, 0, 1, 1, 2);
328 attach (_vscroll, 2, 3, 1, 2, SHRINK);
329 attach (_hscroll, 0, 1, 2, 3, FILL | EXPAND, SHRINK);
330 attach (_vbox, 1, 2, 1, 2, SHRINK);
331 attach (_hbox, 0, 1, 0, 1, FILL | EXPAND, SHRINK);
333 set_col_spacing (1, 4);
334 set_row_spacing (1, 4);
338 /** @return columns list */
339 PortGroupList const *
340 PortMatrix::columns () const
342 return &_ports[_column_index];
345 boost::shared_ptr<const PortGroup>
346 PortMatrix::visible_columns () const
348 return visible_ports (_column_index);
351 /* @return rows list */
352 PortGroupList const *
353 PortMatrix::rows () const
355 return &_ports[_row_index];
358 boost::shared_ptr<const PortGroup>
359 PortMatrix::visible_rows () const
361 return visible_ports (_row_index);
364 void
365 PortMatrix::popup_menu (BundleChannel column, BundleChannel row, uint32_t t)
367 using namespace Menu_Helpers;
369 delete _menu;
371 _menu = new Menu;
372 _menu->set_name ("ArdourContextMenu");
374 MenuList& items = _menu->items ();
376 BundleChannel bc[2];
377 bc[_column_index] = column;
378 bc[_row_index] = row;
380 char buf [64];
381 bool need_separator = false;
383 for (int dim = 0; dim < 2; ++dim) {
385 if (bc[dim].bundle) {
387 Menu* m = manage (new Menu);
388 MenuList& sub = m->items ();
390 boost::weak_ptr<Bundle> w (bc[dim].bundle);
392 bool can_add_or_rename = false;
394 if (can_add_channel (bc[dim].bundle)) {
395 snprintf (buf, sizeof (buf), _("Add %s"), channel_noun().c_str());
396 sub.push_back (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::add_channel_proxy), w)));
397 can_add_or_rename = true;
401 if (can_rename_channels (bc[dim].bundle)) {
402 snprintf (
403 buf, sizeof (buf), _("Rename '%s'..."),
404 escape_underscores (bc[dim].bundle->channel_name (bc[dim].channel)).c_str()
406 sub.push_back (
407 MenuElem (
408 buf,
409 sigc::bind (sigc::mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc[dim].channel)
412 can_add_or_rename = true;
415 if (can_add_or_rename) {
416 sub.push_back (SeparatorElem ());
419 if (can_remove_channels (bc[dim].bundle)) {
420 if (bc[dim].channel != -1) {
421 add_remove_option (sub, w, bc[dim].channel);
422 } else {
424 snprintf (buf, sizeof (buf), _("Remove all"));
425 sub.push_back (
426 MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_all_channels), w))
429 for (uint32_t i = 0; i < bc[dim].bundle->nchannels(); ++i) {
430 add_remove_option (sub, w, i);
435 if (_show_only_bundles || bc[dim].bundle->nchannels() <= 1) {
436 snprintf (buf, sizeof (buf), _("%s all"), disassociation_verb().c_str());
437 sub.push_back (
438 MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc[dim].channel, dim))
441 } else {
443 if (bc[dim].channel != -1) {
444 add_disassociate_option (sub, w, dim, bc[dim].channel);
445 } else {
446 snprintf (buf, sizeof (buf), _("%s all"), disassociation_verb().c_str());
447 sub.push_back (
448 MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_bundle), w, dim))
451 for (uint32_t i = 0; i < bc[dim].bundle->nchannels(); ++i) {
452 add_disassociate_option (sub, w, dim, i);
457 items.push_back (MenuElem (escape_underscores (bc[dim].bundle->name()).c_str(), *m));
458 need_separator = true;
463 if (need_separator) {
464 items.push_back (SeparatorElem ());
467 items.push_back (MenuElem (_("Rescan"), sigc::mem_fun (*this, &PortMatrix::setup_all_ports)));
468 items.push_back (CheckMenuElem (_("Show individual ports"), sigc::mem_fun (*this, &PortMatrix::toggle_show_only_bundles)));
469 CheckMenuItem* i = dynamic_cast<CheckMenuItem*> (&items.back());
470 _inhibit_toggle_show_only_bundles = true;
471 i->set_active (!_show_only_bundles);
472 _inhibit_toggle_show_only_bundles = false;
474 _menu->popup (1, t);
477 void
478 PortMatrix::remove_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
480 boost::shared_ptr<Bundle> sb = b.lock ();
481 if (!sb) {
482 return;
485 remove_channel (BundleChannel (sb, c));
489 void
490 PortMatrix::rename_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
492 boost::shared_ptr<Bundle> sb = b.lock ();
493 if (!sb) {
494 return;
497 rename_channel (BundleChannel (sb, c));
500 void
501 PortMatrix::disassociate_all_on_bundle (boost::weak_ptr<Bundle> bundle, int dim)
503 boost::shared_ptr<Bundle> sb = bundle.lock ();
504 if (!sb) {
505 return;
508 for (uint32_t i = 0; i < sb->nchannels(); ++i) {
509 disassociate_all_on_channel (bundle, i, dim);
513 void
514 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<Bundle> bundle, uint32_t channel, int dim)
516 boost::shared_ptr<Bundle> sb = bundle.lock ();
517 if (!sb) {
518 return;
521 PortGroup::BundleList a = _ports[1-dim].bundles ();
523 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
524 for (uint32_t j = 0; j < (*i)->bundle->nchannels(); ++j) {
526 BundleChannel c[2];
527 c[dim] = BundleChannel (sb, channel);
528 c[1-dim] = BundleChannel ((*i)->bundle, j);
530 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
531 set_state (c, false);
536 _body->rebuild_and_draw_grid ();
539 void
540 PortMatrix::setup_global_ports ()
542 ENSURE_GUI_THREAD (*this, &PortMatrix::setup_global_ports)
544 for (int i = 0; i < 2; ++i) {
545 if (list_is_global (i)) {
546 setup_ports (i);
551 void
552 PortMatrix::setup_all_ports ()
554 if (_session->deletion_in_progress()) {
555 return;
558 ENSURE_GUI_THREAD (*this, &PortMatrix::setup_all_ports)
560 setup_ports (0);
561 setup_ports (1);
564 void
565 PortMatrix::toggle_show_only_bundles ()
567 if (_inhibit_toggle_show_only_bundles) {
568 return;
571 _show_only_bundles = !_show_only_bundles;
573 setup ();
576 pair<uint32_t, uint32_t>
577 PortMatrix::max_size () const
579 pair<uint32_t, uint32_t> m = _body->max_size ();
581 m.first += _vscroll.get_width ();
582 m.second += _hscroll.get_height ();
584 return m;
587 bool
588 PortMatrix::on_scroll_event (GdkEventScroll* ev)
590 double const h = _hscroll.get_value ();
591 double const v = _vscroll.get_value ();
593 switch (ev->direction) {
594 case GDK_SCROLL_UP:
595 _vscroll.set_value (v - PortMatrixComponent::grid_spacing ());
596 break;
597 case GDK_SCROLL_DOWN:
598 _vscroll.set_value (v + PortMatrixComponent::grid_spacing ());
599 break;
600 case GDK_SCROLL_LEFT:
601 _hscroll.set_value (h - PortMatrixComponent::grid_spacing ());
602 break;
603 case GDK_SCROLL_RIGHT:
604 _hscroll.set_value (h + PortMatrixComponent::grid_spacing ());
605 break;
608 return true;
611 boost::shared_ptr<IO>
612 PortMatrix::io_from_bundle (boost::shared_ptr<Bundle> b) const
614 boost::shared_ptr<IO> io = _ports[0].io_from_bundle (b);
615 if (!io) {
616 io = _ports[1].io_from_bundle (b);
619 return io;
622 bool
623 PortMatrix::can_add_channel (boost::shared_ptr<Bundle> b) const
625 return io_from_bundle (b);
628 void
629 PortMatrix::add_channel (boost::shared_ptr<Bundle> b)
631 boost::shared_ptr<IO> io = io_from_bundle (b);
633 if (io) {
634 io->add_port ("", this, _type);
638 bool
639 PortMatrix::can_remove_channels (boost::shared_ptr<Bundle> b) const
641 return io_from_bundle (b);
644 void
645 PortMatrix::remove_channel (ARDOUR::BundleChannel b)
647 boost::shared_ptr<IO> io = io_from_bundle (b.bundle);
649 if (io) {
650 Port* p = io->nth (b.channel);
651 if (p) {
652 io->remove_port (p, this);
657 void
658 PortMatrix::remove_all_channels (boost::weak_ptr<Bundle> w)
660 boost::shared_ptr<Bundle> b = w.lock ();
661 if (!b) {
662 return;
665 for (uint32_t i = 0; i < b->nchannels(); ++i) {
666 remove_channel (ARDOUR::BundleChannel (b, i));
670 void
671 PortMatrix::add_channel_proxy (boost::weak_ptr<Bundle> w)
673 boost::shared_ptr<Bundle> b = w.lock ();
674 if (!b) {
675 return;
678 add_channel (b);
681 void
682 PortMatrix::setup_notebooks ()
684 int const h_current_page = _hnotebook.get_current_page ();
685 int const v_current_page = _vnotebook.get_current_page ();
687 /* for some reason best known to GTK, erroneous switch_page signals seem to be generated
688 when adding or removing pages to or from notebooks, so ignore them */
690 _ignore_notebook_page_selected = true;
692 remove_notebook_pages (_hnotebook);
693 remove_notebook_pages (_vnotebook);
695 for (PortGroupList::List::const_iterator i = _ports[_row_index].begin(); i != _ports[_row_index].end(); ++i) {
696 HBox* dummy = manage (new HBox);
697 dummy->show ();
698 Label* label = manage (new Label ((*i)->name));
699 label->set_angle (_arrangement == LEFT_TO_BOTTOM ? 90 : -90);
700 label->show ();
701 _vnotebook.prepend_page (*dummy, *label);
704 for (PortGroupList::List::const_iterator i = _ports[_column_index].begin(); i != _ports[_column_index].end(); ++i) {
705 HBox* dummy = manage (new HBox);
706 dummy->show ();
707 _hnotebook.append_page (*dummy, (*i)->name);
710 _ignore_notebook_page_selected = false;
712 _vnotebook.set_tab_pos (POS_LEFT);
713 _hnotebook.set_tab_pos (POS_TOP);
715 if (h_current_page != -1 && _hnotebook.get_n_pages() > h_current_page) {
716 _hnotebook.set_current_page (h_current_page);
717 } else {
718 _hnotebook.set_current_page (0);
721 if (v_current_page != -1 && _vnotebook.get_n_pages() > v_current_page) {
722 _vnotebook.set_current_page (v_current_page);
723 } else {
724 _vnotebook.set_current_page (0);
727 if (_hnotebook.get_n_pages() <= 1) {
728 _hbox.hide ();
729 } else {
730 _hbox.show ();
733 if (_vnotebook.get_n_pages() <= 1) {
734 _vbox.hide ();
735 } else {
736 _vbox.show ();
740 void
741 PortMatrix::remove_notebook_pages (Notebook& n)
743 int const N = n.get_n_pages ();
745 for (int i = 0; i < N; ++i) {
746 n.remove_page ();
750 void
751 PortMatrix::notebook_page_selected (GtkNotebookPage *, guint)
753 if (_ignore_notebook_page_selected) {
754 return;
757 _body->setup ();
758 setup_scrollbars ();
759 queue_draw ();
762 void
763 PortMatrix::session_going_away ()
765 _session = 0;
768 void
769 PortMatrix::body_dimensions_changed ()
771 _hspacer.set_size_request (_body->column_labels_border_x (), -1);
772 if (_arrangement == TOP_TO_RIGHT) {
773 _vspacer.set_size_request (-1, _body->column_labels_height ());
774 _vspacer.show ();
775 } else {
776 _vspacer.hide ();
782 boost::shared_ptr<const PortGroup>
783 PortMatrix::visible_ports (int d) const
785 PortGroupList const & p = _ports[d];
786 PortGroupList::List::const_iterator j = p.begin ();
788 int n = 0;
789 if (d == _row_index) {
790 n = p.size() - _vnotebook.get_current_page () - 1;
791 } else {
792 n = _hnotebook.get_current_page ();
795 int i = 0;
796 while (i != int (n) && j != p.end ()) {
797 ++i;
798 ++j;
801 if (j == p.end()) {
802 return boost::shared_ptr<const PortGroup> ();
805 return *j;
808 void
809 PortMatrix::add_remove_option (Menu_Helpers::MenuList& m, boost::weak_ptr<Bundle> w, int c)
811 using namespace Menu_Helpers;
813 boost::shared_ptr<Bundle> b = w.lock ();
814 if (!b) {
815 return;
818 char buf [64];
819 snprintf (buf, sizeof (buf), _("Remove '%s'"), escape_underscores (b->channel_name (c)).c_str());
820 m.push_back (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_channel_proxy), w, c)));
823 void
824 PortMatrix::add_disassociate_option (Menu_Helpers::MenuList& m, boost::weak_ptr<Bundle> w, int d, int c)
826 using namespace Menu_Helpers;
828 boost::shared_ptr<Bundle> b = w.lock ();
829 if (!b) {
830 return;
833 char buf [64];
834 snprintf (buf, sizeof (buf), _("%s all from '%s'"), disassociation_verb().c_str(), escape_underscores (b->channel_name (c)).c_str());
835 m.push_back (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, c, d)));
838 void
839 PortMatrix::port_connected_or_disconnected ()
841 _body->rebuild_and_draw_grid ();
844 string
845 PortMatrix::channel_noun () const
847 return _("channel");