changes associated with save/restore of AutomationControl id's
[ardour2.git] / gtk2_ardour / port_matrix.cc
blob052a26c25cf1c2f2cd3e33075f7021b0a05dc254
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 <gtkmm/stock.h>
29 #include "ardour/bundle.h"
30 #include "ardour/types.h"
31 #include "ardour/session.h"
32 #include "ardour/route.h"
33 #include "ardour/audioengine.h"
34 #include "port_matrix.h"
35 #include "port_matrix_body.h"
36 #include "port_matrix_component.h"
37 #include "ardour_dialog.h"
38 #include "i18n.h"
39 #include "gui_thread.h"
40 #include "utils.h"
42 using namespace std;
43 using namespace Gtk;
44 using namespace ARDOUR;
46 /** PortMatrix constructor.
47 * @param session Our session.
48 * @param type Port type that we are handling.
50 PortMatrix::PortMatrix (Window* parent, Session* session, DataType type)
51 : Table (4, 4)
52 , _parent (parent)
53 , _type (type)
54 , _menu (0)
55 , _arrangement (TOP_TO_RIGHT)
56 , _row_index (0)
57 , _column_index (1)
58 , _min_height_divisor (1)
59 , _show_only_bundles (false)
60 , _inhibit_toggle_show_only_bundles (false)
61 , _ignore_notebook_page_selected (false)
63 set_session (session);
65 _body = new PortMatrixBody (this);
66 _body->DimensionsChanged.connect (sigc::mem_fun (*this, &PortMatrix::body_dimensions_changed));
68 _hbox.pack_end (_hspacer, true, true);
69 _hbox.pack_end (_hnotebook, false, false);
70 _hbox.pack_end (_hlabel, false, false);
72 _vnotebook.signal_switch_page().connect (sigc::mem_fun (*this, &PortMatrix::notebook_page_selected));
73 _vnotebook.property_tab_border() = 4;
74 _vnotebook.set_name (X_("PortMatrixLabel"));
75 _hnotebook.signal_switch_page().connect (sigc::mem_fun (*this, &PortMatrix::notebook_page_selected));
76 _hnotebook.property_tab_border() = 4;
77 _hnotebook.set_name (X_("PortMatrixLabel"));
79 _vlabel.set_use_markup ();
80 _vlabel.set_alignment (1, 1);
81 _vlabel.set_padding (4, 16);
82 _vlabel.set_name (X_("PortMatrixLabel"));
83 _hlabel.set_use_markup ();
84 _hlabel.set_alignment (1, 0.5);
85 _hlabel.set_padding (16, 4);
86 _hlabel.set_name (X_("PortMatrixLabel"));
88 set_row_spacing (0, 8);
89 set_col_spacing (0, 8);
90 set_row_spacing (2, 8);
91 set_col_spacing (2, 8);
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());
158 /* watch for route order keys changing, which changes the order of things in our global ports list(s) */
159 _session->RouteOrderKeyChanged.connect (_session_connections, invalidator (*this), boost::bind (&PortMatrix::setup_global_ports_proxy, this), gui_context());
161 /* Part 3: other stuff */
163 _session->engine().PortConnectedOrDisconnected.connect (_session_connections, invalidator (*this), boost::bind (&PortMatrix::port_connected_or_disconnected, this), gui_context ());
165 _hscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::hscroll_changed));
166 _vscroll.signal_value_changed().connect (sigc::mem_fun (*this, &PortMatrix::vscroll_changed));
168 reconnect_to_routes ();
170 setup ();
173 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
174 void
175 PortMatrix::reconnect_to_routes ()
177 _route_connections.drop_connections ();
179 boost::shared_ptr<RouteList> routes = _session->get_routes ();
180 for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
181 (*i)->processors_changed.connect (_route_connections, invalidator (*this), ui_bind (&PortMatrix::route_processors_changed, this, _1), gui_context());
185 void
186 PortMatrix::route_processors_changed (RouteProcessorChange c)
188 if (c.type == RouteProcessorChange::MeterPointChange) {
189 /* this change has no impact on the port matrix */
190 return;
193 setup_global_ports ();
196 /** A route has been added to or removed from the session */
197 void
198 PortMatrix::routes_changed ()
200 reconnect_to_routes ();
201 setup_global_ports ();
204 /** Set up everything that depends on the content of _ports[] */
205 void
206 PortMatrix::setup ()
208 /* this needs to be done first, as the visible_ports() method uses the
209 notebook state to decide which ports are being shown */
211 setup_notebooks ();
213 _body->setup ();
214 setup_scrollbars ();
215 queue_draw ();
218 void
219 PortMatrix::set_type (DataType t)
221 _type = t;
224 void
225 PortMatrix::hscroll_changed ()
227 _body->set_xoffset (_hscroll.get_adjustment()->get_value());
230 void
231 PortMatrix::vscroll_changed ()
233 _body->set_yoffset (_vscroll.get_adjustment()->get_value());
236 void
237 PortMatrix::setup_scrollbars ()
239 Adjustment* a = _hscroll.get_adjustment ();
240 a->set_lower (0);
241 a->set_upper (_body->full_scroll_width());
242 a->set_page_size (_body->alloc_scroll_width());
243 a->set_step_increment (32);
244 a->set_page_increment (128);
246 a = _vscroll.get_adjustment ();
247 a->set_lower (0);
248 a->set_upper (_body->full_scroll_height());
249 a->set_page_size (_body->alloc_scroll_height());
250 a->set_step_increment (32);
251 a->set_page_increment (128);
254 /** Disassociate all of our ports from each other */
255 void
256 PortMatrix::disassociate_all ()
258 PortGroup::BundleList a = _ports[0].bundles ();
259 PortGroup::BundleList b = _ports[1].bundles ();
261 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
262 for (uint32_t j = 0; j < (*i)->bundle->nchannels().n_total(); ++j) {
263 for (PortGroup::BundleList::iterator k = b.begin(); k != b.end(); ++k) {
264 for (uint32_t l = 0; l < (*k)->bundle->nchannels().n_total(); ++l) {
266 if (!should_show ((*i)->bundle->channel_type(j)) || !should_show ((*k)->bundle->channel_type(l))) {
267 continue;
270 BundleChannel c[2] = {
271 BundleChannel ((*i)->bundle, j),
272 BundleChannel ((*k)->bundle, l)
275 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
276 set_state (c, false);
284 _body->rebuild_and_draw_grid ();
287 /* Decide how to arrange the components of the matrix */
288 void
289 PortMatrix::select_arrangement ()
291 uint32_t const N[2] = {
292 count_of_our_type (_ports[0].total_channels()),
293 count_of_our_type (_ports[1].total_channels())
296 /* XXX: shirley there's an easier way than this */
298 if (_vspacer.get_parent()) {
299 _vbox.remove (_vspacer);
302 if (_vnotebook.get_parent()) {
303 _vbox.remove (_vnotebook);
306 if (_vlabel.get_parent()) {
307 _vbox.remove (_vlabel);
310 /* The list with the most channels goes on left or right, so that the most channel
311 names are printed horizontally and hence more readable. However we also
312 maintain notional `signal flow' vaguely from left to right. Subclasses
313 should choose where to put ports based on signal flowing from _ports[0]
314 to _ports[1] */
316 if (N[0] > N[1]) {
318 _row_index = 0;
319 _column_index = 1;
320 _arrangement = LEFT_TO_BOTTOM;
321 _vlabel.set_label (_("<b>Sources</b>"));
322 _hlabel.set_label (_("<b>Destinations</b>"));
323 _vlabel.set_angle (90);
325 _vbox.pack_end (_vlabel, false, false);
326 _vbox.pack_end (_vnotebook, false, false);
327 _vbox.pack_end (_vspacer, true, true);
329 attach (*_body, 2, 3, 1, 2, FILL | EXPAND, FILL | EXPAND);
330 attach (_vscroll, 3, 4, 1, 2, SHRINK);
331 attach (_hscroll, 2, 3, 3, 4, FILL | EXPAND, SHRINK);
332 attach (_vbox, 1, 2, 1, 2, SHRINK);
333 attach (_hbox, 2, 3, 2, 3, FILL | EXPAND, SHRINK);
335 } else {
337 _row_index = 1;
338 _column_index = 0;
339 _arrangement = TOP_TO_RIGHT;
340 _hlabel.set_label (_("<b>Sources</b>"));
341 _vlabel.set_label (_("<b>Destinations</b>"));
342 _vlabel.set_angle (-90);
344 _vbox.pack_end (_vspacer, true, true);
345 _vbox.pack_end (_vnotebook, false, false);
346 _vbox.pack_end (_vlabel, false, false);
348 attach (*_body, 1, 2, 2, 3, FILL | EXPAND, FILL | EXPAND);
349 attach (_vscroll, 3, 4, 2, 3, SHRINK);
350 attach (_hscroll, 1, 2, 3, 4, FILL | EXPAND, SHRINK);
351 attach (_vbox, 2, 3, 2, 3, SHRINK);
352 attach (_hbox, 1, 2, 1, 2, FILL | EXPAND, SHRINK);
356 /** @return columns list */
357 PortGroupList const *
358 PortMatrix::columns () const
360 return &_ports[_column_index];
363 boost::shared_ptr<const PortGroup>
364 PortMatrix::visible_columns () const
366 return visible_ports (_column_index);
369 /* @return rows list */
370 PortGroupList const *
371 PortMatrix::rows () const
373 return &_ports[_row_index];
376 boost::shared_ptr<const PortGroup>
377 PortMatrix::visible_rows () const
379 return visible_ports (_row_index);
382 void
383 PortMatrix::popup_menu (BundleChannel column, BundleChannel row, uint32_t t)
385 using namespace Menu_Helpers;
387 delete _menu;
389 _menu = new Menu;
390 _menu->set_name ("ArdourContextMenu");
392 MenuList& items = _menu->items ();
394 BundleChannel bc[2];
395 bc[_column_index] = column;
396 bc[_row_index] = row;
398 char buf [64];
399 bool need_separator = false;
401 for (int dim = 0; dim < 2; ++dim) {
403 if (bc[dim].bundle) {
405 Menu* m = manage (new Menu);
406 MenuList& sub = m->items ();
408 boost::weak_ptr<Bundle> w (bc[dim].bundle);
410 bool can_add_or_rename = false;
412 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
413 if (should_show (*i)) {
414 snprintf (buf, sizeof (buf), _("Add %s %s"), (*i).to_i18n_string(), channel_noun().c_str());
415 sub.push_back (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::add_channel_proxy), w, *i)));
416 can_add_or_rename = true;
420 if (can_rename_channels (bc[dim].bundle)) {
421 snprintf (
422 buf, sizeof (buf), _("Rename '%s'..."),
423 escape_underscores (bc[dim].bundle->channel_name (bc[dim].channel)).c_str()
425 sub.push_back (
426 MenuElem (
427 buf,
428 sigc::bind (sigc::mem_fun (*this, &PortMatrix::rename_channel_proxy), w, bc[dim].channel)
431 can_add_or_rename = true;
434 if (can_add_or_rename) {
435 sub.push_back (SeparatorElem ());
438 if (can_remove_channels (bc[dim].bundle)) {
439 if (bc[dim].channel != -1) {
440 add_remove_option (sub, w, bc[dim].channel);
441 } else {
443 snprintf (buf, sizeof (buf), _("Remove all"));
444 sub.push_back (
445 MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_all_channels), w))
448 for (uint32_t i = 0; i < bc[dim].bundle->nchannels().n_total(); ++i) {
449 if (should_show (bc[dim].bundle->channel_type(i))) {
450 add_remove_option (sub, w, i);
456 if (_show_only_bundles || count_of_our_type (bc[dim].bundle->nchannels()) <= 1) {
457 snprintf (buf, sizeof (buf), _("%s all"), disassociation_verb().c_str());
458 sub.push_back (
459 MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, bc[dim].channel, dim))
462 } else {
464 if (bc[dim].channel != -1) {
465 add_disassociate_option (sub, w, dim, bc[dim].channel);
466 } else {
467 snprintf (buf, sizeof (buf), _("%s all"), disassociation_verb().c_str());
468 sub.push_back (
469 MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_bundle), w, dim))
472 for (uint32_t i = 0; i < bc[dim].bundle->nchannels().n_total(); ++i) {
473 if (should_show (bc[dim].bundle->channel_type(i))) {
474 add_disassociate_option (sub, w, dim, i);
480 items.push_back (MenuElem (escape_underscores (bc[dim].bundle->name()).c_str(), *m));
481 need_separator = true;
486 if (need_separator) {
487 items.push_back (SeparatorElem ());
490 items.push_back (MenuElem (_("Rescan"), sigc::mem_fun (*this, &PortMatrix::setup_all_ports)));
491 items.push_back (CheckMenuElem (_("Show individual ports"), sigc::mem_fun (*this, &PortMatrix::toggle_show_only_bundles)));
492 CheckMenuItem* i = dynamic_cast<CheckMenuItem*> (&items.back());
493 _inhibit_toggle_show_only_bundles = true;
494 i->set_active (!_show_only_bundles);
495 _inhibit_toggle_show_only_bundles = false;
497 _menu->popup (1, t);
500 void
501 PortMatrix::remove_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
503 boost::shared_ptr<Bundle> sb = b.lock ();
504 if (!sb) {
505 return;
508 remove_channel (BundleChannel (sb, c));
512 void
513 PortMatrix::rename_channel_proxy (boost::weak_ptr<Bundle> b, uint32_t c)
515 boost::shared_ptr<Bundle> sb = b.lock ();
516 if (!sb) {
517 return;
520 rename_channel (BundleChannel (sb, c));
523 void
524 PortMatrix::disassociate_all_on_bundle (boost::weak_ptr<Bundle> bundle, int dim)
526 boost::shared_ptr<Bundle> sb = bundle.lock ();
527 if (!sb) {
528 return;
531 for (uint32_t i = 0; i < sb->nchannels().n_total(); ++i) {
532 if (should_show (sb->channel_type(i))) {
533 disassociate_all_on_channel (bundle, i, dim);
538 void
539 PortMatrix::disassociate_all_on_channel (boost::weak_ptr<Bundle> bundle, uint32_t channel, int dim)
541 boost::shared_ptr<Bundle> sb = bundle.lock ();
542 if (!sb) {
543 return;
546 PortGroup::BundleList a = _ports[1-dim].bundles ();
548 for (PortGroup::BundleList::iterator i = a.begin(); i != a.end(); ++i) {
549 for (uint32_t j = 0; j < (*i)->bundle->nchannels().n_total(); ++j) {
551 if (should_show ((*i)->bundle->channel_type(j))) {
552 continue;
555 BundleChannel c[2];
556 c[dim] = BundleChannel (sb, channel);
557 c[1-dim] = BundleChannel ((*i)->bundle, j);
559 if (get_state (c) == PortMatrixNode::ASSOCIATED) {
560 set_state (c, false);
565 _body->rebuild_and_draw_grid ();
568 void
569 PortMatrix::setup_global_ports ()
571 ENSURE_GUI_THREAD (*this, &PortMatrix::setup_global_ports)
573 for (int i = 0; i < 2; ++i) {
574 if (list_is_global (i)) {
575 setup_ports (i);
580 void
581 PortMatrix::setup_global_ports_proxy ()
583 /* Avoid a deadlock by calling this in an idle handler: see IOSelector::io_changed_proxy
584 for a discussion.
587 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &PortMatrix::setup_global_ports));
590 void
591 PortMatrix::setup_all_ports ()
593 if (_session->deletion_in_progress()) {
594 return;
597 ENSURE_GUI_THREAD (*this, &PortMatrix::setup_all_ports)
599 setup_ports (0);
600 setup_ports (1);
603 void
604 PortMatrix::toggle_show_only_bundles ()
606 if (_inhibit_toggle_show_only_bundles) {
607 return;
610 _show_only_bundles = !_show_only_bundles;
612 setup ();
615 pair<uint32_t, uint32_t>
616 PortMatrix::max_size () const
618 pair<uint32_t, uint32_t> m = _body->max_size ();
620 m.first += _vscroll.get_width () + _vbox.get_width () + 4;
621 m.second += _hscroll.get_height () + _hbox.get_height () + 4;
623 return m;
626 bool
627 PortMatrix::on_scroll_event (GdkEventScroll* ev)
629 double const h = _hscroll.get_value ();
630 double const v = _vscroll.get_value ();
632 switch (ev->direction) {
633 case GDK_SCROLL_UP:
634 _vscroll.set_value (v - PortMatrixComponent::grid_spacing ());
635 break;
636 case GDK_SCROLL_DOWN:
637 _vscroll.set_value (v + PortMatrixComponent::grid_spacing ());
638 break;
639 case GDK_SCROLL_LEFT:
640 _hscroll.set_value (h - PortMatrixComponent::grid_spacing ());
641 break;
642 case GDK_SCROLL_RIGHT:
643 _hscroll.set_value (h + PortMatrixComponent::grid_spacing ());
644 break;
647 return true;
650 boost::shared_ptr<IO>
651 PortMatrix::io_from_bundle (boost::shared_ptr<Bundle> b) const
653 boost::shared_ptr<IO> io = _ports[0].io_from_bundle (b);
654 if (!io) {
655 io = _ports[1].io_from_bundle (b);
658 return io;
661 bool
662 PortMatrix::can_add_channel (boost::shared_ptr<Bundle> b) const
664 return io_from_bundle (b);
667 void
668 PortMatrix::add_channel (boost::shared_ptr<Bundle> b, DataType t)
670 boost::shared_ptr<IO> io = io_from_bundle (b);
672 if (io) {
673 io->add_port ("", this, t);
677 bool
678 PortMatrix::can_remove_channels (boost::shared_ptr<Bundle> b) const
680 return io_from_bundle (b);
683 void
684 PortMatrix::remove_channel (ARDOUR::BundleChannel b)
686 boost::shared_ptr<IO> io = io_from_bundle (b.bundle);
688 if (io) {
689 Port* p = io->nth (b.channel);
690 if (p) {
691 int const r = io->remove_port (p, this);
692 if (r == -1) {
693 ArdourDialog d (_("Port removal not allowed"));
694 Label l (_("This port cannot be removed, as the first plugin in the track or buss cannot accept the new number of inputs."));
695 d.get_vbox()->pack_start (l);
696 d.add_button (Stock::OK, RESPONSE_ACCEPT);
697 d.set_modal (true);
698 d.show_all ();
699 d.run ();
705 void
706 PortMatrix::remove_all_channels (boost::weak_ptr<Bundle> w)
708 boost::shared_ptr<Bundle> b = w.lock ();
709 if (!b) {
710 return;
713 for (uint32_t i = 0; i < b->nchannels().n_total(); ++i) {
714 if (should_show (b->channel_type(i))) {
715 remove_channel (ARDOUR::BundleChannel (b, i));
720 void
721 PortMatrix::add_channel_proxy (boost::weak_ptr<Bundle> w, DataType t)
723 boost::shared_ptr<Bundle> b = w.lock ();
724 if (!b) {
725 return;
728 add_channel (b, t);
731 void
732 PortMatrix::setup_notebooks ()
734 int const h_current_page = _hnotebook.get_current_page ();
735 int const v_current_page = _vnotebook.get_current_page ();
737 /* for some reason best known to GTK, erroneous switch_page signals seem to be generated
738 when adding or removing pages to or from notebooks, so ignore them */
740 _ignore_notebook_page_selected = true;
742 remove_notebook_pages (_hnotebook);
743 remove_notebook_pages (_vnotebook);
745 for (PortGroupList::List::const_iterator i = _ports[_row_index].begin(); i != _ports[_row_index].end(); ++i) {
746 HBox* dummy = manage (new HBox);
747 dummy->show ();
748 Label* label = manage (new Label ((*i)->name));
749 label->set_angle (_arrangement == LEFT_TO_BOTTOM ? 90 : -90);
750 label->show ();
751 _vnotebook.prepend_page (*dummy, *label);
754 for (PortGroupList::List::const_iterator i = _ports[_column_index].begin(); i != _ports[_column_index].end(); ++i) {
755 HBox* dummy = manage (new HBox);
756 dummy->show ();
757 _hnotebook.append_page (*dummy, (*i)->name);
760 _ignore_notebook_page_selected = false;
762 if (_arrangement == TOP_TO_RIGHT) {
763 _vnotebook.set_tab_pos (POS_RIGHT);
764 _hnotebook.set_tab_pos (POS_TOP);
765 } else {
766 _vnotebook.set_tab_pos (POS_LEFT);
767 _hnotebook.set_tab_pos (POS_BOTTOM);
770 if (h_current_page != -1 && _hnotebook.get_n_pages() > h_current_page) {
771 _hnotebook.set_current_page (h_current_page);
772 } else {
773 _hnotebook.set_current_page (0);
776 if (v_current_page != -1 && _vnotebook.get_n_pages() > v_current_page) {
777 _vnotebook.set_current_page (v_current_page);
778 } else {
779 _vnotebook.set_current_page (0);
782 if (_hnotebook.get_n_pages() <= 1) {
783 _hbox.hide ();
784 } else {
785 _hbox.show ();
788 if (_vnotebook.get_n_pages() <= 1) {
789 _vbox.hide ();
790 } else {
791 _vbox.show ();
795 void
796 PortMatrix::remove_notebook_pages (Notebook& n)
798 int const N = n.get_n_pages ();
800 for (int i = 0; i < N; ++i) {
801 n.remove_page ();
805 void
806 PortMatrix::notebook_page_selected (GtkNotebookPage *, guint)
808 if (_ignore_notebook_page_selected) {
809 return;
812 _body->setup ();
813 setup_scrollbars ();
814 queue_draw ();
817 void
818 PortMatrix::session_going_away ()
820 _session = 0;
823 void
824 PortMatrix::body_dimensions_changed ()
826 _hspacer.set_size_request (_body->column_labels_border_x (), -1);
827 if (_arrangement == TOP_TO_RIGHT) {
828 _vspacer.set_size_request (-1, _body->column_labels_height ());
829 _vspacer.show ();
830 } else {
831 _vspacer.hide ();
834 int curr_width;
835 int curr_height;
836 _parent->get_size (curr_width, curr_height);
838 pair<uint32_t, uint32_t> m = max_size ();
840 /* Don't shrink the window */
841 m.first = max (int (m.first), curr_width);
842 m.second = max (int (m.second), curr_height);
844 resize_window_to_proportion_of_monitor (_parent, m.first, m.second);
848 boost::shared_ptr<const PortGroup>
849 PortMatrix::visible_ports (int d) const
851 PortGroupList const & p = _ports[d];
852 PortGroupList::List::const_iterator j = p.begin ();
854 int n = 0;
855 if (d == _row_index) {
856 n = p.size() - _vnotebook.get_current_page () - 1;
857 } else {
858 n = _hnotebook.get_current_page ();
861 int i = 0;
862 while (i != int (n) && j != p.end ()) {
863 ++i;
864 ++j;
867 if (j == p.end()) {
868 return boost::shared_ptr<const PortGroup> ();
871 return *j;
874 void
875 PortMatrix::add_remove_option (Menu_Helpers::MenuList& m, boost::weak_ptr<Bundle> w, int c)
877 using namespace Menu_Helpers;
879 boost::shared_ptr<Bundle> b = w.lock ();
880 if (!b) {
881 return;
884 char buf [64];
885 snprintf (buf, sizeof (buf), _("Remove '%s'"), escape_underscores (b->channel_name (c)).c_str());
886 m.push_back (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_channel_proxy), w, c)));
889 void
890 PortMatrix::add_disassociate_option (Menu_Helpers::MenuList& m, boost::weak_ptr<Bundle> w, int d, int c)
892 using namespace Menu_Helpers;
894 boost::shared_ptr<Bundle> b = w.lock ();
895 if (!b) {
896 return;
899 char buf [64];
900 snprintf (buf, sizeof (buf), _("%s all from '%s'"), disassociation_verb().c_str(), escape_underscores (b->channel_name (c)).c_str());
901 m.push_back (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_channel), w, c, d)));
904 void
905 PortMatrix::port_connected_or_disconnected ()
907 _body->rebuild_and_draw_grid ();
910 string
911 PortMatrix::channel_noun () const
913 return _("channel");
916 /** @return true if this matrix should show bundles / ports of type \t */
917 bool
918 PortMatrix::should_show (DataType t) const
920 return (_type == DataType::NIL || t == _type);
923 uint32_t
924 PortMatrix::count_of_our_type (ChanCount c) const
926 if (_type == DataType::NIL) {
927 return c.n_total ();
930 return c.get (_type);