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 (_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]
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 bool can_add_or_rename
= false;
412 /* Start off with options for the `natural' port type */
413 for (DataType::iterator i
= DataType::begin(); i
!= DataType::end(); ++i
) {
414 if (should_show (*i
)) {
415 snprintf (buf
, sizeof (buf
), _("Add %s %s"), (*i
).to_i18n_string(), channel_noun().c_str());
416 sub
.push_back (MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::add_channel_proxy
), w
, *i
)));
417 can_add_or_rename
= true;
421 /* Now add other ones */
422 for (DataType::iterator i
= DataType::begin(); i
!= DataType::end(); ++i
) {
423 if (!should_show (*i
)) {
424 snprintf (buf
, sizeof (buf
), _("Add %s %s"), (*i
).to_i18n_string(), channel_noun().c_str());
425 sub
.push_back (MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::add_channel_proxy
), w
, *i
)));
426 can_add_or_rename
= true;
430 if (can_rename_channels (bc
[dim
].bundle
)) {
432 buf
, sizeof (buf
), _("Rename '%s'..."),
433 escape_underscores (bc
[dim
].bundle
->channel_name (bc
[dim
].channel
)).c_str()
438 sigc::bind (sigc::mem_fun (*this, &PortMatrix::rename_channel_proxy
), w
, bc
[dim
].channel
)
441 can_add_or_rename
= true;
444 if (can_add_or_rename
) {
445 sub
.push_back (SeparatorElem ());
448 if (can_remove_channels (bc
[dim
].bundle
)) {
449 if (bc
[dim
].channel
!= -1) {
450 add_remove_option (sub
, w
, bc
[dim
].channel
);
453 snprintf (buf
, sizeof (buf
), _("Remove all"));
455 MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_all_channels
), w
))
458 if (bc
[dim
].bundle
->nchannels().n_total() > 1) {
459 for (uint32_t i
= 0; i
< bc
[dim
].bundle
->nchannels().n_total(); ++i
) {
460 if (should_show (bc
[dim
].bundle
->channel_type(i
))) {
461 add_remove_option (sub
, w
, i
);
468 if (_show_only_bundles
|| count_of_our_type (bc
[dim
].bundle
->nchannels()) <= 1) {
469 snprintf (buf
, sizeof (buf
), _("%s all"), disassociation_verb().c_str());
471 MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_channel
), w
, bc
[dim
].channel
, dim
))
476 if (bc
[dim
].channel
!= -1) {
477 add_disassociate_option (sub
, w
, dim
, bc
[dim
].channel
);
479 snprintf (buf
, sizeof (buf
), _("%s all"), disassociation_verb().c_str());
481 MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_bundle
), w
, dim
))
484 for (uint32_t i
= 0; i
< bc
[dim
].bundle
->nchannels().n_total(); ++i
) {
485 if (should_show (bc
[dim
].bundle
->channel_type(i
))) {
486 add_disassociate_option (sub
, w
, dim
, i
);
492 items
.push_back (MenuElem (escape_underscores (bc
[dim
].bundle
->name()).c_str(), *m
));
493 need_separator
= true;
498 if (need_separator
) {
499 items
.push_back (SeparatorElem ());
502 items
.push_back (MenuElem (_("Rescan"), sigc::mem_fun (*this, &PortMatrix::setup_all_ports
)));
503 items
.push_back (CheckMenuElem (_("Show individual ports"), sigc::mem_fun (*this, &PortMatrix::toggle_show_only_bundles
)));
504 CheckMenuItem
* i
= dynamic_cast<CheckMenuItem
*> (&items
.back());
505 _inhibit_toggle_show_only_bundles
= true;
506 i
->set_active (!_show_only_bundles
);
507 _inhibit_toggle_show_only_bundles
= false;
513 PortMatrix::remove_channel_proxy (boost::weak_ptr
<Bundle
> b
, uint32_t c
)
515 boost::shared_ptr
<Bundle
> sb
= b
.lock ();
520 remove_channel (BundleChannel (sb
, c
));
525 PortMatrix::rename_channel_proxy (boost::weak_ptr
<Bundle
> b
, uint32_t c
)
527 boost::shared_ptr
<Bundle
> sb
= b
.lock ();
532 rename_channel (BundleChannel (sb
, c
));
536 PortMatrix::disassociate_all_on_bundle (boost::weak_ptr
<Bundle
> bundle
, int dim
)
538 boost::shared_ptr
<Bundle
> sb
= bundle
.lock ();
543 for (uint32_t i
= 0; i
< sb
->nchannels().n_total(); ++i
) {
544 if (should_show (sb
->channel_type(i
))) {
545 disassociate_all_on_channel (bundle
, i
, dim
);
551 PortMatrix::disassociate_all_on_channel (boost::weak_ptr
<Bundle
> bundle
, uint32_t channel
, int dim
)
553 boost::shared_ptr
<Bundle
> sb
= bundle
.lock ();
558 PortGroup::BundleList a
= _ports
[1-dim
].bundles ();
560 for (PortGroup::BundleList::iterator i
= a
.begin(); i
!= a
.end(); ++i
) {
561 for (uint32_t j
= 0; j
< (*i
)->bundle
->nchannels().n_total(); ++j
) {
563 if (should_show ((*i
)->bundle
->channel_type(j
))) {
568 c
[dim
] = BundleChannel (sb
, channel
);
569 c
[1-dim
] = BundleChannel ((*i
)->bundle
, j
);
571 if (get_state (c
) == PortMatrixNode::ASSOCIATED
) {
572 set_state (c
, false);
577 _body
->rebuild_and_draw_grid ();
581 PortMatrix::setup_global_ports ()
583 ENSURE_GUI_THREAD (*this, &PortMatrix::setup_global_ports
)
585 for (int i
= 0; i
< 2; ++i
) {
586 if (list_is_global (i
)) {
593 PortMatrix::setup_global_ports_proxy ()
595 /* Avoid a deadlock by calling this in an idle handler: see IOSelector::io_changed_proxy
599 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &PortMatrix::setup_global_ports
));
603 PortMatrix::setup_all_ports ()
605 if (_session
->deletion_in_progress()) {
609 ENSURE_GUI_THREAD (*this, &PortMatrix::setup_all_ports
)
616 PortMatrix::toggle_show_only_bundles ()
618 if (_inhibit_toggle_show_only_bundles
) {
622 _show_only_bundles
= !_show_only_bundles
;
627 pair
<uint32_t, uint32_t>
628 PortMatrix::max_size () const
630 pair
<uint32_t, uint32_t> m
= _body
->max_size ();
632 m
.first
+= _vscroll
.get_width () + _vbox
.get_width () + 4;
633 m
.second
+= _hscroll
.get_height () + _hbox
.get_height () + 4;
639 PortMatrix::on_scroll_event (GdkEventScroll
* ev
)
641 double const h
= _hscroll
.get_value ();
642 double const v
= _vscroll
.get_value ();
644 switch (ev
->direction
) {
646 _vscroll
.set_value (v
- PortMatrixComponent::grid_spacing ());
648 case GDK_SCROLL_DOWN
:
649 _vscroll
.set_value (v
+ PortMatrixComponent::grid_spacing ());
651 case GDK_SCROLL_LEFT
:
652 _hscroll
.set_value (h
- PortMatrixComponent::grid_spacing ());
654 case GDK_SCROLL_RIGHT
:
655 _hscroll
.set_value (h
+ PortMatrixComponent::grid_spacing ());
662 boost::shared_ptr
<IO
>
663 PortMatrix::io_from_bundle (boost::shared_ptr
<Bundle
> b
) const
665 boost::shared_ptr
<IO
> io
= _ports
[0].io_from_bundle (b
);
667 io
= _ports
[1].io_from_bundle (b
);
674 PortMatrix::can_add_channel (boost::shared_ptr
<Bundle
> b
) const
676 return io_from_bundle (b
);
680 PortMatrix::add_channel (boost::shared_ptr
<Bundle
> b
, DataType t
)
682 boost::shared_ptr
<IO
> io
= io_from_bundle (b
);
685 io
->add_port ("", this, t
);
690 PortMatrix::can_remove_channels (boost::shared_ptr
<Bundle
> b
) const
692 return io_from_bundle (b
);
696 PortMatrix::remove_channel (ARDOUR::BundleChannel b
)
698 boost::shared_ptr
<IO
> io
= io_from_bundle (b
.bundle
);
701 Port
* p
= io
->nth (b
.channel
);
703 int const r
= io
->remove_port (p
, this);
705 ArdourDialog
d (_("Port removal not allowed"));
706 Label
l (_("This port cannot be removed, as the first plugin in the track or buss cannot accept the new number of inputs."));
707 d
.get_vbox()->pack_start (l
);
708 d
.add_button (Stock::OK
, RESPONSE_ACCEPT
);
718 PortMatrix::remove_all_channels (boost::weak_ptr
<Bundle
> w
)
720 boost::shared_ptr
<Bundle
> b
= w
.lock ();
725 for (uint32_t i
= 0; i
< b
->nchannels().n_total(); ++i
) {
726 if (should_show (b
->channel_type(i
))) {
727 remove_channel (ARDOUR::BundleChannel (b
, i
));
733 PortMatrix::add_channel_proxy (boost::weak_ptr
<Bundle
> w
, DataType t
)
735 boost::shared_ptr
<Bundle
> b
= w
.lock ();
744 PortMatrix::setup_notebooks ()
746 int const h_current_page
= _hnotebook
.get_current_page ();
747 int const v_current_page
= _vnotebook
.get_current_page ();
749 /* for some reason best known to GTK, erroneous switch_page signals seem to be generated
750 when adding or removing pages to or from notebooks, so ignore them */
752 _ignore_notebook_page_selected
= true;
754 remove_notebook_pages (_hnotebook
);
755 remove_notebook_pages (_vnotebook
);
757 for (PortGroupList::List::const_iterator i
= _ports
[_row_index
].begin(); i
!= _ports
[_row_index
].end(); ++i
) {
758 HBox
* dummy
= manage (new HBox
);
760 Label
* label
= manage (new Label ((*i
)->name
));
761 label
->set_angle (_arrangement
== LEFT_TO_BOTTOM
? 90 : -90);
763 _vnotebook
.prepend_page (*dummy
, *label
);
766 for (PortGroupList::List::const_iterator i
= _ports
[_column_index
].begin(); i
!= _ports
[_column_index
].end(); ++i
) {
767 HBox
* dummy
= manage (new HBox
);
769 _hnotebook
.append_page (*dummy
, (*i
)->name
);
772 _ignore_notebook_page_selected
= false;
774 if (_arrangement
== TOP_TO_RIGHT
) {
775 _vnotebook
.set_tab_pos (POS_RIGHT
);
776 _hnotebook
.set_tab_pos (POS_TOP
);
778 _vnotebook
.set_tab_pos (POS_LEFT
);
779 _hnotebook
.set_tab_pos (POS_BOTTOM
);
782 if (h_current_page
!= -1 && _hnotebook
.get_n_pages() > h_current_page
) {
783 _hnotebook
.set_current_page (h_current_page
);
785 _hnotebook
.set_current_page (0);
788 if (v_current_page
!= -1 && _vnotebook
.get_n_pages() > v_current_page
) {
789 _vnotebook
.set_current_page (v_current_page
);
791 _vnotebook
.set_current_page (0);
794 if (_hnotebook
.get_n_pages() <= 1) {
800 if (_vnotebook
.get_n_pages() <= 1) {
808 PortMatrix::remove_notebook_pages (Notebook
& n
)
810 int const N
= n
.get_n_pages ();
812 for (int i
= 0; i
< N
; ++i
) {
818 PortMatrix::notebook_page_selected (GtkNotebookPage
*, guint
)
820 if (_ignore_notebook_page_selected
) {
830 PortMatrix::session_going_away ()
836 PortMatrix::body_dimensions_changed ()
838 _hspacer
.set_size_request (_body
->column_labels_border_x (), -1);
839 if (_arrangement
== TOP_TO_RIGHT
) {
840 _vspacer
.set_size_request (-1, _body
->column_labels_height ());
848 _parent
->get_size (curr_width
, curr_height
);
850 pair
<uint32_t, uint32_t> m
= max_size ();
852 /* Don't shrink the window */
853 m
.first
= max (int (m
.first
), curr_width
);
854 m
.second
= max (int (m
.second
), curr_height
);
856 resize_window_to_proportion_of_monitor (_parent
, m
.first
, m
.second
);
860 boost::shared_ptr
<const PortGroup
>
861 PortMatrix::visible_ports (int d
) const
863 PortGroupList
const & p
= _ports
[d
];
864 PortGroupList::List::const_iterator j
= p
.begin ();
867 if (d
== _row_index
) {
868 n
= p
.size() - _vnotebook
.get_current_page () - 1;
870 n
= _hnotebook
.get_current_page ();
874 while (i
!= int (n
) && j
!= p
.end ()) {
880 return boost::shared_ptr
<const PortGroup
> ();
887 PortMatrix::add_remove_option (Menu_Helpers::MenuList
& m
, boost::weak_ptr
<Bundle
> w
, int c
)
889 using namespace Menu_Helpers
;
891 boost::shared_ptr
<Bundle
> b
= w
.lock ();
897 snprintf (buf
, sizeof (buf
), _("Remove '%s'"), escape_underscores (b
->channel_name (c
)).c_str());
898 m
.push_back (MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::remove_channel_proxy
), w
, c
)));
902 PortMatrix::add_disassociate_option (Menu_Helpers::MenuList
& m
, boost::weak_ptr
<Bundle
> w
, int d
, int c
)
904 using namespace Menu_Helpers
;
906 boost::shared_ptr
<Bundle
> b
= w
.lock ();
912 snprintf (buf
, sizeof (buf
), _("%s all from '%s'"), disassociation_verb().c_str(), escape_underscores (b
->channel_name (c
)).c_str());
913 m
.push_back (MenuElem (buf
, sigc::bind (sigc::mem_fun (*this, &PortMatrix::disassociate_all_on_channel
), w
, c
, d
)));
917 PortMatrix::port_connected_or_disconnected ()
919 _body
->rebuild_and_draw_grid ();
923 PortMatrix::channel_noun () const
928 /** @return true if this matrix should show bundles / ports of type \t */
930 PortMatrix::should_show (DataType t
) const
932 return (_type
== DataType::NIL
|| t
== _type
);
936 PortMatrix::count_of_our_type (ChanCount c
) const
938 if (_type
== DataType::NIL
) {
942 return c
.get (_type
);
945 PortMatrixNode::State
946 PortMatrix::get_association (PortMatrixNode node
) const
948 if (show_only_bundles ()) {
950 bool have_off_diagonal_association
= false;
951 bool have_diagonal_association
= false;
952 bool have_diagonal_not_association
= false;
954 for (uint32_t i
= 0; i
< node
.row
.bundle
->nchannels().n_total(); ++i
) {
956 for (uint32_t j
= 0; j
< node
.column
.bundle
->nchannels().n_total(); ++j
) {
958 if (!should_show (node
.row
.bundle
->channel_type(i
)) || !should_show (node
.column
.bundle
->channel_type(j
))) {
962 ARDOUR::BundleChannel c
[2];
963 c
[row_index()] = ARDOUR::BundleChannel (node
.row
.bundle
, i
);
964 c
[column_index()] = ARDOUR::BundleChannel (node
.column
.bundle
, j
);
966 PortMatrixNode::State
const s
= get_state (c
);
969 case PortMatrixNode::ASSOCIATED
:
971 have_diagonal_association
= true;
973 have_off_diagonal_association
= true;
977 case PortMatrixNode::NOT_ASSOCIATED
:
979 have_diagonal_not_association
= true;
989 if (have_diagonal_association
&& !have_off_diagonal_association
&& !have_diagonal_not_association
) {
990 return PortMatrixNode::ASSOCIATED
;
991 } else if (!have_diagonal_association
&& !have_off_diagonal_association
) {
992 return PortMatrixNode::NOT_ASSOCIATED
;
995 return PortMatrixNode::PARTIAL
;
999 ARDOUR::BundleChannel c
[2];
1000 c
[column_index()] = node
.column
;
1001 c
[row_index()] = node
.row
;
1002 return get_state (c
);
1007 return PortMatrixNode::NOT_ASSOCIATED
;