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;
270 if (!settings || ((prop = settings->property ("mixer-rhs-pane1-pos")) == 0) || ((fract = atof (prop->value())) > 1.0)) {
271 rhs_pane1.set_divider (0, 0.6f);
273 rhs_pane1.set_divider (0, fract);
275 if (!settings || ((prop = settings->property ("mixer-rhs-pane2-pos")) == 0) || ((fract = atof (prop->value())) > 1.0)) {
276 rhs_pane2.set_divider (0, 0.7f);
278 rhs_pane2.set_divider (0, fract);
280 if (!settings || ((prop = settings->property ("mixer-list-hpane-pos")) == 0) || ((fract = atof (prop->value())) > 1.0)) {
281 list_hpane.set_divider (0, 0.2f);
283 list_hpane.set_divider (0, fract);
285 if (!settings || ((prop = settings->property ("mixer-inner-pos")) == 0) || ((fract = atof (prop->value())) > 1.0)) {
286 inner_pane.set_divider (0, 0.8f);
288 inner_pane.set_divider (0, atof (prop->value()));
291 rhs_pane1.set_drag_cursor (*PublicEditor::instance().cursors()->expand_up_down);
292 rhs_pane2.set_drag_cursor (*PublicEditor::instance().cursors()->expand_up_down);
293 list_hpane.set_drag_cursor (*PublicEditor::instance().cursors()->expand_left_right);
294 inner_pane.set_drag_cursor (*PublicEditor::instance().cursors()->expand_left_right);
296 _content.pack_start (list_hpane, true, true);
300 route_group_display_button_box->show();
301 route_group_add_button->show();
302 route_group_remove_button->show();
305 _content.set_name ("MixerWindow");
307 global_hpacker.show();
309 scroller_base.show();
310 scroller_hpacker.show();
311 mixer_scroller_vpacker.show();
313 group_display_button_label.show();
314 group_display_button.show();
315 group_display_scroller.show();
316 favorite_plugins_scroller.show();
317 group_display_vbox.show();
318 group_display_frame.show();
319 favorite_plugins_frame.show();
326 vca_scroller_base.show();
329 group_display.show();
330 favorite_plugins_display.show();
332 MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context());
336 ARDOUR_UI::instance()->Escape.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::escape, this), gui_context());
338 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
339 _plugin_selector = new PluginSelector (PluginManager::instance ());
341 #error implement deferred Plugin-Favorite list
343 PluginManager::instance ().PluginListChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
344 PluginManager::instance ().PluginStatusesChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
345 ARDOUR::Plugin::PresetsChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
348 Mixer_UI::~Mixer_UI ()
350 if (_monitor_section) {
351 monitor_section_detached ();
352 delete _monitor_section;
354 delete _plugin_selector;
364 Mixer_UI::track_editor_selection ()
366 PublicEditor::instance().get_selection().TracksChanged.connect (sigc::mem_fun (*this, &Mixer_UI::follow_editor_selection));
370 Mixer_UI::use_own_window (bool and_fill_it)
372 bool new_window = !own_window();
374 Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
377 if (win && new_window) {
378 win->set_name ("MixerWindow");
379 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Mixer"), this);
380 win->signal_scroll_event().connect (sigc::mem_fun (*this, &Mixer_UI::on_scroll_event), false);
381 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
382 win->set_data ("ardour-bindings", bindings);
390 Mixer_UI::show_window ()
392 Tabbable::show_window ();
394 /* show/hide group tabs as required */
395 parameter_changed ("show-group-tabs");
397 /* now reset each strips width so the right widgets are shown */
400 TreeModel::Children rows = track_model->children();
401 TreeModel::Children::iterator ri;
403 for (ri = rows.begin(); ri != rows.end(); ++ri) {
404 ms = (*ri)[track_columns.strip];
408 ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
409 /* Fix visibility of mixer strip stuff */
410 ms->parameter_changed (X_("mixer-element-visibility"));
413 /* force focus into main area */
414 scroller_base.grab_focus ();
418 Mixer_UI::add_masters (VCAList& vcas)
420 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
422 VCAMasterStrip* vms = new VCAMasterStrip (_session, *v);
424 TreeModel::Row row = *(track_model->append());
425 row[track_columns.text] = (*v)->name();
426 row[track_columns.visible] = true;
427 row[track_columns.vca] = vms;
429 vms->CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_master, this, _1), gui_context());
432 redisplay_track_list ();
436 Mixer_UI::remove_master (VCAMasterStrip* vms)
438 if (_session && _session->deletion_in_progress()) {
439 /* its all being taken care of */
443 TreeModel::Children rows = track_model->children();
444 TreeModel::Children::iterator ri;
446 for (ri = rows.begin(); ri != rows.end(); ++ri) {
447 if ((*ri)[track_columns.vca] == vms) {
448 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
449 track_model->erase (ri);
456 Mixer_UI::masters_scroller_button_release (GdkEventButton* ev)
458 using namespace Menu_Helpers;
460 if (Keyboard::is_context_menu_event (ev)) {
461 ARDOUR_UI::instance()->add_route ();
469 Mixer_UI::add_strips (RouteList& routes)
471 Gtk::TreeModel::Children::iterator insert_iter = track_model->children().end();
472 uint32_t nroutes = 0;
474 for (Gtk::TreeModel::Children::iterator it = track_model->children().begin(); it != track_model->children().end(); ++it) {
475 boost::shared_ptr<Route> r = (*it)[track_columns.route];
483 if (r->presentation_info().group_order() == (routes.front()->presentation_info().group_order() + routes.size())) {
490 _selection.clear_routes ();
496 no_track_list_redisplay = true;
497 track_display.set_model (Glib::RefPtr<ListStore>());
499 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
500 boost::shared_ptr<Route> route = (*x);
502 if (route->is_auditioner()) {
506 if (route->is_monitor()) {
508 if (!_monitor_section) {
509 _monitor_section = new MonitorSection (_session);
511 XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
513 _monitor_section->tearoff().set_state (*mnode);
517 out_packer.pack_end (_monitor_section->tearoff(), false, false);
518 _monitor_section->set_session (_session);
519 _monitor_section->tearoff().show_all ();
521 _monitor_section->tearoff().Detach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_detached));
522 _monitor_section->tearoff().Attach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_attached));
524 monitor_section_attached ();
526 route->DropReferences.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::monitor_section_going_away, this), gui_context());
528 /* no regular strip shown for control out */
533 strip = new MixerStrip (*this, _session, route);
534 strips.push_back (strip);
536 UIConfiguration::instance().get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
538 if (strip->width_owner() != strip) {
539 strip->set_width_enum (_strip_width, this);
544 TreeModel::Row row = *(track_model->insert(insert_iter));
545 row[track_columns.text] = route->name();
546 row[track_columns.visible] = strip->route()->is_master() ? true : strip->marked_for_display();
547 row[track_columns.route] = route;
548 row[track_columns.strip] = strip;
549 row[track_columns.vca] = 0;
552 _selection.add (strip);
555 route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
557 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
558 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
561 } catch (const std::exception& e) {
562 error << string_compose (_("Error adding GUI elements for new tracks/busses %1"), e.what()) << endmsg;
565 no_track_list_redisplay = false;
566 track_display.set_model (track_model);
568 sync_presentation_info_from_treeview ();
569 redisplay_track_list ();
573 Mixer_UI::deselect_all_strip_processors ()
575 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
576 (*i)->deselect_all_processors();
581 Mixer_UI::select_strip (MixerStrip& ms, bool add)
584 _selection.add (&ms);
586 _selection.set (&ms);
591 Mixer_UI::select_none ()
593 _selection.clear_routes();
594 deselect_all_strip_processors();
598 Mixer_UI::delete_processors ()
600 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
601 (*i)->delete_processors();
607 Mixer_UI::remove_strip (MixerStrip* strip)
609 if (_session && _session->deletion_in_progress()) {
610 /* its all being taken care of */
614 TreeModel::Children rows = track_model->children();
615 TreeModel::Children::iterator ri;
616 list<MixerStrip *>::iterator i;
618 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
622 for (ri = rows.begin(); ri != rows.end(); ++ri) {
623 if ((*ri)[track_columns.strip] == strip) {
624 PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
625 track_model->erase (ri);
632 Mixer_UI::sync_presentation_info_from_treeview ()
634 if (ignore_reorder || !_session || _session->deletion_in_progress() || (Config->get_remote_model() != MixerOrdered)) {
638 TreeModel::Children rows = track_model->children();
644 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync presentation info from treeview\n");
646 TreeModel::Children::iterator ri;
650 for (ri = rows.begin(); ri != rows.end(); ++ri) {
651 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
652 bool visible = (*ri)[track_columns.visible];
659 if (route->presentation_info().special()) {
664 route->presentation_info().set_flag (PresentationInfo::Hidden);
666 route->presentation_info().unset_flag (PresentationInfo::Hidden);
669 if (order != route->presentation_info().group_order()) {
670 route->set_presentation_group_order_explicit (order);
678 DEBUG_TRACE (DEBUG::OrderKeys, "... notify PI change from mixer GUI\n");
679 _session->notify_presentation_info_change ();
684 Mixer_UI::sync_treeview_from_presentation_info ()
686 if (!_session || _session->deletion_in_progress()) {
690 DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync model from presentation info.\n");
692 /* we could get here after either a change in the Mixer or Editor sort
693 * order, but either way, the mixer order keys reflect the intended
694 * order for the GUI, so reorder the treeview model to match it.
697 vector<int> neworder;
698 TreeModel::Children rows = track_model->children();
699 uint32_t old_order = 0;
700 bool changed = false;
707 uint32_t vca_cnt = 0;
708 uint32_t max_route_order_key = 0;
710 /* count number of Routes in track_model (there may be some odd reason
711 why this is not the same as the number in the session, but here we
712 care about the track model.
715 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
716 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
718 max_route_order_key = max (route->presentation_info().group_order(), max_route_order_key);
722 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
723 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
725 /* VCAs need to sort after all routes. We don't display
726 * them in the same place (March 2016), but we don't
727 * want them intermixed in the track_model
729 sorted.push_back (OrderKeys (old_order, max_route_order_key + ++vca_cnt));
731 sorted.push_back (OrderKeys (old_order, route->presentation_info().group_order()));
735 SortByNewDisplayOrder cmp;
737 sort (sorted.begin(), sorted.end(), cmp);
738 neworder.assign (sorted.size(), 0);
742 for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
744 neworder[n] = sr->old_display_order;
746 if (sr->old_display_order != n) {
752 Unwinder<bool> uw (ignore_reorder, true);
753 track_model->reorder (neworder);
756 redisplay_track_list ();
760 Mixer_UI::follow_editor_selection ()
762 if (_following_editor_selection) {
766 _following_editor_selection = true;
767 _selection.block_routes_changed (true);
769 TrackSelection& s (PublicEditor::instance().get_selection().tracks);
771 _selection.clear_routes ();
773 for (TrackViewList::iterator i = s.begin(); i != s.end(); ++i) {
774 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
776 MixerStrip* ms = strip_by_route (rtav->route());
783 _following_editor_selection = false;
784 _selection.block_routes_changed (false);
789 Mixer_UI::strip_by_route (boost::shared_ptr<Route> r)
791 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
792 if ((*i)->route() == r) {
801 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
803 if (ev->button == 1) {
804 if (_selection.selected (strip)) {
805 /* primary-click: toggle selection state of strip */
806 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
807 _selection.remove (strip);
808 } else if (_selection.routes.size() > 1) {
809 /* de-select others */
810 _selection.set (strip);
813 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
814 _selection.add (strip);
815 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::RangeSelectModifier)) {
817 if (!_selection.selected(strip)) {
819 /* extend selection */
821 vector<MixerStrip*> tmp;
822 bool accumulate = false;
823 bool found_another = false;
825 tmp.push_back (strip);
827 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
829 /* hit clicked strip, start accumulating till we hit the first
838 } else if (_selection.selected (*i)) {
839 /* hit selected strip. if currently accumulating others,
840 we're done. if not accumulating others, start doing so.
842 found_another = true;
857 for (vector<MixerStrip*>::iterator i = tmp.begin(); i != tmp.end(); ++i) {
861 _selection.set (strip); //user wants to start a range selection, but there aren't any others selected yet
865 _selection.set (strip);
874 Mixer_UI::set_session (Session* sess)
876 SessionHandlePtr::set_session (sess);
878 if (_plugin_selector) {
879 _plugin_selector->set_session (_session);
882 _group_tabs->set_session (sess);
888 refill_favorite_plugins();
890 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
891 set_state (*node, 0);
895 initial_track_display ();
897 _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_strips, this, _1), gui_context());
898 _session->route_group_added.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_route_group, this, _1), gui_context());
899 _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
900 _session->route_groups_reordered.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
901 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
902 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
903 _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
905 _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_masters, this, _1), gui_context());
907 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
909 route_groups_changed ();
918 Mixer_UI::session_going_away ()
920 ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away);
922 _in_group_rebuild_or_clear = true;
923 group_model->clear ();
924 _in_group_rebuild_or_clear = false;
927 track_model->clear ();
929 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
933 if (_monitor_section) {
934 _monitor_section->tearoff().hide_visible ();
937 monitor_section_detached ();
943 SessionHandlePtr::session_going_away ();
950 Mixer_UI::track_visibility_changed (std::string const & path)
952 if (_session && _session->deletion_in_progress()) {
958 if ((iter = track_model->get_iter (path))) {
959 MixerStrip* strip = (*iter)[track_columns.strip];
961 bool visible = (*iter)[track_columns.visible];
963 if (strip->set_marked_for_display (!visible)) {
964 update_track_visibility ();
971 Mixer_UI::update_track_visibility ()
973 TreeModel::Children rows = track_model->children();
974 TreeModel::Children::iterator i;
977 Unwinder<bool> uw (no_track_list_redisplay, true);
979 for (i = rows.begin(); i != rows.end(); ++i) {
980 MixerStrip *strip = (*i)[track_columns.strip];
982 (*i)[track_columns.visible] = strip->marked_for_display ();
986 /* force presentation catch up with visibility changes
989 sync_presentation_info_from_treeview ();
992 redisplay_track_list ();
996 Mixer_UI::show_strip (MixerStrip* ms)
998 TreeModel::Children rows = track_model->children();
999 TreeModel::Children::iterator i;
1001 for (i = rows.begin(); i != rows.end(); ++i) {
1003 MixerStrip* strip = (*i)[track_columns.strip];
1005 (*i)[track_columns.visible] = true;
1006 redisplay_track_list ();
1013 Mixer_UI::hide_strip (MixerStrip* ms)
1015 TreeModel::Children rows = track_model->children();
1016 TreeModel::Children::iterator i;
1018 for (i = rows.begin(); i != rows.end(); ++i) {
1020 MixerStrip* strip = (*i)[track_columns.strip];
1022 (*i)[track_columns.visible] = false;
1023 redisplay_track_list ();
1030 Mixer_UI::start_updating ()
1032 fast_screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
1037 Mixer_UI::stop_updating ()
1039 fast_screen_update_connection.disconnect();
1044 Mixer_UI::fast_update_strips ()
1046 if (_content.is_mapped () && _session) {
1047 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1048 (*i)->fast_update ();
1054 Mixer_UI::set_all_strips_visibility (bool yn)
1056 TreeModel::Children rows = track_model->children();
1057 TreeModel::Children::iterator i;
1060 Unwinder<bool> uw (no_track_list_redisplay, true);
1062 for (i = rows.begin(); i != rows.end(); ++i) {
1064 TreeModel::Row row = (*i);
1065 MixerStrip* strip = row[track_columns.strip];
1071 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1075 (*i)[track_columns.visible] = yn;
1079 redisplay_track_list ();
1084 Mixer_UI::set_all_audio_midi_visibility (int tracks, bool yn)
1086 TreeModel::Children rows = track_model->children();
1087 TreeModel::Children::iterator i;
1090 Unwinder<bool> uw (no_track_list_redisplay, true);
1092 for (i = rows.begin(); i != rows.end(); ++i) {
1093 TreeModel::Row row = (*i);
1094 MixerStrip* strip = row[track_columns.strip];
1100 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1104 boost::shared_ptr<AudioTrack> at = strip->audio_track();
1105 boost::shared_ptr<MidiTrack> mt = strip->midi_track();
1109 (*i)[track_columns.visible] = yn;
1113 if (at) { /* track */
1114 (*i)[track_columns.visible] = yn;
1119 if (!at && !mt) { /* bus */
1120 (*i)[track_columns.visible] = yn;
1125 if (mt) { /* midi-track */
1126 (*i)[track_columns.visible] = yn;
1133 redisplay_track_list ();
1137 Mixer_UI::hide_all_routes ()
1139 set_all_strips_visibility (false);
1143 Mixer_UI::show_all_routes ()
1145 set_all_strips_visibility (true);
1149 Mixer_UI::show_all_audiobus ()
1151 set_all_audio_midi_visibility (2, true);
1154 Mixer_UI::hide_all_audiobus ()
1156 set_all_audio_midi_visibility (2, false);
1160 Mixer_UI::show_all_audiotracks()
1162 set_all_audio_midi_visibility (1, true);
1165 Mixer_UI::hide_all_audiotracks ()
1167 set_all_audio_midi_visibility (1, false);
1171 Mixer_UI::show_all_miditracks()
1173 set_all_audio_midi_visibility (3, true);
1176 Mixer_UI::hide_all_miditracks ()
1178 set_all_audio_midi_visibility (3, false);
1183 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
1185 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview reordered\n");
1186 sync_presentation_info_from_treeview ();
1190 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
1192 /* this happens as the second step of a DnD within the treeview as well
1193 as when a row/route is actually deleted.
1195 if it was a deletion then we have to force a redisplay because
1196 order keys may not have changed.
1199 DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
1200 sync_presentation_info_from_treeview ();
1202 if (_route_deletion_in_progress) {
1203 redisplay_track_list ();
1208 Mixer_UI::spill_redisplay (boost::shared_ptr<VCA> vca)
1210 TreeModel::Children rows = track_model->children();
1212 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1214 MixerStrip* strip = (*i)[track_columns.strip];
1217 /* we're in the middle of changing a row, don't worry */
1221 if (!strip->route()) {
1222 /* non-route element */
1226 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1230 if (strip->route()->slaved_to (vca)) {
1232 strip->set_gui_property ("visible", true);
1234 if (strip->packed()) {
1235 strip_packer.reorder_child (*strip, -1); /* put at end */
1237 strip_packer.pack_start (*strip, false, false);
1238 strip->set_packed (true);
1243 strip->set_gui_property ("visible", false);
1245 if (strip->packed()) {
1246 strip_packer.remove (*strip);
1247 strip->set_packed (false);
1254 Mixer_UI::redisplay_track_list ()
1256 if (no_track_list_redisplay) {
1260 boost::shared_ptr<VCA> sv = spilled_vca.lock ();
1263 spill_redisplay (sv);
1267 TreeModel::Children rows = track_model->children();
1268 TreeModel::Children::iterator i;
1269 uint32_t n_masters = 0;
1271 container_clear (vca_packer);
1272 vca_packer.pack_end (vca_scroller_base, true, true);
1274 for (i = rows.begin(); i != rows.end(); ++i) {
1276 VCAMasterStrip* vms = (*i)[track_columns.vca];
1279 vca_packer.pack_start (*vms, false, false);
1285 MixerStrip* strip = (*i)[track_columns.strip];
1288 /* we're in the middle of changing a row, don't worry */
1292 bool const visible = (*i)[track_columns.visible];
1295 strip->set_gui_property ("visible", true);
1297 if (strip->packed()) {
1299 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1300 out_packer.reorder_child (*strip, -1);
1303 strip_packer.reorder_child (*strip, -1); /* put at end */
1308 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1309 out_packer.pack_start (*strip, false, false);
1311 strip_packer.pack_start (*strip, false, false);
1313 strip->set_packed (true);
1318 strip->set_gui_property ("visible", false);
1320 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1321 /* do nothing, these cannot be hidden */
1323 if (strip->packed()) {
1324 strip_packer.remove (*strip);
1325 strip->set_packed (false);
1331 /* update visibility of VCA assign buttons */
1333 if (n_masters == 0) {
1334 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::remove_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1335 vca_scroller.hide ();
1337 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::add_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1338 vca_scroller.show ();
1341 _group_tabs->set_dirty ();
1345 Mixer_UI::strip_width_changed ()
1347 _group_tabs->set_dirty ();
1350 TreeModel::Children rows = track_model->children();
1351 TreeModel::Children::iterator i;
1354 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
1355 MixerStrip* strip = (*i)[track_columns.strip];
1361 bool visible = (*i)[track_columns.visible];
1364 strip->queue_draw();
1371 struct PresentationInfoRouteSorter
1373 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1374 return a->presentation_info().global_order () < b->presentation_info().global_order ();
1379 Mixer_UI::initial_track_display ()
1381 boost::shared_ptr<RouteList> routes = _session->get_routes();
1382 RouteList copy (*routes);
1383 PresentationInfoRouteSorter sorter;
1388 Unwinder<bool> uw1 (no_track_list_redisplay, true);
1389 Unwinder<bool> uw2 (ignore_reorder, true);
1391 track_model->clear ();
1392 VCAList vcas = _session->vca_manager().vcas();
1397 redisplay_track_list ();
1401 Mixer_UI::show_track_list_menu ()
1403 if (track_menu == 0) {
1404 build_track_menu ();
1407 track_menu->popup (1, gtk_get_current_event_time());
1411 Mixer_UI::track_display_button_press (GdkEventButton* ev)
1413 if (Keyboard::is_context_menu_event (ev)) {
1414 show_track_list_menu ();
1422 Mixer_UI::build_track_menu ()
1424 using namespace Menu_Helpers;
1425 using namespace Gtk;
1427 track_menu = new Menu;
1428 track_menu->set_name ("ArdourContextMenu");
1429 MenuList& items = track_menu->items();
1431 items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1432 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1433 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1434 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1435 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1436 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1437 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::show_all_miditracks)));
1438 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::hide_all_miditracks)));
1443 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1445 if (!what_changed.contains (ARDOUR::Properties::name)) {
1449 ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1451 TreeModel::Children rows = track_model->children();
1452 TreeModel::Children::iterator i;
1454 for (i = rows.begin(); i != rows.end(); ++i) {
1455 if ((*i)[track_columns.strip] == mx) {
1456 (*i)[track_columns.text] = mx->route()->name();
1461 error << _("track display list item for renamed strip not found!") << endmsg;
1465 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1467 TreeModel::Path path;
1468 TreeViewColumn* column;
1472 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1473 _group_tabs->get_menu(0)->popup (1, ev->time);
1477 TreeIter iter = group_model->get_iter (path);
1479 _group_tabs->get_menu(0)->popup (1, ev->time);
1483 RouteGroup* group = (*iter)[group_columns.group];
1485 if (Keyboard::is_context_menu_event (ev)) {
1486 _group_tabs->get_menu(group)->popup (1, ev->time);
1490 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1492 if (Keyboard::is_edit_event (ev)) {
1494 // edit_route_group (group);
1496 group_display.queue_draw();
1505 bool visible = (*iter)[group_columns.visible];
1506 (*iter)[group_columns.visible] = !visible;
1508 group_display.queue_draw();
1521 Mixer_UI::activate_all_route_groups ()
1523 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1527 Mixer_UI::disable_all_route_groups ()
1529 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1533 Mixer_UI::route_groups_changed ()
1535 ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed);
1537 _in_group_rebuild_or_clear = true;
1539 /* just rebuild the while thing */
1541 group_model->clear ();
1544 /* this is currently not used,
1545 * Mixer_UI::group_display_button_press() has a case for it,
1546 * and a commented edit_route_group() but that's n/a since 2011.
1548 * This code is left as reminder that
1549 * row[group_columns.group] = 0 has special meaning.
1553 row = *(group_model->append());
1554 row[group_columns.visible] = true;
1555 row[group_columns.text] = (_("-all-"));
1556 row[group_columns.group] = 0;
1560 _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1562 _group_tabs->set_dirty ();
1563 _in_group_rebuild_or_clear = false;
1567 Mixer_UI::new_route_group ()
1571 _group_tabs->run_new_group_dialog (rl, false);
1575 Mixer_UI::remove_selected_route_group ()
1577 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1578 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1584 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1587 /* selection mode is single, so rows.begin() is it */
1589 if ((iter = group_model->get_iter (*i))) {
1591 RouteGroup* rg = (*iter)[group_columns.group];
1594 _session->remove_route_group (*rg);
1600 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1602 if (in_group_row_change) {
1606 /* force an update of any mixer strips that are using this group,
1607 otherwise mix group names don't change in mixer strips
1610 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1611 if ((*i)->route_group() == group) {
1612 (*i)->route_group_changed();
1616 TreeModel::iterator i;
1617 TreeModel::Children rows = group_model->children();
1618 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1620 in_group_row_change = true;
1622 for (i = rows.begin(); i != rows.end(); ++i) {
1623 if ((*i)[group_columns.group] == group) {
1624 (*i)[group_columns.visible] = !group->is_hidden ();
1625 (*i)[group_columns.text] = group->name ();
1630 in_group_row_change = false;
1632 if (change.contains (Properties::name)) {
1633 _group_tabs->set_dirty ();
1636 for (list<MixerStrip*>::iterator j = strips.begin(); j != strips.end(); ++j) {
1637 if ((*j)->route_group() == group) {
1638 if (group->is_hidden ()) {
1648 Mixer_UI::show_mixer_list (bool yn)
1651 list_vpacker.show ();
1653 list_vpacker.hide ();
1656 _show_mixer_list = yn;
1660 Mixer_UI::show_monitor_section (bool yn)
1662 if (!monitor_section()) {
1665 if (monitor_section()->tearoff().torn_off()) {
1670 monitor_section()->tearoff().show();
1672 monitor_section()->tearoff().hide();
1677 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
1682 if ((iter = group_model->get_iter (path))) {
1684 if ((group = (*iter)[group_columns.group]) == 0) {
1688 if (new_text != group->name()) {
1689 group->set_name (new_text);
1695 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1699 if (in_group_row_change) {
1703 if ((group = (*iter)[group_columns.group]) == 0) {
1707 std::string name = (*iter)[group_columns.text];
1709 if (name != group->name()) {
1710 group->set_name (name);
1713 bool hidden = !(*iter)[group_columns.visible];
1715 if (hidden != group->is_hidden ()) {
1716 group->set_hidden (hidden, this);
1720 /** Called when a group model row is deleted, but also when the model is
1721 * reordered by a user drag-and-drop; the latter is what we are
1722 * interested in here.
1725 Mixer_UI::route_group_row_deleted (Gtk::TreeModel::Path const &)
1727 if (_in_group_rebuild_or_clear) {
1731 /* Re-write the session's route group list so that the new order is preserved */
1733 list<RouteGroup*> new_list;
1735 Gtk::TreeModel::Children children = group_model->children();
1736 for (Gtk::TreeModel::Children::iterator i = children.begin(); i != children.end(); ++i) {
1737 RouteGroup* g = (*i)[group_columns.group];
1739 new_list.push_back (g);
1743 _session->reorder_route_groups (new_list);
1748 Mixer_UI::add_route_group (RouteGroup* group)
1750 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1753 in_group_row_change = true;
1755 TreeModel::Row row = *(group_model->append());
1756 row[group_columns.visible] = !group->is_hidden ();
1757 row[group_columns.group] = group;
1758 if (!group->name().empty()) {
1759 row[group_columns.text] = group->name();
1761 row[group_columns.text] = _("unnamed");
1765 group->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
1768 TreeViewColumn* col = group_display.get_column (0);
1769 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1770 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1773 _group_tabs->set_dirty ();
1775 in_group_row_change = false;
1779 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1781 using namespace Menu_Helpers;
1783 if (Keyboard::is_context_menu_event (ev)) {
1784 ARDOUR_UI::instance()->add_route ();
1792 Mixer_UI::scroller_drag_data_received (const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, guint info, guint time)
1794 printf ("Mixer_UI::scroller_drag_data_received\n");
1795 if (data.get_target() != "PluginFavoritePtr") {
1796 context->drag_finish (false, false, time);
1800 const void * d = data.get_data();
1801 const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>* tv = reinterpret_cast<const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>*>(d);
1803 PluginPresetList nfos;
1805 tv->get_object_drag_data (nfos, &source);
1807 Route::ProcessorList pl;
1810 for (list<PluginPresetPtr>::const_iterator i = nfos.begin(); i != nfos.end(); ++i) {
1811 PluginPresetPtr ppp = (*i);
1812 PluginInfoPtr pip = ppp->_pip;
1813 if (!pip->is_instrument ()) {
1816 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);
1820 context->drag_finish (ok, false, time);
1824 Mixer_UI::set_strip_width (Width w, bool save)
1828 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1829 (*i)->set_width_enum (w, save ? (*i)->width_owner() : this);
1834 struct PluginStateSorter {
1836 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
1837 std::list<std::string>::const_iterator aiter = std::find(_user.begin(), _user.end(), (*a).unique_id);
1838 std::list<std::string>::const_iterator biter = std::find(_user.begin(), _user.end(), (*b).unique_id);
1839 if (aiter != _user.end() && biter != _user.end()) {
1840 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
1842 if (aiter != _user.end()) {
1845 if (biter != _user.end()) {
1848 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
1851 PluginStateSorter(std::list<std::string> user) : _user (user) {}
1853 std::list<std::string> _user;
1857 Mixer_UI::set_state (const XMLNode& node, int version)
1859 XMLProperty const * prop;
1861 Tabbable::set_state (node, version);
1863 if ((prop = node.property ("narrow-strips"))) {
1864 if (string_is_affirmative (prop->value())) {
1865 set_strip_width (Narrow);
1867 set_strip_width (Wide);
1871 if ((prop = node.property ("show-mixer"))) {
1872 if (string_is_affirmative (prop->value())) {
1877 if ((prop = node.property ("maximised"))) {
1878 bool yn = string_is_affirmative (prop->value());
1879 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalMixer"));
1881 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1882 bool fs = tact && tact->get_active();
1884 ActionManager::do_action ("Common", "ToggleMaximalMixer");
1888 if ((prop = node.property ("show-mixer-list"))) {
1889 bool yn = string_is_affirmative (prop->value());
1890 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMixerList"));
1892 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1894 /* do it twice to force the change */
1895 tact->set_active (!yn);
1896 tact->set_active (yn);
1900 XMLNode* plugin_order;
1901 if ((plugin_order = find_named_node (node, "PluginOrder")) != 0) {
1902 store_current_favorite_order ();
1903 std::list<string> order;
1904 const XMLNodeList& kids = plugin_order->children("PluginInfo");
1905 XMLNodeConstIterator i;
1906 for (i = kids.begin(); i != kids.end(); ++i) {
1907 if ((prop = (*i)->property ("unique-id"))) {
1908 std::string unique_id = prop->value();
1909 order.push_back (unique_id);
1910 if ((prop = (*i)->property ("expanded"))) {
1911 favorite_ui_state[unique_id] = string_is_affirmative (prop->value());
1915 PluginStateSorter cmp (order);
1916 favorite_order.sort (cmp);
1917 sync_treeview_from_favorite_order ();
1923 Mixer_UI::get_state ()
1925 XMLNode* node = new XMLNode (X_("Mixer"));
1929 node->add_child_nocopy (Tabbable::get_state());
1931 snprintf(buf,sizeof(buf), "%f", rhs_pane1.get_divider());
1932 node->add_property(X_("mixer-rhs-pane1-pos"), string(buf));
1933 snprintf(buf,sizeof(buf), "%f", rhs_pane2.get_divider());
1934 node->add_property(X_("mixer-rhs_pane2-pos"), string(buf));
1935 snprintf(buf,sizeof(buf), "%f", list_hpane.get_divider());
1936 node->add_property(X_("mixer-list-hpane-pos"), string(buf));
1937 snprintf(buf,sizeof(buf), "%f", inner_pane.get_divider());
1938 node->add_property(X_("mixer-inner-pane-pos"), string(buf));
1940 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1941 node->add_property ("show-mixer", _visible ? "yes" : "no");
1942 node->add_property ("show-mixer-list", _show_mixer_list ? "yes" : "no");
1943 node->add_property ("maximised", _maximised ? "yes" : "no");
1945 store_current_favorite_order ();
1946 XMLNode* plugin_order = new XMLNode ("PluginOrder");
1948 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i, ++cnt) {
1949 XMLNode* p = new XMLNode ("PluginInfo");
1950 p->add_property ("sort", cnt);
1951 p->add_property ("unique-id", (*i)->unique_id);
1952 if (favorite_ui_state.find ((*i)->unique_id) != favorite_ui_state.end ()) {
1953 p->add_property ("expanded", favorite_ui_state[(*i)->unique_id]);
1955 plugin_order->add_child_nocopy (*p);
1957 node->add_child_nocopy (*plugin_order);
1963 Mixer_UI::scroll_left ()
1965 if (!scroller.get_hscrollbar()) return;
1966 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1967 /* stupid GTK: can't rely on clamping across versions */
1968 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1972 Mixer_UI::scroll_right ()
1974 if (!scroller.get_hscrollbar()) return;
1975 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1976 /* stupid GTK: can't rely on clamping across versions */
1977 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1981 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1983 switch (ev->direction) {
1984 case GDK_SCROLL_LEFT:
1988 if (ev->state & Keyboard::TertiaryModifier) {
1994 case GDK_SCROLL_RIGHT:
1998 case GDK_SCROLL_DOWN:
1999 if (ev->state & Keyboard::TertiaryModifier) {
2011 Mixer_UI::parameter_changed (string const & p)
2013 if (p == "show-group-tabs") {
2014 bool const s = _session->config.get_show_group_tabs ();
2016 _group_tabs->show ();
2018 _group_tabs->hide ();
2020 } else if (p == "default-narrow_ms") {
2021 bool const s = UIConfiguration::instance().get_default_narrow_ms ();
2022 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2023 (*i)->set_width_enum (s ? Narrow : Wide, this);
2025 } else if (p == "use-monitor-bus") {
2026 if (_session && !_session->monitor_out()) {
2027 monitor_section_detached ();
2033 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
2035 g->set_active (a, this);
2039 Mixer_UI::plugin_selector()
2041 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
2042 if (!_plugin_selector)
2043 _plugin_selector = new PluginSelector (PluginManager::instance());
2046 return _plugin_selector;
2050 Mixer_UI::setup_track_display ()
2052 track_model = ListStore::create (track_columns);
2053 track_display.set_model (track_model);
2054 track_display.append_column (_("Strips"), track_columns.text);
2055 track_display.append_column (_("Show"), track_columns.visible);
2056 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
2057 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
2058 track_display.get_column (0)->set_expand(true);
2059 track_display.get_column (1)->set_expand(false);
2060 track_display.get_column (0)->set_sizing (Gtk::TREE_VIEW_COLUMN_FIXED);
2061 track_display.set_name (X_("EditGroupList"));
2062 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
2063 track_display.set_reorderable (true);
2064 track_display.set_headers_visible (true);
2065 track_display.set_can_focus(false);
2067 track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
2068 track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
2070 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
2071 track_list_visible_cell->property_activatable() = true;
2072 track_list_visible_cell->property_radio() = false;
2073 track_list_visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::track_visibility_changed));
2075 track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
2077 track_display_scroller.add (track_display);
2078 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
2080 VBox* v = manage (new VBox);
2082 v->pack_start (track_display_scroller, true, true);
2084 Button* b = manage (new Button);
2086 Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
2090 b->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
2092 v->pack_start (*b, false, false);
2094 track_display_frame.set_name("BaseFrame");
2095 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
2096 track_display_frame.add (*v);
2098 track_display_scroller.show();
2099 track_display_frame.show();
2100 track_display.show();
2104 Mixer_UI::new_track_or_bus ()
2106 ARDOUR_UI::instance()->add_route ();
2110 Mixer_UI::update_title ()
2112 if (!own_window()) {
2119 if (_session->snap_name() != _session->name()) {
2120 n = _session->snap_name ();
2122 n = _session->name ();
2125 if (_session->dirty ()) {
2129 WindowTitle title (n);
2130 title += S_("Window|Mixer");
2131 title += Glib::get_application_name ();
2132 own_window()->set_title (title.get_string());
2136 WindowTitle title (S_("Window|Mixer"));
2137 title += Glib::get_application_name ();
2138 own_window()->set_title (title.get_string());
2143 Mixer_UI::strip_by_x (int x)
2145 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2148 (*i)->translate_coordinates (_content, 0, 0, x1, y);
2149 x2 = x1 + (*i)->get_width();
2151 if (x >= x1 && x <= x2) {
2160 Mixer_UI::set_route_targets_for_operation ()
2162 _route_targets.clear ();
2164 if (!_selection.empty()) {
2165 _route_targets = _selection.routes;
2169 // removed "implicit" selections of strips, after discussion on IRC
2174 Mixer_UI::monitor_section_going_away ()
2176 if (_monitor_section) {
2177 monitor_section_detached ();
2178 out_packer.remove (_monitor_section->tearoff());
2179 _monitor_section->set_session (0);
2180 delete _monitor_section;
2181 _monitor_section = 0;
2186 Mixer_UI::toggle_midi_input_active (bool flip_others)
2188 boost::shared_ptr<RouteList> rl (new RouteList);
2191 set_route_targets_for_operation ();
2193 for (RouteUISelection::iterator r = _route_targets.begin(); r != _route_targets.end(); ++r) {
2194 boost::shared_ptr<MidiTrack> mt = (*r)->midi_track();
2197 rl->push_back ((*r)->route());
2198 onoff = !mt->input_active();
2202 _session->set_exclusive_input_active (rl, onoff, flip_others);
2206 Mixer_UI::maximise_mixer_space ()
2208 if (!own_window()) {
2216 _window->fullscreen ();
2221 Mixer_UI::restore_mixer_space ()
2223 if (!own_window()) {
2231 own_window()->unfullscreen();
2236 Mixer_UI::monitor_section_attached ()
2238 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2239 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2240 act->set_sensitive (true);
2241 tact->set_active ();
2245 Mixer_UI::monitor_section_detached ()
2247 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2248 act->set_sensitive (false);
2252 Mixer_UI::store_current_favorite_order ()
2254 typedef Gtk::TreeModel::Children type_children;
2255 type_children children = favorite_plugins_model->children();
2256 favorite_order.clear();
2257 for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
2259 Gtk::TreeModel::Row row = *iter;
2260 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2261 favorite_order.push_back (ppp->_pip);
2262 std::string name = row[favorite_plugins_columns.name];
2263 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2268 Mixer_UI::save_favorite_ui_state (const TreeModel::iterator& iter, const TreeModel::Path& path)
2270 Gtk::TreeModel::Row row = *iter;
2271 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2273 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2277 Mixer_UI::refiller (PluginInfoList& result, const PluginInfoList& plugs)
2279 PluginManager& manager (PluginManager::instance());
2280 for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
2281 if (manager.get_status (*i) != PluginManager::Favorite) {
2284 result.push_back (*i);
2288 struct PluginCustomSorter {
2290 bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
2291 PluginInfoList::const_iterator aiter = _user.begin();
2292 PluginInfoList::const_iterator biter = _user.begin();
2293 while (aiter != _user.end()) { if ((*aiter)->unique_id == a->unique_id) { break; } ++aiter; }
2294 while (biter != _user.end()) { if ((*biter)->unique_id == b->unique_id) { break; } ++biter; }
2296 if (aiter != _user.end() && biter != _user.end()) {
2297 return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
2299 if (aiter != _user.end()) {
2302 if (biter != _user.end()) {
2305 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
2307 PluginCustomSorter(PluginInfoList user) : _user (user) {}
2309 PluginInfoList _user;
2313 Mixer_UI::refill_favorite_plugins ()
2315 PluginInfoList plugs;
2316 PluginManager& mgr (PluginManager::instance());
2319 refiller (plugs, mgr.lv2_plugin_info ());
2321 #ifdef WINDOWS_VST_SUPPORT
2322 refiller (plugs, mgr.windows_vst_plugin_info ());
2324 #ifdef LXVST_SUPPORT
2325 refiller (plugs, mgr.lxvst_plugin_info ());
2327 #ifdef AUDIOUNIT_SUPPORT
2328 refiller (plugs, mgr.au_plugin_info ());
2330 refiller (plugs, mgr.ladspa_plugin_info ());
2331 refiller (plugs, mgr.lua_plugin_info ());
2333 store_current_favorite_order ();
2335 PluginCustomSorter cmp (favorite_order);
2338 favorite_order = plugs;
2340 sync_treeview_from_favorite_order ();
2344 Mixer_UI::sync_treeview_favorite_ui_state (const TreeModel::Path& path, const TreeModel::iterator&)
2347 if (!(iter = favorite_plugins_model->get_iter (path))) {
2350 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2354 PluginInfoPtr pip = ppp->_pip;
2355 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2356 if (favorite_ui_state[pip->unique_id]) {
2357 favorite_plugins_display.expand_row (path, true);
2363 Mixer_UI::sync_treeview_from_favorite_order ()
2365 favorite_plugins_model->clear ();
2366 for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i) {
2367 PluginInfoPtr pip = (*i);
2369 TreeModel::Row newrow = *(favorite_plugins_model->append());
2370 newrow[favorite_plugins_columns.name] = (*i)->name;
2371 newrow[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip));
2376 vector<ARDOUR::Plugin::PresetRecord> presets = (*i)->get_presets (true);
2377 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator j = presets.begin(); j != presets.end(); ++j) {
2378 Gtk::TreeModel::Row child_row = *(favorite_plugins_model->append (newrow.children()));
2379 child_row[favorite_plugins_columns.name] = (*j).label;
2380 child_row[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip, &(*j)));
2382 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2383 if (favorite_ui_state[pip->unique_id]) {
2384 favorite_plugins_display.expand_row (favorite_plugins_model->get_path(newrow), true);
2391 Mixer_UI::popup_note_context_menu (GdkEventButton *ev)
2393 using namespace Gtk::Menu_Helpers;
2395 Gtk::Menu* m = manage (new Menu);
2396 MenuList& items = m->items ();
2398 if (_selection.routes.empty()) {
2399 items.push_back (MenuElem (_("No Track/Bus is selected.")));
2401 items.push_back (MenuElem (_("Add at the top"),
2402 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddTop)));
2403 items.push_back (MenuElem (_("Add Pre-Fader"),
2404 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPreFader)));
2405 items.push_back (MenuElem (_("Add Post-Fader"),
2406 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPostFader)));
2407 items.push_back (MenuElem (_("Add at the end"),
2408 sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddBottom)));
2411 items.push_back (SeparatorElem());
2413 items.push_back (MenuElem (_("Remove from favorites"), sigc::mem_fun (*this, &Mixer_UI::remove_selected_from_favorites)));
2415 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2416 if (ppp && ppp->_preset.valid && ppp->_preset.user) {
2417 // we cannot currently delete AU presets
2418 if (!ppp->_pip || ppp->_pip->type != AudioUnit) {
2419 items.push_back (MenuElem (_("Delete Preset"), sigc::mem_fun (*this, &Mixer_UI::delete_selected_preset)));
2423 m->popup (ev->button, ev->time);
2427 Mixer_UI::plugin_row_button_press (GdkEventButton *ev)
2429 if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3) ) {
2430 TreeModel::Path path;
2431 TreeViewColumn* column;
2433 if (favorite_plugins_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
2434 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2436 selection->unselect_all();
2437 selection->select(path);
2440 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2442 popup_note_context_menu (ev);
2450 Mixer_UI::selected_plugin ()
2452 Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2454 return PluginPresetPtr();
2456 Gtk::TreeModel::iterator iter = selection->get_selected();
2458 return PluginPresetPtr();
2460 return (*iter)[favorite_plugins_columns.plugin];
2464 Mixer_UI::add_selected_processor (ProcessorPosition pos)
2466 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2468 add_favorite_processor (ppp, pos);
2473 Mixer_UI::delete_selected_preset ()
2478 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2479 if (!ppp || !ppp->_preset.valid || !ppp->_preset.user) {
2482 PluginPtr plugin = ppp->_pip->load (*_session);
2483 plugin->get_presets();
2484 plugin->remove_preset (ppp->_preset.label);
2488 Mixer_UI::remove_selected_from_favorites ()
2490 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2494 PluginManager::PluginStatusType status = PluginManager::Normal;
2495 PluginManager& manager (PluginManager::instance());
2497 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2498 manager.save_statuses ();
2502 Mixer_UI::plugin_row_activated (const TreeModel::Path& path, TreeViewColumn* column)
2505 if (!(iter = favorite_plugins_model->get_iter (path))) {
2508 ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2509 add_favorite_processor (ppp, AddPreFader); // TODO: preference?!
2513 Mixer_UI::add_favorite_processor (ARDOUR::PluginPresetPtr ppp, ProcessorPosition pos)
2515 if (!_session || _selection.routes.empty()) {
2519 PluginInfoPtr pip = ppp->_pip;
2520 for (RouteUISelection::iterator i = _selection.routes.begin(); i != _selection.routes.end(); ++i) {
2521 boost::shared_ptr<ARDOUR::Route> rt = (*i)->route();
2522 if (!rt) { continue; }
2524 PluginPtr p = pip->load (*_session);
2525 if (!p) { continue; }
2527 if (ppp->_preset.valid) {
2528 p->load_preset (ppp->_preset);
2531 Route::ProcessorStreams err;
2532 boost::shared_ptr<Processor> processor (new PluginInsert (*_session, p));
2536 rt->add_processor_by_index (processor, 0, &err, Config->get_new_plugins_active ());
2539 rt->add_processor (processor, PreFader, &err, Config->get_new_plugins_active ());
2546 boost::shared_ptr<Processor> np = rt->nth_processor (idx);
2550 if (!np->display_to_user()) {
2553 if (boost::dynamic_pointer_cast<Amp> (np) && // Fader, not Trim
2554 boost::dynamic_pointer_cast<Amp> (np)->gain_control()->parameter().type() == GainAutomation) {
2559 rt->add_processor_by_index (processor, ++pos, &err, Config->get_new_plugins_active ());
2563 rt->add_processor_by_index (processor, -1, &err, Config->get_new_plugins_active ());
2570 PluginTreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& data) const
2572 if (data.get_target() != "GTK_TREE_MODEL_ROW") {
2576 // only allow to re-order top-level items
2578 if (TreePath::get_from_selection_data (data, src)) {
2579 if (src.up() && src.up()) {
2584 // don't allow to drop as child-rows.
2585 Gtk::TreeModel::Path _dest = dest; // un const
2586 const bool is_child = _dest.up (); // explicit bool for clang
2587 if (!is_child || _dest.empty ()) {
2594 Mixer_UI::plugin_drop (const Glib::RefPtr<Gdk::DragContext>&, const Gtk::SelectionData& data)
2596 if (data.get_target() != "PluginPresetPtr") {
2599 if (data.get_length() != sizeof (PluginPresetPtr)) {
2602 const void *d = data.get_data();
2603 const PluginPresetPtr ppp = *(static_cast<const PluginPresetPtr*> (d));
2605 PluginManager::PluginStatusType status = PluginManager::Favorite;
2606 PluginManager& manager (PluginManager::instance());
2608 manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2609 manager.save_statuses ();
2613 Mixer_UI::do_vca_assign (boost::shared_ptr<VCA> vca)
2615 /* call protected MixerActor:: method */
2620 Mixer_UI::do_vca_unassign (boost::shared_ptr<VCA> vca)
2622 /* call protected MixerActor:: method */
2627 Mixer_UI::show_vca_slaves (boost::shared_ptr<VCA> vca)
2629 boost::shared_ptr<VCA> v = spilled_vca.lock();
2632 show_vca_change (vca); /* EMIT SIGNAL */
2633 redisplay_track_list ();
2638 Mixer_UI::showing_vca_slaves_for (boost::shared_ptr<VCA> vca) const
2640 return vca == spilled_vca.lock();