ab437725528ef368cdd5668cdfe6db9837ec86dc
[ardour.git] / gtk2_ardour / mixer_ui.cc
1 /*
2     Copyright (C) 2000-2004 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23
24 #include <algorithm>
25 #include <map>
26 #include <sigc++/bind.h>
27
28 #include <gtkmm/accelmap.h>
29
30 #include "pbd/convert.h"
31 #include "pbd/stacktrace.h"
32 #include "pbd/unwind.h"
33
34 #include <glibmm/threads.h>
35
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>
42
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"
52
53 #include "keyboard.h"
54 #include "mixer_ui.h"
55 #include "mixer_strip.h"
56 #include "monitor_section.h"
57 #include "plugin_selector.h"
58 #include "public_editor.h"
59 #include "ardour_ui.h"
60 #include "prompter.h"
61 #include "utils.h"
62 #include "route_sorter.h"
63 #include "actions.h"
64 #include "gui_thread.h"
65 #include "mixer_group_tabs.h"
66 #include "timers.h"
67 #include "ui_config.h"
68 #include "vca_master_strip.h"
69
70 #include "i18n.h"
71
72 using namespace ARDOUR;
73 using namespace ARDOUR_UI_UTILS;
74 using namespace PBD;
75 using namespace Gtk;
76 using namespace Glib;
77 using namespace Gtkmm2ext;
78 using namespace std;
79
80 using PBD::atoi;
81 using PBD::Unwinder;
82
83 Mixer_UI* Mixer_UI::_instance = 0;
84
85 Mixer_UI*
86 Mixer_UI::instance ()
87 {
88         if (!_instance) {
89                 _instance  = new Mixer_UI;
90         }
91
92         return _instance;
93 }
94
95 Mixer_UI::Mixer_UI ()
96         : Tabbable (_content, _("Mixer"))
97         , no_track_list_redisplay (false)
98         , in_group_row_change (false)
99         , track_menu (0)
100         , _monitor_section (0)
101         , _plugin_selector (0)
102         , _strip_width (UIConfiguration::instance().get_default_narrow_ms() ? Narrow : Wide)
103         , ignore_reorder (false)
104         , _in_group_rebuild_or_clear (false)
105         , _route_deletion_in_progress (false)
106         , _following_editor_selection (false)
107         , _maximised (false)
108         , _show_mixer_list (true)
109 {
110         Stripable::PresentationInfoChange.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::sync_treeview_from_presentation_info, this), gui_context());
111
112         /* bindings was already set in MixerActor constructor */
113
114         _content.set_data ("ardour-bindings", bindings);
115
116         scroller.set_can_default (true);
117         // set_default (scroller);
118
119         scroller_base.set_flags (Gtk::CAN_FOCUS);
120         scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
121         scroller_base.set_name ("MixerWindow");
122         scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
123
124         /* set up drag-n-drop */
125         vector<TargetEntry> target_table;
126         target_table.push_back (TargetEntry ("PluginFavoritePtr"));
127         scroller_base.drag_dest_set (target_table);
128         scroller_base.signal_drag_data_received().connect (sigc::mem_fun(*this, &Mixer_UI::scroller_drag_data_received));
129
130         // add as last item of strip packer
131         strip_packer.pack_end (scroller_base, true, true);
132
133         _group_tabs = new MixerGroupTabs (this);
134         VBox* b = manage (new VBox);
135         b->pack_start (*_group_tabs, PACK_SHRINK);
136         b->pack_start (strip_packer);
137         b->show_all ();
138
139         scroller.add (*b);
140         scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
141
142         setup_track_display ();
143
144         group_model = ListStore::create (group_columns);
145         group_display.set_model (group_model);
146         group_display.append_column (_("Group"), group_columns.text);
147         group_display.append_column (_("Show"), group_columns.visible);
148         group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
149         group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
150         group_display.get_column (0)->set_expand(true);
151         group_display.get_column (1)->set_expand(false);
152         group_display.set_name ("EditGroupList");
153         group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
154         group_display.set_reorderable (true);
155         group_display.set_headers_visible (true);
156         group_display.set_rules_hint (true);
157         group_display.set_can_focus(false);
158
159         /* name is directly editable */
160
161         CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
162         name_cell->property_editable() = true;
163         name_cell->signal_edited().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_name_edit));
164
165         /* use checkbox for the active column */
166
167         CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
168         active_cell->property_activatable() = true;
169         active_cell->property_radio() = false;
170
171         group_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_change));
172         /* We use this to notice drag-and-drop reorders of the group list */
173         group_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_deleted));
174         group_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::group_display_button_press), false);
175
176         group_display_scroller.add (group_display);
177         group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
178
179         HBox* route_group_display_button_box = manage (new HBox());
180
181         Button* route_group_add_button = manage (new Button ());
182         Button* route_group_remove_button = manage (new Button ());
183
184         Widget* w;
185
186         w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
187         w->show();
188         route_group_add_button->add (*w);
189
190         w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
191         w->show();
192         route_group_remove_button->add (*w);
193
194         route_group_display_button_box->set_homogeneous (true);
195
196         route_group_add_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_route_group));
197         route_group_remove_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::remove_selected_route_group));
198
199         route_group_display_button_box->add (*route_group_add_button);
200         route_group_display_button_box->add (*route_group_remove_button);
201
202         group_display_vbox.pack_start (group_display_scroller, true, true);
203         group_display_vbox.pack_start (*route_group_display_button_box, false, false);
204
205         group_display_frame.set_name ("BaseFrame");
206         group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
207         group_display_frame.add (group_display_vbox);
208
209
210         list<TargetEntry> target_list;
211         target_list.push_back (TargetEntry ("PluginPresetPtr"));
212
213         favorite_plugins_model = PluginTreeStore::create (favorite_plugins_columns);
214         favorite_plugins_display.set_model (favorite_plugins_model);
215         favorite_plugins_display.append_column (_("Favorite Plugins"), favorite_plugins_columns.name);
216         favorite_plugins_display.set_name ("EditGroupList");
217         favorite_plugins_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
218         favorite_plugins_display.set_reorderable (false);
219         favorite_plugins_display.set_headers_visible (true);
220         favorite_plugins_display.set_rules_hint (true);
221         favorite_plugins_display.set_can_focus (false);
222         favorite_plugins_display.add_object_drag (favorite_plugins_columns.plugin.index(), "PluginFavoritePtr");
223         favorite_plugins_display.set_drag_column (favorite_plugins_columns.name.index());
224         favorite_plugins_display.add_drop_targets (target_list);
225         favorite_plugins_display.signal_row_activated().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_activated));
226         favorite_plugins_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_button_press), false);
227         favorite_plugins_display.signal_drop.connect (sigc::mem_fun (*this, &Mixer_UI::plugin_drop));
228         favorite_plugins_display.signal_row_expanded().connect (sigc::mem_fun (*this, &Mixer_UI::save_favorite_ui_state));
229         favorite_plugins_display.signal_row_collapsed().connect (sigc::mem_fun (*this, &Mixer_UI::save_favorite_ui_state));
230         favorite_plugins_model->signal_row_has_child_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::sync_treeview_favorite_ui_state));
231
232         favorite_plugins_scroller.add (favorite_plugins_display);
233         favorite_plugins_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
234
235         favorite_plugins_frame.set_name ("BaseFrame");
236         favorite_plugins_frame.set_shadow_type (Gtk::SHADOW_IN);
237         favorite_plugins_frame.add (favorite_plugins_scroller);
238
239         rhs_pane1.pack1 (favorite_plugins_frame, false, true);
240         rhs_pane1.pack2 (track_display_frame);
241         rhs_pane2.pack1 (rhs_pane1);
242         rhs_pane2.pack2 (group_display_frame);
243
244         list_vpacker.pack_start (rhs_pane2, true, true);
245
246         vca_scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
247         vca_scroller_base.set_name ("MixerWindow");
248         vca_scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::masters_scroller_button_release), false);
249         vca_packer.pack_end (vca_scroller_base, true, true);
250
251         vca_scroller.add (vca_packer);
252         vca_scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
253         vca_scroller.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
254
255         inner_pane.pack1 (scroller);
256         inner_pane.pack2 (vca_scroller);
257
258         global_hpacker.pack_start (inner_pane, true, true);
259         global_hpacker.pack_start (out_packer, false, false);
260
261         list_hpane.pack1(list_vpacker, false, true);
262         list_hpane.pack2(global_hpacker, true, false);
263
264         rhs_pane1.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
265                                                         static_cast<Gtk::Paned*> (&rhs_pane1)));
266         rhs_pane2.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
267                                                         static_cast<Gtk::Paned*> (&rhs_pane2)));
268         list_hpane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
269                                                          static_cast<Gtk::Paned*> (&list_hpane)));
270         inner_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
271                                                          static_cast<Gtk::Paned*> (&inner_pane)));
272
273         _content.pack_start (list_hpane, true, true);
274
275         update_title ();
276
277         route_group_display_button_box->show();
278         route_group_add_button->show();
279         route_group_remove_button->show();
280
281         _content.show ();
282         _content.set_name ("MixerWindow");
283
284         global_hpacker.show();
285         scroller.show();
286         scroller_base.show();
287         scroller_hpacker.show();
288         mixer_scroller_vpacker.show();
289         list_vpacker.show();
290         group_display_button_label.show();
291         group_display_button.show();
292         group_display_scroller.show();
293         favorite_plugins_scroller.show();
294         group_display_vbox.show();
295         group_display_frame.show();
296         favorite_plugins_frame.show();
297         rhs_pane1.show();
298         rhs_pane2.show();
299         strip_packer.show();
300         inner_pane.show();
301         vca_scroller.show();
302         vca_packer.show();
303         vca_scroller_base.show();
304         out_packer.show();
305         list_hpane.show();
306         group_display.show();
307         favorite_plugins_display.show();
308
309         MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context());
310
311 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
312         _plugin_selector = new PluginSelector (PluginManager::instance ());
313 #else
314 #error implement deferred Plugin-Favorite list
315 #endif
316         PluginManager::instance ().PluginListChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
317         PluginManager::instance ().PluginStatusesChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
318         ARDOUR::Plugin::PresetsChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
319 }
320
321 Mixer_UI::~Mixer_UI ()
322 {
323         if (_monitor_section) {
324                 monitor_section_detached ();
325                 delete _monitor_section;
326         }
327         delete _plugin_selector;
328 }
329
330 void
331 Mixer_UI::track_editor_selection ()
332 {
333         PublicEditor::instance().get_selection().TracksChanged.connect (sigc::mem_fun (*this, &Mixer_UI::follow_editor_selection));
334 }
335
336 Gtk::Window*
337 Mixer_UI::use_own_window (bool and_fill_it)
338 {
339         bool new_window = !own_window();
340
341         Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
342
343
344         if (win && new_window) {
345                 win->set_name ("MixerWindow");
346                 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Mixer"), this);
347                 win->signal_scroll_event().connect (sigc::mem_fun (*this, &Mixer_UI::on_scroll_event), false);
348                 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
349                 win->set_data ("ardour-bindings", bindings);
350                 update_title ();
351         }
352
353         return win;
354 }
355
356 void
357 Mixer_UI::show_window ()
358 {
359         Tabbable::show_window ();
360
361         /* show/hide group tabs as required */
362         parameter_changed ("show-group-tabs");
363
364         /* now reset each strips width so the right widgets are shown */
365         MixerStrip* ms;
366
367         TreeModel::Children rows = track_model->children();
368         TreeModel::Children::iterator ri;
369
370         for (ri = rows.begin(); ri != rows.end(); ++ri) {
371                 ms = (*ri)[track_columns.strip];
372                 if (!ms) {
373                         continue;
374                 }
375                 ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
376                 /* Fix visibility of mixer strip stuff */
377                 ms->parameter_changed (X_("mixer-element-visibility"));
378         }
379
380         /* force focus into main area */
381         scroller_base.grab_focus ();
382 }
383
384 void
385 Mixer_UI::add_masters (VCAList& vcas)
386 {
387         for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
388
389                 VCAMasterStrip* vms = new VCAMasterStrip (_session, *v);
390
391                 TreeModel::Row row = *(track_model->append());
392                 row[track_columns.text] = (*v)->name();
393                 row[track_columns.visible] = true;
394                 row[track_columns.vca] = vms;
395
396                 vms->CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_master, this, _1), gui_context());
397         }
398
399         redisplay_track_list ();
400 }
401
402 void
403 Mixer_UI::remove_master (VCAMasterStrip* vms)
404 {
405         if (_session && _session->deletion_in_progress()) {
406                 /* its all being taken care of */
407                 return;
408         }
409
410         TreeModel::Children rows = track_model->children();
411         TreeModel::Children::iterator ri;
412
413         for (ri = rows.begin(); ri != rows.end(); ++ri) {
414                 if ((*ri)[track_columns.vca] == vms) {
415                         PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
416                         track_model->erase (ri);
417                         break;
418                 }
419         }
420 }
421
422 bool
423 Mixer_UI::masters_scroller_button_release (GdkEventButton* ev)
424 {
425         using namespace Menu_Helpers;
426
427         if (Keyboard::is_context_menu_event (ev)) {
428                 ARDOUR_UI::instance()->add_route ();
429                 return true;
430         }
431
432         return false;
433 }
434
435 void
436 Mixer_UI::add_strips (RouteList& routes)
437 {
438         Gtk::TreeModel::Children::iterator insert_iter = track_model->children().end();
439         uint32_t nroutes = 0;
440
441         for (Gtk::TreeModel::Children::iterator it = track_model->children().begin(); it != track_model->children().end(); ++it) {
442                 boost::shared_ptr<Route> r = (*it)[track_columns.route];
443
444                 if (!r) {
445                         continue;
446                 }
447
448                 nroutes++;
449
450                 if (r->presentation_info().group_order() == (routes.front()->presentation_info().group_order() + routes.size())) {
451                         insert_iter = it;
452                         break;
453                 }
454         }
455
456         if (nroutes) {
457                 _selection.clear_routes ();
458         }
459
460         MixerStrip* strip;
461
462         try {
463                 no_track_list_redisplay = true;
464                 track_display.set_model (Glib::RefPtr<ListStore>());
465
466                 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
467                         boost::shared_ptr<Route> route = (*x);
468
469                         if (route->is_auditioner()) {
470                                 continue;
471                         }
472
473                         if (route->is_monitor()) {
474
475                                 if (!_monitor_section) {
476                                         _monitor_section = new MonitorSection (_session);
477
478                                         XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
479                                         if (mnode) {
480                                                 _monitor_section->tearoff().set_state (*mnode);
481                                         }
482                                 }
483
484                                 out_packer.pack_end (_monitor_section->tearoff(), false, false);
485                                 _monitor_section->set_session (_session);
486                                 _monitor_section->tearoff().show_all ();
487
488                                 _monitor_section->tearoff().Detach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_detached));
489                                 _monitor_section->tearoff().Attach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_attached));
490
491                                 monitor_section_attached ();
492
493                                 route->DropReferences.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::monitor_section_going_away, this), gui_context());
494
495                                 /* no regular strip shown for control out */
496
497                                 continue;
498                         }
499
500                         strip = new MixerStrip (*this, _session, route);
501                         strips.push_back (strip);
502
503                         UIConfiguration::instance().get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
504
505                         if (strip->width_owner() != strip) {
506                                 strip->set_width_enum (_strip_width, this);
507                         }
508
509                         show_strip (strip);
510
511                         TreeModel::Row row = *(track_model->insert(insert_iter));
512                         row[track_columns.text] = route->name();
513                         row[track_columns.visible] = strip->route()->is_master() ? true : strip->marked_for_display();
514                         row[track_columns.route] = route;
515                         row[track_columns.strip] = strip;
516                         row[track_columns.vca] = 0;
517
518                         if (nroutes != 0) {
519                                 _selection.add (strip);
520                         }
521
522                         route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
523
524                         strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
525                         strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
526                 }
527
528         } catch (const std::exception& e) {
529                 error << string_compose (_("Error adding GUI elements for new tracks/busses %1"), e.what()) << endmsg;
530         }
531
532         no_track_list_redisplay = false;
533         track_display.set_model (track_model);
534
535         sync_presentation_info_from_treeview ();
536         redisplay_track_list ();
537 }
538
539 void
540 Mixer_UI::deselect_all_strip_processors ()
541 {
542         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
543                 (*i)->deselect_all_processors();
544         }
545 }
546
547 void
548 Mixer_UI::select_strip (MixerStrip& ms, bool add)
549 {
550         if (add) {
551                 _selection.add (&ms);
552         } else {
553                 _selection.set (&ms);
554         }
555 }
556
557 void
558 Mixer_UI::select_none ()
559 {
560         _selection.clear_routes();
561         deselect_all_strip_processors();
562 }
563
564 void
565 Mixer_UI::delete_processors ()
566 {
567         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
568                 (*i)->delete_processors();
569         }
570 }
571
572
573 void
574 Mixer_UI::remove_strip (MixerStrip* strip)
575 {
576         if (_session && _session->deletion_in_progress()) {
577                 /* its all being taken care of */
578                 return;
579         }
580
581         TreeModel::Children rows = track_model->children();
582         TreeModel::Children::iterator ri;
583         list<MixerStrip *>::iterator i;
584
585         if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
586                 strips.erase (i);
587         }
588
589         for (ri = rows.begin(); ri != rows.end(); ++ri) {
590                 if ((*ri)[track_columns.strip] == strip) {
591                         PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
592                         track_model->erase (ri);
593                         break;
594                 }
595         }
596 }
597
598 void
599 Mixer_UI::sync_presentation_info_from_treeview ()
600 {
601         if (ignore_reorder || !_session || _session->deletion_in_progress() || (Config->get_remote_model() != MixerOrdered)) {
602                 return;
603         }
604
605         TreeModel::Children rows = track_model->children();
606
607         if (rows.empty()) {
608                 return;
609         }
610
611         DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync presentation info from treeview\n");
612
613         TreeModel::Children::iterator ri;
614         bool change = false;
615         uint32_t order = 0;
616
617         for (ri = rows.begin(); ri != rows.end(); ++ri) {
618                 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
619                 bool visible = (*ri)[track_columns.visible];
620
621
622                 if (!route) {
623                         continue;
624                 }
625
626                 if (route->presentation_info().special()) {
627                         continue;
628                 }
629
630                 if (!visible) {
631                         route->presentation_info().set_flag (PresentationInfo::Hidden);
632                 } else {
633                         route->presentation_info().unset_flag (PresentationInfo::Hidden);
634                 }
635
636                 if (order != route->presentation_info().group_order()) {
637                         route->set_presentation_group_order_explicit (order);
638                         change = true;
639                 }
640
641                 ++order;
642         }
643
644         if (change) {
645                 DEBUG_TRACE (DEBUG::OrderKeys, "... notify PI change from mixer GUI\n");
646                 _session->notify_presentation_info_change ();
647         }
648 }
649
650 void
651 Mixer_UI::sync_treeview_from_presentation_info ()
652 {
653         if (!_session || _session->deletion_in_progress()) {
654                 return;
655         }
656
657         DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync model from presentation info.\n");
658
659         /* we could get here after either a change in the Mixer or Editor sort
660          * order, but either way, the mixer order keys reflect the intended
661          * order for the GUI, so reorder the treeview model to match it.
662          */
663
664         vector<int> neworder;
665         TreeModel::Children rows = track_model->children();
666         uint32_t old_order = 0;
667         bool changed = false;
668
669         if (rows.empty()) {
670                 return;
671         }
672
673         OrderingKeys sorted;
674         uint32_t vca_cnt = 0;
675         uint32_t max_route_order_key = 0;
676
677         /* count number of Routes in track_model (there may be some odd reason
678            why this is not the same as the number in the session, but here we
679            care about the track model.
680         */
681
682         for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
683                 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
684                 if (route) {
685                         max_route_order_key = max (route->presentation_info().group_order(), max_route_order_key);
686                 }
687         }
688
689         for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
690                 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
691                 if (!route) {
692                         /* VCAs need to sort after all routes. We don't display
693                          * them in the same place (March 2016), but we don't
694                          * want them intermixed in the track_model
695                          */
696                         sorted.push_back (OrderKeys (old_order, max_route_order_key + ++vca_cnt));
697                 } else {
698                         sorted.push_back (OrderKeys (old_order, route->presentation_info().group_order()));
699                 }
700         }
701
702         SortByNewDisplayOrder cmp;
703
704         sort (sorted.begin(), sorted.end(), cmp);
705         neworder.assign (sorted.size(), 0);
706
707         uint32_t n = 0;
708
709         for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
710
711                 neworder[n] = sr->old_display_order;
712
713                 if (sr->old_display_order != n) {
714                         changed = true;
715                 }
716         }
717
718         if (changed) {
719                 Unwinder<bool> uw (ignore_reorder, true);
720                 track_model->reorder (neworder);
721         }
722
723         redisplay_track_list ();
724 }
725
726 void
727 Mixer_UI::follow_editor_selection ()
728 {
729         if (_following_editor_selection) {
730                 return;
731         }
732
733         _following_editor_selection = true;
734         _selection.block_routes_changed (true);
735
736         TrackSelection& s (PublicEditor::instance().get_selection().tracks);
737
738         _selection.clear_routes ();
739
740         for (TrackViewList::iterator i = s.begin(); i != s.end(); ++i) {
741                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
742                 if (rtav) {
743                         MixerStrip* ms = strip_by_route (rtav->route());
744                         if (ms) {
745                                 _selection.add (ms);
746                         }
747                 }
748         }
749
750         _following_editor_selection = false;
751         _selection.block_routes_changed (false);
752 }
753
754
755 MixerStrip*
756 Mixer_UI::strip_by_route (boost::shared_ptr<Route> r)
757 {
758         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
759                 if ((*i)->route() == r) {
760                         return (*i);
761                 }
762         }
763
764         return 0;
765 }
766
767 bool
768 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
769 {
770         if (ev->button == 1) {
771                 if (_selection.selected (strip)) {
772                         /* primary-click: toggle selection state of strip */
773                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
774                                 _selection.remove (strip);
775                         } else if (_selection.routes.size() > 1) {
776                                 /* de-select others */
777                                 _selection.set (strip);
778                         }
779                 } else {
780                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
781                                 _selection.add (strip);
782                         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::RangeSelectModifier)) {
783
784                                 if (!_selection.selected(strip)) {
785
786                                         /* extend selection */
787
788                                         vector<MixerStrip*> tmp;
789                                         bool accumulate = false;
790                                         bool found_another = false;
791
792                                         tmp.push_back (strip);
793
794                                         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
795                                                 if ((*i) == strip) {
796                                                         /* hit clicked strip, start accumulating till we hit the first
797                                                            selected strip
798                                                         */
799                                                         if (accumulate) {
800                                                                 /* done */
801                                                                 break;
802                                                         } else {
803                                                                 accumulate = true;
804                                                         }
805                                                 } else if (_selection.selected (*i)) {
806                                                         /* hit selected strip. if currently accumulating others,
807                                                            we're done. if not accumulating others, start doing so.
808                                                         */
809                                                         found_another = true;
810                                                         if (accumulate) {
811                                                                 /* done */
812                                                                 break;
813                                                         } else {
814                                                                 accumulate = true;
815                                                         }
816                                                 } else {
817                                                         if (accumulate) {
818                                                                 tmp.push_back (*i);
819                                                         }
820                                                 }
821                                         }
822
823                                         if (found_another) {
824                                                 for (vector<MixerStrip*>::iterator i = tmp.begin(); i != tmp.end(); ++i) {
825                                                         _selection.add (*i);
826                                                 }
827                                         } else
828                                                 _selection.set (strip);  //user wants to start a range selection, but there aren't any others selected yet
829                                 }
830
831                         } else {
832                                 _selection.set (strip);
833                         }
834                 }
835         }
836
837         return true;
838 }
839
840 void
841 Mixer_UI::set_session (Session* sess)
842 {
843         SessionHandlePtr::set_session (sess);
844
845         if (_plugin_selector) {
846                 _plugin_selector->set_session (_session);
847         }
848
849         _group_tabs->set_session (sess);
850
851         if (!_session) {
852                 return;
853         }
854
855         refill_favorite_plugins();
856
857         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
858         set_state (*node, 0);
859
860         update_title ();
861
862         initial_track_display ();
863
864         _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_strips, this, _1), gui_context());
865         _session->route_group_added.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_route_group, this, _1), gui_context());
866         _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
867         _session->route_groups_reordered.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
868         _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
869         _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
870         _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
871
872         _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_masters, this, _1), gui_context());
873
874         Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
875
876         route_groups_changed ();
877
878         if (_visible) {
879                 show_window();
880         }
881         start_updating ();
882 }
883
884 void
885 Mixer_UI::session_going_away ()
886 {
887         ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away);
888
889         _in_group_rebuild_or_clear = true;
890         group_model->clear ();
891         _in_group_rebuild_or_clear = false;
892
893         _selection.clear ();
894         track_model->clear ();
895
896         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
897                 delete (*i);
898         }
899
900         if (_monitor_section) {
901                 _monitor_section->tearoff().hide_visible ();
902         }
903
904         monitor_section_detached ();
905
906         strips.clear ();
907
908         stop_updating ();
909
910         SessionHandlePtr::session_going_away ();
911
912         _session = 0;
913         update_title ();
914 }
915
916 void
917 Mixer_UI::track_visibility_changed (std::string const & path)
918 {
919         if (_session && _session->deletion_in_progress()) {
920                 return;
921         }
922
923         TreeIter iter;
924
925         if ((iter = track_model->get_iter (path))) {
926                 MixerStrip* strip = (*iter)[track_columns.strip];
927                 if (strip) {
928                         bool visible = (*iter)[track_columns.visible];
929
930                         if (strip->set_marked_for_display (!visible)) {
931                                 update_track_visibility ();
932                         }
933                 }
934         }
935 }
936
937 void
938 Mixer_UI::update_track_visibility ()
939 {
940         TreeModel::Children rows = track_model->children();
941         TreeModel::Children::iterator i;
942
943         {
944                 Unwinder<bool> uw (no_track_list_redisplay, true);
945
946                 for (i = rows.begin(); i != rows.end(); ++i) {
947                         MixerStrip *strip = (*i)[track_columns.strip];
948                         if (strip) {
949                                 (*i)[track_columns.visible] = strip->marked_for_display ();
950                         }
951                 }
952
953                 /* force presentation catch up with visibility changes
954                  */
955
956                 sync_presentation_info_from_treeview ();
957         }
958
959         redisplay_track_list ();
960 }
961
962 void
963 Mixer_UI::show_strip (MixerStrip* ms)
964 {
965         TreeModel::Children rows = track_model->children();
966         TreeModel::Children::iterator i;
967
968         for (i = rows.begin(); i != rows.end(); ++i) {
969
970                 MixerStrip* strip = (*i)[track_columns.strip];
971                 if (strip == ms) {
972                         (*i)[track_columns.visible] = true;
973                         redisplay_track_list ();
974                         break;
975                 }
976         }
977 }
978
979 void
980 Mixer_UI::hide_strip (MixerStrip* ms)
981 {
982         TreeModel::Children rows = track_model->children();
983         TreeModel::Children::iterator i;
984
985         for (i = rows.begin(); i != rows.end(); ++i) {
986
987                 MixerStrip* strip = (*i)[track_columns.strip];
988                 if (strip == ms) {
989                         (*i)[track_columns.visible] = false;
990                         redisplay_track_list ();
991                         break;
992                 }
993         }
994 }
995
996 gint
997 Mixer_UI::start_updating ()
998 {
999     fast_screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
1000     return 0;
1001 }
1002
1003 gint
1004 Mixer_UI::stop_updating ()
1005 {
1006     fast_screen_update_connection.disconnect();
1007     return 0;
1008 }
1009
1010 void
1011 Mixer_UI::fast_update_strips ()
1012 {
1013         if (_content.is_mapped () && _session) {
1014                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1015                         (*i)->fast_update ();
1016                 }
1017         }
1018 }
1019
1020 void
1021 Mixer_UI::set_all_strips_visibility (bool yn)
1022 {
1023         TreeModel::Children rows = track_model->children();
1024         TreeModel::Children::iterator i;
1025
1026         {
1027                 Unwinder<bool> uw (no_track_list_redisplay, true);
1028
1029                 for (i = rows.begin(); i != rows.end(); ++i) {
1030
1031                         TreeModel::Row row = (*i);
1032                         MixerStrip* strip = row[track_columns.strip];
1033
1034                         if (!strip) {
1035                                 continue;
1036                         }
1037
1038                         if (strip->route()->is_master() || strip->route()->is_monitor()) {
1039                                 continue;
1040                         }
1041
1042                         (*i)[track_columns.visible] = yn;
1043                 }
1044         }
1045
1046         redisplay_track_list ();
1047 }
1048
1049
1050 void
1051 Mixer_UI::set_all_audio_midi_visibility (int tracks, bool yn)
1052 {
1053         TreeModel::Children rows = track_model->children();
1054         TreeModel::Children::iterator i;
1055
1056         {
1057                 Unwinder<bool> uw (no_track_list_redisplay, true);
1058
1059                 for (i = rows.begin(); i != rows.end(); ++i) {
1060                         TreeModel::Row row = (*i);
1061                         MixerStrip* strip = row[track_columns.strip];
1062
1063                         if (!strip) {
1064                                 continue;
1065                         }
1066
1067                         if (strip->route()->is_master() || strip->route()->is_monitor()) {
1068                                 continue;
1069                         }
1070
1071                         boost::shared_ptr<AudioTrack> at = strip->audio_track();
1072                         boost::shared_ptr<MidiTrack> mt = strip->midi_track();
1073
1074                         switch (tracks) {
1075                         case 0:
1076                                 (*i)[track_columns.visible] = yn;
1077                                 break;
1078
1079                         case 1:
1080                                 if (at) { /* track */
1081                                         (*i)[track_columns.visible] = yn;
1082                                 }
1083                                 break;
1084
1085                         case 2:
1086                                 if (!at && !mt) { /* bus */
1087                                         (*i)[track_columns.visible] = yn;
1088                                 }
1089                                 break;
1090
1091                         case 3:
1092                                 if (mt) { /* midi-track */
1093                                         (*i)[track_columns.visible] = yn;
1094                                 }
1095                                 break;
1096                         }
1097                 }
1098         }
1099
1100         redisplay_track_list ();
1101 }
1102
1103 void
1104 Mixer_UI::hide_all_routes ()
1105 {
1106         set_all_strips_visibility (false);
1107 }
1108
1109 void
1110 Mixer_UI::show_all_routes ()
1111 {
1112         set_all_strips_visibility (true);
1113 }
1114
1115 void
1116 Mixer_UI::show_all_audiobus ()
1117 {
1118         set_all_audio_midi_visibility (2, true);
1119 }
1120 void
1121 Mixer_UI::hide_all_audiobus ()
1122 {
1123         set_all_audio_midi_visibility (2, false);
1124 }
1125
1126 void
1127 Mixer_UI::show_all_audiotracks()
1128 {
1129         set_all_audio_midi_visibility (1, true);
1130 }
1131 void
1132 Mixer_UI::hide_all_audiotracks ()
1133 {
1134         set_all_audio_midi_visibility (1, false);
1135 }
1136
1137 void
1138 Mixer_UI::show_all_miditracks()
1139 {
1140         set_all_audio_midi_visibility (3, true);
1141 }
1142 void
1143 Mixer_UI::hide_all_miditracks ()
1144 {
1145         set_all_audio_midi_visibility (3, false);
1146 }
1147
1148
1149 void
1150 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
1151 {
1152         DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview reordered\n");
1153         sync_presentation_info_from_treeview ();
1154 }
1155
1156 void
1157 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
1158 {
1159         /* this happens as the second step of a DnD within the treeview as well
1160            as when a row/route is actually deleted.
1161
1162            if it was a deletion then we have to force a redisplay because
1163            order keys may not have changed.
1164         */
1165
1166         DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
1167         sync_presentation_info_from_treeview ();
1168
1169         if (_route_deletion_in_progress) {
1170                 redisplay_track_list ();
1171         }
1172 }
1173
1174 void
1175 Mixer_UI::spill_redisplay (boost::shared_ptr<VCA> vca)
1176 {
1177         TreeModel::Children rows = track_model->children();
1178
1179         for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1180
1181                 MixerStrip* strip = (*i)[track_columns.strip];
1182
1183                 if (!strip) {
1184                         /* we're in the middle of changing a row, don't worry */
1185                         continue;
1186                 }
1187
1188                 if (!strip->route()) {
1189                         /* non-route element */
1190                         continue;
1191                 }
1192
1193                 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1194                         continue;
1195                 }
1196
1197                 if (strip->route()->slaved_to (vca)) {
1198
1199                         strip->set_gui_property ("visible", true);
1200
1201                         if (strip->packed()) {
1202                                 strip_packer.reorder_child (*strip, -1); /* put at end */
1203                         } else {
1204                                 strip_packer.pack_start (*strip, false, false);
1205                                 strip->set_packed (true);
1206                         }
1207
1208                 } else {
1209
1210                         strip->set_gui_property ("visible", false);
1211
1212                         if (strip->packed()) {
1213                                 strip_packer.remove (*strip);
1214                                 strip->set_packed (false);
1215                         }
1216                 }
1217         }
1218 }
1219
1220 void
1221 Mixer_UI::redisplay_track_list ()
1222 {
1223         if (no_track_list_redisplay) {
1224                 return;
1225         }
1226
1227         boost::shared_ptr<VCA> sv = spilled_vca.lock ();
1228
1229         if (sv) {
1230                 spill_redisplay (sv);
1231                 return;
1232         }
1233
1234         TreeModel::Children rows = track_model->children();
1235         TreeModel::Children::iterator i;
1236         uint32_t n_masters = 0;
1237
1238         container_clear (vca_packer);
1239         vca_packer.pack_end (vca_scroller_base, true, true);
1240
1241         for (i = rows.begin(); i != rows.end(); ++i) {
1242
1243                 VCAMasterStrip* vms = (*i)[track_columns.vca];
1244
1245                 if (vms) {
1246                         vca_packer.pack_start (*vms, false, false);
1247                         vms->show ();
1248                         n_masters++;
1249                         continue;
1250                 }
1251
1252                 MixerStrip* strip = (*i)[track_columns.strip];
1253
1254                 if (!strip) {
1255                         /* we're in the middle of changing a row, don't worry */
1256                         continue;
1257                 }
1258
1259                 bool const visible = (*i)[track_columns.visible];
1260
1261                 if (visible) {
1262                         strip->set_gui_property ("visible", true);
1263
1264                         if (strip->packed()) {
1265
1266                                 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1267                                         out_packer.reorder_child (*strip, -1);
1268
1269                                 } else {
1270                                         strip_packer.reorder_child (*strip, -1); /* put at end */
1271                                 }
1272
1273                         } else {
1274
1275                                 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1276                                         out_packer.pack_start (*strip, false, false);
1277                                 } else {
1278                                         strip_packer.pack_start (*strip, false, false);
1279                                 }
1280                                 strip->set_packed (true);
1281                         }
1282
1283                 } else {
1284
1285                         strip->set_gui_property ("visible", false);
1286
1287                         if (strip->route()->is_master() || strip->route()->is_monitor()) {
1288                                 /* do nothing, these cannot be hidden */
1289                         } else {
1290                                 if (strip->packed()) {
1291                                         strip_packer.remove (*strip);
1292                                         strip->set_packed (false);
1293                                 }
1294                         }
1295                 }
1296         }
1297
1298         /* update visibility of VCA assign buttons */
1299
1300         if (n_masters == 0) {
1301                 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::remove_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1302                 vca_scroller.hide ();
1303         } else {
1304                 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::add_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1305                 vca_scroller.show ();
1306         }
1307
1308         _group_tabs->set_dirty ();
1309 }
1310
1311 void
1312 Mixer_UI::strip_width_changed ()
1313 {
1314         _group_tabs->set_dirty ();
1315
1316 #ifdef __APPLE__
1317         TreeModel::Children rows = track_model->children();
1318         TreeModel::Children::iterator i;
1319         long order;
1320
1321         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
1322                 MixerStrip* strip = (*i)[track_columns.strip];
1323
1324                 if (strip == 0) {
1325                         continue;
1326                 }
1327
1328                 bool visible = (*i)[track_columns.visible];
1329
1330                 if (visible) {
1331                         strip->queue_draw();
1332                 }
1333         }
1334 #endif
1335
1336 }
1337
1338 struct PresentationInfoRouteSorter
1339 {
1340         bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1341                 return a->presentation_info().global_order () < b->presentation_info().global_order ();
1342         }
1343 };
1344
1345 void
1346 Mixer_UI::initial_track_display ()
1347 {
1348         boost::shared_ptr<RouteList> routes = _session->get_routes();
1349         RouteList copy (*routes);
1350         PresentationInfoRouteSorter sorter;
1351
1352         copy.sort (sorter);
1353
1354         {
1355                 Unwinder<bool> uw1 (no_track_list_redisplay, true);
1356                 Unwinder<bool> uw2 (ignore_reorder, true);
1357
1358                 track_model->clear ();
1359                 VCAList vcas = _session->vca_manager().vcas();
1360                 add_masters (vcas);
1361                 add_strips (copy);
1362         }
1363
1364         redisplay_track_list ();
1365 }
1366
1367 void
1368 Mixer_UI::show_track_list_menu ()
1369 {
1370         if (track_menu == 0) {
1371                 build_track_menu ();
1372         }
1373
1374         track_menu->popup (1, gtk_get_current_event_time());
1375 }
1376
1377 bool
1378 Mixer_UI::track_display_button_press (GdkEventButton* ev)
1379 {
1380         if (Keyboard::is_context_menu_event (ev)) {
1381                 show_track_list_menu ();
1382                 return true;
1383         }
1384
1385         return false;
1386 }
1387
1388 void
1389 Mixer_UI::build_track_menu ()
1390 {
1391         using namespace Menu_Helpers;
1392         using namespace Gtk;
1393
1394         track_menu = new Menu;
1395         track_menu->set_name ("ArdourContextMenu");
1396         MenuList& items = track_menu->items();
1397
1398         items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1399         items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1400         items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1401         items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1402         items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1403         items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1404         items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::show_all_miditracks)));
1405         items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::hide_all_miditracks)));
1406
1407 }
1408
1409 void
1410 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1411 {
1412         if (!what_changed.contains (ARDOUR::Properties::name)) {
1413                 return;
1414         }
1415
1416         ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1417
1418         TreeModel::Children rows = track_model->children();
1419         TreeModel::Children::iterator i;
1420
1421         for (i = rows.begin(); i != rows.end(); ++i) {
1422                 if ((*i)[track_columns.strip] == mx) {
1423                         (*i)[track_columns.text] = mx->route()->name();
1424                         return;
1425                 }
1426         }
1427
1428         error << _("track display list item for renamed strip not found!") << endmsg;
1429 }
1430
1431 bool
1432 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1433 {
1434         TreeModel::Path path;
1435         TreeViewColumn* column;
1436         int cellx;
1437         int celly;
1438
1439         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1440                 _group_tabs->get_menu(0)->popup (1, ev->time);
1441                 return true;
1442         }
1443
1444         TreeIter iter = group_model->get_iter (path);
1445         if (!iter) {
1446                 _group_tabs->get_menu(0)->popup (1, ev->time);
1447                 return true;
1448         }
1449
1450         RouteGroup* group = (*iter)[group_columns.group];
1451
1452         if (Keyboard::is_context_menu_event (ev)) {
1453                 _group_tabs->get_menu(group)->popup (1, ev->time);
1454                 return true;
1455         }
1456
1457         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1458         case 0:
1459                 if (Keyboard::is_edit_event (ev)) {
1460                         if (group) {
1461                                 // edit_route_group (group);
1462 #ifdef __APPLE__
1463                                 group_display.queue_draw();
1464 #endif
1465                                 return true;
1466                         }
1467                 }
1468                 break;
1469
1470         case 1:
1471         {
1472                 bool visible = (*iter)[group_columns.visible];
1473                 (*iter)[group_columns.visible] = !visible;
1474 #ifdef __APPLE__
1475                 group_display.queue_draw();
1476 #endif
1477                 return true;
1478         }
1479
1480         default:
1481                 break;
1482         }
1483
1484         return false;
1485  }
1486
1487 void
1488 Mixer_UI::activate_all_route_groups ()
1489 {
1490         _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1491 }
1492
1493 void
1494 Mixer_UI::disable_all_route_groups ()
1495 {
1496         _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1497 }
1498
1499 void
1500 Mixer_UI::route_groups_changed ()
1501 {
1502         ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed);
1503
1504         _in_group_rebuild_or_clear = true;
1505
1506         /* just rebuild the while thing */
1507
1508         group_model->clear ();
1509
1510 #if 0
1511         /* this is currently not used,
1512          * Mixer_UI::group_display_button_press() has a case for it,
1513          * and a commented edit_route_group() but that's n/a since 2011.
1514          *
1515          * This code is left as reminder that
1516          * row[group_columns.group] = 0 has special meaning.
1517          */
1518         {
1519                 TreeModel::Row row;
1520                 row = *(group_model->append());
1521                 row[group_columns.visible] = true;
1522                 row[group_columns.text] = (_("-all-"));
1523                 row[group_columns.group] = 0;
1524         }
1525 #endif
1526
1527         _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1528
1529         _group_tabs->set_dirty ();
1530         _in_group_rebuild_or_clear = false;
1531 }
1532
1533 void
1534 Mixer_UI::new_route_group ()
1535 {
1536         RouteList rl;
1537
1538         _group_tabs->run_new_group_dialog (rl);
1539 }
1540
1541 void
1542 Mixer_UI::remove_selected_route_group ()
1543 {
1544         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1545         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1546
1547         if (rows.empty()) {
1548                 return;
1549         }
1550
1551         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1552         TreeIter iter;
1553
1554         /* selection mode is single, so rows.begin() is it */
1555
1556         if ((iter = group_model->get_iter (*i))) {
1557
1558                 RouteGroup* rg = (*iter)[group_columns.group];
1559
1560                 if (rg) {
1561                         _session->remove_route_group (*rg);
1562                 }
1563         }
1564 }
1565
1566 void
1567 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1568 {
1569         if (in_group_row_change) {
1570                 return;
1571         }
1572
1573         /* force an update of any mixer strips that are using this group,
1574            otherwise mix group names don't change in mixer strips
1575         */
1576
1577         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1578                 if ((*i)->route_group() == group) {
1579                         (*i)->route_group_changed();
1580                 }
1581         }
1582
1583         TreeModel::iterator i;
1584         TreeModel::Children rows = group_model->children();
1585         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1586
1587         in_group_row_change = true;
1588
1589         for (i = rows.begin(); i != rows.end(); ++i) {
1590                 if ((*i)[group_columns.group] == group) {
1591                         (*i)[group_columns.visible] = !group->is_hidden ();
1592                         (*i)[group_columns.text] = group->name ();
1593                         break;
1594                 }
1595         }
1596
1597         in_group_row_change = false;
1598
1599         if (change.contains (Properties::name)) {
1600                 _group_tabs->set_dirty ();
1601         }
1602
1603         for (list<MixerStrip*>::iterator j = strips.begin(); j != strips.end(); ++j) {
1604                 if ((*j)->route_group() == group) {
1605                         if (group->is_hidden ()) {
1606                                 hide_strip (*j);
1607                         } else {
1608                                 show_strip (*j);
1609                         }
1610                 }
1611         }
1612 }
1613
1614 void
1615 Mixer_UI::show_mixer_list (bool yn)
1616 {
1617         if (yn) {
1618                 list_vpacker.show ();
1619
1620                 //if user wants to show the pane, we should make sure that it is wide enough to be visible
1621                 int width = list_hpane.get_position();
1622                 if (width < 40) {
1623                         list_hpane.set_position(40);
1624                 }
1625         } else {
1626                 list_vpacker.hide ();
1627         }
1628
1629         _show_mixer_list = yn;
1630 }
1631
1632 void
1633 Mixer_UI::show_monitor_section (bool yn)
1634 {
1635         if (!monitor_section()) {
1636                 return;
1637         }
1638         if (monitor_section()->tearoff().torn_off()) {
1639                 return;
1640         }
1641
1642         if (yn) {
1643                 monitor_section()->tearoff().show();
1644         } else {
1645                 monitor_section()->tearoff().hide();
1646         }
1647 }
1648
1649 void
1650 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
1651 {
1652         RouteGroup* group;
1653         TreeIter iter;
1654
1655         if ((iter = group_model->get_iter (path))) {
1656
1657                 if ((group = (*iter)[group_columns.group]) == 0) {
1658                         return;
1659                 }
1660
1661                 if (new_text != group->name()) {
1662                         group->set_name (new_text);
1663                 }
1664         }
1665 }
1666
1667 void
1668 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1669 {
1670         RouteGroup* group;
1671
1672         if (in_group_row_change) {
1673                 return;
1674         }
1675
1676         if ((group = (*iter)[group_columns.group]) == 0) {
1677                 return;
1678         }
1679
1680         std::string name = (*iter)[group_columns.text];
1681
1682         if (name != group->name()) {
1683                 group->set_name (name);
1684         }
1685
1686         bool hidden = !(*iter)[group_columns.visible];
1687
1688         if (hidden != group->is_hidden ()) {
1689                 group->set_hidden (hidden, this);
1690         }
1691 }
1692
1693 /** Called when a group model row is deleted, but also when the model is
1694  *  reordered by a user drag-and-drop; the latter is what we are
1695  *  interested in here.
1696  */
1697 void
1698 Mixer_UI::route_group_row_deleted (Gtk::TreeModel::Path const &)
1699 {
1700         if (_in_group_rebuild_or_clear) {
1701                 return;
1702         }
1703
1704         /* Re-write the session's route group list so that the new order is preserved */
1705
1706         list<RouteGroup*> new_list;
1707
1708         Gtk::TreeModel::Children children = group_model->children();
1709         for (Gtk::TreeModel::Children::iterator i = children.begin(); i != children.end(); ++i) {
1710                 RouteGroup* g = (*i)[group_columns.group];
1711                 if (g) {
1712                         new_list.push_back (g);
1713                 }
1714         }
1715
1716         _session->reorder_route_groups (new_list);
1717 }
1718
1719
1720 void
1721 Mixer_UI::add_route_group (RouteGroup* group)
1722 {
1723         ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1724         bool focus = false;
1725
1726         in_group_row_change = true;
1727
1728         TreeModel::Row row = *(group_model->append());
1729         row[group_columns.visible] = !group->is_hidden ();
1730         row[group_columns.group] = group;
1731         if (!group->name().empty()) {
1732                 row[group_columns.text] = group->name();
1733         } else {
1734                 row[group_columns.text] = _("unnamed");
1735                 focus = true;
1736         }
1737
1738         group->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
1739
1740         if (focus) {
1741                 TreeViewColumn* col = group_display.get_column (0);
1742                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1743                 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1744         }
1745
1746         _group_tabs->set_dirty ();
1747
1748         in_group_row_change = false;
1749 }
1750
1751 bool
1752 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1753 {
1754         using namespace Menu_Helpers;
1755
1756         if (Keyboard::is_context_menu_event (ev)) {
1757                 ARDOUR_UI::instance()->add_route ();
1758                 return true;
1759         }
1760
1761         return false;
1762 }
1763
1764 void
1765 Mixer_UI::scroller_drag_data_received (const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, guint info, guint time)
1766 {
1767         printf ("Mixer_UI::scroller_drag_data_received\n");
1768         if (data.get_target() != "PluginFavoritePtr") {
1769                 context->drag_finish (false, false, time);
1770                 return;
1771         }
1772
1773         const void * d = data.get_data();
1774         const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>* tv = reinterpret_cast<const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>*>(d);
1775
1776         PluginPresetList nfos;
1777         TreeView* source;
1778         tv->get_object_drag_data (nfos, &source);
1779
1780         Route::ProcessorList pl;
1781         bool ok = false;
1782
1783         for (list<PluginPresetPtr>::const_iterator i = nfos.begin(); i != nfos.end(); ++i) {
1784                 PluginPresetPtr ppp = (*i);
1785                 PluginInfoPtr pip = ppp->_pip;
1786                 if (!pip->is_instrument ()) {
1787                         continue;
1788                 }
1789                 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);
1790                 ok = true;
1791         }
1792
1793         context->drag_finish (ok, false, time);
1794 }
1795
1796 void
1797 Mixer_UI::set_strip_width (Width w, bool save)
1798 {
1799         _strip_width = w;
1800
1801         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1802                 (*i)->set_width_enum (w, save ? (*i)->width_owner() : this);
1803         }
1804 }
1805
1806
1807 struct PluginStateSorter {
1808 public:
1809         bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
1810                 std::list<std::string>::const_iterator aiter = std::find(_user.begin(), _user.end(), (*a).unique_id);
1811                 std::list<std::string>::const_iterator biter = std::find(_user.begin(), _user.end(), (*b).unique_id);
1812                 if (aiter != _user.end() && biter != _user.end()) {
1813                         return std::distance (_user.begin(), aiter)  < std::distance (_user.begin(), biter);
1814                 }
1815                 if (aiter != _user.end()) {
1816                         return true;
1817                 }
1818                 if (biter != _user.end()) {
1819                         return false;
1820                 }
1821                 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
1822         }
1823
1824         PluginStateSorter(std::list<std::string> user) : _user (user)  {}
1825 private:
1826         std::list<std::string> _user;
1827 };
1828
1829 int
1830 Mixer_UI::set_state (const XMLNode& node, int version)
1831 {
1832         XMLProperty const * prop;
1833
1834         Tabbable::set_state (node, version);
1835
1836         if ((prop = node.property ("narrow-strips"))) {
1837                 if (string_is_affirmative (prop->value())) {
1838                         set_strip_width (Narrow);
1839                 } else {
1840                         set_strip_width (Wide);
1841                 }
1842         }
1843
1844         if ((prop = node.property ("show-mixer"))) {
1845                 if (string_is_affirmative (prop->value())) {
1846                        _visible = true;
1847                 }
1848         }
1849
1850         if ((prop = node.property ("maximised"))) {
1851                 bool yn = string_is_affirmative (prop->value());
1852                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalMixer"));
1853                 assert (act);
1854                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1855                 bool fs = tact && tact->get_active();
1856                 if (yn ^ fs) {
1857                         ActionManager::do_action ("Common", "ToggleMaximalMixer");
1858                 }
1859         }
1860
1861         if ((prop = node.property ("show-mixer-list"))) {
1862                 bool yn = string_is_affirmative (prop->value());
1863                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMixerList"));
1864                 assert (act);
1865                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1866
1867                 /* do it twice to force the change */
1868                 tact->set_active (!yn);
1869                 tact->set_active (yn);
1870         }
1871
1872
1873         XMLNode* plugin_order;
1874         if ((plugin_order = find_named_node (node, "PluginOrder")) != 0) {
1875                 store_current_favorite_order ();
1876                 std::list<string> order;
1877                 const XMLNodeList& kids = plugin_order->children("PluginInfo");
1878                 XMLNodeConstIterator i;
1879                 for (i = kids.begin(); i != kids.end(); ++i) {
1880                         if ((prop = (*i)->property ("unique-id"))) {
1881                                 std::string unique_id = prop->value();
1882                                 order.push_back (unique_id);
1883                                 if ((prop = (*i)->property ("expanded"))) {
1884                                         favorite_ui_state[unique_id] = string_is_affirmative (prop->value());
1885                                 }
1886                         }
1887                 }
1888                 PluginStateSorter cmp (order);
1889                 favorite_order.sort (cmp);
1890                 sync_treeview_from_favorite_order ();
1891         }
1892         return 0;
1893 }
1894
1895 XMLNode&
1896 Mixer_UI::get_state ()
1897 {
1898         XMLNode* node = new XMLNode (X_("Mixer"));
1899         char buf[128];
1900
1901         node->add_child_nocopy (Tabbable::get_state());
1902
1903         snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (rhs_pane1, true));
1904         node->add_property(X_("mixer-rhs-pane1-pos"), string(buf));
1905         snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (rhs_pane2, true));
1906         node->add_property(X_("mixer-rhs_pane2-pos"), string(buf));
1907         snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (list_hpane, false));
1908         node->add_property(X_("mixer-list-hpane-pos"), string(buf));
1909         snprintf(buf,sizeof(buf), "%f", paned_position_as_fraction (inner_pane, false));
1910         node->add_property(X_("mixer-inner-pane-pos"), string(buf));
1911
1912         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1913         node->add_property ("show-mixer", _visible ? "yes" : "no");
1914         node->add_property ("show-mixer-list", _show_mixer_list ? "yes" : "no");
1915         node->add_property ("maximised", _maximised ? "yes" : "no");
1916
1917         store_current_favorite_order ();
1918         XMLNode* plugin_order = new XMLNode ("PluginOrder");
1919         int cnt = 0;
1920         for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i, ++cnt) {
1921                 XMLNode* p = new XMLNode ("PluginInfo");
1922                 p->add_property ("sort", cnt);
1923                 p->add_property ("unique-id", (*i)->unique_id);
1924                 if (favorite_ui_state.find ((*i)->unique_id) != favorite_ui_state.end ()) {
1925                         p->add_property ("expanded", favorite_ui_state[(*i)->unique_id]);
1926                 }
1927                 plugin_order->add_child_nocopy (*p);
1928         }
1929         node->add_child_nocopy (*plugin_order);
1930
1931         return *node;
1932 }
1933
1934 void
1935 Mixer_UI::pane_allocation_handler (Allocation& allocation, Gtk::Paned* which)
1936 {
1937         float pos;
1938         XMLProperty* prop = 0;
1939         XMLNode* geometry = ARDOUR_UI::instance()->mixer_settings();
1940         int height = default_height;
1941         static bool done[4] = { false, false, false, false };
1942
1943         /* Gtk::Paned behaves very oddly and rather undesirably. Setting the
1944          * position is a crapshoot if you time it incorrectly with the overall
1945          * sizing of the Paned. For example, if you might retrieve the size with
1946          * ::get_position() and then later call ::set_position() on a Paned at
1947          * a time when its allocation is different than it was when you retrieved
1948          * the position. The position will be interpreted as the size of the
1949          * first (top or left) child widget. If packing/size allocation later
1950          * resizes the Paned to a (final) smaller size, the position will be
1951          * used in ways that mean that the Paned ends up NOT in the same state
1952          * that it was in when you originally saved the position.
1953          *
1954          * Concrete example: Paned is 800 pixels wide, position is 400
1955          * (halfway).  Save position as 400. During program restart, set
1956          * position to 400, before Paned has been allocated any space. Paned
1957          * ends up initially sized to 1200 pixels.  Position is now 1/3 of the
1958          * way across/down.  Subsequent resizes will leave the position 1/3 of
1959          * the way across, rather than leaving it as a fixed pixel
1960          * position. Eventually, the Paned ends up 800 pixels wide/high again,
1961          * but the position is now 267, not 400.
1962          *
1963          * So ...
1964          *
1965          * We deal with this by using two strategies:
1966          *
1967          * 1) only set the position if the allocated size of the Paned is at
1968          * least as big as it needs to be for the position to make sense.
1969          *
1970          * 2) in recent versions of Ardour, save the position as a fraction,
1971          * and restore it using that fraction.
1972          *
1973          * So, we will only call ::set_position() AFTER the Paned is of a
1974          * sensible size, and then in addition, we will set the position in a
1975          * way that will be maintained as/when/if the Paned is resized.
1976          *
1977          * Once we've called ::set_position() on a Paned, we don't do it
1978          * again.
1979          */
1980
1981         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1982
1983                 if (done[0]) {
1984                         return;
1985                 }
1986
1987                 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1988                         pos = height / 3;
1989                 } else {
1990                         pos = atof (prop->value());
1991                 }
1992
1993                 if (pos > 1.0f) {
1994                         /* older versions of Ardour stored absolute position */
1995                         if ((done[0] = (allocation.get_height() > pos))) {
1996                                 rhs_pane1.set_position (pos);
1997                         }
1998                 } else {
1999                         if ((done[0] = (allocation.get_height() > 1.0/pos))) {
2000                                 paned_set_position_as_fraction (rhs_pane1, pos, true);
2001                         }
2002                 }
2003         }
2004
2005         if (which == static_cast<Gtk::Paned*> (&rhs_pane2)) {
2006
2007                 if (done[1]) {
2008                         return;
2009                 }
2010
2011                 if (!geometry || (prop = geometry->property("mixer-rhs-pane2-pos")) == 0) {
2012                         pos = 2 * height / 3;
2013                 } else {
2014                         pos = atof (prop->value());
2015                 }
2016
2017                 if (pos > 1.0f) {
2018                         /* older versions of Ardour stored absolute position */
2019                         if ((done[1] = (allocation.get_height() > pos))) {
2020                                 rhs_pane2.set_position (pos);
2021                         }
2022                 } else {
2023                         if ((done[1] = (allocation.get_height() > 1.0/pos))) {
2024                                 paned_set_position_as_fraction (rhs_pane2, pos, true);
2025                         }
2026                 }
2027         }
2028
2029         if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
2030
2031                 if (done[2]) {
2032                         return;
2033                 }
2034
2035                 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
2036                         pos = std::max ((float)100, rintf ((float) 125 * UIConfiguration::instance().get_ui_scale()));
2037                 } else {
2038                         pos = max (0.1, atof (prop->value ()));
2039                 }
2040
2041                 if (pos > 1.0f) {
2042                         if ((done[2] = (allocation.get_width() > pos))) {
2043                                 list_hpane.set_position (pos);
2044                         }
2045                 } else {
2046                         if ((done[2] = (allocation.get_width() > 1.0/pos))) {
2047                                 paned_set_position_as_fraction (list_hpane, pos, false);
2048                         }
2049                 }
2050         }
2051
2052         if (which == static_cast<Gtk::Paned*> (&inner_pane)) {
2053
2054                 if (done[3]) {
2055                         return;
2056                 }
2057
2058                 if (!geometry || (prop = geometry->property("mixer-inner-pane-pos")) == 0) {
2059                         pos = std::max ((float)100, rintf ((float) 125 * UIConfiguration::instance().get_ui_scale()));
2060                 } else {
2061                         pos = max (0.1, atof (prop->value ()));
2062                 }
2063
2064                 if (pos > 1.0f) {
2065                         /* older versions of Ardour stored absolute position */
2066                         if ((done[3] = (allocation.get_width() > pos))) {
2067                                 inner_pane.set_position (pos);
2068                         }
2069                 } else {
2070                         if ((done[3] = (allocation.get_width() > 1.0/pos))) {
2071                                 paned_set_position_as_fraction (inner_pane, pos, false);
2072                         }
2073                 }
2074         }
2075 }
2076
2077 void
2078 Mixer_UI::scroll_left ()
2079 {
2080         if (!scroller.get_hscrollbar()) return;
2081         Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2082         /* stupid GTK: can't rely on clamping across versions */
2083         scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
2084 }
2085
2086 void
2087 Mixer_UI::scroll_right ()
2088 {
2089         if (!scroller.get_hscrollbar()) return;
2090         Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2091         /* stupid GTK: can't rely on clamping across versions */
2092         scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
2093 }
2094
2095 bool
2096 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
2097 {
2098         switch (ev->direction) {
2099         case GDK_SCROLL_LEFT:
2100                 scroll_left ();
2101                 return true;
2102         case GDK_SCROLL_UP:
2103                 if (ev->state & Keyboard::TertiaryModifier) {
2104                         scroll_left ();
2105                         return true;
2106                 }
2107                 return false;
2108
2109         case GDK_SCROLL_RIGHT:
2110                 scroll_right ();
2111                 return true;
2112
2113         case GDK_SCROLL_DOWN:
2114                 if (ev->state & Keyboard::TertiaryModifier) {
2115                         scroll_right ();
2116                         return true;
2117                 }
2118                 return false;
2119         }
2120
2121         return false;
2122 }
2123
2124
2125 void
2126 Mixer_UI::parameter_changed (string const & p)
2127 {
2128         if (p == "show-group-tabs") {
2129                 bool const s = _session->config.get_show_group_tabs ();
2130                 if (s) {
2131                         _group_tabs->show ();
2132                 } else {
2133                         _group_tabs->hide ();
2134                 }
2135         } else if (p == "default-narrow_ms") {
2136                 bool const s = UIConfiguration::instance().get_default_narrow_ms ();
2137                 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2138                         (*i)->set_width_enum (s ? Narrow : Wide, this);
2139                 }
2140         } else if (p == "use-monitor-bus") {
2141                 if (_session && !_session->monitor_out()) {
2142                         monitor_section_detached ();
2143                 }
2144         }
2145 }
2146
2147 void
2148 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
2149 {
2150         g->set_active (a, this);
2151 }
2152
2153 PluginSelector*
2154 Mixer_UI::plugin_selector()
2155 {
2156 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
2157         if (!_plugin_selector)
2158                 _plugin_selector = new PluginSelector (PluginManager::instance());
2159 #endif
2160
2161         return _plugin_selector;
2162 }
2163
2164 void
2165 Mixer_UI::setup_track_display ()
2166 {
2167         track_model = ListStore::create (track_columns);
2168         track_display.set_model (track_model);
2169         track_display.append_column (_("Strips"), track_columns.text);
2170         track_display.append_column (_("Show"), track_columns.visible);
2171         track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
2172         track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
2173         track_display.get_column (0)->set_expand(true);
2174         track_display.get_column (1)->set_expand(false);
2175         track_display.get_column (0)->set_sizing (Gtk::TREE_VIEW_COLUMN_FIXED);
2176         track_display.set_name (X_("EditGroupList"));
2177         track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
2178         track_display.set_reorderable (true);
2179         track_display.set_headers_visible (true);
2180         track_display.set_can_focus(false);
2181
2182         track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
2183         track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
2184
2185         CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
2186         track_list_visible_cell->property_activatable() = true;
2187         track_list_visible_cell->property_radio() = false;
2188         track_list_visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::track_visibility_changed));
2189
2190         track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
2191
2192         track_display_scroller.add (track_display);
2193         track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
2194
2195         VBox* v = manage (new VBox);
2196         v->show ();
2197         v->pack_start (track_display_scroller, true, true);
2198
2199         Button* b = manage (new Button);
2200         b->show ();
2201         Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
2202         w->show ();
2203         b->add (*w);
2204
2205         b->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
2206
2207         v->pack_start (*b, false, false);
2208
2209         track_display_frame.set_name("BaseFrame");
2210         track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
2211         track_display_frame.add (*v);
2212
2213         track_display_scroller.show();
2214         track_display_frame.show();
2215         track_display.show();
2216 }
2217
2218 void
2219 Mixer_UI::new_track_or_bus ()
2220 {
2221         ARDOUR_UI::instance()->add_route ();
2222 }
2223
2224 void
2225 Mixer_UI::update_title ()
2226 {
2227         if (!own_window()) {
2228                 return;
2229         }
2230
2231         if (_session) {
2232                 string n;
2233
2234                 if (_session->snap_name() != _session->name()) {
2235                         n = _session->snap_name ();
2236                 } else {
2237                         n = _session->name ();
2238                 }
2239
2240                 if (_session->dirty ()) {
2241                         n = "*" + n;
2242                 }
2243
2244                 WindowTitle title (n);
2245                 title += S_("Window|Mixer");
2246                 title += Glib::get_application_name ();
2247                 own_window()->set_title (title.get_string());
2248
2249         } else {
2250
2251                 WindowTitle title (S_("Window|Mixer"));
2252                 title += Glib::get_application_name ();
2253                 own_window()->set_title (title.get_string());
2254         }
2255 }
2256
2257 MixerStrip*
2258 Mixer_UI::strip_by_x (int x)
2259 {
2260         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2261                 int x1, x2, y;
2262
2263                 (*i)->translate_coordinates (_content, 0, 0, x1, y);
2264                 x2 = x1 + (*i)->get_width();
2265
2266                 if (x >= x1 && x <= x2) {
2267                         return (*i);
2268                 }
2269         }
2270
2271         return 0;
2272 }
2273
2274 void
2275 Mixer_UI::set_route_targets_for_operation ()
2276 {
2277         _route_targets.clear ();
2278
2279         if (!_selection.empty()) {
2280                 _route_targets = _selection.routes;
2281                 return;
2282         }
2283
2284 //  removed "implicit" selections of strips, after discussion on IRC
2285
2286 }
2287
2288 void
2289 Mixer_UI::monitor_section_going_away ()
2290 {
2291         if (_monitor_section) {
2292                 monitor_section_detached ();
2293                 out_packer.remove (_monitor_section->tearoff());
2294                 _monitor_section->set_session (0);
2295                 delete _monitor_section;
2296                 _monitor_section = 0;
2297         }
2298 }
2299
2300 void
2301 Mixer_UI::toggle_midi_input_active (bool flip_others)
2302 {
2303         boost::shared_ptr<RouteList> rl (new RouteList);
2304         bool onoff = false;
2305
2306         set_route_targets_for_operation ();
2307
2308         for (RouteUISelection::iterator r = _route_targets.begin(); r != _route_targets.end(); ++r) {
2309                 boost::shared_ptr<MidiTrack> mt = (*r)->midi_track();
2310
2311                 if (mt) {
2312                         rl->push_back ((*r)->route());
2313                         onoff = !mt->input_active();
2314                 }
2315         }
2316
2317         _session->set_exclusive_input_active (rl, onoff, flip_others);
2318 }
2319
2320 void
2321 Mixer_UI::maximise_mixer_space ()
2322 {
2323         if (!own_window()) {
2324                 return;
2325         }
2326
2327         if (_maximised) {
2328                 return;
2329         }
2330
2331         _window->fullscreen ();
2332         _maximised = true;
2333 }
2334
2335 void
2336 Mixer_UI::restore_mixer_space ()
2337 {
2338         if (!own_window()) {
2339                 return;
2340         }
2341
2342         if (!_maximised) {
2343                 return;
2344         }
2345
2346         own_window()->unfullscreen();
2347         _maximised = false;
2348 }
2349
2350 void
2351 Mixer_UI::monitor_section_attached ()
2352 {
2353         Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2354         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2355         act->set_sensitive (true);
2356         tact->set_active ();
2357 }
2358
2359 void
2360 Mixer_UI::monitor_section_detached ()
2361 {
2362         Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
2363         act->set_sensitive (false);
2364 }
2365
2366 void
2367 Mixer_UI::store_current_favorite_order ()
2368 {
2369         typedef Gtk::TreeModel::Children type_children;
2370         type_children children = favorite_plugins_model->children();
2371         favorite_order.clear();
2372         for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
2373         {
2374                 Gtk::TreeModel::Row row = *iter;
2375                 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2376                 favorite_order.push_back (ppp->_pip);
2377                 std::string name = row[favorite_plugins_columns.name];
2378                 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2379         }
2380 }
2381
2382 void
2383 Mixer_UI::save_favorite_ui_state (const TreeModel::iterator& iter, const TreeModel::Path& path)
2384 {
2385         Gtk::TreeModel::Row row = *iter;
2386         ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2387         assert (ppp);
2388         favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2389 }
2390
2391 void
2392 Mixer_UI::refiller (PluginInfoList& result, const PluginInfoList& plugs)
2393 {
2394         PluginManager& manager (PluginManager::instance());
2395         for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
2396                 if (manager.get_status (*i) != PluginManager::Favorite) {
2397                         continue;
2398                 }
2399                 result.push_back (*i);
2400         }
2401 }
2402
2403 struct PluginCustomSorter {
2404 public:
2405         bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
2406                 PluginInfoList::const_iterator aiter = _user.begin();
2407                 PluginInfoList::const_iterator biter = _user.begin();
2408                 while (aiter != _user.end()) { if ((*aiter)->unique_id == a->unique_id) { break; } ++aiter; }
2409                 while (biter != _user.end()) { if ((*biter)->unique_id == b->unique_id) { break; } ++biter; }
2410
2411                 if (aiter != _user.end() && biter != _user.end()) {
2412                         return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
2413                 }
2414                 if (aiter != _user.end()) {
2415                         return true;
2416                 }
2417                 if (biter != _user.end()) {
2418                         return false;
2419                 }
2420                 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
2421         }
2422         PluginCustomSorter(PluginInfoList user) : _user (user)  {}
2423 private:
2424         PluginInfoList _user;
2425 };
2426
2427 void
2428 Mixer_UI::refill_favorite_plugins ()
2429 {
2430         PluginInfoList plugs;
2431         PluginManager& mgr (PluginManager::instance());
2432
2433 #ifdef LV2_SUPPORT
2434         refiller (plugs, mgr.lv2_plugin_info ());
2435 #endif
2436 #ifdef WINDOWS_VST_SUPPORT
2437         refiller (plugs, mgr.windows_vst_plugin_info ());
2438 #endif
2439 #ifdef LXVST_SUPPORT
2440         refiller (plugs, mgr.lxvst_plugin_info ());
2441 #endif
2442 #ifdef AUDIOUNIT_SUPPORT
2443         refiller (plugs, mgr.au_plugin_info ());
2444 #endif
2445         refiller (plugs, mgr.ladspa_plugin_info ());
2446         refiller (plugs, mgr.lua_plugin_info ());
2447
2448         store_current_favorite_order ();
2449
2450         PluginCustomSorter cmp (favorite_order);
2451         plugs.sort (cmp);
2452
2453         favorite_order = plugs;
2454
2455         sync_treeview_from_favorite_order ();
2456 }
2457
2458 void
2459 Mixer_UI::sync_treeview_favorite_ui_state (const TreeModel::Path& path, const TreeModel::iterator&)
2460 {
2461         TreeIter iter;
2462         if (!(iter = favorite_plugins_model->get_iter (path))) {
2463                 return;
2464         }
2465         ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2466         if (!ppp) {
2467                 return;
2468         }
2469         PluginInfoPtr pip = ppp->_pip;
2470         if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2471                 if (favorite_ui_state[pip->unique_id]) {
2472                         favorite_plugins_display.expand_row (path, true);
2473                 }
2474         }
2475 }
2476
2477 void
2478 Mixer_UI::sync_treeview_from_favorite_order ()
2479 {
2480         favorite_plugins_model->clear ();
2481         for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i) {
2482                 PluginInfoPtr pip = (*i);
2483
2484                 TreeModel::Row newrow = *(favorite_plugins_model->append());
2485                 newrow[favorite_plugins_columns.name] = (*i)->name;
2486                 newrow[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip));
2487                 if (!_session) {
2488                         continue;
2489                 }
2490
2491                 vector<ARDOUR::Plugin::PresetRecord> presets = (*i)->get_presets (true);
2492                 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator j = presets.begin(); j != presets.end(); ++j) {
2493                         Gtk::TreeModel::Row child_row = *(favorite_plugins_model->append (newrow.children()));
2494                         child_row[favorite_plugins_columns.name] = (*j).label;
2495                         child_row[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip, &(*j)));
2496                 }
2497                 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
2498                         if (favorite_ui_state[pip->unique_id]) {
2499                                 favorite_plugins_display.expand_row (favorite_plugins_model->get_path(newrow), true);
2500                         }
2501                 }
2502         }
2503 }
2504
2505 void
2506 Mixer_UI::popup_note_context_menu (GdkEventButton *ev)
2507 {
2508         using namespace Gtk::Menu_Helpers;
2509
2510         Gtk::Menu* m = manage (new Menu);
2511         MenuList& items = m->items ();
2512
2513         if (_selection.routes.empty()) {
2514                 items.push_back (MenuElem (_("No Track/Bus is selected.")));
2515         } else {
2516                 items.push_back (MenuElem (_("Add at the top"),
2517                                         sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddTop)));
2518                 items.push_back (MenuElem (_("Add Pre-Fader"),
2519                                         sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPreFader)));
2520                 items.push_back (MenuElem (_("Add Post-Fader"),
2521                                         sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPostFader)));
2522                 items.push_back (MenuElem (_("Add at the end"),
2523                                         sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddBottom)));
2524         }
2525
2526         items.push_back (SeparatorElem());
2527
2528         items.push_back (MenuElem (_("Remove from favorites"), sigc::mem_fun (*this, &Mixer_UI::remove_selected_from_favorites)));
2529
2530         ARDOUR::PluginPresetPtr ppp = selected_plugin();
2531         if (ppp && ppp->_preset.valid && ppp->_preset.user) {
2532                 // we cannot currently delete AU presets
2533                 if (!ppp->_pip || ppp->_pip->type != AudioUnit) {
2534                         items.push_back (MenuElem (_("Delete Preset"), sigc::mem_fun (*this, &Mixer_UI::delete_selected_preset)));
2535                 }
2536         }
2537
2538         m->popup (ev->button, ev->time);
2539 }
2540
2541 bool
2542 Mixer_UI::plugin_row_button_press (GdkEventButton *ev)
2543 {
2544         if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3) ) {
2545                 TreeModel::Path path;
2546                 TreeViewColumn* column;
2547                 int cellx, celly;
2548                 if (favorite_plugins_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
2549                         Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2550                         if (selection) {
2551                                 selection->unselect_all();
2552                                 selection->select(path);
2553                         }
2554                 }
2555                 ARDOUR::PluginPresetPtr ppp = selected_plugin();
2556                 if (ppp) {
2557                         popup_note_context_menu (ev);
2558                 }
2559         }
2560         return false;
2561 }
2562
2563
2564 PluginPresetPtr
2565 Mixer_UI::selected_plugin ()
2566 {
2567         Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
2568         if (!selection) {
2569                 return PluginPresetPtr();
2570         }
2571         Gtk::TreeModel::iterator iter = selection->get_selected();
2572         if (!iter) {
2573                 return PluginPresetPtr();
2574         }
2575         return (*iter)[favorite_plugins_columns.plugin];
2576 }
2577
2578 void
2579 Mixer_UI::add_selected_processor (ProcessorPosition pos)
2580 {
2581         ARDOUR::PluginPresetPtr ppp = selected_plugin();
2582         if (ppp) {
2583                 add_favorite_processor (ppp, pos);
2584         }
2585 }
2586
2587 void
2588 Mixer_UI::delete_selected_preset ()
2589 {
2590         if (!_session) {
2591                 return;
2592         }
2593         ARDOUR::PluginPresetPtr ppp = selected_plugin();
2594         if (!ppp || !ppp->_preset.valid || !ppp->_preset.user) {
2595                 return;
2596         }
2597         PluginPtr plugin = ppp->_pip->load (*_session);
2598         plugin->get_presets();
2599         plugin->remove_preset (ppp->_preset.label);
2600 }
2601
2602 void
2603 Mixer_UI::remove_selected_from_favorites ()
2604 {
2605         ARDOUR::PluginPresetPtr ppp = selected_plugin();
2606         if (!ppp) {
2607                 return;
2608         }
2609         PluginManager::PluginStatusType status = PluginManager::Normal;
2610         PluginManager& manager (PluginManager::instance());
2611
2612         manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2613         manager.save_statuses ();
2614 }
2615
2616 void
2617 Mixer_UI::plugin_row_activated (const TreeModel::Path& path, TreeViewColumn* column)
2618 {
2619         TreeIter iter;
2620         if (!(iter = favorite_plugins_model->get_iter (path))) {
2621                 return;
2622         }
2623         ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
2624         add_favorite_processor (ppp, AddPreFader); // TODO: preference?!
2625 }
2626
2627 void
2628 Mixer_UI::add_favorite_processor (ARDOUR::PluginPresetPtr ppp, ProcessorPosition pos)
2629 {
2630         if (!_session || _selection.routes.empty()) {
2631                 return;
2632         }
2633
2634         PluginInfoPtr pip = ppp->_pip;
2635         for (RouteUISelection::iterator i = _selection.routes.begin(); i != _selection.routes.end(); ++i) {
2636                 boost::shared_ptr<ARDOUR::Route> rt = (*i)->route();
2637                 if (!rt) { continue; }
2638
2639                 PluginPtr p = pip->load (*_session);
2640                 if (!p) { continue; }
2641
2642                 if (ppp->_preset.valid) {
2643                         p->load_preset (ppp->_preset);
2644                 }
2645
2646                 Route::ProcessorStreams err;
2647                 boost::shared_ptr<Processor> processor (new PluginInsert (*_session, p));
2648
2649                 switch (pos) {
2650                         case AddTop:
2651                                 rt->add_processor_by_index (processor, 0, &err, Config->get_new_plugins_active ());
2652                                 break;
2653                         case AddPreFader:
2654                                 rt->add_processor (processor, PreFader, &err, Config->get_new_plugins_active ());
2655                                 break;
2656                         case AddPostFader:
2657                                 {
2658                                         int idx = 0;
2659                                         int pos = 0;
2660                                         for (;;++idx) {
2661                                                 boost::shared_ptr<Processor> np = rt->nth_processor (idx);
2662                                                 if (!np) {
2663                                                         break;
2664                                                 }
2665                                                 if (!np->display_to_user()) {
2666                                                         continue;
2667                                                 }
2668                                                 if (boost::dynamic_pointer_cast<Amp> (np) && // Fader, not Trim
2669                                                                 boost::dynamic_pointer_cast<Amp> (np)->gain_control()->parameter().type() == GainAutomation) {
2670                                                         break;
2671                                                 }
2672                                                 ++pos;
2673                                         }
2674                                         rt->add_processor_by_index (processor, ++pos, &err, Config->get_new_plugins_active ());
2675                                 }
2676                                 break;
2677                         case AddBottom:
2678                                 rt->add_processor_by_index (processor, -1, &err, Config->get_new_plugins_active ());
2679                                 break;
2680                 }
2681         }
2682 }
2683
2684 bool
2685 PluginTreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& data) const
2686 {
2687         if (data.get_target() != "GTK_TREE_MODEL_ROW") {
2688                 return false;
2689         }
2690
2691         // only allow to re-order top-level items
2692         TreePath src;
2693         if (TreePath::get_from_selection_data (data, src)) {
2694                 if (src.up() && src.up()) {
2695                         return false;
2696                 }
2697         }
2698
2699         // don't allow to drop as child-rows.
2700         Gtk::TreeModel::Path _dest = dest; // un const
2701         const bool is_child = _dest.up (); // explicit bool for clang
2702         if (!is_child || _dest.empty ()) {
2703                 return true;
2704         }
2705         return false;
2706 }
2707
2708 void
2709 Mixer_UI::plugin_drop (const Glib::RefPtr<Gdk::DragContext>&, const Gtk::SelectionData& data)
2710 {
2711         if (data.get_target() != "PluginPresetPtr") {
2712                 return;
2713         }
2714         if (data.get_length() != sizeof (PluginPresetPtr)) {
2715                 return;
2716         }
2717         const void *d = data.get_data();
2718         const PluginPresetPtr ppp = *(static_cast<const PluginPresetPtr*> (d));
2719
2720         PluginManager::PluginStatusType status = PluginManager::Favorite;
2721         PluginManager& manager (PluginManager::instance());
2722
2723         manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
2724         manager.save_statuses ();
2725 }
2726
2727 void
2728 Mixer_UI::do_vca_assign (boost::shared_ptr<VCA> vca)
2729 {
2730         /* call protected MixerActor:: method */
2731         vca_assign (vca);
2732 }
2733
2734 void
2735 Mixer_UI::do_vca_unassign (boost::shared_ptr<VCA> vca)
2736 {
2737         /* call protected MixerActor:: method */
2738         vca_unassign (vca);
2739 }
2740
2741 void
2742 Mixer_UI::show_vca_slaves (boost::shared_ptr<VCA> vca)
2743 {
2744         boost::shared_ptr<VCA> v = spilled_vca.lock();
2745         if (v != vca) {
2746                 spilled_vca = vca;
2747                 show_vca_change (vca); /* EMIT SIGNAL */
2748                 redisplay_track_list ();
2749         }
2750 }
2751
2752 bool
2753 Mixer_UI::showing_vca_slaves_for (boost::shared_ptr<VCA> vca) const
2754 {
2755        return vca == spilled_vca.lock();
2756 }