2 Copyright (C) 2000-2004 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 "gtk2ardour-config.h"
26 #include <sigc++/bind.h>
28 #include <gtkmm/accelmap.h>
30 #include "pbd/convert.h"
31 #include "pbd/stacktrace.h"
32 #include "pbd/unwind.h"
34 #include <glibmm/threads.h>
36 #include <gtkmm2ext/gtk_ui.h>
37 #include <gtkmm2ext/keyboard.h>
38 #include <gtkmm2ext/utils.h>
39 #include <gtkmm2ext/tearoff.h>
40 #include <gtkmm2ext/window_title.h>
41 #include <gtkmm2ext/doi.h>
43 #include "ardour/amp.h"
44 #include "ardour/debug.h"
45 #include "ardour/audio_track.h"
46 #include "ardour/midi_track.h"
47 #include "ardour/plugin_manager.h"
48 #include "ardour/route_group.h"
49 #include "ardour/session.h"
50 #include "ardour/vca.h"
51 #include "ardour/vca_manager.h"
55 #include "mixer_strip.h"
56 #include "monitor_section.h"
57 #include "plugin_selector.h"
58 #include "public_editor.h"
59 #include "mouse_cursors.h"
60 #include "ardour_ui.h"
63 #include "route_sorter.h"
65 #include "gui_thread.h"
66 #include "mixer_group_tabs.h"
68 #include "ui_config.h"
69 #include "vca_master_strip.h"
73 using namespace ARDOUR;
74 using namespace ARDOUR_UI_UTILS;
78 using namespace Gtkmm2ext;
84 Mixer_UI* Mixer_UI::_instance = 0;
90 _instance = new Mixer_UI;
97 : Tabbable (_content, _("Mixer"))
98 , no_track_list_redisplay (false)
99 , in_group_row_change (false)
101 , _monitor_section (0)
102 , _plugin_selector (0)
103 , _strip_width (UIConfiguration::instance().get_default_narrow_ms() ? Narrow : Wide)
104 , ignore_reorder (false)
105 , _in_group_rebuild_or_clear (false)
106 , _route_deletion_in_progress (false)
107 , _following_editor_selection (false)
109 , _show_mixer_list (true)
111 Stripable::PresentationInfoChange.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::sync_treeview_from_presentation_info, this), gui_context());
113 /* bindings was already set in MixerActor constructor */
115 _content.set_data ("ardour-bindings", bindings);
117 scroller.set_can_default (true);
118 // set_default (scroller);
120 scroller_base.set_flags (Gtk::CAN_FOCUS);
121 scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
122 scroller_base.set_name ("MixerWindow");
123 scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
125 /* set up drag-n-drop */
126 vector<TargetEntry> target_table;
127 target_table.push_back (TargetEntry ("PluginFavoritePtr"));
128 scroller_base.drag_dest_set (target_table);
129 scroller_base.signal_drag_data_received().connect (sigc::mem_fun(*this, &Mixer_UI::scroller_drag_data_received));
131 // add as last item of strip packer
132 strip_packer.pack_end (scroller_base, true, true);
134 _group_tabs = new MixerGroupTabs (this);
135 VBox* b = manage (new VBox);
136 b->pack_start (*_group_tabs, PACK_SHRINK);
137 b->pack_start (strip_packer);
141 scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
143 setup_track_display ();
145 group_model = ListStore::create (group_columns);
146 group_display.set_model (group_model);
147 group_display.append_column (_("Group"), group_columns.text);
148 group_display.append_column (_("Show"), group_columns.visible);
149 group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
150 group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
151 group_display.get_column (0)->set_expand(true);
152 group_display.get_column (1)->set_expand(false);
153 group_display.set_name ("EditGroupList");
154 group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
155 group_display.set_reorderable (true);
156 group_display.set_headers_visible (true);
157 group_display.set_rules_hint (true);
158 group_display.set_can_focus(false);
160 /* name is directly editable */
162 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
163 name_cell->property_editable() = true;
164 name_cell->signal_edited().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_name_edit));
166 /* use checkbox for the active column */
168 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
169 active_cell->property_activatable() = true;
170 active_cell->property_radio() = false;
172 group_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_change));
173 /* We use this to notice drag-and-drop reorders of the group list */
174 group_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_deleted));
175 group_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::group_display_button_press), false);
177 group_display_scroller.add (group_display);
178 group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
180 HBox* route_group_display_button_box = manage (new HBox());
182 Button* route_group_add_button = manage (new Button ());
183 Button* route_group_remove_button = manage (new Button ());
187 w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
189 route_group_add_button->add (*w);
191 w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
193 route_group_remove_button->add (*w);
195 route_group_display_button_box->set_homogeneous (true);
197 route_group_add_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_route_group));
198 route_group_remove_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::remove_selected_route_group));
200 route_group_display_button_box->add (*route_group_add_button);
201 route_group_display_button_box->add (*route_group_remove_button);
203 group_display_vbox.pack_start (group_display_scroller, true, true);
204 group_display_vbox.pack_start (*route_group_display_button_box, false, false);
206 group_display_frame.set_name ("BaseFrame");
207 group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
208 group_display_frame.add (group_display_vbox);
211 list<TargetEntry> target_list;
212 target_list.push_back (TargetEntry ("PluginPresetPtr"));
214 favorite_plugins_model = PluginTreeStore::create (favorite_plugins_columns);
215 favorite_plugins_display.set_model (favorite_plugins_model);
216 favorite_plugins_display.append_column (_("Favorite Plugins"), favorite_plugins_columns.name);
217 favorite_plugins_display.set_name ("EditGroupList");
218 favorite_plugins_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
219 favorite_plugins_display.set_reorderable (false);
220 favorite_plugins_display.set_headers_visible (true);
221 favorite_plugins_display.set_rules_hint (true);
222 favorite_plugins_display.set_can_focus (false);
223 favorite_plugins_display.add_object_drag (favorite_plugins_columns.plugin.index(), "PluginFavoritePtr");
224 favorite_plugins_display.set_drag_column (favorite_plugins_columns.name.index());
225 favorite_plugins_display.add_drop_targets (target_list);
226 favorite_plugins_display.signal_row_activated().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_activated));
227 favorite_plugins_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_button_press), false);
228 favorite_plugins_display.signal_drop.connect (sigc::mem_fun (*this, &Mixer_UI::plugin_drop));
229 favorite_plugins_display.signal_row_expanded().connect (sigc::mem_fun (*this, &Mixer_UI::save_favorite_ui_state));
230 favorite_plugins_display.signal_row_collapsed().connect (sigc::mem_fun (*this, &Mixer_UI::save_favorite_ui_state));
231 favorite_plugins_model->signal_row_has_child_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::sync_treeview_favorite_ui_state));
233 favorite_plugins_scroller.add (favorite_plugins_display);
234 favorite_plugins_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
236 favorite_plugins_frame.set_name ("BaseFrame");
237 favorite_plugins_frame.set_shadow_type (Gtk::SHADOW_IN);
238 favorite_plugins_frame.add (favorite_plugins_scroller);
240 rhs_pane1.add (favorite_plugins_frame);
241 rhs_pane1.add (track_display_frame);
242 rhs_pane2.add (rhs_pane1);
243 rhs_pane2.add (group_display_frame);
245 list_vpacker.pack_start (rhs_pane2, true, true);
247 vca_scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
248 vca_scroller_base.set_name ("MixerWindow");
249 vca_scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::masters_scroller_button_release), false);
250 vca_packer.pack_end (vca_scroller_base, true, true);
252 vca_scroller.add (vca_packer);
253 vca_scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
254 vca_scroller.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
256 inner_pane.add (scroller);
257 inner_pane.add (vca_scroller);
259 global_hpacker.pack_start (inner_pane, true, true);
260 global_hpacker.pack_start (out_packer, false, false);
262 list_hpane.add (list_vpacker);
263 list_hpane.add (global_hpacker);
266 XMLNode const * settings = ARDOUR_UI::instance()->mixer_settings();
267 XMLProperty const * prop;
269 if (!settings || ((prop = settings->property ("mixer-rhs-pane1-pos")) == 0)) {
270 rhs_pane1.set_divider (0, 0.6f);
272 rhs_pane1.set_divider (0, atof (prop->value()));
274 if (!settings || ((prop = settings->property ("mixer-rhs-pane2-pos")) == 0)) {
275 rhs_pane2.set_divider (0, 0.7f);
277 rhs_pane2.set_divider (0, atof (prop->value()));
279 if (!settings || ((prop = settings->property ("mixer-list-hpane-pos")) == 0)) {
280 list_hpane.set_divider (0, 0.2f);
282 list_hpane.set_divider (0, atof (prop->value()));
284 if (!settings || ((prop = settings->property ("mixer-inner-pos")) == 0)) {
285 inner_pane.set_divider (0, 0.8f);
287 inner_pane.set_divider (0, atof (prop->value()));
290 rhs_pane1.set_drag_cursor (*PublicEditor::instance().cursors()->expand_up_down);
291 rhs_pane2.set_drag_cursor (*PublicEditor::instance().cursors()->expand_up_down);
292 list_hpane.set_drag_cursor (*PublicEditor::instance().cursors()->expand_left_right);
293 inner_pane.set_drag_cursor (*PublicEditor::instance().cursors()->expand_left_right);
295 _content.pack_start (list_hpane, true, true);
299 route_group_display_button_box->show();
300 route_group_add_button->show();
301 route_group_remove_button->show();
304 _content.set_name ("MixerWindow");
306 global_hpacker.show();
308 scroller_base.show();
309 scroller_hpacker.show();
310 mixer_scroller_vpacker.show();
312 group_display_button_label.show();
313 group_display_button.show();
314 group_display_scroller.show();
315 favorite_plugins_scroller.show();
316 group_display_vbox.show();
317 group_display_frame.show();
318 favorite_plugins_frame.show();
325 vca_scroller_base.show();
328 group_display.show();
329 favorite_plugins_display.show();
331 MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context());
335 ARDOUR_UI::instance()->Escape.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::escape, this), gui_context());
337 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
338 _plugin_selector = new PluginSelector (PluginManager::instance ());
340 #error implement deferred Plugin-Favorite list
342 PluginManager::instance ().PluginListChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
343 PluginManager::instance ().PluginStatusesChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
344 ARDOUR::Plugin::PresetsChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
347 Mixer_UI::~Mixer_UI ()
349 if (_monitor_section) {
350 monitor_section_detached ();
351 delete _monitor_section;
353 delete _plugin_selector;
363 Mixer_UI::track_editor_selection ()
365 PublicEditor::instance().get_selection().TracksChanged.connect (sigc::mem_fun (*this, &Mixer_UI::follow_editor_selection));
369 Mixer_UI::use_own_window (bool and_fill_it)
371 bool new_window = !own_window();
373 Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
376 if (win && new_window) {
377 win->set_name ("MixerWindow");
378 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Mixer"), this);
379 win->signal_scroll_event().connect (sigc::mem_fun (*this, &Mixer_UI::on_scroll_event), false);
380 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
381 win->set_data ("ardour-bindings", bindings);
389 Mixer_UI::show_window ()
391 Tabbable::show_window ();
393 /* show/hide group tabs as required */
394 parameter_changed ("show-group-tabs");
396 /* now reset each strips width so the right widgets are shown */
399 TreeModel::Children rows = track_model->children();
400 TreeModel::Children::iterator ri;
402 for (ri = rows.begin(); ri != rows.end(); ++ri) {
403 ms = (*ri)[track_columns.strip];
407 ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
408 /* Fix visibility of mixer strip stuff */
409 ms->parameter_changed (X_("mixer-element-visibility"));
412 /* force focus into main area */
413 scroller_base.grab_focus ();
417 Mixer_UI::add_masters (VCAList& vcas)
419 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
421 VCAMasterStrip* vms = new VCAMasterStrip (_session, *v);
423 TreeModel::Row row = *(track_model->append());
424 row[track_columns.text] = (*v)->name();
425 row[track_columns.visible] = true;
426 row[track_columns.vca] = vms;
428 vms->CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_master, this, _1), gui_context());
431 redisplay_track_list ();
435 Mixer_UI::remove_master (VCAMasterStrip* vms)
437 if (_session && _session->deletion_in_progress()) {
438 /* its all being taken care of */
442 TreeModel::Children rows = track_model->children();
443 TreeModel::Children::iterator ri;
445 for (ri = rows.begin(); ri != rows.end(); ++ri) {
446 if ((*ri)[track_columns.vca] == vms) {
447 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
448 track_model->erase (ri);
455 Mixer_UI::masters_scroller_button_release (GdkEventButton* ev)
457 using namespace Menu_Helpers;
459 if (Keyboard::is_context_menu_event (ev)) {
460 ARDOUR_UI::instance()->add_route ();
468 Mixer_UI::add_strips (RouteList& routes)
470 Gtk::TreeModel::Children::iterator insert_iter = track_model->children().end();
471 uint32_t nroutes = 0;
473 for (Gtk::TreeModel::Children::iterator it = track_model->children().begin(); it != track_model->children().end(); ++it) {
474 boost::shared_ptr<Route> r = (*it)[track_columns.route];
482 if (r->presentation_info().group_order() == (routes.front()->presentation_info().group_order() + routes.size())) {
489 _selection.clear_routes ();
495 no_track_list_redisplay = true;
496 track_display.set_model (Glib::RefPtr<ListStore>());
498 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
499 boost::shared_ptr<Route> route = (*x);
501 if (route->is_auditioner()) {
505 if (route->is_monitor()) {
507 if (!_monitor_section) {
508 _monitor_section = new MonitorSection (_session);
510 XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
512 _monitor_section->tearoff().set_state (*mnode);
516 out_packer.pack_end (_monitor_section->tearoff(), false, false);
517 _monitor_section->set_session (_session);
518 _monitor_section->tearoff().show_all ();
520 _monitor_section->tearoff().Detach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_detached));
521 _monitor_section->tearoff().Attach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_attached));
523 monitor_section_attached ();
525 route->DropReferences.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::monitor_section_going_away, this), gui_context());
527 /* no regular strip shown for control out */
532 strip = new MixerStrip (*this, _session, route);
533 strips.push_back (strip);
535 UIConfiguration::instance().get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
537 if (strip->width_owner() != strip) {
538 strip->set_width_enum (_strip_width, this);
543 TreeModel::Row row = *(track_model->insert(insert_iter));
544 row[track_columns.text] = route->name();
545 row[track_columns.visible] = strip->route()->is_master() ? true : strip->marked_for_display();
546 row[track_columns.route] = route;
547 row[track_columns.strip] = strip;
548 row[track_columns.vca] = 0;
551 _selection.add (strip);
554 route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
556 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
557 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
560 } catch (const std::exception& e) {
561 error << string_compose (_("Error adding GUI elements for new tracks/busses %1"), e.what()) << endmsg;
564 no_track_list_redisplay = false;
565 track_display.set_model (track_model);
567 sync_presentation_info_from_treeview ();
568 redisplay_track_list ();
572 Mixer_UI::deselect_all_strip_processors ()
574 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
575 (*i)->deselect_all_processors();
580 Mixer_UI::select_strip (MixerStrip& ms, bool add)
583 _selection.add (&ms);
585 _selection.set (&ms);
590 Mixer_UI::select_none ()
592 _selection.clear_routes();
593 deselect_all_strip_processors();
597 Mixer_UI::delete_processors ()
599 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
600 (*i)->delete_processors();
606 Mixer_UI::remove_strip (MixerStrip* strip)
608 if (_session && _session->deletion_in_progress()) {
609 /* its all being taken care of */
613 TreeModel::Children rows = track_model->children();
614 TreeModel::Children::iterator ri;
615 list<MixerStrip *>::iterator i;
617 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
621 for (ri = rows.begin(); ri != rows.end(); ++ri) {
622 if ((*ri)[track_columns.strip] == strip) {
623 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
624 track_model->erase (ri);
631 Mixer_UI::sync_presentation_info_from_treeview ()
633 if (ignore_reorder || !_session || _session->deletion_in_progress() || (Config->get_remote_model() != MixerOrdered)) {
637 TreeModel::Children rows = track_model->children();
643 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync presentation info from treeview\n");
645 TreeModel::Children::iterator ri;
649 for (ri = rows.begin(); ri != rows.end(); ++ri) {
650 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
651 bool visible = (*ri)[track_columns.visible];
658 if (route->presentation_info().special()) {
663 route->presentation_info().set_flag (PresentationInfo::Hidden);
665 route->presentation_info().unset_flag (PresentationInfo::Hidden);
668 if (order != route->presentation_info().group_order()) {
669 route->set_presentation_group_order_explicit (order);
677 DEBUG_TRACE (DEBUG::OrderKeys, "... notify PI change from mixer GUI\n");
678 _session->notify_presentation_info_change ();
683 Mixer_UI::sync_treeview_from_presentation_info ()
685 if (!_session || _session->deletion_in_progress()) {
689 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync model from presentation info.\n");
691 /* we could get here after either a change in the Mixer or Editor sort
692 * order, but either way, the mixer order keys reflect the intended
693 * order for the GUI, so reorder the treeview model to match it.
696 vector<int> neworder;
697 TreeModel::Children rows = track_model->children();
698 uint32_t old_order = 0;
699 bool changed = false;
706 uint32_t vca_cnt = 0;
707 uint32_t max_route_order_key = 0;
709 /* count number of Routes in track_model (there may be some odd reason
710 why this is not the same as the number in the session, but here we
711 care about the track model.
714 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
715 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
717 max_route_order_key = max (route->presentation_info().group_order(), max_route_order_key);
721 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
722 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
724 /* VCAs need to sort after all routes. We don't display
725 * them in the same place (March 2016), but we don't
726 * want them intermixed in the track_model
728 sorted.push_back (OrderKeys (old_order, max_route_order_key + ++vca_cnt));
730 sorted.push_back (OrderKeys (old_order, route->presentation_info().group_order()));
734 SortByNewDisplayOrder cmp;
736 sort (sorted.begin(), sorted.end(), cmp);
737 neworder.assign (sorted.size(), 0);
741 for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
743 neworder[n] = sr->old_display_order;
745 if (sr->old_display_order != n) {
751 Unwinder<bool> uw (ignore_reorder, true);
752 track_model->reorder (neworder);
755 redisplay_track_list ();
759 Mixer_UI::follow_editor_selection ()
761 if (_following_editor_selection) {
765 _following_editor_selection = true;
766 _selection.block_routes_changed (true);
768 TrackSelection& s (PublicEditor::instance().get_selection().tracks);
770 _selection.clear_routes ();
772 for (TrackViewList::iterator i = s.begin(); i != s.end(); ++i) {
773 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
775 MixerStrip* ms = strip_by_route (rtav->route());
782 _following_editor_selection = false;
783 _selection.block_routes_changed (false);
788 Mixer_UI::strip_by_route (boost::shared_ptr<Route> r)
790 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
791 if ((*i)->route() == r) {
800 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
802 if (ev->button == 1) {
803 if (_selection.selected (strip)) {
804 /* primary-click: toggle selection state of strip */
805 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
806 _selection.remove (strip);
807 } else if (_selection.routes.size() > 1) {
808 /* de-select others */
809 _selection.set (strip);
812 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
813 _selection.add (strip);
814 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::RangeSelectModifier)) {
816 if (!_selection.selected(strip)) {
818 /* extend selection */
820 vector<MixerStrip*> tmp;
821 bool accumulate = false;
822 bool found_another = false;
824 tmp.push_back (strip);
826 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
828 /* hit clicked strip, start accumulating till we hit the first
837 } else if (_selection.selected (*i)) {
838 /* hit selected strip. if currently accumulating others,
839 we're done. if not accumulating others, start doing so.
841 found_another = true;
856 for (vector<MixerStrip*>::iterator i = tmp.begin(); i != tmp.end(); ++i) {
860 _selection.set (strip); //user wants to start a range selection, but there aren't any others selected yet
864 _selection.set (strip);
873 Mixer_UI::set_session (Session* sess)
875 SessionHandlePtr::set_session (sess);
877 if (_plugin_selector) {
878 _plugin_selector->set_session (_session);
881 _group_tabs->set_session (sess);
887 refill_favorite_plugins();
889 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
890 set_state (*node, 0);
894 initial_track_display ();
896 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_strips, this, _1), gui_context());
897 _session->route_group_added.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_route_group, this, _1), gui_context());
898 _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
899 _session->route_groups_reordered.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
900 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
901 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
902 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
904 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_masters, this, _1), gui_context());
906 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
908 route_groups_changed ();
917 Mixer_UI::session_going_away ()
919 ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away);
921 _in_group_rebuild_or_clear = true;
922 group_model->clear ();
923 _in_group_rebuild_or_clear = false;
926 track_model->clear ();
928 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
932 if (_monitor_section) {
933 _monitor_section->tearoff().hide_visible ();
936 monitor_section_detached ();
942 SessionHandlePtr::session_going_away ();
949 Mixer_UI::track_visibility_changed (std::string const & path)
951 if (_session && _session->deletion_in_progress()) {
957 if ((iter = track_model->get_iter (path))) {
958 MixerStrip* strip = (*iter)[track_columns.strip];
960 bool visible = (*iter)[track_columns.visible];
962 if (strip->set_marked_for_display (!visible)) {
963 update_track_visibility ();
970 Mixer_UI::update_track_visibility ()
972 TreeModel::Children rows = track_model->children();
973 TreeModel::Children::iterator i;
976 Unwinder<bool> uw (no_track_list_redisplay, true);
978 for (i = rows.begin(); i != rows.end(); ++i) {
979 MixerStrip *strip = (*i)[track_columns.strip];
981 (*i)[track_columns.visible] = strip->marked_for_display ();
985 /* force presentation catch up with visibility changes
988 sync_presentation_info_from_treeview ();
991 redisplay_track_list ();
995 Mixer_UI::show_strip (MixerStrip* ms)
997 TreeModel::Children rows = track_model->children();
998 TreeModel::Children::iterator i;
1000 for (i = rows.begin(); i != rows.end(); ++i) {
1002 MixerStrip* strip = (*i)[track_columns.strip];
1004 (*i)[track_columns.visible] = true;
1005 redisplay_track_list ();
1012 Mixer_UI::hide_strip (MixerStrip* ms)
1014 TreeModel::Children rows = track_model->children();
1015 TreeModel::Children::iterator i;
1017 for (i = rows.begin(); i != rows.end(); ++i) {
1019 MixerStrip* strip = (*i)[track_columns.strip];
1021 (*i)[track_columns.visible] = false;
1022 redisplay_track_list ();
1029 Mixer_UI::start_updating ()
1031 fast_screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
1036 Mixer_UI::stop_updating ()
1038 fast_screen_update_connection.disconnect();
1043 Mixer_UI::fast_update_strips ()
1045 if (_content.is_mapped () && _session) {
1046 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1047 (*i)->fast_update ();
1053 Mixer_UI::set_all_strips_visibility (bool yn)
1055 TreeModel::Children rows = track_model->children();
1056 TreeModel::Children::iterator i;
1059 Unwinder<bool> uw (no_track_list_redisplay, true);
1061 for (i = rows.begin(); i != rows.end(); ++i) {
1063 TreeModel::Row row = (*i);
1064 MixerStrip* strip = row[track_columns.strip];
1070 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1074 (*i)[track_columns.visible] = yn;
1078 redisplay_track_list ();
1083 Mixer_UI::set_all_audio_midi_visibility (int tracks, bool yn)
1085 TreeModel::Children rows = track_model->children();
1086 TreeModel::Children::iterator i;
1089 Unwinder<bool> uw (no_track_list_redisplay, true);
1091 for (i = rows.begin(); i != rows.end(); ++i) {
1092 TreeModel::Row row = (*i);
1093 MixerStrip* strip = row[track_columns.strip];
1099 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1103 boost::shared_ptr<AudioTrack> at = strip->audio_track();
1104 boost::shared_ptr<MidiTrack> mt = strip->midi_track();
1108 (*i)[track_columns.visible] = yn;
1112 if (at) { /* track */
1113 (*i)[track_columns.visible] = yn;
1118 if (!at && !mt) { /* bus */
1119 (*i)[track_columns.visible] = yn;
1124 if (mt) { /* midi-track */
1125 (*i)[track_columns.visible] = yn;
1132 redisplay_track_list ();
1136 Mixer_UI::hide_all_routes ()
1138 set_all_strips_visibility (false);
1142 Mixer_UI::show_all_routes ()
1144 set_all_strips_visibility (true);
1148 Mixer_UI::show_all_audiobus ()
1150 set_all_audio_midi_visibility (2, true);
1153 Mixer_UI::hide_all_audiobus ()
1155 set_all_audio_midi_visibility (2, false);
1159 Mixer_UI::show_all_audiotracks()
1161 set_all_audio_midi_visibility (1, true);
1164 Mixer_UI::hide_all_audiotracks ()
1166 set_all_audio_midi_visibility (1, false);
1170 Mixer_UI::show_all_miditracks()
1172 set_all_audio_midi_visibility (3, true);
1175 Mixer_UI::hide_all_miditracks ()
1177 set_all_audio_midi_visibility (3, false);
1182 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
1184 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview reordered\n");
1185 sync_presentation_info_from_treeview ();
1189 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
1191 /* this happens as the second step of a DnD within the treeview as well
1192 as when a row/route is actually deleted.
1194 if it was a deletion then we have to force a redisplay because
1195 order keys may not have changed.
1198 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
1199 sync_presentation_info_from_treeview ();
1201 if (_route_deletion_in_progress) {
1202 redisplay_track_list ();
1207 Mixer_UI::spill_redisplay (boost::shared_ptr<VCA> vca)
1209 TreeModel::Children rows = track_model->children();
1211 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1213 MixerStrip* strip = (*i)[track_columns.strip];
1216 /* we're in the middle of changing a row, don't worry */
1220 if (!strip->route()) {
1221 /* non-route element */
1225 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1229 if (strip->route()->slaved_to (vca)) {
1231 strip->set_gui_property ("visible", true);
1233 if (strip->packed()) {
1234 strip_packer.reorder_child (*strip, -1); /* put at end */
1236 strip_packer.pack_start (*strip, false, false);
1237 strip->set_packed (true);
1242 strip->set_gui_property ("visible", false);
1244 if (strip->packed()) {
1245 strip_packer.remove (*strip);
1246 strip->set_packed (false);
1253 Mixer_UI::redisplay_track_list ()
1255 if (no_track_list_redisplay) {
1259 boost::shared_ptr<VCA> sv = spilled_vca.lock ();
1262 spill_redisplay (sv);
1266 TreeModel::Children rows = track_model->children();
1267 TreeModel::Children::iterator i;
1268 uint32_t n_masters = 0;
1270 container_clear (vca_packer);
1271 vca_packer.pack_end (vca_scroller_base, true, true);
1273 for (i = rows.begin(); i != rows.end(); ++i) {
1275 VCAMasterStrip* vms = (*i)[track_columns.vca];
1278 vca_packer.pack_start (*vms, false, false);
1284 MixerStrip* strip = (*i)[track_columns.strip];
1287 /* we're in the middle of changing a row, don't worry */
1291 bool const visible = (*i)[track_columns.visible];
1294 strip->set_gui_property ("visible", true);
1296 if (strip->packed()) {
1298 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1299 out_packer.reorder_child (*strip, -1);
1302 strip_packer.reorder_child (*strip, -1); /* put at end */
1307 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1308 out_packer.pack_start (*strip, false, false);
1310 strip_packer.pack_start (*strip, false, false);
1312 strip->set_packed (true);
1317 strip->set_gui_property ("visible", false);
1319 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1320 /* do nothing, these cannot be hidden */
1322 if (strip->packed()) {
1323 strip_packer.remove (*strip);
1324 strip->set_packed (false);
1330 /* update visibility of VCA assign buttons */
1332 if (n_masters == 0) {
1333 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::remove_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1334 vca_scroller.hide ();
1336 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::add_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1337 vca_scroller.show ();
1340 _group_tabs->set_dirty ();
1344 Mixer_UI::strip_width_changed ()
1346 _group_tabs->set_dirty ();
1349 TreeModel::Children rows = track_model->children();
1350 TreeModel::Children::iterator i;
1353 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
1354 MixerStrip* strip = (*i)[track_columns.strip];
1360 bool visible = (*i)[track_columns.visible];
1363 strip->queue_draw();
1370 struct PresentationInfoRouteSorter
1372 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1373 return a->presentation_info().global_order () < b->presentation_info().global_order ();
1378 Mixer_UI::initial_track_display ()
1380 boost::shared_ptr<RouteList> routes = _session->get_routes();
1381 RouteList copy (*routes);
1382 PresentationInfoRouteSorter sorter;
1387 Unwinder<bool> uw1 (no_track_list_redisplay, true);
1388 Unwinder<bool> uw2 (ignore_reorder, true);
1390 track_model->clear ();
1391 VCAList vcas = _session->vca_manager().vcas();
1396 redisplay_track_list ();
1400 Mixer_UI::show_track_list_menu ()
1402 if (track_menu == 0) {
1403 build_track_menu ();
1406 track_menu->popup (1, gtk_get_current_event_time());
1410 Mixer_UI::track_display_button_press (GdkEventButton* ev)
1412 if (Keyboard::is_context_menu_event (ev)) {
1413 show_track_list_menu ();
1421 Mixer_UI::build_track_menu ()
1423 using namespace Menu_Helpers;
1424 using namespace Gtk;
1426 track_menu = new Menu;
1427 track_menu->set_name ("ArdourContextMenu");
1428 MenuList& items = track_menu->items();
1430 items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1431 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1432 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1433 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1434 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1435 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1436 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::show_all_miditracks)));
1437 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::hide_all_miditracks)));
1442 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1444 if (!what_changed.contains (ARDOUR::Properties::name)) {
1448 ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1450 TreeModel::Children rows = track_model->children();
1451 TreeModel::Children::iterator i;
1453 for (i = rows.begin(); i != rows.end(); ++i) {
1454 if ((*i)[track_columns.strip] == mx) {
1455 (*i)[track_columns.text] = mx->route()->name();
1460 error << _("track display list item for renamed strip not found!") << endmsg;
1464 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1466 TreeModel::Path path;
1467 TreeViewColumn* column;
1471 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1472 _group_tabs->get_menu(0)->popup (1, ev->time);
1476 TreeIter iter = group_model->get_iter (path);
1478 _group_tabs->get_menu(0)->popup (1, ev->time);
1482 RouteGroup* group = (*iter)[group_columns.group];
1484 if (Keyboard::is_context_menu_event (ev)) {
1485 _group_tabs->get_menu(group)->popup (1, ev->time);
1489 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1491 if (Keyboard::is_edit_event (ev)) {
1493 // edit_route_group (group);
1495 group_display.queue_draw();
1504 bool visible = (*iter)[group_columns.visible];
1505 (*iter)[group_columns.visible] = !visible;
1507 group_display.queue_draw();
1520 Mixer_UI::activate_all_route_groups ()
1522 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1526 Mixer_UI::disable_all_route_groups ()
1528 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1532 Mixer_UI::route_groups_changed ()
1534 ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed);
1536 _in_group_rebuild_or_clear = true;
1538 /* just rebuild the while thing */
1540 group_model->clear ();
1543 /* this is currently not used,
1544 * Mixer_UI::group_display_button_press() has a case for it,
1545 * and a commented edit_route_group() but that's n/a since 2011.
1547 * This code is left as reminder that
1548 * row[group_columns.group] = 0 has special meaning.
1552 row = *(group_model->append());
1553 row[group_columns.visible] = true;
1554 row[group_columns.text] = (_("-all-"));
1555 row[group_columns.group] = 0;
1559 _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1561 _group_tabs->set_dirty ();
1562 _in_group_rebuild_or_clear = false;
1566 Mixer_UI::new_route_group ()
1570 _group_tabs->run_new_group_dialog (rl, false);
1574 Mixer_UI::remove_selected_route_group ()
1576 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1577 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1583 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1586 /* selection mode is single, so rows.begin() is it */
1588 if ((iter = group_model->get_iter (*i))) {
1590 RouteGroup* rg = (*iter)[group_columns.group];
1593 _session->remove_route_group (*rg);
1599 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1601 if (in_group_row_change) {
1605 /* force an update of any mixer strips that are using this group,
1606 otherwise mix group names don't change in mixer strips
1609 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1610 if ((*i)->route_group() == group) {
1611 (*i)->route_group_changed();
1615 TreeModel::iterator i;
1616 TreeModel::Children rows = group_model->children();
1617 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1619 in_group_row_change = true;
1621 for (i = rows.begin(); i != rows.end(); ++i) {
1622 if ((*i)[group_columns.group] == group) {
1623 (*i)[group_columns.visible] = !group->is_hidden ();
1624 (*i)[group_columns.text] = group->name ();
1629 in_group_row_change = false;
1631 if (change.contains (Properties::name)) {
1632 _group_tabs->set_dirty ();
1635 for (list<MixerStrip*>::iterator j = strips.begin(); j != strips.end(); ++j) {
1636 if ((*j)->route_group() == group) {
1637 if (group->is_hidden ()) {
1647 Mixer_UI::show_mixer_list (bool yn)
1650 list_vpacker.show ();
1652 list_vpacker.hide ();
1655 _show_mixer_list = yn;
1659 Mixer_UI::show_monitor_section (bool yn)
1661 if (!monitor_section()) {
1664 if (monitor_section()->tearoff().torn_off()) {
1669 monitor_section()->tearoff().show();
1671 monitor_section()->tearoff().hide();
1676 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
1681 if ((iter = group_model->get_iter (path))) {
1683 if ((group = (*iter)[group_columns.group]) == 0) {
1687 if (new_text != group->name()) {
1688 group->set_name (new_text);
1694 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1698 if (in_group_row_change) {
1702 if ((group = (*iter)[group_columns.group]) == 0) {
1706 std::string name = (*iter)[group_columns.text];
1708 if (name != group->name()) {
1709 group->set_name (name);
1712 bool hidden = !(*iter)[group_columns.visible];
1714 if (hidden != group->is_hidden ()) {
1715 group->set_hidden (hidden, this);
1719 /** Called when a group model row is deleted, but also when the model is
1720 * reordered by a user drag-and-drop; the latter is what we are
1721 * interested in here.
1724 Mixer_UI::route_group_row_deleted (Gtk::TreeModel::Path const &)
1726 if (_in_group_rebuild_or_clear) {
1730 /* Re-write the session's route group list so that the new order is preserved */
1732 list<RouteGroup*> new_list;
1734 Gtk::TreeModel::Children children = group_model->children();
1735 for (Gtk::TreeModel::Children::iterator i = children.begin(); i != children.end(); ++i) {
1736 RouteGroup* g = (*i)[group_columns.group];
1738 new_list.push_back (g);
1742 _session->reorder_route_groups (new_list);
1747 Mixer_UI::add_route_group (RouteGroup* group)
1749 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1752 in_group_row_change = true;
1754 TreeModel::Row row = *(group_model->append());
1755 row[group_columns.visible] = !group->is_hidden ();
1756 row[group_columns.group] = group;
1757 if (!group->name().empty()) {
1758 row[group_columns.text] = group->name();
1760 row[group_columns.text] = _("unnamed");
1764 group->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
1767 TreeViewColumn* col = group_display.get_column (0);
1768 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1769 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1772 _group_tabs->set_dirty ();
1774 in_group_row_change = false;
1778 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1780 using namespace Menu_Helpers;
1782 if (Keyboard::is_context_menu_event (ev)) {
1783 ARDOUR_UI::instance()->add_route ();
1791 Mixer_UI::scroller_drag_data_received (const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, guint info, guint time)
1793 printf ("Mixer_UI::scroller_drag_data_received\n");
1794 if (data.get_target() != "PluginFavoritePtr") {
1795 context->drag_finish (false, false, time);
1799 const void * d = data.get_data();
1800 const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>* tv = reinterpret_cast<const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>*>(d);
1802 PluginPresetList nfos;
1804 tv->get_object_drag_data (nfos, &source);
1806 Route::ProcessorList pl;
1809 for (list<PluginPresetPtr>::const_iterator i = nfos.begin(); i != nfos.end(); ++i) {
1810 PluginPresetPtr ppp = (*i);
1811 PluginInfoPtr pip = ppp->_pip;
1812 if (!pip->is_instrument ()) {
1815 ARDOUR_UI::instance()->session_add_midi_track ((RouteGroup*) 0, 1, _("MIDI"), Config->get_strict_io (), pip, ppp->_preset.valid ? &ppp->_preset : 0, PresentationInfo::max_order);
1819 context->drag_finish (ok, false, time);
1823 Mixer_UI::set_strip_width (Width w, bool save)
1827 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1828 (*i)->set_width_enum (w, save ? (*i)->width_owner() : this);
1833 struct PluginStateSorter {
1835 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
1836 std::list<std::string>::const_iterator aiter = std::find(_user.begin(), _user.end(), (*a).unique_id);
1837 std::list<std::string>::const_iterator biter = std::find(_user.begin(), _user.end(), (*b).unique_id);
1838 if (aiter != _user.end() && biter != _user.end()) {
1839 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
1841 if (aiter != _user.end()) {
1844 if (biter != _user.end()) {
1847 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
1850 PluginStateSorter(std::list<std::string> user) : _user (user) {}
1852 std::list<std::string> _user;
1856 Mixer_UI::set_state (const XMLNode& node, int version)
1858 XMLProperty const * prop;
1860 Tabbable::set_state (node, version);
1862 if ((prop = node.property ("narrow-strips"))) {
1863 if (string_is_affirmative (prop->value())) {
1864 set_strip_width (Narrow);
1866 set_strip_width (Wide);
1870 if ((prop = node.property ("show-mixer"))) {
1871 if (string_is_affirmative (prop->value())) {
1876 if ((prop = node.property ("maximised"))) {
1877 bool yn = string_is_affirmative (prop->value());
1878 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalMixer"));
1880 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1881 bool fs = tact && tact->get_active();
1883 ActionManager::do_action ("Common", "ToggleMaximalMixer");
1887 if ((prop = node.property ("show-mixer-list"))) {
1888 bool yn = string_is_affirmative (prop->value());
1889 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMixerList"));
1891 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1893 /* do it twice to force the change */
1894 tact->set_active (!yn);
1895 tact->set_active (yn);
1899 XMLNode* plugin_order;
1900 if ((plugin_order = find_named_node (node, "PluginOrder")) != 0) {
1901 store_current_favorite_order ();
1902 std::list<string> order;
1903 const XMLNodeList& kids = plugin_order->children("PluginInfo");
1904 XMLNodeConstIterator i;
1905 for (i = kids.begin(); i != kids.end(); ++i) {
1906 if ((prop = (*i)->property ("unique-id"))) {
1907 std::string unique_id = prop->value();
1908 order.push_back (unique_id);
1909 if ((prop = (*i)->property ("expanded"))) {
1910 favorite_ui_state[unique_id] = string_is_affirmative (prop->value());
1914 PluginStateSorter cmp (order);
1915 favorite_order.sort (cmp);
1916 sync_treeview_from_favorite_order ();
1922 Mixer_UI::get_state ()
1924 XMLNode* node = new XMLNode (X_("Mixer"));
1927 node->add_child_nocopy (Tabbable::get_state());
1929 snprintf(buf,sizeof(buf), "%f", rhs_pane1.get_divider());
1930 node->add_property(X_("mixer-rhs-pane1-pos"), string(buf));
1931 snprintf(buf,sizeof(buf), "%f", rhs_pane2.get_divider());
1932 node->add_property(X_("mixer-rhs_pane2-pos"), string(buf));
1933 snprintf(buf,sizeof(buf), "%f", list_hpane.get_divider());
1934 node->add_property(X_("mixer-list-hpane-pos"), string(buf));
1935 snprintf(buf,sizeof(buf), "%f", inner_pane.get_divider());
1936 node->add_property(X_("mixer-inner-pane-pos"), string(buf));
1938 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1939 node->add_property ("show-mixer", _visible ? "yes" : "no");
1940 node->add_property ("show-mixer-list", _show_mixer_list ? "yes" : "no");
1941 node->add_property ("maximised", _maximised ? "yes" : "no");
1943 store_current_favorite_order ();
1944 XMLNode* plugin_order = new XMLNode ("PluginOrder");
1946 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i, ++cnt) {
1947 XMLNode* p = new XMLNode ("PluginInfo");
1948 p->add_property ("sort", cnt);
1949 p->add_property ("unique-id", (*i)->unique_id);
1950 if (favorite_ui_state.find ((*i)->unique_id) != favorite_ui_state.end ()) {
1951 p->add_property ("expanded", favorite_ui_state[(*i)->unique_id]);
1953 plugin_order->add_child_nocopy (*p);
1955 node->add_child_nocopy (*plugin_order);
1961 Mixer_UI::scroll_left ()
1963 if (!scroller.get_hscrollbar()) return;
1964 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1965 /* stupid GTK: can't rely on clamping across versions */
1966 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1970 Mixer_UI::scroll_right ()
1972 if (!scroller.get_hscrollbar()) return;
1973 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1974 /* stupid GTK: can't rely on clamping across versions */
1975 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1979 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1981 switch (ev->direction) {
1982 case GDK_SCROLL_LEFT:
1986 if (ev->state & Keyboard::TertiaryModifier) {
1992 case GDK_SCROLL_RIGHT:
1996 case GDK_SCROLL_DOWN:
1997 if (ev->state & Keyboard::TertiaryModifier) {
2009 Mixer_UI::parameter_changed (string const & p)
2011 if (p == "show-group-tabs") {
2012 bool const s = _session->config.get_show_group_tabs ();
2014 _group_tabs->show ();
2016 _group_tabs->hide ();
2018 } else if (p == "default-narrow_ms") {
2019 bool const s = UIConfiguration::instance().get_default_narrow_ms ();
2020 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2021 (*i)->set_width_enum (s ? Narrow : Wide, this);
2023 } else if (p == "use-monitor-bus") {
2024 if (_session && !_session->monitor_out()) {
2025 monitor_section_detached ();
2031 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
2033 g->set_active (a, this);
2037 Mixer_UI::plugin_selector()
2039 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
2040 if (!_plugin_selector)
2041 _plugin_selector = new PluginSelector (PluginManager::instance());
2044 return _plugin_selector;
2048 Mixer_UI::setup_track_display ()
2050 track_model = ListStore::create (track_columns);
2051 track_display.set_model (track_model);
2052 track_display.append_column (_("Strips"), track_columns.text);
2053 track_display.append_column (_("Show"), track_columns.visible);
2054 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
2055 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
2056 track_display.get_column (0)->set_expand(true);
2057 track_display.get_column (1)->set_expand(false);
2058 track_display.get_column (0)->set_sizing (Gtk::TREE_VIEW_COLUMN_FIXED);
2059 track_display.set_name (X_("EditGroupList"));
2060 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
2061 track_display.set_reorderable (true);
2062 track_display.set_headers_visible (true);
2063 track_display.set_can_focus(false);
2065 track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
2066 track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
2068 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
2069 track_list_visible_cell->property_activatable() = true;
2070 track_list_visible_cell->property_radio() = false;
2071 track_list_visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::track_visibility_changed));
2073 track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
2075 track_display_scroller.add (track_display);
2076 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
2078 VBox* v = manage (new VBox);
2080 v->pack_start (track_display_scroller, true, true);
2082 Button* b = manage (new Button);
2084 Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
2088 b->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
2090 v->pack_start (*b, false, false);
2092 track_display_frame.set_name("BaseFrame");
2093 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
2094 track_display_frame.add (*v);
2096 track_display_scroller.show();
2097 track_display_frame.show();
2098 track_display.show();
2102 Mixer_UI::new_track_or_bus ()
2104 ARDOUR_UI::instance()->add_route ();
2108 Mixer_UI::update_title ()
2110 if (!own_window()) {
2117 if (_session->snap_name() != _session->name()) {
2118 n = _session->snap_name ();
2120 n = _session->name ();
2123 if (_session->dirty ()) {
2127 WindowTitle title (n);
2128 title += S_("Window|Mixer");
2129 title += Glib::get_application_name ();
2130 own_window()->set_title (title.get_string());
2134 WindowTitle title (S_("Window|Mixer"));
2135 title += Glib::get_application_name ();
2136 own_window()->set_title (title.get_string());
2141 Mixer_UI::strip_by_x (int x)
2143 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2146 (*i)->translate_coordinates (_content, 0, 0, x1, y);
2147 x2 = x1 + (*i)->get_width();
2149 if (x >= x1 && x <= x2) {
2158 Mixer_UI::set_route_targets_for_operation ()
2160 _route_targets.clear ();
2162 if (!_selection.empty()) {
2163 _route_targets = _selection.routes;
2167 // removed "implicit" selections of strips, after discussion on IRC
2172 Mixer_UI::monitor_section_going_away ()
2174 if (_monitor_section) {
2175 monitor_section_detached ();
2176 out_packer.remove (_monitor_section->tearoff());
2177 _monitor_section->set_session (0);
2178 delete _monitor_section;
2179 _monitor_section = 0;
2184 Mixer_UI::toggle_midi_input_active (bool flip_others)
2186 boost::shared_ptr<RouteList> rl (new RouteList);
2189 set_route_targets_for_operation ();
2191 for (RouteUISelection::iterator r = _route_targets.begin(); r != _route_targets.end(); ++r) {
2192 boost::shared_ptr<MidiTrack> mt = (*r)->midi_track();
2195 rl->push_back ((*r)->route());
2196 onoff = !mt->input_active();
2200 _session->set_exclusive_input_active (rl, onoff, flip_others);
2204 Mixer_UI::maximise_mixer_space ()
2206 if (!own_window()) {
2214 _window->fullscreen ();
2219 Mixer_UI::restore_mixer_space ()
2221 if (!own_window()) {
2229 own_window()->unfullscreen();
2234 Mixer_UI::monitor_section_attached ()
2236 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2237 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2238 act->set_sensitive (true);
2239 tact->set_active ();
2243 Mixer_UI::monitor_section_detached ()
2245 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2246 act->set_sensitive (false);
2250 Mixer_UI::store_current_favorite_order ()
2252 typedef Gtk::TreeModel::Children type_children;
2253 type_children children = favorite_plugins_model->children();
2254 favorite_order.clear();
2255 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
2257 Gtk::TreeModel::Row row = *iter;
2258 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2259 favorite_order.push_back (ppp->_pip);
2260 std::string name = row[favorite_plugins_columns.name];
2261 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2266 Mixer_UI::save_favorite_ui_state (const TreeModel::iterator& iter, const TreeModel::Path& path)
2268 Gtk::TreeModel::Row row = *iter;
2269 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2271 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2275 Mixer_UI::refiller (PluginInfoList& result, const PluginInfoList& plugs)
2277 PluginManager& manager (PluginManager::instance());
2278 for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
2279 if (manager.get_status (*i) != PluginManager::Favorite) {
2282 result.push_back (*i);
2286 struct PluginCustomSorter {
2288 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
2289 PluginInfoList::const_iterator aiter = _user.begin();
2290 PluginInfoList::const_iterator biter = _user.begin();
2291 while (aiter != _user.end()) { if ((*aiter)->unique_id == a->unique_id) { break; } ++aiter; }
2292 while (biter != _user.end()) { if ((*biter)->unique_id == b->unique_id) { break; } ++biter; }
2294 if (aiter != _user.end() && biter != _user.end()) {
2295 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
2297 if (aiter != _user.end()) {
2300 if (biter != _user.end()) {
2303 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
2305 PluginCustomSorter(PluginInfoList user) : _user (user) {}
2307 PluginInfoList _user;
2311 Mixer_UI::refill_favorite_plugins ()
2313 PluginInfoList plugs;
2314 PluginManager& mgr (PluginManager::instance());
2317 refiller (plugs, mgr.lv2_plugin_info ());
2319 #ifdef WINDOWS_VST_SUPPORT
2320 refiller (plugs, mgr.windows_vst_plugin_info ());
2322 #ifdef LXVST_SUPPORT
2323 refiller (plugs, mgr.lxvst_plugin_info ());
2325 #ifdef AUDIOUNIT_SUPPORT
2326 refiller (plugs, mgr.au_plugin_info ());
2328 refiller (plugs, mgr.ladspa_plugin_info ());
2329 refiller (plugs, mgr.lua_plugin_info ());
2331 store_current_favorite_order ();
2333 PluginCustomSorter cmp (favorite_order);
2336 favorite_order = plugs;
2338 sync_treeview_from_favorite_order ();
2342 Mixer_UI::sync_treeview_favorite_ui_state (const TreeModel::Path& path, const TreeModel::iterator&)
2345 if (!(iter = favorite_plugins_model->get_iter (path))) {
2348 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2352 PluginInfoPtr pip = ppp->_pip;
2353 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2354 if (favorite_ui_state[pip->unique_id]) {
2355 favorite_plugins_display.expand_row (path, true);
2361 Mixer_UI::sync_treeview_from_favorite_order ()
2363 favorite_plugins_model->clear ();
2364 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i) {
2365 PluginInfoPtr pip = (*i);
2367 TreeModel::Row newrow = *(favorite_plugins_model->append());
2368 newrow[favorite_plugins_columns.name] = (*i)->name;
2369 newrow[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip));
2374 vector<ARDOUR::Plugin::PresetRecord> presets = (*i)->get_presets (true);
2375 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator j = presets.begin(); j != presets.end(); ++j) {
2376 Gtk::TreeModel::Row child_row = *(favorite_plugins_model->append (newrow.children()));
2377 child_row[favorite_plugins_columns.name] = (*j).label;
2378 child_row[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip, &(*j)));
2380 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2381 if (favorite_ui_state[pip->unique_id]) {
2382 favorite_plugins_display.expand_row (favorite_plugins_model->get_path(newrow), true);
2389 Mixer_UI::popup_note_context_menu (GdkEventButton *ev)
2391 using namespace Gtk::Menu_Helpers;
2393 Gtk::Menu* m = manage (new Menu);
2394 MenuList& items = m->items ();
2396 if (_selection.routes.empty()) {
2397 items.push_back (MenuElem (_("No Track/Bus is selected.")));
2399 items.push_back (MenuElem (_("Add at the top"),
2400 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddTop)));
2401 items.push_back (MenuElem (_("Add Pre-Fader"),
2402 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPreFader)));
2403 items.push_back (MenuElem (_("Add Post-Fader"),
2404 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPostFader)));
2405 items.push_back (MenuElem (_("Add at the end"),
2406 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddBottom)));
2409 items.push_back (SeparatorElem());
2411 items.push_back (MenuElem (_("Remove from favorites"), sigc::mem_fun (*this, &Mixer_UI::remove_selected_from_favorites)));
2413 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2414 if (ppp && ppp->_preset.valid && ppp->_preset.user) {
2415 // we cannot currently delete AU presets
2416 if (!ppp->_pip || ppp->_pip->type != AudioUnit) {
2417 items.push_back (MenuElem (_("Delete Preset"), sigc::mem_fun (*this, &Mixer_UI::delete_selected_preset)));
2421 m->popup (ev->button, ev->time);
2425 Mixer_UI::plugin_row_button_press (GdkEventButton *ev)
2427 if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3) ) {
2428 TreeModel::Path path;
2429 TreeViewColumn* column;
2431 if (favorite_plugins_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
2432 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2434 selection->unselect_all();
2435 selection->select(path);
2438 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2440 popup_note_context_menu (ev);
2448 Mixer_UI::selected_plugin ()
2450 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2452 return PluginPresetPtr();
2454 Gtk::TreeModel::iterator iter = selection->get_selected();
2456 return PluginPresetPtr();
2458 return (*iter)[favorite_plugins_columns.plugin];
2462 Mixer_UI::add_selected_processor (ProcessorPosition pos)
2464 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2466 add_favorite_processor (ppp, pos);
2471 Mixer_UI::delete_selected_preset ()
2476 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2477 if (!ppp || !ppp->_preset.valid || !ppp->_preset.user) {
2480 PluginPtr plugin = ppp->_pip->load (*_session);
2481 plugin->get_presets();
2482 plugin->remove_preset (ppp->_preset.label);
2486 Mixer_UI::remove_selected_from_favorites ()
2488 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2492 PluginManager::PluginStatusType status = PluginManager::Normal;
2493 PluginManager& manager (PluginManager::instance());
2495 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2496 manager.save_statuses ();
2500 Mixer_UI::plugin_row_activated (const TreeModel::Path& path, TreeViewColumn* column)
2503 if (!(iter = favorite_plugins_model->get_iter (path))) {
2506 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2507 add_favorite_processor (ppp, AddPreFader); // TODO: preference?!
2511 Mixer_UI::add_favorite_processor (ARDOUR::PluginPresetPtr ppp, ProcessorPosition pos)
2513 if (!_session || _selection.routes.empty()) {
2517 PluginInfoPtr pip = ppp->_pip;
2518 for (RouteUISelection::iterator i = _selection.routes.begin(); i != _selection.routes.end(); ++i) {
2519 boost::shared_ptr<ARDOUR::Route> rt = (*i)->route();
2520 if (!rt) { continue; }
2522 PluginPtr p = pip->load (*_session);
2523 if (!p) { continue; }
2525 if (ppp->_preset.valid) {
2526 p->load_preset (ppp->_preset);
2529 Route::ProcessorStreams err;
2530 boost::shared_ptr<Processor> processor (new PluginInsert (*_session, p));
2534 rt->add_processor_by_index (processor, 0, &err, Config->get_new_plugins_active ());
2537 rt->add_processor (processor, PreFader, &err, Config->get_new_plugins_active ());
2544 boost::shared_ptr<Processor> np = rt->nth_processor (idx);
2548 if (!np->display_to_user()) {
2551 if (boost::dynamic_pointer_cast<Amp> (np) && // Fader, not Trim
2552 boost::dynamic_pointer_cast<Amp> (np)->gain_control()->parameter().type() == GainAutomation) {
2557 rt->add_processor_by_index (processor, ++pos, &err, Config->get_new_plugins_active ());
2561 rt->add_processor_by_index (processor, -1, &err, Config->get_new_plugins_active ());
2568 PluginTreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& data) const
2570 if (data.get_target() != "GTK_TREE_MODEL_ROW") {
2574 // only allow to re-order top-level items
2576 if (TreePath::get_from_selection_data (data, src)) {
2577 if (src.up() && src.up()) {
2582 // don't allow to drop as child-rows.
2583 Gtk::TreeModel::Path _dest = dest; // un const
2584 const bool is_child = _dest.up (); // explicit bool for clang
2585 if (!is_child || _dest.empty ()) {
2592 Mixer_UI::plugin_drop (const Glib::RefPtr<Gdk::DragContext>&, const Gtk::SelectionData& data)
2594 if (data.get_target() != "PluginPresetPtr") {
2597 if (data.get_length() != sizeof (PluginPresetPtr)) {
2600 const void *d = data.get_data();
2601 const PluginPresetPtr ppp = *(static_cast<const PluginPresetPtr*> (d));
2603 PluginManager::PluginStatusType status = PluginManager::Favorite;
2604 PluginManager& manager (PluginManager::instance());
2606 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2607 manager.save_statuses ();
2611 Mixer_UI::do_vca_assign (boost::shared_ptr<VCA> vca)
2613 /* call protected MixerActor:: method */
2618 Mixer_UI::do_vca_unassign (boost::shared_ptr<VCA> vca)
2620 /* call protected MixerActor:: method */
2625 Mixer_UI::show_vca_slaves (boost::shared_ptr<VCA> vca)
2627 boost::shared_ptr<VCA> v = spilled_vca.lock();
2630 show_vca_change (vca); /* EMIT SIGNAL */
2631 redisplay_track_list ();
2636 Mixer_UI::showing_vca_slaves_for (boost::shared_ptr<VCA> vca) const
2638 return vca == spilled_vca.lock();