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 PresentationInfo::Change.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;
273 if (!settings || ((prop = settings->property ("mixer-rhs-pane1-pos")) == 0) || ((fract = atof (prop->value())) > 1.0)) {
274 rhs_pane1.set_divider (0, 0.6f);
276 rhs_pane1.set_divider (0, fract);
278 if (!settings || ((prop = settings->property ("mixer-rhs-pane2-pos")) == 0) || ((fract = atof (prop->value())) > 1.0)) {
279 rhs_pane2.set_divider (0, 0.7f);
281 rhs_pane2.set_divider (0, fract);
283 if (!settings || ((prop = settings->property ("mixer-list-hpane-pos")) == 0) || ((fract = atof (prop->value())) > 1.0)) {
284 list_hpane.set_divider (0, 0.2f);
286 list_hpane.set_divider (0, fract);
288 if (!settings || ((prop = settings->property ("mixer-inner-pos")) == 0) || ((fract = atof (prop->value())) > 1.0)) {
289 inner_pane.set_divider (0, 0.8f);
291 inner_pane.set_divider (0, atof (prop->value()));
295 rhs_pane1.set_drag_cursor (*PublicEditor::instance().cursors()->expand_up_down);
296 rhs_pane2.set_drag_cursor (*PublicEditor::instance().cursors()->expand_up_down);
297 list_hpane.set_drag_cursor (*PublicEditor::instance().cursors()->expand_left_right);
298 inner_pane.set_drag_cursor (*PublicEditor::instance().cursors()->expand_left_right);
300 _content.pack_start (list_hpane, true, true);
304 route_group_display_button_box->show();
305 route_group_add_button->show();
306 route_group_remove_button->show();
309 _content.set_name ("MixerWindow");
311 global_hpacker.show();
313 scroller_base.show();
314 scroller_hpacker.show();
315 mixer_scroller_vpacker.show();
317 group_display_button_label.show();
318 group_display_button.show();
319 group_display_scroller.show();
320 favorite_plugins_scroller.show();
321 group_display_vbox.show();
322 group_display_frame.show();
323 favorite_plugins_frame.show();
330 vca_scroller_base.show();
333 group_display.show();
334 favorite_plugins_display.show();
336 MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context());
340 ARDOUR_UI::instance()->Escape.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::escape, this), gui_context());
342 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
343 _plugin_selector = new PluginSelector (PluginManager::instance ());
345 #error implement deferred Plugin-Favorite list
347 PluginManager::instance ().PluginListChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
348 PluginManager::instance ().PluginStatusesChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
349 ARDOUR::Plugin::PresetsChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
352 Mixer_UI::~Mixer_UI ()
354 if (_monitor_section) {
355 monitor_section_detached ();
356 delete _monitor_section;
358 delete _plugin_selector;
368 Mixer_UI::track_editor_selection ()
370 PublicEditor::instance().get_selection().TracksChanged.connect (sigc::mem_fun (*this, &Mixer_UI::follow_editor_selection));
374 Mixer_UI::use_own_window (bool and_fill_it)
376 bool new_window = !own_window();
378 Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
381 if (win && new_window) {
382 win->set_name ("MixerWindow");
383 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Mixer"), this);
384 win->signal_scroll_event().connect (sigc::mem_fun (*this, &Mixer_UI::on_scroll_event), false);
385 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
386 win->set_data ("ardour-bindings", bindings);
394 Mixer_UI::show_window ()
396 Tabbable::show_window ();
398 /* show/hide group tabs as required */
399 parameter_changed ("show-group-tabs");
401 /* now reset each strips width so the right widgets are shown */
404 TreeModel::Children rows = track_model->children();
405 TreeModel::Children::iterator ri;
407 for (ri = rows.begin(); ri != rows.end(); ++ri) {
408 ms = (*ri)[track_columns.strip];
412 ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
413 /* Fix visibility of mixer strip stuff */
414 ms->parameter_changed (X_("mixer-element-visibility"));
417 /* force focus into main area */
418 scroller_base.grab_focus ();
422 Mixer_UI::add_masters (VCAList& vcas)
424 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
426 VCAMasterStrip* vms = new VCAMasterStrip (_session, *v);
428 TreeModel::Row row = *(track_model->append());
429 row[track_columns.text] = (*v)->name();
430 row[track_columns.visible] = true;
431 row[track_columns.vca] = vms;
433 vms->CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_master, this, _1), gui_context());
436 redisplay_track_list ();
440 Mixer_UI::remove_master (VCAMasterStrip* vms)
442 if (_session && _session->deletion_in_progress()) {
443 /* its all being taken care of */
447 TreeModel::Children rows = track_model->children();
448 TreeModel::Children::iterator ri;
450 for (ri = rows.begin(); ri != rows.end(); ++ri) {
451 if ((*ri)[track_columns.vca] == vms) {
452 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
453 track_model->erase (ri);
460 Mixer_UI::masters_scroller_button_release (GdkEventButton* ev)
462 using namespace Menu_Helpers;
464 if (Keyboard::is_context_menu_event (ev)) {
465 ARDOUR_UI::instance()->add_route ();
473 Mixer_UI::add_strips (RouteList& routes)
475 Gtk::TreeModel::Children::iterator insert_iter = track_model->children().end();
476 uint32_t nroutes = 0;
478 for (Gtk::TreeModel::Children::iterator it = track_model->children().begin(); it != track_model->children().end(); ++it) {
479 boost::shared_ptr<Route> r = (*it)[track_columns.route];
487 if (r->presentation_info().order() == (routes.front()->presentation_info().order() + routes.size())) {
494 _selection.clear_routes ();
500 no_track_list_redisplay = true;
501 track_display.set_model (Glib::RefPtr<ListStore>());
503 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
504 boost::shared_ptr<Route> route = (*x);
506 if (route->is_auditioner()) {
510 if (route->is_monitor()) {
512 if (!_monitor_section) {
513 _monitor_section = new MonitorSection (_session);
515 XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
517 _monitor_section->tearoff().set_state (*mnode);
521 out_packer.pack_end (_monitor_section->tearoff(), false, false);
522 _monitor_section->set_session (_session);
523 _monitor_section->tearoff().show_all ();
525 _monitor_section->tearoff().Detach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_detached));
526 _monitor_section->tearoff().Attach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_attached));
528 monitor_section_attached ();
530 route->DropReferences.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::monitor_section_going_away, this), gui_context());
532 /* no regular strip shown for control out */
537 strip = new MixerStrip (*this, _session, route);
538 strips.push_back (strip);
540 UIConfiguration::instance().get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
542 if (strip->width_owner() != strip) {
543 strip->set_width_enum (_strip_width, this);
548 TreeModel::Row row = *(track_model->insert(insert_iter));
549 row[track_columns.text] = route->name();
550 row[track_columns.visible] = strip->route()->is_master() ? true : strip->marked_for_display();
551 row[track_columns.route] = route;
552 row[track_columns.strip] = strip;
553 row[track_columns.vca] = 0;
556 _selection.add (strip);
559 route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
561 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
562 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
565 } catch (const std::exception& e) {
566 error << string_compose (_("Error adding GUI elements for new tracks/busses %1"), e.what()) << endmsg;
569 no_track_list_redisplay = false;
570 track_display.set_model (track_model);
572 sync_presentation_info_from_treeview ();
573 redisplay_track_list ();
577 Mixer_UI::deselect_all_strip_processors ()
579 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
580 (*i)->deselect_all_processors();
585 Mixer_UI::select_strip (MixerStrip& ms, bool add)
588 _selection.add (&ms);
590 _selection.set (&ms);
595 Mixer_UI::select_none ()
597 _selection.clear_routes();
598 deselect_all_strip_processors();
602 Mixer_UI::delete_processors ()
604 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
605 (*i)->delete_processors();
611 Mixer_UI::remove_strip (MixerStrip* strip)
613 if (_session && _session->deletion_in_progress()) {
614 /* its all being taken care of */
618 TreeModel::Children rows = track_model->children();
619 TreeModel::Children::iterator ri;
620 list<MixerStrip *>::iterator i;
622 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
626 for (ri = rows.begin(); ri != rows.end(); ++ri) {
627 if ((*ri)[track_columns.strip] == strip) {
628 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
629 track_model->erase (ri);
636 Mixer_UI::sync_presentation_info_from_treeview ()
638 if (ignore_reorder || !_session || _session->deletion_in_progress() || (Config->get_remote_model() != MixerOrdered)) {
642 TreeModel::Children rows = track_model->children();
648 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync presentation info from treeview\n");
650 TreeModel::Children::iterator ri;
654 for (ri = rows.begin(); ri != rows.end(); ++ri) {
655 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
656 bool visible = (*ri)[track_columns.visible];
663 if (route->presentation_info().special()) {
668 route->presentation_info().set_flag (PresentationInfo::Hidden);
670 route->presentation_info().unset_flag (PresentationInfo::Hidden);
673 if (order != route->presentation_info().order()) {
674 route->set_presentation_group_order_explicit (order);
682 DEBUG_TRACE (DEBUG::OrderKeys, "... notify PI change from mixer GUI\n");
683 _session->notify_presentation_info_change ();
688 Mixer_UI::sync_treeview_from_presentation_info ()
690 if (!_session || _session->deletion_in_progress()) {
694 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync model from presentation info.\n");
696 /* we could get here after either a change in the Mixer or Editor sort
697 * order, but either way, the mixer order keys reflect the intended
698 * order for the GUI, so reorder the treeview model to match it.
701 vector<int> neworder;
702 TreeModel::Children rows = track_model->children();
703 uint32_t old_order = 0;
704 bool changed = false;
711 uint32_t vca_cnt = 0;
712 uint32_t max_route_order_key = 0;
714 /* count number of Routes in track_model (there may be some odd reason
715 why this is not the same as the number in the session, but here we
716 care about the track model.
719 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
720 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
722 max_route_order_key = max (route->presentation_info().order(), max_route_order_key);
726 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
727 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
729 /* VCAs need to sort after all routes. We don't display
730 * them in the same place (March 2016), but we don't
731 * want them intermixed in the track_model
733 sorted.push_back (OrderKeys (old_order, max_route_order_key + ++vca_cnt));
735 sorted.push_back (OrderKeys (old_order, route->presentation_info().order()));
739 SortByNewDisplayOrder cmp;
741 sort (sorted.begin(), sorted.end(), cmp);
742 neworder.assign (sorted.size(), 0);
746 for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
748 neworder[n] = sr->old_display_order;
750 if (sr->old_display_order != n) {
756 Unwinder<bool> uw (ignore_reorder, true);
757 track_model->reorder (neworder);
760 redisplay_track_list ();
764 Mixer_UI::follow_editor_selection ()
766 if (_following_editor_selection) {
770 _following_editor_selection = true;
771 _selection.block_routes_changed (true);
773 TrackSelection& s (PublicEditor::instance().get_selection().tracks);
775 _selection.clear_routes ();
777 for (TrackViewList::iterator i = s.begin(); i != s.end(); ++i) {
778 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
780 MixerStrip* ms = strip_by_route (rtav->route());
787 _following_editor_selection = false;
788 _selection.block_routes_changed (false);
793 Mixer_UI::strip_by_route (boost::shared_ptr<Route> r)
795 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
796 if ((*i)->route() == r) {
805 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
807 if (ev->button == 1) {
808 if (_selection.selected (strip)) {
809 /* primary-click: toggle selection state of strip */
810 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
811 _selection.remove (strip);
812 } else if (_selection.routes.size() > 1) {
813 /* de-select others */
814 _selection.set (strip);
817 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
818 _selection.add (strip);
819 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::RangeSelectModifier)) {
821 if (!_selection.selected(strip)) {
823 /* extend selection */
825 vector<MixerStrip*> tmp;
826 bool accumulate = false;
827 bool found_another = false;
829 tmp.push_back (strip);
831 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
833 /* hit clicked strip, start accumulating till we hit the first
842 } else if (_selection.selected (*i)) {
843 /* hit selected strip. if currently accumulating others,
844 we're done. if not accumulating others, start doing so.
846 found_another = true;
861 for (vector<MixerStrip*>::iterator i = tmp.begin(); i != tmp.end(); ++i) {
865 _selection.set (strip); //user wants to start a range selection, but there aren't any others selected yet
869 _selection.set (strip);
878 Mixer_UI::set_session (Session* sess)
880 SessionHandlePtr::set_session (sess);
882 if (_plugin_selector) {
883 _plugin_selector->set_session (_session);
886 _group_tabs->set_session (sess);
892 refill_favorite_plugins();
894 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
895 set_state (*node, 0);
899 initial_track_display ();
901 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_strips, this, _1), gui_context());
902 _session->route_group_added.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_route_group, this, _1), gui_context());
903 _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
904 _session->route_groups_reordered.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
905 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
906 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
907 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
909 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_masters, this, _1), gui_context());
911 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
913 route_groups_changed ();
922 Mixer_UI::session_going_away ()
924 ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away);
926 _in_group_rebuild_or_clear = true;
927 group_model->clear ();
928 _in_group_rebuild_or_clear = false;
931 track_model->clear ();
933 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
937 if (_monitor_section) {
938 _monitor_section->tearoff().hide_visible ();
941 monitor_section_detached ();
947 SessionHandlePtr::session_going_away ();
954 Mixer_UI::track_visibility_changed (std::string const & path)
956 if (_session && _session->deletion_in_progress()) {
962 if ((iter = track_model->get_iter (path))) {
963 MixerStrip* strip = (*iter)[track_columns.strip];
965 bool visible = (*iter)[track_columns.visible];
967 if (strip->set_marked_for_display (!visible)) {
968 update_track_visibility ();
975 Mixer_UI::update_track_visibility ()
977 TreeModel::Children rows = track_model->children();
978 TreeModel::Children::iterator i;
981 Unwinder<bool> uw (no_track_list_redisplay, true);
983 for (i = rows.begin(); i != rows.end(); ++i) {
984 MixerStrip *strip = (*i)[track_columns.strip];
986 (*i)[track_columns.visible] = strip->marked_for_display ();
990 /* force presentation catch up with visibility changes
993 sync_presentation_info_from_treeview ();
996 redisplay_track_list ();
1000 Mixer_UI::show_strip (MixerStrip* ms)
1002 TreeModel::Children rows = track_model->children();
1003 TreeModel::Children::iterator i;
1005 for (i = rows.begin(); i != rows.end(); ++i) {
1007 MixerStrip* strip = (*i)[track_columns.strip];
1009 (*i)[track_columns.visible] = true;
1010 redisplay_track_list ();
1017 Mixer_UI::hide_strip (MixerStrip* ms)
1019 TreeModel::Children rows = track_model->children();
1020 TreeModel::Children::iterator i;
1022 for (i = rows.begin(); i != rows.end(); ++i) {
1024 MixerStrip* strip = (*i)[track_columns.strip];
1026 (*i)[track_columns.visible] = false;
1027 redisplay_track_list ();
1034 Mixer_UI::start_updating ()
1036 fast_screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
1041 Mixer_UI::stop_updating ()
1043 fast_screen_update_connection.disconnect();
1048 Mixer_UI::fast_update_strips ()
1050 if (_content.is_mapped () && _session) {
1051 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1052 (*i)->fast_update ();
1058 Mixer_UI::set_all_strips_visibility (bool yn)
1060 TreeModel::Children rows = track_model->children();
1061 TreeModel::Children::iterator i;
1064 Unwinder<bool> uw (no_track_list_redisplay, true);
1066 for (i = rows.begin(); i != rows.end(); ++i) {
1068 TreeModel::Row row = (*i);
1069 MixerStrip* strip = row[track_columns.strip];
1075 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1079 (*i)[track_columns.visible] = yn;
1083 redisplay_track_list ();
1088 Mixer_UI::set_all_audio_midi_visibility (int tracks, bool yn)
1090 TreeModel::Children rows = track_model->children();
1091 TreeModel::Children::iterator i;
1094 Unwinder<bool> uw (no_track_list_redisplay, true);
1096 for (i = rows.begin(); i != rows.end(); ++i) {
1097 TreeModel::Row row = (*i);
1098 MixerStrip* strip = row[track_columns.strip];
1104 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1108 boost::shared_ptr<AudioTrack> at = strip->audio_track();
1109 boost::shared_ptr<MidiTrack> mt = strip->midi_track();
1113 (*i)[track_columns.visible] = yn;
1117 if (at) { /* track */
1118 (*i)[track_columns.visible] = yn;
1123 if (!at && !mt) { /* bus */
1124 (*i)[track_columns.visible] = yn;
1129 if (mt) { /* midi-track */
1130 (*i)[track_columns.visible] = yn;
1137 redisplay_track_list ();
1141 Mixer_UI::hide_all_routes ()
1143 set_all_strips_visibility (false);
1147 Mixer_UI::show_all_routes ()
1149 set_all_strips_visibility (true);
1153 Mixer_UI::show_all_audiobus ()
1155 set_all_audio_midi_visibility (2, true);
1158 Mixer_UI::hide_all_audiobus ()
1160 set_all_audio_midi_visibility (2, false);
1164 Mixer_UI::show_all_audiotracks()
1166 set_all_audio_midi_visibility (1, true);
1169 Mixer_UI::hide_all_audiotracks ()
1171 set_all_audio_midi_visibility (1, false);
1175 Mixer_UI::show_all_miditracks()
1177 set_all_audio_midi_visibility (3, true);
1180 Mixer_UI::hide_all_miditracks ()
1182 set_all_audio_midi_visibility (3, false);
1187 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
1189 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview reordered\n");
1190 sync_presentation_info_from_treeview ();
1194 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
1196 /* this happens as the second step of a DnD within the treeview as well
1197 as when a row/route is actually deleted.
1199 if it was a deletion then we have to force a redisplay because
1200 order keys may not have changed.
1203 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
1204 sync_presentation_info_from_treeview ();
1206 if (_route_deletion_in_progress) {
1207 redisplay_track_list ();
1212 Mixer_UI::spill_redisplay (boost::shared_ptr<VCA> vca)
1214 TreeModel::Children rows = track_model->children();
1216 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1218 MixerStrip* strip = (*i)[track_columns.strip];
1221 /* we're in the middle of changing a row, don't worry */
1225 if (!strip->route()) {
1226 /* non-route element */
1230 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1234 if (strip->route()->slaved_to (vca)) {
1236 strip->set_gui_property ("visible", true);
1238 if (strip->packed()) {
1239 strip_packer.reorder_child (*strip, -1); /* put at end */
1241 strip_packer.pack_start (*strip, false, false);
1242 strip->set_packed (true);
1247 strip->set_gui_property ("visible", false);
1249 if (strip->packed()) {
1250 strip_packer.remove (*strip);
1251 strip->set_packed (false);
1258 Mixer_UI::redisplay_track_list ()
1260 if (no_track_list_redisplay) {
1264 boost::shared_ptr<VCA> sv = spilled_vca.lock ();
1267 spill_redisplay (sv);
1271 TreeModel::Children rows = track_model->children();
1272 TreeModel::Children::iterator i;
1273 uint32_t n_masters = 0;
1275 container_clear (vca_packer);
1276 vca_packer.pack_end (vca_scroller_base, true, true);
1278 for (i = rows.begin(); i != rows.end(); ++i) {
1280 VCAMasterStrip* vms = (*i)[track_columns.vca];
1283 vca_packer.pack_start (*vms, false, false);
1289 MixerStrip* strip = (*i)[track_columns.strip];
1292 /* we're in the middle of changing a row, don't worry */
1296 bool const visible = (*i)[track_columns.visible];
1299 strip->set_gui_property ("visible", true);
1301 if (strip->packed()) {
1303 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1304 out_packer.reorder_child (*strip, -1);
1307 strip_packer.reorder_child (*strip, -1); /* put at end */
1312 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1313 out_packer.pack_start (*strip, false, false);
1315 strip_packer.pack_start (*strip, false, false);
1317 strip->set_packed (true);
1322 strip->set_gui_property ("visible", false);
1324 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1325 /* do nothing, these cannot be hidden */
1327 if (strip->packed()) {
1328 strip_packer.remove (*strip);
1329 strip->set_packed (false);
1335 /* update visibility of VCA assign buttons */
1337 if (n_masters == 0) {
1338 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::remove_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1339 vca_scroller.hide ();
1341 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::add_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1342 vca_scroller.show ();
1345 _group_tabs->set_dirty ();
1349 Mixer_UI::strip_width_changed ()
1351 _group_tabs->set_dirty ();
1354 TreeModel::Children rows = track_model->children();
1355 TreeModel::Children::iterator i;
1358 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
1359 MixerStrip* strip = (*i)[track_columns.strip];
1365 bool visible = (*i)[track_columns.visible];
1368 strip->queue_draw();
1375 struct PresentationInfoRouteSorter
1377 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1378 return a->presentation_info().order () < b->presentation_info().order ();
1383 Mixer_UI::initial_track_display ()
1385 boost::shared_ptr<RouteList> routes = _session->get_routes();
1386 RouteList copy (*routes);
1387 PresentationInfoRouteSorter sorter;
1392 Unwinder<bool> uw1 (no_track_list_redisplay, true);
1393 Unwinder<bool> uw2 (ignore_reorder, true);
1395 track_model->clear ();
1396 VCAList vcas = _session->vca_manager().vcas();
1401 redisplay_track_list ();
1405 Mixer_UI::show_track_list_menu ()
1407 if (track_menu == 0) {
1408 build_track_menu ();
1411 track_menu->popup (1, gtk_get_current_event_time());
1415 Mixer_UI::track_display_button_press (GdkEventButton* ev)
1417 if (Keyboard::is_context_menu_event (ev)) {
1418 show_track_list_menu ();
1426 Mixer_UI::build_track_menu ()
1428 using namespace Menu_Helpers;
1429 using namespace Gtk;
1431 track_menu = new Menu;
1432 track_menu->set_name ("ArdourContextMenu");
1433 MenuList& items = track_menu->items();
1435 items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1436 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1437 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1438 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1439 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1440 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1441 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::show_all_miditracks)));
1442 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::hide_all_miditracks)));
1447 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1449 if (!what_changed.contains (ARDOUR::Properties::name)) {
1453 ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1455 TreeModel::Children rows = track_model->children();
1456 TreeModel::Children::iterator i;
1458 for (i = rows.begin(); i != rows.end(); ++i) {
1459 if ((*i)[track_columns.strip] == mx) {
1460 (*i)[track_columns.text] = mx->route()->name();
1465 error << _("track display list item for renamed strip not found!") << endmsg;
1469 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1471 TreeModel::Path path;
1472 TreeViewColumn* column;
1476 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1477 _group_tabs->get_menu(0)->popup (1, ev->time);
1481 TreeIter iter = group_model->get_iter (path);
1483 _group_tabs->get_menu(0)->popup (1, ev->time);
1487 RouteGroup* group = (*iter)[group_columns.group];
1489 if (Keyboard::is_context_menu_event (ev)) {
1490 _group_tabs->get_menu(group)->popup (1, ev->time);
1494 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1496 if (Keyboard::is_edit_event (ev)) {
1498 // edit_route_group (group);
1500 group_display.queue_draw();
1509 bool visible = (*iter)[group_columns.visible];
1510 (*iter)[group_columns.visible] = !visible;
1512 group_display.queue_draw();
1525 Mixer_UI::activate_all_route_groups ()
1527 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1531 Mixer_UI::disable_all_route_groups ()
1533 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1537 Mixer_UI::route_groups_changed ()
1539 ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed);
1541 _in_group_rebuild_or_clear = true;
1543 /* just rebuild the while thing */
1545 group_model->clear ();
1548 /* this is currently not used,
1549 * Mixer_UI::group_display_button_press() has a case for it,
1550 * and a commented edit_route_group() but that's n/a since 2011.
1552 * This code is left as reminder that
1553 * row[group_columns.group] = 0 has special meaning.
1557 row = *(group_model->append());
1558 row[group_columns.visible] = true;
1559 row[group_columns.text] = (_("-all-"));
1560 row[group_columns.group] = 0;
1564 _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1566 _group_tabs->set_dirty ();
1567 _in_group_rebuild_or_clear = false;
1571 Mixer_UI::new_route_group ()
1575 _group_tabs->run_new_group_dialog (rl, false);
1579 Mixer_UI::remove_selected_route_group ()
1581 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1582 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1588 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1591 /* selection mode is single, so rows.begin() is it */
1593 if ((iter = group_model->get_iter (*i))) {
1595 RouteGroup* rg = (*iter)[group_columns.group];
1598 _session->remove_route_group (*rg);
1604 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1606 if (in_group_row_change) {
1610 /* force an update of any mixer strips that are using this group,
1611 otherwise mix group names don't change in mixer strips
1614 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1615 if ((*i)->route_group() == group) {
1616 (*i)->route_group_changed();
1620 TreeModel::iterator i;
1621 TreeModel::Children rows = group_model->children();
1622 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1624 in_group_row_change = true;
1626 for (i = rows.begin(); i != rows.end(); ++i) {
1627 if ((*i)[group_columns.group] == group) {
1628 (*i)[group_columns.visible] = !group->is_hidden ();
1629 (*i)[group_columns.text] = group->name ();
1634 in_group_row_change = false;
1636 if (change.contains (Properties::name)) {
1637 _group_tabs->set_dirty ();
1640 for (list<MixerStrip*>::iterator j = strips.begin(); j != strips.end(); ++j) {
1641 if ((*j)->route_group() == group) {
1642 if (group->is_hidden ()) {
1652 Mixer_UI::show_mixer_list (bool yn)
1655 list_vpacker.show ();
1657 list_vpacker.hide ();
1660 _show_mixer_list = yn;
1664 Mixer_UI::show_monitor_section (bool yn)
1666 if (!monitor_section()) {
1669 if (monitor_section()->tearoff().torn_off()) {
1674 monitor_section()->tearoff().show();
1676 monitor_section()->tearoff().hide();
1681 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
1686 if ((iter = group_model->get_iter (path))) {
1688 if ((group = (*iter)[group_columns.group]) == 0) {
1692 if (new_text != group->name()) {
1693 group->set_name (new_text);
1699 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1703 if (in_group_row_change) {
1707 if ((group = (*iter)[group_columns.group]) == 0) {
1711 std::string name = (*iter)[group_columns.text];
1713 if (name != group->name()) {
1714 group->set_name (name);
1717 bool hidden = !(*iter)[group_columns.visible];
1719 if (hidden != group->is_hidden ()) {
1720 group->set_hidden (hidden, this);
1724 /** Called when a group model row is deleted, but also when the model is
1725 * reordered by a user drag-and-drop; the latter is what we are
1726 * interested in here.
1729 Mixer_UI::route_group_row_deleted (Gtk::TreeModel::Path const &)
1731 if (_in_group_rebuild_or_clear) {
1735 /* Re-write the session's route group list so that the new order is preserved */
1737 list<RouteGroup*> new_list;
1739 Gtk::TreeModel::Children children = group_model->children();
1740 for (Gtk::TreeModel::Children::iterator i = children.begin(); i != children.end(); ++i) {
1741 RouteGroup* g = (*i)[group_columns.group];
1743 new_list.push_back (g);
1747 _session->reorder_route_groups (new_list);
1752 Mixer_UI::add_route_group (RouteGroup* group)
1754 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1757 in_group_row_change = true;
1759 TreeModel::Row row = *(group_model->append());
1760 row[group_columns.visible] = !group->is_hidden ();
1761 row[group_columns.group] = group;
1762 if (!group->name().empty()) {
1763 row[group_columns.text] = group->name();
1765 row[group_columns.text] = _("unnamed");
1769 group->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
1772 TreeViewColumn* col = group_display.get_column (0);
1773 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1774 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1777 _group_tabs->set_dirty ();
1779 in_group_row_change = false;
1783 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1785 using namespace Menu_Helpers;
1787 if (Keyboard::is_context_menu_event (ev)) {
1788 ARDOUR_UI::instance()->add_route ();
1796 Mixer_UI::scroller_drag_data_received (const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, guint info, guint time)
1798 printf ("Mixer_UI::scroller_drag_data_received\n");
1799 if (data.get_target() != "PluginFavoritePtr") {
1800 context->drag_finish (false, false, time);
1804 const void * d = data.get_data();
1805 const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>* tv = reinterpret_cast<const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>*>(d);
1807 PluginPresetList nfos;
1809 tv->get_object_drag_data (nfos, &source);
1811 Route::ProcessorList pl;
1814 for (list<PluginPresetPtr>::const_iterator i = nfos.begin(); i != nfos.end(); ++i) {
1815 PluginPresetPtr ppp = (*i);
1816 PluginInfoPtr pip = ppp->_pip;
1817 if (!pip->is_instrument ()) {
1820 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);
1824 context->drag_finish (ok, false, time);
1828 Mixer_UI::set_strip_width (Width w, bool save)
1832 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1833 (*i)->set_width_enum (w, save ? (*i)->width_owner() : this);
1838 struct PluginStateSorter {
1840 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
1841 std::list<std::string>::const_iterator aiter = std::find(_user.begin(), _user.end(), (*a).unique_id);
1842 std::list<std::string>::const_iterator biter = std::find(_user.begin(), _user.end(), (*b).unique_id);
1843 if (aiter != _user.end() && biter != _user.end()) {
1844 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
1846 if (aiter != _user.end()) {
1849 if (biter != _user.end()) {
1852 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
1855 PluginStateSorter(std::list<std::string> user) : _user (user) {}
1857 std::list<std::string> _user;
1861 Mixer_UI::set_state (const XMLNode& node, int version)
1863 XMLProperty const * prop;
1866 Tabbable::set_state (node, version);
1868 if ((prop = node.property ("narrow-strips"))) {
1869 if (string_is_affirmative (prop->value())) {
1870 set_strip_width (Narrow);
1872 set_strip_width (Wide);
1876 if ((prop = node.property ("show-mixer"))) {
1877 if (string_is_affirmative (prop->value())) {
1882 if ((prop = node.property ("maximised"))) {
1883 bool yn = string_is_affirmative (prop->value());
1884 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalMixer"));
1886 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1887 bool fs = tact && tact->get_active();
1889 ActionManager::do_action ("Common", "ToggleMaximalMixer");
1893 if ((prop = node.property ("show-mixer-list"))) {
1894 bool yn = string_is_affirmative (prop->value());
1895 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMixerList"));
1897 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1899 /* do it twice to force the change */
1900 tact->set_active (!yn);
1901 tact->set_active (yn);
1905 XMLNode* plugin_order;
1906 if ((plugin_order = find_named_node (node, "PluginOrder")) != 0) {
1907 store_current_favorite_order ();
1908 std::list<string> order;
1909 const XMLNodeList& kids = plugin_order->children("PluginInfo");
1910 XMLNodeConstIterator i;
1911 for (i = kids.begin(); i != kids.end(); ++i) {
1912 if ((prop = (*i)->property ("unique-id"))) {
1913 std::string unique_id = prop->value();
1914 order.push_back (unique_id);
1915 if ((prop = (*i)->property ("expanded"))) {
1916 favorite_ui_state[unique_id] = string_is_affirmative (prop->value());
1920 PluginStateSorter cmp (order);
1921 favorite_order.sort (cmp);
1922 sync_treeview_from_favorite_order ();
1928 Mixer_UI::get_state ()
1930 XMLNode* node = new XMLNode (X_("Mixer"));
1934 node->add_child_nocopy (Tabbable::get_state());
1936 snprintf(buf,sizeof(buf), "%f", rhs_pane1.get_divider());
1937 node->add_property(X_("mixer-rhs-pane1-pos"), string(buf));
1938 snprintf(buf,sizeof(buf), "%f", rhs_pane2.get_divider());
1939 node->add_property(X_("mixer-rhs_pane2-pos"), string(buf));
1940 snprintf(buf,sizeof(buf), "%f", list_hpane.get_divider());
1941 node->add_property(X_("mixer-list-hpane-pos"), string(buf));
1942 snprintf(buf,sizeof(buf), "%f", inner_pane.get_divider());
1943 node->add_property(X_("mixer-inner-pane-pos"), string(buf));
1945 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1946 node->add_property ("show-mixer", _visible ? "yes" : "no");
1947 node->add_property ("show-mixer-list", _show_mixer_list ? "yes" : "no");
1948 node->add_property ("maximised", _maximised ? "yes" : "no");
1950 store_current_favorite_order ();
1951 XMLNode* plugin_order = new XMLNode ("PluginOrder");
1953 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i, ++cnt) {
1954 XMLNode* p = new XMLNode ("PluginInfo");
1955 p->add_property ("sort", cnt);
1956 p->add_property ("unique-id", (*i)->unique_id);
1957 if (favorite_ui_state.find ((*i)->unique_id) != favorite_ui_state.end ()) {
1958 p->add_property ("expanded", favorite_ui_state[(*i)->unique_id]);
1960 plugin_order->add_child_nocopy (*p);
1962 node->add_child_nocopy (*plugin_order);
1968 Mixer_UI::scroll_left ()
1970 if (!scroller.get_hscrollbar()) return;
1971 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1972 /* stupid GTK: can't rely on clamping across versions */
1973 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1977 Mixer_UI::scroll_right ()
1979 if (!scroller.get_hscrollbar()) return;
1980 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1981 /* stupid GTK: can't rely on clamping across versions */
1982 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1986 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1988 switch (ev->direction) {
1989 case GDK_SCROLL_LEFT:
1993 if (ev->state & Keyboard::TertiaryModifier) {
1999 case GDK_SCROLL_RIGHT:
2003 case GDK_SCROLL_DOWN:
2004 if (ev->state & Keyboard::TertiaryModifier) {
2016 Mixer_UI::parameter_changed (string const & p)
2018 if (p == "show-group-tabs") {
2019 bool const s = _session->config.get_show_group_tabs ();
2021 _group_tabs->show ();
2023 _group_tabs->hide ();
2025 } else if (p == "default-narrow_ms") {
2026 bool const s = UIConfiguration::instance().get_default_narrow_ms ();
2027 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2028 (*i)->set_width_enum (s ? Narrow : Wide, this);
2030 } else if (p == "use-monitor-bus") {
2031 if (_session && !_session->monitor_out()) {
2032 monitor_section_detached ();
2038 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
2040 g->set_active (a, this);
2044 Mixer_UI::plugin_selector()
2046 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
2047 if (!_plugin_selector)
2048 _plugin_selector = new PluginSelector (PluginManager::instance());
2051 return _plugin_selector;
2055 Mixer_UI::setup_track_display ()
2057 track_model = ListStore::create (track_columns);
2058 track_display.set_model (track_model);
2059 track_display.append_column (_("Strips"), track_columns.text);
2060 track_display.append_column (_("Show"), track_columns.visible);
2061 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
2062 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
2063 track_display.get_column (0)->set_expand(true);
2064 track_display.get_column (1)->set_expand(false);
2065 track_display.get_column (0)->set_sizing (Gtk::TREE_VIEW_COLUMN_FIXED);
2066 track_display.set_name (X_("EditGroupList"));
2067 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
2068 track_display.set_reorderable (true);
2069 track_display.set_headers_visible (true);
2070 track_display.set_can_focus(false);
2072 track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
2073 track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
2075 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
2076 track_list_visible_cell->property_activatable() = true;
2077 track_list_visible_cell->property_radio() = false;
2078 track_list_visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::track_visibility_changed));
2080 track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
2082 track_display_scroller.add (track_display);
2083 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
2085 VBox* v = manage (new VBox);
2087 v->pack_start (track_display_scroller, true, true);
2089 Button* b = manage (new Button);
2091 Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
2095 b->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
2097 v->pack_start (*b, false, false);
2099 track_display_frame.set_name("BaseFrame");
2100 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
2101 track_display_frame.add (*v);
2103 track_display_scroller.show();
2104 track_display_frame.show();
2105 track_display.show();
2109 Mixer_UI::new_track_or_bus ()
2111 ARDOUR_UI::instance()->add_route ();
2115 Mixer_UI::update_title ()
2117 if (!own_window()) {
2124 if (_session->snap_name() != _session->name()) {
2125 n = _session->snap_name ();
2127 n = _session->name ();
2130 if (_session->dirty ()) {
2134 WindowTitle title (n);
2135 title += S_("Window|Mixer");
2136 title += Glib::get_application_name ();
2137 own_window()->set_title (title.get_string());
2141 WindowTitle title (S_("Window|Mixer"));
2142 title += Glib::get_application_name ();
2143 own_window()->set_title (title.get_string());
2148 Mixer_UI::strip_by_x (int x)
2150 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2153 (*i)->translate_coordinates (_content, 0, 0, x1, y);
2154 x2 = x1 + (*i)->get_width();
2156 if (x >= x1 && x <= x2) {
2165 Mixer_UI::set_route_targets_for_operation ()
2167 _route_targets.clear ();
2169 if (!_selection.empty()) {
2170 _route_targets = _selection.routes;
2174 // removed "implicit" selections of strips, after discussion on IRC
2179 Mixer_UI::monitor_section_going_away ()
2181 if (_monitor_section) {
2182 monitor_section_detached ();
2183 out_packer.remove (_monitor_section->tearoff());
2184 _monitor_section->set_session (0);
2185 delete _monitor_section;
2186 _monitor_section = 0;
2191 Mixer_UI::toggle_midi_input_active (bool flip_others)
2193 boost::shared_ptr<RouteList> rl (new RouteList);
2196 set_route_targets_for_operation ();
2198 for (RouteUISelection::iterator r = _route_targets.begin(); r != _route_targets.end(); ++r) {
2199 boost::shared_ptr<MidiTrack> mt = (*r)->midi_track();
2202 rl->push_back ((*r)->route());
2203 onoff = !mt->input_active();
2207 _session->set_exclusive_input_active (rl, onoff, flip_others);
2211 Mixer_UI::maximise_mixer_space ()
2213 if (!own_window()) {
2221 _window->fullscreen ();
2226 Mixer_UI::restore_mixer_space ()
2228 if (!own_window()) {
2236 own_window()->unfullscreen();
2241 Mixer_UI::monitor_section_attached ()
2243 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2244 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2245 act->set_sensitive (true);
2246 tact->set_active ();
2250 Mixer_UI::monitor_section_detached ()
2252 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2253 act->set_sensitive (false);
2257 Mixer_UI::store_current_favorite_order ()
2259 typedef Gtk::TreeModel::Children type_children;
2260 type_children children = favorite_plugins_model->children();
2261 favorite_order.clear();
2262 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
2264 Gtk::TreeModel::Row row = *iter;
2265 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2266 favorite_order.push_back (ppp->_pip);
2267 std::string name = row[favorite_plugins_columns.name];
2268 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2273 Mixer_UI::save_favorite_ui_state (const TreeModel::iterator& iter, const TreeModel::Path& path)
2275 Gtk::TreeModel::Row row = *iter;
2276 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2278 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2282 Mixer_UI::refiller (PluginInfoList& result, const PluginInfoList& plugs)
2284 PluginManager& manager (PluginManager::instance());
2285 for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
2286 if (manager.get_status (*i) != PluginManager::Favorite) {
2289 result.push_back (*i);
2293 struct PluginCustomSorter {
2295 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
2296 PluginInfoList::const_iterator aiter = _user.begin();
2297 PluginInfoList::const_iterator biter = _user.begin();
2298 while (aiter != _user.end()) { if ((*aiter)->unique_id == a->unique_id) { break; } ++aiter; }
2299 while (biter != _user.end()) { if ((*biter)->unique_id == b->unique_id) { break; } ++biter; }
2301 if (aiter != _user.end() && biter != _user.end()) {
2302 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
2304 if (aiter != _user.end()) {
2307 if (biter != _user.end()) {
2310 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
2312 PluginCustomSorter(PluginInfoList user) : _user (user) {}
2314 PluginInfoList _user;
2318 Mixer_UI::refill_favorite_plugins ()
2320 PluginInfoList plugs;
2321 PluginManager& mgr (PluginManager::instance());
2324 refiller (plugs, mgr.lv2_plugin_info ());
2326 #ifdef WINDOWS_VST_SUPPORT
2327 refiller (plugs, mgr.windows_vst_plugin_info ());
2329 #ifdef LXVST_SUPPORT
2330 refiller (plugs, mgr.lxvst_plugin_info ());
2332 #ifdef AUDIOUNIT_SUPPORT
2333 refiller (plugs, mgr.au_plugin_info ());
2335 refiller (plugs, mgr.ladspa_plugin_info ());
2336 refiller (plugs, mgr.lua_plugin_info ());
2338 store_current_favorite_order ();
2340 PluginCustomSorter cmp (favorite_order);
2343 favorite_order = plugs;
2345 sync_treeview_from_favorite_order ();
2349 Mixer_UI::sync_treeview_favorite_ui_state (const TreeModel::Path& path, const TreeModel::iterator&)
2352 if (!(iter = favorite_plugins_model->get_iter (path))) {
2355 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2359 PluginInfoPtr pip = ppp->_pip;
2360 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2361 if (favorite_ui_state[pip->unique_id]) {
2362 favorite_plugins_display.expand_row (path, true);
2368 Mixer_UI::sync_treeview_from_favorite_order ()
2370 favorite_plugins_model->clear ();
2371 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i) {
2372 PluginInfoPtr pip = (*i);
2374 TreeModel::Row newrow = *(favorite_plugins_model->append());
2375 newrow[favorite_plugins_columns.name] = (*i)->name;
2376 newrow[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip));
2381 vector<ARDOUR::Plugin::PresetRecord> presets = (*i)->get_presets (true);
2382 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator j = presets.begin(); j != presets.end(); ++j) {
2383 Gtk::TreeModel::Row child_row = *(favorite_plugins_model->append (newrow.children()));
2384 child_row[favorite_plugins_columns.name] = (*j).label;
2385 child_row[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip, &(*j)));
2387 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2388 if (favorite_ui_state[pip->unique_id]) {
2389 favorite_plugins_display.expand_row (favorite_plugins_model->get_path(newrow), true);
2396 Mixer_UI::popup_note_context_menu (GdkEventButton *ev)
2398 using namespace Gtk::Menu_Helpers;
2400 Gtk::Menu* m = manage (new Menu);
2401 MenuList& items = m->items ();
2403 if (_selection.routes.empty()) {
2404 items.push_back (MenuElem (_("No Track/Bus is selected.")));
2406 items.push_back (MenuElem (_("Add at the top"),
2407 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddTop)));
2408 items.push_back (MenuElem (_("Add Pre-Fader"),
2409 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPreFader)));
2410 items.push_back (MenuElem (_("Add Post-Fader"),
2411 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPostFader)));
2412 items.push_back (MenuElem (_("Add at the end"),
2413 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddBottom)));
2416 items.push_back (SeparatorElem());
2418 items.push_back (MenuElem (_("Remove from favorites"), sigc::mem_fun (*this, &Mixer_UI::remove_selected_from_favorites)));
2420 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2421 if (ppp && ppp->_preset.valid && ppp->_preset.user) {
2422 // we cannot currently delete AU presets
2423 if (!ppp->_pip || ppp->_pip->type != AudioUnit) {
2424 items.push_back (MenuElem (_("Delete Preset"), sigc::mem_fun (*this, &Mixer_UI::delete_selected_preset)));
2428 m->popup (ev->button, ev->time);
2432 Mixer_UI::plugin_row_button_press (GdkEventButton *ev)
2434 if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3) ) {
2435 TreeModel::Path path;
2436 TreeViewColumn* column;
2438 if (favorite_plugins_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
2439 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2441 selection->unselect_all();
2442 selection->select(path);
2445 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2447 popup_note_context_menu (ev);
2455 Mixer_UI::selected_plugin ()
2457 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2459 return PluginPresetPtr();
2461 Gtk::TreeModel::iterator iter = selection->get_selected();
2463 return PluginPresetPtr();
2465 return (*iter)[favorite_plugins_columns.plugin];
2469 Mixer_UI::add_selected_processor (ProcessorPosition pos)
2471 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2473 add_favorite_processor (ppp, pos);
2478 Mixer_UI::delete_selected_preset ()
2483 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2484 if (!ppp || !ppp->_preset.valid || !ppp->_preset.user) {
2487 PluginPtr plugin = ppp->_pip->load (*_session);
2488 plugin->get_presets();
2489 plugin->remove_preset (ppp->_preset.label);
2493 Mixer_UI::remove_selected_from_favorites ()
2495 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2499 PluginManager::PluginStatusType status = PluginManager::Normal;
2500 PluginManager& manager (PluginManager::instance());
2502 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2503 manager.save_statuses ();
2507 Mixer_UI::plugin_row_activated (const TreeModel::Path& path, TreeViewColumn* column)
2510 if (!(iter = favorite_plugins_model->get_iter (path))) {
2513 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2514 add_favorite_processor (ppp, AddPreFader); // TODO: preference?!
2518 Mixer_UI::add_favorite_processor (ARDOUR::PluginPresetPtr ppp, ProcessorPosition pos)
2520 if (!_session || _selection.routes.empty()) {
2524 PluginInfoPtr pip = ppp->_pip;
2525 for (RouteUISelection::iterator i = _selection.routes.begin(); i != _selection.routes.end(); ++i) {
2526 boost::shared_ptr<ARDOUR::Route> rt = (*i)->route();
2527 if (!rt) { continue; }
2529 PluginPtr p = pip->load (*_session);
2530 if (!p) { continue; }
2532 if (ppp->_preset.valid) {
2533 p->load_preset (ppp->_preset);
2536 Route::ProcessorStreams err;
2537 boost::shared_ptr<Processor> processor (new PluginInsert (*_session, p));
2541 rt->add_processor_by_index (processor, 0, &err, Config->get_new_plugins_active ());
2544 rt->add_processor (processor, PreFader, &err, Config->get_new_plugins_active ());
2551 boost::shared_ptr<Processor> np = rt->nth_processor (idx);
2555 if (!np->display_to_user()) {
2558 if (boost::dynamic_pointer_cast<Amp> (np) && // Fader, not Trim
2559 boost::dynamic_pointer_cast<Amp> (np)->gain_control()->parameter().type() == GainAutomation) {
2564 rt->add_processor_by_index (processor, ++pos, &err, Config->get_new_plugins_active ());
2568 rt->add_processor_by_index (processor, -1, &err, Config->get_new_plugins_active ());
2575 PluginTreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& data) const
2577 if (data.get_target() != "GTK_TREE_MODEL_ROW") {
2581 // only allow to re-order top-level items
2583 if (TreePath::get_from_selection_data (data, src)) {
2584 if (src.up() && src.up()) {
2589 // don't allow to drop as child-rows.
2590 Gtk::TreeModel::Path _dest = dest; // un const
2591 const bool is_child = _dest.up (); // explicit bool for clang
2592 if (!is_child || _dest.empty ()) {
2599 Mixer_UI::plugin_drop (const Glib::RefPtr<Gdk::DragContext>&, const Gtk::SelectionData& data)
2601 if (data.get_target() != "PluginPresetPtr") {
2604 if (data.get_length() != sizeof (PluginPresetPtr)) {
2607 const void *d = data.get_data();
2608 const PluginPresetPtr ppp = *(static_cast<const PluginPresetPtr*> (d));
2610 PluginManager::PluginStatusType status = PluginManager::Favorite;
2611 PluginManager& manager (PluginManager::instance());
2613 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2614 manager.save_statuses ();
2618 Mixer_UI::do_vca_assign (boost::shared_ptr<VCA> vca)
2620 /* call protected MixerActor:: method */
2625 Mixer_UI::do_vca_unassign (boost::shared_ptr<VCA> vca)
2627 /* call protected MixerActor:: method */
2632 Mixer_UI::show_vca_slaves (boost::shared_ptr<VCA> vca)
2634 boost::shared_ptr<VCA> v = spilled_vca.lock();
2637 show_vca_change (vca); /* EMIT SIGNAL */
2638 redisplay_track_list ();
2643 Mixer_UI::showing_vca_slaves_for (boost::shared_ptr<VCA> vca) const
2645 return vca == spilled_vca.lock();