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 <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"
39 #include "gui_thread.h"
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
)
55 , _arrangement (TOP_TO_RIGHT
)
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);
106 PortMatrix::~PortMatrix ()
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
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());
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 ();
173 /** Disconnect from and reconnect to routes' signals that we need to watch for things that affect the matrix */
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());
186 PortMatrix::route_processors_changed (RouteProcessorChange c
)
188 if (c
.type
== RouteProcessorChange::MeterPointChange
) {
189 /* this change has no impact on the port matrix */
193 setup_global_ports ();
196 /** A route has been added to or removed from the session */
198 PortMatrix::routes_changed ()
200 reconnect_to_routes ();
201 setup_global_ports ();
204 /** Set up everything that depends on the content of _ports[] */
208 /* this needs to be done first, as the visible_ports() method uses the
209 notebook state to decide which ports are being shown */
219 PortMatrix::set_type (DataType t
)
225 PortMatrix::hscroll_changed ()
227 _body
->set_xoffset (_hscroll
.get_adjustment()->get_value());
231 PortMatrix::vscroll_changed ()
233 _body
->set_yoffset (_vscroll
.get_adjustment()->get_value());
237 PortMatrix::setup_scrollbars ()
239 Adjustment
* a
= _hscroll
.get_adjustment ();
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 ();
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 */
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
))) {
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 */
289 PortMatrix::select_arrangement ()
291 uint32_t const N
[2] = {
292 count_of_our_type_min_1 (_ports
[0].total_channels()),
293 count_of_our_type_min_1 (_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]
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
);
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
);
383 PortMatrix::popup_menu (BundleChannel column
, BundleChannel row
, uint32_t t
)
385 using namespace Menu_Helpers
;
390 _menu
->set_name ("ArdourContextMenu");
392 MenuList
& items
= _menu
->items ();
395 bc
[_column_index
] = column
;
396 bc
[_row_index
] = row
;
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 /* Start off with options for the `natural' port type */
411 for (DataType::iterator i
= DataType::begin(); i
!= DataType::end(); ++i
) {
412 if (should_show (*i
)) {
413 snprintf (buf
, sizeof (buf
), _("Add %s %s"), (*i
).to_i18n_string(), channel_noun().c_str());
414 sub
.push_back (MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::add_channel_proxy
), w
, *i
)));
418 /* Now add other ones */
419 for (DataType::iterator i
= DataType::begin(); i
!= DataType::end(); ++i
) {
420 if (!should_show (*i
)) {
421 snprintf (buf
, sizeof (buf
), _("Add %s %s"), (*i
).to_i18n_string(), channel_noun().c_str());
422 sub
.push_back (MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::add_channel_proxy
), w
, *i
)));
426 if (can_rename_channels (bc
[dim
].bundle
)) {
428 buf
, sizeof (buf
), _("Rename '%s'..."),
429 escape_underscores (bc
[dim
].bundle
->channel_name (bc
[dim
].channel
)).c_str()
434 sigc::bind (sigc::mem_fun (*this, &PortMatrix::rename_channel_proxy
), w
, bc
[dim
].channel
)
439 if (can_remove_channels (bc
[dim
].bundle
) && bc
[dim
].bundle
->nchannels() != ARDOUR::ChanCount::ZERO
) {
440 if (bc
[dim
].channel
!= -1) {
441 add_remove_option (sub
, w
, bc
[dim
].channel
);
444 snprintf (buf
, sizeof (buf
), _("Remove all"));
446 MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_all_channels
), w
))
449 if (bc
[dim
].bundle
->nchannels().n_total() > 1) {
450 for (uint32_t i
= 0; i
< bc
[dim
].bundle
->nchannels().n_total(); ++i
) {
451 if (should_show (bc
[dim
].bundle
->channel_type(i
))) {
452 add_remove_option (sub
, w
, i
);
459 uint32_t c
= count_of_our_type (bc
[dim
].bundle
->nchannels ());
460 if ((_show_only_bundles
&& c
> 0) || c
== 1) {
461 snprintf (buf
, sizeof (buf
), _("%s all"), disassociation_verb().c_str());
463 MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_channel
), w
, bc
[dim
].channel
, dim
))
468 if (bc
[dim
].channel
!= -1) {
469 add_disassociate_option (sub
, w
, dim
, bc
[dim
].channel
);
470 } else if (count_of_our_type (bc
[dim
].bundle
->nchannels()) != 0) {
471 snprintf (buf
, sizeof (buf
), _("%s all"), disassociation_verb().c_str());
473 MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_bundle
), w
, dim
))
476 for (uint32_t i
= 0; i
< bc
[dim
].bundle
->nchannels().n_total(); ++i
) {
477 if (should_show (bc
[dim
].bundle
->channel_type(i
))) {
478 add_disassociate_option (sub
, w
, dim
, i
);
484 items
.push_back (MenuElem (escape_underscores (bc
[dim
].bundle
->name()).c_str(), *m
));
485 need_separator
= true;
490 if (need_separator
) {
491 items
.push_back (SeparatorElem ());
494 items
.push_back (MenuElem (_("Rescan"), sigc::mem_fun (*this, &PortMatrix::setup_all_ports
)));
495 items
.push_back (CheckMenuElem (_("Show individual ports"), sigc::mem_fun (*this, &PortMatrix::toggle_show_only_bundles
)));
496 CheckMenuItem
* i
= dynamic_cast<CheckMenuItem
*> (&items
.back());
497 _inhibit_toggle_show_only_bundles
= true;
498 i
->set_active (!_show_only_bundles
);
499 _inhibit_toggle_show_only_bundles
= false;
505 PortMatrix::remove_channel_proxy (boost::weak_ptr
<Bundle
> b
, uint32_t c
)
507 boost::shared_ptr
<Bundle
> sb
= b
.lock ();
512 remove_channel (BundleChannel (sb
, c
));
517 PortMatrix::rename_channel_proxy (boost::weak_ptr
<Bundle
> b
, uint32_t c
)
519 boost::shared_ptr
<Bundle
> sb
= b
.lock ();
524 rename_channel (BundleChannel (sb
, c
));
528 PortMatrix::disassociate_all_on_bundle (boost::weak_ptr
<Bundle
> bundle
, int dim
)
530 boost::shared_ptr
<Bundle
> sb
= bundle
.lock ();
535 for (uint32_t i
= 0; i
< sb
->nchannels().n_total(); ++i
) {
536 if (should_show (sb
->channel_type(i
))) {
537 disassociate_all_on_channel (bundle
, i
, dim
);
543 PortMatrix::disassociate_all_on_channel (boost::weak_ptr
<Bundle
> bundle
, uint32_t channel
, int dim
)
545 boost::shared_ptr
<Bundle
> sb
= bundle
.lock ();
550 PortGroup::BundleList a
= _ports
[1-dim
].bundles ();
552 for (PortGroup::BundleList::iterator i
= a
.begin(); i
!= a
.end(); ++i
) {
553 for (uint32_t j
= 0; j
< (*i
)->bundle
->nchannels().n_total(); ++j
) {
555 if (should_show ((*i
)->bundle
->channel_type(j
))) {
560 c
[dim
] = BundleChannel (sb
, channel
);
561 c
[1-dim
] = BundleChannel ((*i
)->bundle
, j
);
563 if (get_state (c
) == PortMatrixNode::ASSOCIATED
) {
564 set_state (c
, false);
569 _body
->rebuild_and_draw_grid ();
573 PortMatrix::setup_global_ports ()
575 ENSURE_GUI_THREAD (*this, &PortMatrix::setup_global_ports
)
577 for (int i
= 0; i
< 2; ++i
) {
578 if (list_is_global (i
)) {
585 PortMatrix::setup_global_ports_proxy ()
587 /* Avoid a deadlock by calling this in an idle handler: see IOSelector::io_changed_proxy
591 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &PortMatrix::setup_global_ports
));
595 PortMatrix::setup_all_ports ()
597 if (_session
->deletion_in_progress()) {
601 ENSURE_GUI_THREAD (*this, &PortMatrix::setup_all_ports
)
608 PortMatrix::toggle_show_only_bundles ()
610 if (_inhibit_toggle_show_only_bundles
) {
614 _show_only_bundles
= !_show_only_bundles
;
619 pair
<uint32_t, uint32_t>
620 PortMatrix::max_size () const
622 pair
<uint32_t, uint32_t> m
= _body
->max_size ();
624 m
.first
+= _vscroll
.get_width () + _vbox
.get_width () + 4;
625 m
.second
+= _hscroll
.get_height () + _hbox
.get_height () + 4;
631 PortMatrix::on_scroll_event (GdkEventScroll
* ev
)
633 double const h
= _hscroll
.get_value ();
634 double const v
= _vscroll
.get_value ();
636 switch (ev
->direction
) {
638 _vscroll
.set_value (v
- PortMatrixComponent::grid_spacing ());
640 case GDK_SCROLL_DOWN
:
641 _vscroll
.set_value (v
+ PortMatrixComponent::grid_spacing ());
643 case GDK_SCROLL_LEFT
:
644 _hscroll
.set_value (h
- PortMatrixComponent::grid_spacing ());
646 case GDK_SCROLL_RIGHT
:
647 _hscroll
.set_value (h
+ PortMatrixComponent::grid_spacing ());
654 boost::shared_ptr
<IO
>
655 PortMatrix::io_from_bundle (boost::shared_ptr
<Bundle
> b
) const
657 boost::shared_ptr
<IO
> io
= _ports
[0].io_from_bundle (b
);
659 io
= _ports
[1].io_from_bundle (b
);
666 PortMatrix::can_add_channel (boost::shared_ptr
<Bundle
> b
) const
668 return io_from_bundle (b
);
672 PortMatrix::add_channel (boost::shared_ptr
<Bundle
> b
, DataType t
)
674 boost::shared_ptr
<IO
> io
= io_from_bundle (b
);
677 io
->add_port ("", this, t
);
682 PortMatrix::can_remove_channels (boost::shared_ptr
<Bundle
> b
) const
684 return io_from_bundle (b
);
688 PortMatrix::remove_channel (ARDOUR::BundleChannel b
)
690 boost::shared_ptr
<IO
> io
= io_from_bundle (b
.bundle
);
693 Port
* p
= io
->nth (b
.channel
);
695 int const r
= io
->remove_port (p
, this);
697 ArdourDialog
d (_("Port removal not allowed"));
698 Label
l (_("This port cannot be removed, as the first plugin in the track or buss cannot accept the new number of inputs."));
699 d
.get_vbox()->pack_start (l
);
700 d
.add_button (Stock::OK
, RESPONSE_ACCEPT
);
710 PortMatrix::remove_all_channels (boost::weak_ptr
<Bundle
> w
)
712 boost::shared_ptr
<Bundle
> b
= w
.lock ();
717 /* Remove channels backwards so that we don't renumber channels
718 that we are about to remove.
720 for (int i
= (b
->nchannels().n_total() - 1); i
>= 0; --i
) {
721 if (should_show (b
->channel_type(i
))) {
722 remove_channel (ARDOUR::BundleChannel (b
, i
));
728 PortMatrix::add_channel_proxy (boost::weak_ptr
<Bundle
> w
, DataType t
)
730 boost::shared_ptr
<Bundle
> b
= w
.lock ();
739 PortMatrix::setup_notebooks ()
741 int const h_current_page
= _hnotebook
.get_current_page ();
742 int const v_current_page
= _vnotebook
.get_current_page ();
744 /* for some reason best known to GTK, erroneous switch_page signals seem to be generated
745 when adding or removing pages to or from notebooks, so ignore them */
747 _ignore_notebook_page_selected
= true;
749 remove_notebook_pages (_hnotebook
);
750 remove_notebook_pages (_vnotebook
);
752 for (PortGroupList::List::const_iterator i
= _ports
[_row_index
].begin(); i
!= _ports
[_row_index
].end(); ++i
) {
753 HBox
* dummy
= manage (new HBox
);
755 Label
* label
= manage (new Label ((*i
)->name
));
756 label
->set_angle (_arrangement
== LEFT_TO_BOTTOM
? 90 : -90);
758 _vnotebook
.prepend_page (*dummy
, *label
);
761 for (PortGroupList::List::const_iterator i
= _ports
[_column_index
].begin(); i
!= _ports
[_column_index
].end(); ++i
) {
762 HBox
* dummy
= manage (new HBox
);
764 _hnotebook
.append_page (*dummy
, (*i
)->name
);
767 _ignore_notebook_page_selected
= false;
769 if (_arrangement
== TOP_TO_RIGHT
) {
770 _vnotebook
.set_tab_pos (POS_RIGHT
);
771 _hnotebook
.set_tab_pos (POS_TOP
);
773 _vnotebook
.set_tab_pos (POS_LEFT
);
774 _hnotebook
.set_tab_pos (POS_BOTTOM
);
777 if (h_current_page
!= -1 && _hnotebook
.get_n_pages() > h_current_page
) {
778 _hnotebook
.set_current_page (h_current_page
);
780 _hnotebook
.set_current_page (0);
783 if (v_current_page
!= -1 && _vnotebook
.get_n_pages() > v_current_page
) {
784 _vnotebook
.set_current_page (v_current_page
);
786 _vnotebook
.set_current_page (0);
789 if (_hnotebook
.get_n_pages() <= 1) {
795 if (_vnotebook
.get_n_pages() <= 1) {
803 PortMatrix::remove_notebook_pages (Notebook
& n
)
805 int const N
= n
.get_n_pages ();
807 for (int i
= 0; i
< N
; ++i
) {
813 PortMatrix::notebook_page_selected (GtkNotebookPage
*, guint
)
815 if (_ignore_notebook_page_selected
) {
825 PortMatrix::session_going_away ()
831 PortMatrix::body_dimensions_changed ()
833 _hspacer
.set_size_request (_body
->column_labels_border_x (), -1);
834 if (_arrangement
== TOP_TO_RIGHT
) {
835 _vspacer
.set_size_request (-1, _body
->column_labels_height ());
843 _parent
->get_size (curr_width
, curr_height
);
845 pair
<uint32_t, uint32_t> m
= max_size ();
847 /* Don't shrink the window */
848 m
.first
= max (int (m
.first
), curr_width
);
849 m
.second
= max (int (m
.second
), curr_height
);
851 resize_window_to_proportion_of_monitor (_parent
, m
.first
, m
.second
);
854 /** @return The PortGroup that is currently visible (ie selected by
855 * the notebook) along a given axis.
857 boost::shared_ptr
<const PortGroup
>
858 PortMatrix::visible_ports (int d
) const
860 PortGroupList
const & p
= _ports
[d
];
861 PortGroupList::List::const_iterator j
= p
.begin ();
864 if (d
== _row_index
) {
865 n
= p
.size() - _vnotebook
.get_current_page () - 1;
867 n
= _hnotebook
.get_current_page ();
871 while (i
!= int (n
) && j
!= p
.end ()) {
877 return boost::shared_ptr
<const PortGroup
> ();
884 PortMatrix::add_remove_option (Menu_Helpers::MenuList
& m
, boost::weak_ptr
<Bundle
> w
, int c
)
886 using namespace Menu_Helpers
;
888 boost::shared_ptr
<Bundle
> b
= w
.lock ();
894 snprintf (buf
, sizeof (buf
), _("Remove '%s'"), escape_underscores (b
->channel_name (c
)).c_str());
895 m
.push_back (MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_channel_proxy
), w
, c
)));
899 PortMatrix::add_disassociate_option (Menu_Helpers::MenuList
& m
, boost::weak_ptr
<Bundle
> w
, int d
, int c
)
901 using namespace Menu_Helpers
;
903 boost::shared_ptr
<Bundle
> b
= w
.lock ();
909 snprintf (buf
, sizeof (buf
), _("%s all from '%s'"), disassociation_verb().c_str(), escape_underscores (b
->channel_name (c
)).c_str());
910 m
.push_back (MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_channel
), w
, c
, d
)));
914 PortMatrix::port_connected_or_disconnected ()
916 _body
->rebuild_and_draw_grid ();
920 PortMatrix::channel_noun () const
925 /** @return true if this matrix should show bundles / ports of type \t */
927 PortMatrix::should_show (DataType t
) const
929 return (_type
== DataType::NIL
|| t
== _type
);
933 PortMatrix::count_of_our_type (ChanCount c
) const
935 if (_type
== DataType::NIL
) {
939 return c
.get (_type
);
942 /** @return The number of ports of our type in the given channel count,
943 * but returning 1 if there are no ports.
946 PortMatrix::count_of_our_type_min_1 (ChanCount c
) const
948 uint32_t n
= count_of_our_type (c
);
956 PortMatrixNode::State
957 PortMatrix::get_association (PortMatrixNode node
) const
959 if (show_only_bundles ()) {
961 bool have_off_diagonal_association
= false;
962 bool have_diagonal_association
= false;
963 bool have_diagonal_not_association
= false;
965 for (uint32_t i
= 0; i
< node
.row
.bundle
->nchannels().n_total(); ++i
) {
967 for (uint32_t j
= 0; j
< node
.column
.bundle
->nchannels().n_total(); ++j
) {
969 if (!should_show (node
.row
.bundle
->channel_type(i
)) || !should_show (node
.column
.bundle
->channel_type(j
))) {
973 ARDOUR::BundleChannel c
[2];
974 c
[row_index()] = ARDOUR::BundleChannel (node
.row
.bundle
, i
);
975 c
[column_index()] = ARDOUR::BundleChannel (node
.column
.bundle
, j
);
977 PortMatrixNode::State
const s
= get_state (c
);
980 case PortMatrixNode::ASSOCIATED
:
982 have_diagonal_association
= true;
984 have_off_diagonal_association
= true;
988 case PortMatrixNode::NOT_ASSOCIATED
:
990 have_diagonal_not_association
= true;
1000 if (have_diagonal_association
&& !have_off_diagonal_association
&& !have_diagonal_not_association
) {
1001 return PortMatrixNode::ASSOCIATED
;
1002 } else if (!have_diagonal_association
&& !have_off_diagonal_association
) {
1003 return PortMatrixNode::NOT_ASSOCIATED
;
1006 return PortMatrixNode::PARTIAL
;
1010 ARDOUR::BundleChannel c
[2];
1011 c
[column_index()] = node
.column
;
1012 c
[row_index()] = node
.row
;
1013 return get_state (c
);
1018 return PortMatrixNode::NOT_ASSOCIATED
;
1021 /** @return true if b is a non-zero pointer and the bundle it points to has some channels */
1023 PortMatrix::bundle_with_channels (boost::shared_ptr
<ARDOUR::Bundle
> b
)
1025 return b
&& b
->nchannels() != ARDOUR::ChanCount::ZERO
;