desensitize foldback show unless there is a foldback bus
[ardour.git] / gtk2_ardour / mixer_ui.cc
1 /*
2  * Copyright (C) 2005-2006 Taybin Rutkin <taybin@taybin.com>
3  * Copyright (C) 2005-2018 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2006-2007 Doug McLain <doug@nostar.net>
5  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
6  * Copyright (C) 2007-2012 David Robillard <d@drobilla.net>
7  * Copyright (C) 2007-2016 Tim Mayberry <mojofunk@gmail.com>
8  * Copyright (C) 2013-2015 Nick Mainsbridge <mainsbridge@gmail.com>
9  * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
10  * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
11  * Copyright (C) 2016-2018 Len Ovens <len@ovenwerks.net>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with this program; if not, write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27
28 #ifdef WAF_BUILD
29 #include "gtk2ardour-config.h"
30 #endif
31
32 #include <algorithm>
33 #include <map>
34 #include <sigc++/bind.h>
35
36 #include <boost/foreach.hpp>
37
38 #include <glibmm/threads.h>
39
40 #include <gtkmm/accelmap.h>
41 #include <gtkmm/offscreenwindow.h>
42 #include <gtkmm/stock.h>
43
44 #include "pbd/convert.h"
45 #include "pbd/stacktrace.h"
46 #include "pbd/unwind.h"
47
48 #include "ardour/amp.h"
49 #include "ardour/debug.h"
50 #include "ardour/audio_track.h"
51 #include "ardour/midi_track.h"
52 #include "ardour/plugin_manager.h"
53 #include "ardour/route_group.h"
54 #include "ardour/selection.h"
55 #include "ardour/session.h"
56 #include "ardour/vca.h"
57 #include "ardour/vca_manager.h"
58
59 #include "gtkmm2ext/gtk_ui.h"
60 #include "gtkmm2ext/keyboard.h"
61 #include "gtkmm2ext/utils.h"
62 #include "gtkmm2ext/window_title.h"
63 #include "gtkmm2ext/doi.h"
64
65 #include "widgets/tearoff.h"
66
67 #include "foldback_strip.h"
68 #include "keyboard.h"
69 #include "mixer_ui.h"
70 #include "mixer_strip.h"
71 #include "monitor_section.h"
72 #include "plugin_selector.h"
73 #include "public_editor.h"
74 #include "mouse_cursors.h"
75 #include "ardour_ui.h"
76 #include "utils.h"
77 #include "route_sorter.h"
78 #include "actions.h"
79 #include "gui_thread.h"
80 #include "mixer_group_tabs.h"
81 #include "route_sorter.h"
82 #include "timers.h"
83 #include "ui_config.h"
84 #include "vca_master_strip.h"
85
86 #include "pbd/i18n.h"
87
88 using namespace ARDOUR;
89 using namespace ARDOUR_UI_UTILS;
90 using namespace PBD;
91 using namespace Gtk;
92 using namespace Glib;
93 using namespace Gtkmm2ext;
94 using namespace std;
95
96 using PBD::atoi;
97 using PBD::Unwinder;
98
99 Mixer_UI* Mixer_UI::_instance = 0;
100
101 Mixer_UI*
102 Mixer_UI::instance ()
103 {
104         if (!_instance) {
105                 _instance  = new Mixer_UI;
106         }
107
108         return _instance;
109 }
110
111 Mixer_UI::Mixer_UI ()
112         : Tabbable (_content, _("Mixer"))
113         , no_track_list_redisplay (false)
114         , in_group_row_change (false)
115         , track_menu (0)
116         , _plugin_selector (0)
117         , foldback_strip (0)
118         , _show_foldback_strip (true)
119         , _strip_width (UIConfiguration::instance().get_default_narrow_ms() ? Narrow : Wide)
120         , _spill_scroll_position (0)
121         , ignore_reorder (false)
122         , _in_group_rebuild_or_clear (false)
123         , _route_deletion_in_progress (false)
124         , _maximised (false)
125         , _strip_selection_change_without_scroll (false)
126         , _selection (*this, *this)
127 {
128         load_bindings ();
129         register_actions ();
130         Glib::RefPtr<ToggleAction> fb_act = ActionManager::get_toggle_action ("Mixer", "ToggleFoldbackStrip");
131         fb_act->set_sensitive (false);
132
133         _content.set_data ("ardour-bindings", bindings);
134
135         PresentationInfo::Change.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::presentation_info_changed, this, _1), gui_context());
136
137         scroller.set_can_default (true);
138         // set_default (scroller);
139
140         scroller_base.set_flags (Gtk::CAN_FOCUS);
141         scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
142         scroller_base.set_name ("MixerWindow");
143         scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
144
145         /* set up drag-n-drop */
146         vector<TargetEntry> target_table;
147         target_table.push_back (TargetEntry ("PluginFavoritePtr"));
148         scroller_base.drag_dest_set (target_table);
149         scroller_base.signal_drag_data_received().connect (sigc::mem_fun(*this, &Mixer_UI::scroller_drag_data_received));
150
151         /* create a button to add VCA strips ... will get packed in redisplay_track_list() */
152         Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
153         w->show ();
154         add_vca_button.add (*w);
155         add_vca_button.set_can_focus(false);
156         add_vca_button.signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
157
158         /* create a button to add mixer strips */
159         w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
160         w->show ();
161         add_button.add (*w);
162         add_button.set_can_focus(false);
163         add_button.signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
164
165         /* add as last item of strip packer */
166         strip_packer.pack_end (scroller_base, true, true);
167         strip_packer.pack_end (add_button, false, false);
168
169 #ifdef MIXBUS
170         /* create a drop-shadow at the end of the mixer strips */
171         mb_shadow.set_size_request( 4, -1 );
172         mb_shadow.set_name("EditorWindow");
173         mb_shadow.show();
174         strip_packer.pack_end (mb_shadow, false, false);
175 #endif
176
177         _group_tabs = new MixerGroupTabs (this);
178         strip_group_box.set_spacing (0);
179         strip_group_box.set_border_width (0);
180         strip_group_box.pack_start (*_group_tabs, PACK_SHRINK);
181         strip_group_box.pack_start (strip_packer);
182         strip_group_box.show_all ();
183         strip_group_box.signal_scroll_event().connect (sigc::mem_fun (*this, &Mixer_UI::on_scroll_event), false);
184
185         scroller.add (strip_group_box);
186         scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
187
188         setup_track_display ();
189
190         group_model = ListStore::create (group_columns);
191         group_display.set_model (group_model);
192         group_display.append_column (_("Show"), group_columns.visible);
193         group_display.append_column (_("Group"), group_columns.text);
194         group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
195         group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
196         group_display.get_column (0)->set_expand(false);
197         group_display.get_column (1)->set_expand(true);
198         group_display.get_column (1)->set_sizing (Gtk::TREE_VIEW_COLUMN_FIXED);
199         group_display.set_name ("EditGroupList");
200         group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
201         group_display.set_reorderable (true);
202         group_display.set_headers_visible (true);
203         group_display.set_rules_hint (true);
204         group_display.set_can_focus(false);
205
206         /* name is directly editable */
207
208         CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (1));
209         name_cell->property_editable() = true;
210         name_cell->signal_edited().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_name_edit));
211
212         /* use checkbox for the active column */
213
214         CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (0));
215         active_cell->property_activatable() = true;
216         active_cell->property_radio() = false;
217
218         group_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_change));
219         /* We use this to notice drag-and-drop reorders of the group list */
220         group_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_deleted));
221         group_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::group_display_button_press), false);
222
223         group_display_scroller.add (group_display);
224         group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
225
226
227         group_display_vbox.pack_start (group_display_scroller, true, true);
228
229         group_display_frame.set_name ("BaseFrame");
230         group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
231         group_display_frame.add (group_display_vbox);
232
233         list<TargetEntry> target_list;
234         target_list.push_back (TargetEntry ("PluginPresetPtr"));
235
236         favorite_plugins_model = PluginTreeStore::create (favorite_plugins_columns);
237         favorite_plugins_display.set_model (favorite_plugins_model);
238         favorite_plugins_display.append_column (_("Favorite Plugins"), favorite_plugins_columns.name);
239         favorite_plugins_display.set_name ("EditGroupList");
240         favorite_plugins_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
241         favorite_plugins_display.set_reorderable (false);
242         favorite_plugins_display.set_headers_visible (true);
243         favorite_plugins_display.set_rules_hint (true);
244         favorite_plugins_display.set_can_focus (false);
245         favorite_plugins_display.add_object_drag (favorite_plugins_columns.plugin.index(), "PluginFavoritePtr");
246         favorite_plugins_display.set_drag_column (favorite_plugins_columns.name.index());
247         favorite_plugins_display.add_drop_targets (target_list);
248         favorite_plugins_display.signal_row_activated().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_activated));
249         favorite_plugins_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::plugin_row_button_press), false);
250         favorite_plugins_display.signal_drop.connect (sigc::mem_fun (*this, &Mixer_UI::plugin_drop));
251         favorite_plugins_display.signal_row_expanded().connect (sigc::mem_fun (*this, &Mixer_UI::save_favorite_ui_state));
252         favorite_plugins_display.signal_row_collapsed().connect (sigc::mem_fun (*this, &Mixer_UI::save_favorite_ui_state));
253         if (UIConfiguration::instance().get_use_tooltips()) {
254                 favorite_plugins_display.set_tooltip_column (0);
255         }
256         favorite_plugins_model->signal_row_has_child_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::sync_treeview_favorite_ui_state));
257
258         favorite_plugins_scroller.add (favorite_plugins_display);
259         favorite_plugins_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
260
261         favorite_plugins_frame.set_name ("BaseFrame");
262         favorite_plugins_frame.set_shadow_type (Gtk::SHADOW_IN);
263         favorite_plugins_frame.add (favorite_plugins_vbox);
264
265         favorite_plugins_vbox.pack_start (favorite_plugins_scroller, true, true);
266         favorite_plugins_vbox.pack_start (favorite_plugins_tag_combo, false, false);
267         favorite_plugins_tag_combo.signal_changed().connect (sigc::mem_fun (*this, &Mixer_UI::tag_combo_changed));
268
269         rhs_pane1.add (favorite_plugins_frame);
270         rhs_pane1.add (track_display_frame);
271
272         rhs_pane2.add (rhs_pane1);
273         rhs_pane2.add (group_display_frame);
274
275         list_vpacker.pack_start (rhs_pane2, true, true);
276
277         vca_label_bar.set_size_request (-1, 16 + 1); /* must match height in GroupTabs::set_size_request()  + 1 border px*/
278         vca_vpacker.pack_start (vca_label_bar, false, false);
279
280         vca_scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
281         vca_scroller_base.set_name (X_("MixerWindow"));
282         vca_scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::masters_scroller_button_release), false);
283
284         vca_hpacker.signal_scroll_event().connect (sigc::mem_fun (*this, &Mixer_UI::on_vca_scroll_event), false);
285         vca_scroller.add (vca_hpacker);
286         vca_scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
287         vca_scroller.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
288
289         vca_vpacker.pack_start (vca_scroller, true, true);
290
291         inner_pane.add (scroller);
292         inner_pane.add (vca_vpacker);
293
294         global_hpacker.pack_start (inner_pane, true, true);
295         global_hpacker.pack_start (out_packer, false, false);
296
297         list_hpane.set_check_divider_position (true);
298         list_hpane.add (list_vpacker);
299         list_hpane.add (global_hpacker);
300         list_hpane.set_child_minsize (list_vpacker, 30);
301
302         XMLNode const * settings = ARDOUR_UI::instance()->mixer_settings();
303         float fract;
304
305         if (!settings || !settings->get_property ("mixer-rhs-pane1-pos", fract) || fract > 1.0) {
306                 fract = 0.6f;
307         }
308         rhs_pane1.set_divider (0, fract);
309
310         if (!settings || !settings->get_property ("mixer-rhs-pane2-pos", fract) || fract > 1.0) {
311                 fract = 0.7f;
312         }
313         rhs_pane2.set_divider (0, fract);
314
315         if (!settings || !settings->get_property ("mixer-list-hpane-pos", fract) || fract > 1.0) {
316                 fract = 0.2f;
317         }
318         list_hpane.set_divider (0, fract);
319
320         if (!settings || !settings->get_property ("mixer-inner-pane-pos", fract) || fract > 1.0) {
321                 fract = 0.8f;
322         }
323         inner_pane.set_divider (0, fract);
324
325         rhs_pane1.set_drag_cursor (*PublicEditor::instance().cursors()->expand_up_down);
326         rhs_pane2.set_drag_cursor (*PublicEditor::instance().cursors()->expand_up_down);
327         list_hpane.set_drag_cursor (*PublicEditor::instance().cursors()->expand_left_right);
328         inner_pane.set_drag_cursor (*PublicEditor::instance().cursors()->expand_left_right);
329
330         _content.pack_start (list_hpane, true, true);
331
332         update_title ();
333
334         _content.show ();
335         _content.set_name ("MixerWindow");
336
337         global_hpacker.show();
338         scroller.show();
339         scroller_base.show();
340         scroller_hpacker.show();
341         mixer_scroller_vpacker.show();
342         list_vpacker.show();
343         group_display_button_label.show();
344         group_display_scroller.show();
345         favorite_plugins_scroller.show();
346         group_display_vbox.show();
347         group_display_frame.show();
348         favorite_plugins_frame.show();
349         rhs_pane1.show();
350         rhs_pane2.show();
351         strip_packer.show();
352         inner_pane.show();
353         vca_scroller.show();
354         vca_vpacker.show();
355         vca_hpacker.show();
356         vca_label_bar.show();
357         vca_label.show();
358         vca_scroller_base.show();
359         out_packer.show();
360         list_hpane.show();
361         group_display.show();
362         favorite_plugins_display.show();
363         add_button.show ();
364
365         XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
366         if (mnode) {
367                 _monitor_section.tearoff().set_state (*mnode);
368         }
369
370         MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context());
371         VCAMasterStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_master, this, _1), gui_context());
372         FoldbackStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_foldback, this, _1), gui_context());
373
374         /* handle escape */
375
376         ARDOUR_UI::instance()->Escape.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::escape, this), gui_context());
377
378 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
379         _plugin_selector = new PluginSelector (PluginManager::instance ());
380 #else
381 #error implement deferred Plugin-Favorite list
382 #endif
383
384         PluginManager::instance ().PluginListChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::plugin_list_changed, this), gui_context());
385         PluginManager::instance ().PluginStatusChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::plugin_list_changed, this), gui_context());
386         ARDOUR::Plugin::PresetsChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::refill_favorite_plugins, this), gui_context());
387 }
388
389 Mixer_UI::~Mixer_UI ()
390 {
391         monitor_section_detached ();
392
393         StripableList fb;
394         _session->get_stripables (fb, PresentationInfo::FoldbackBus);
395         if (fb.size()) {
396                 if (foldback_strip) {
397                         delete foldback_strip;
398                         foldback_strip = 0;
399                 }
400         }
401         delete _plugin_selector;
402         delete track_menu;
403 }
404
405 struct MixerStripSorter {
406         bool operator() (const MixerStrip* ms_a, const MixerStrip* ms_b)
407         {
408                 boost::shared_ptr<ARDOUR::Stripable> const& a = ms_a->stripable ();
409                 boost::shared_ptr<ARDOUR::Stripable> const& b = ms_b->stripable ();
410                 return ARDOUR::Stripable::Sorter(true)(a, b);
411         }
412 };
413
414
415 void
416 Mixer_UI::escape ()
417 {
418         select_none ();
419 }
420
421 void
422 Mixer_UI::tag_combo_changed ()
423 {
424         refill_favorite_plugins();
425 }
426
427 Gtk::Window*
428 Mixer_UI::use_own_window (bool and_fill_it)
429 {
430         bool new_window = !own_window();
431
432         Gtk::Window* win = Tabbable::use_own_window (and_fill_it);
433
434         if (win && new_window) {
435                 win->set_name ("MixerWindow");
436                 ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Mixer"), this);
437                 win->signal_event().connect (sigc::bind (sigc::ptr_fun (&Keyboard::catch_user_event_for_pre_dialog_focus), win));
438                 win->set_data ("ardour-bindings", bindings);
439                 update_title ();
440                 if (!win->get_focus()) {
441                         /* set focus widget to something, anything */
442                         win->set_focus (scroller);
443                 }
444         }
445
446         return win;
447 }
448
449 void
450 Mixer_UI::show_window ()
451 {
452         Tabbable::show_window ();
453
454         /* show/hide group tabs as required */
455         parameter_changed ("show-group-tabs");
456
457         /* now reset each strips width so the right widgets are shown */
458
459         TreeModel::Children rows = track_model->children();
460         TreeModel::Children::iterator ri;
461
462         for (ri = rows.begin(); ri != rows.end(); ++ri) {
463                 AxisView* av = (*ri)[stripable_columns.strip];
464                 MixerStrip* ms = dynamic_cast<MixerStrip*> (av);
465                 if (!ms) {
466                         continue;
467                 }
468                 ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
469                 /* Fix visibility of mixer strip stuff */
470                 ms->parameter_changed (X_("mixer-element-visibility"));
471         }
472
473         /* force focus into main area */
474         scroller_base.grab_focus ();
475 }
476
477 void
478 Mixer_UI::remove_master (VCAMasterStrip* vms)
479 {
480         if (_session && _session->deletion_in_progress()) {
481                 /* its all being taken care of */
482                 return;
483         }
484
485         TreeModel::Children rows = track_model->children();
486         TreeModel::Children::iterator ri;
487
488         for (ri = rows.begin(); ri != rows.end(); ++ri) {
489                 if ((*ri)[stripable_columns.strip] == vms) {
490                         PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
491                         track_model->erase (ri);
492                         break;
493                 }
494         }
495 }
496
497 bool
498 Mixer_UI::masters_scroller_button_release (GdkEventButton* ev)
499 {
500         using namespace Menu_Helpers;
501
502         if (Keyboard::is_context_menu_event (ev)) {
503                 ARDOUR_UI::instance()->add_route ();
504                 return true;
505         }
506
507         return false;
508 }
509
510 void
511 Mixer_UI::new_masters_created ()
512 {
513         ActionManager::get_toggle_action ("Mixer", "ToggleVCAPane")->set_active (true);
514 }
515
516 void
517 Mixer_UI::add_masters (VCAList& vlist)
518 {
519         StripableList sl;
520
521         for (VCAList::iterator v = vlist.begin(); v != vlist.end(); ++v) {
522                 sl.push_back (boost::dynamic_pointer_cast<Stripable> (*v));
523         }
524
525         add_stripables (sl);
526 }
527
528 void
529 Mixer_UI::add_routes (RouteList& rlist)
530 {
531         StripableList sl;
532
533         for (RouteList::iterator r = rlist.begin(); r != rlist.end(); ++r) {
534                 sl.push_back (*r);
535         }
536
537         add_stripables (sl);
538 }
539
540 void
541 Mixer_UI::add_stripables (StripableList& slist)
542 {
543         Gtk::TreeModel::Children::iterator insert_iter = track_model->children().end();
544         bool from_scratch = (track_model->children().size() == 0);
545         uint32_t nroutes = 0;
546
547         slist.sort (Stripable::Sorter());
548
549         for (Gtk::TreeModel::Children::iterator it = track_model->children().begin(); it != track_model->children().end(); ++it) {
550                 boost::shared_ptr<Stripable> s = (*it)[stripable_columns.stripable];
551
552                 if (!s) {
553                         continue;
554                 }
555
556                 nroutes++;
557
558                 // XXX what does this special case do?
559                 if (s->presentation_info().order() == (slist.front()->presentation_info().order() + slist.size())) {
560                         insert_iter = it;
561                         break;
562                 }
563         }
564
565         MixerStrip* strip;
566
567         try {
568                 PBD::Unwinder<bool> uw (no_track_list_redisplay, true);
569
570                 track_display.set_model (Glib::RefPtr<ListStore>());
571
572                 for (StripableList::iterator s = slist.begin(); s != slist.end(); ++s) {
573
574                         boost::shared_ptr<Route> route;
575                         boost::shared_ptr<VCA> vca;
576
577                         if ((vca  = boost::dynamic_pointer_cast<VCA> (*s))) {
578
579                                 VCAMasterStrip* vms = new VCAMasterStrip (_session, vca);
580
581                                 TreeModel::Row row = *(track_model->append());
582
583                                 row[stripable_columns.text] = vca->name();
584                                 row[stripable_columns.visible] = vms->marked_for_display ();
585                                 row[stripable_columns.strip] = vms;
586                                 row[stripable_columns.stripable] = vca;
587
588                                 vms->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::vca_button_release_event), vms));
589
590                         } else if ((route = boost::dynamic_pointer_cast<Route> (*s))) {
591
592                                 if (route->is_auditioner()) {
593                                         continue;
594                                 }
595
596                                 if (route->is_monitor()) {
597
598                                         out_packer.pack_end (_monitor_section.tearoff(), false, false);
599                                         _monitor_section.set_session (_session);
600                                         _monitor_section.tearoff().show_all ();
601
602                                         _monitor_section.tearoff().Detach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_detached));
603                                         _monitor_section.tearoff().Attach.connect (sigc::mem_fun(*this, &Mixer_UI::monitor_section_attached));
604
605                                         if (_monitor_section.tearoff().torn_off()) {
606                                                 monitor_section_detached ();
607                                         } else {
608                                                 monitor_section_attached ();
609                                         }
610
611                                         route->DropReferences.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::monitor_section_going_away, this), gui_context());
612
613                                         /* no regular strip shown for control out */
614
615                                         continue;
616                                 }
617                                 if (route->is_foldbackbus ()) {
618                                         if (foldback_strip) {
619                                                 // last strip created is shown
620                                                 foldback_strip->set_route (route);
621                                         } else {
622                                                 foldback_strip = new FoldbackStrip (*this, _session, route);
623                                                 out_packer.pack_start (*foldback_strip, false, false);
624                                                 // change 0 to 1 below for foldback to right of master
625                                                 out_packer.reorder_child (*foldback_strip, 0);
626                                                 foldback_strip->set_packed (true);
627                                         }
628                                         /* config from last run is set before there are any foldback strips
629                                          * this takes that setting and applies it after at least one foldback
630                                          * strip exists */
631                                         bool yn = _show_foldback_strip;
632                                         Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action ("Mixer", "ToggleFoldbackStrip");
633                                         act->set_sensitive (true);
634                                         act->set_active(!yn);
635                                         act->set_active(yn);
636                                         continue;
637                                 }
638
639                                 strip = new MixerStrip (*this, _session, route);
640                                 strips.push_back (strip);
641
642                                 UIConfiguration::instance().get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
643
644                                 if (strip->width_owner() != strip) {
645                                         strip->set_width_enum (_strip_width, this);
646                                 }
647
648                                 show_strip (strip);
649
650                                 if (route->is_master()) {
651
652                                         out_packer.pack_start (*strip, false, false);
653                                         strip->set_packed (true);
654
655                                 } else {
656
657                                         TreeModel::Row row = *(track_model->insert (insert_iter));
658
659                                         row[stripable_columns.text] = route->name();
660                                         row[stripable_columns.visible] = strip->marked_for_display();
661                                         row[stripable_columns.stripable] = route;
662                                         row[stripable_columns.strip] = strip;
663                                 }
664
665                                 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
666                                 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
667                         }
668
669                         (*s)->presentation_info().PropertyChanged.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::stripable_property_changed, this, _1, boost::weak_ptr<Stripable>(*s)), gui_context());
670                         (*s)->PropertyChanged.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::stripable_property_changed, this, _1, boost::weak_ptr<Stripable>(*s)), gui_context());
671                 }
672
673         } catch (const std::exception& e) {
674                 error << string_compose (_("Error adding GUI elements for new tracks/busses %1"), e.what()) << endmsg;
675         }
676
677         track_display.set_model (track_model);
678
679         /* catch up on selection state, which we left to the editor to set */
680         sync_treeview_from_presentation_info (PropertyChange (Properties::selected));
681
682         if (!from_scratch) {
683                 sync_presentation_info_from_treeview ();
684         }
685
686         redisplay_track_list ();
687 }
688
689 void
690 Mixer_UI::deselect_all_strip_processors ()
691 {
692         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
693                 (*i)->deselect_all_processors();
694         }
695 }
696
697 void
698 Mixer_UI::select_none ()
699 {
700         _selection.clear_routes();
701         deselect_all_strip_processors();
702 }
703
704 void
705 Mixer_UI::select_next_strip ()
706 {
707         deselect_all_strip_processors();
708         _session->selection().select_next_stripable (true, false);
709 }
710
711 void
712 Mixer_UI::select_prev_strip ()
713 {
714         deselect_all_strip_processors();
715         _session->selection().select_prev_stripable (true, false);
716 }
717
718 void
719 Mixer_UI::delete_processors ()
720 {
721         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
722                 (*i)->delete_processors();
723         }
724 }
725
726
727 void
728 Mixer_UI::remove_strip (MixerStrip* strip)
729 {
730         if (_session && _session->deletion_in_progress()) {
731                 /* its all being taken care of */
732                 return;
733         }
734
735         TreeModel::Children rows = track_model->children();
736         TreeModel::Children::iterator ri;
737         list<MixerStrip *>::iterator i;
738
739         if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
740                 strips.erase (i);
741         }
742
743         PBD::Unwinder<bool> uwi (ignore_reorder, true);
744
745         for (ri = rows.begin(); ri != rows.end(); ++ri) {
746                 if ((*ri)[stripable_columns.strip] == strip) {
747                         PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
748                         track_model->erase (ri);
749                         break;
750                 }
751         }
752 }
753
754 void
755 Mixer_UI::remove_foldback (FoldbackStrip* strip)
756 {
757         if (_session && _session->deletion_in_progress()) {
758                 /* its all being taken care of */
759                 return;
760         }
761         Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action ("Mixer", "ToggleFoldbackStrip");
762         act->set_sensitive (false);
763         if (foldback_strip) {
764                 foldback_strip->destroy_();
765         }
766         foldback_strip = 0;
767 }
768
769 void
770 Mixer_UI::presentation_info_changed (PropertyChange const & what_changed)
771 {
772         if (what_changed.contains (Properties::selected)) {
773                 _selection.presentation_info_changed (what_changed);
774         }
775
776         PropertyChange soh;
777         soh.add (Properties::selected);
778         soh.add (Properties::order);
779         soh.add (Properties::hidden);
780
781         if (what_changed.contains (soh)) {
782                 sync_treeview_from_presentation_info (what_changed);
783         }
784 }
785
786 void
787 Mixer_UI::sync_presentation_info_from_treeview ()
788 {
789         if (ignore_reorder || !_session || _session->deletion_in_progress()) {
790                 return;
791         }
792
793         TreeModel::Children rows = track_model->children();
794
795         if (rows.empty()) {
796                 return;
797         }
798
799         DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync presentation info from treeview\n");
800
801         TreeModel::Children::iterator ri;
802         bool change = false;
803
804         PresentationInfo::order_t master_key = _session->master_order_key ();
805         PresentationInfo::order_t order = 0;
806
807         PresentationInfo::ChangeSuspender cs;
808
809         for (ri = rows.begin(); ri != rows.end(); ++ri) {
810                 bool visible = (*ri)[stripable_columns.visible];
811                 boost::shared_ptr<Stripable> stripable = (*ri)[stripable_columns.stripable];
812
813 #ifndef NDEBUG // these should not exist in the mixer's treeview
814                 if (!stripable) {
815                         assert (0);
816                         continue;
817                 }
818                 if (stripable->is_monitor() || stripable->is_auditioner()) {
819                         assert (0);
820                         continue;
821                 }
822                 if (stripable->is_master()) {
823                         assert (0);
824                         continue;
825                 }
826 #endif
827
828                 stripable->presentation_info().set_hidden (!visible);
829
830                 // leave master where it is.
831                 if (order == master_key) {
832                         ++order;
833                 }
834
835                 if (order != stripable->presentation_info().order()) {
836                         stripable->set_presentation_order (order);
837                         change = true;
838                 }
839                 ++order;
840         }
841
842         change |= _session->ensure_stripable_sort_order ();
843
844         if (change) {
845                 DEBUG_TRACE (DEBUG::OrderKeys, "... notify PI change from mixer GUI\n");
846                 _session->set_dirty();
847         }
848 }
849
850 void
851 Mixer_UI::sync_treeview_from_presentation_info (PropertyChange const & what_changed)
852 {
853         if (!_session || _session->deletion_in_progress()) {
854                 return;
855         }
856
857         DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync model from presentation info.\n");
858
859         /* we could get here after either a change in the Mixer or Editor sort
860          * order, but either way, the mixer order keys reflect the intended
861          * order for the GUI, so reorder the treeview model to match it.
862          */
863
864         vector<int> neworder;
865         TreeModel::Children rows = track_model->children();
866         uint32_t old_order = 0;
867         bool changed = false;
868
869         if (rows.empty()) {
870                 return;
871         }
872
873         TreeOrderKeys sorted;
874         for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
875                 boost::shared_ptr<Stripable> stripable = (*ri)[stripable_columns.stripable];
876                 sorted.push_back (TreeOrderKey (old_order, stripable));
877         }
878
879         TreeOrderKeySorter cmp;
880
881         sort (sorted.begin(), sorted.end(), cmp);
882         neworder.assign (sorted.size(), 0);
883
884         uint32_t n = 0;
885
886         for (TreeOrderKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
887
888                 neworder[n] = sr->old_display_order;
889
890                 if (sr->old_display_order != n) {
891                         changed = true;
892                 }
893         }
894
895         if (changed) {
896                 Unwinder<bool> uw (ignore_reorder, true);
897                 track_model->reorder (neworder);
898         }
899
900         if (what_changed.contains (Properties::selected)) {
901
902                 PresentationInfo::ChangeSuspender cs;
903
904                 for (list<MixerStrip *>::const_iterator i = strips.begin(); i != strips.end(); ++i) {
905                         boost::shared_ptr<Stripable> stripable = (*i)->stripable();
906                         if (stripable && stripable->is_selected()) {
907                                 _selection.add (*i);
908                         } else {
909                                 _selection.remove (*i);
910                         }
911                 }
912
913                 if (!_selection.axes.empty() && !PublicEditor::instance().track_selection_change_without_scroll () && !_strip_selection_change_without_scroll) {
914                         move_stripable_into_view ((*_selection.axes.begin())->stripable());
915                 }
916
917                 TreeModel::Children rows = track_model->children();
918                 for (TreeModel::Children::const_iterator i = rows.begin(); i != rows.end(); ++i) {
919                         AxisView* av = (*i)[stripable_columns.strip];
920                         VCAMasterStrip* vms = dynamic_cast<VCAMasterStrip*> (av);
921                         if (!vms) {
922                                 continue;
923                         }
924                         if (vms->vca() && vms->vca()->is_selected()) {
925                                 _selection.add (vms);
926                         } else {
927                                 _selection.remove (vms);
928                         }
929                 }
930         }
931
932         redisplay_track_list ();
933 }
934
935
936 MixerStrip*
937 Mixer_UI::strip_by_route (boost::shared_ptr<Route> r) const
938 {
939         for (list<MixerStrip *>::const_iterator i = strips.begin(); i != strips.end(); ++i) {
940                 if ((*i)->route() == r) {
941                         return (*i);
942                 }
943         }
944
945         return 0;
946 }
947
948 MixerStrip*
949 Mixer_UI::strip_by_stripable (boost::shared_ptr<Stripable> s) const
950 {
951         for (list<MixerStrip *>::const_iterator i = strips.begin(); i != strips.end(); ++i) {
952                 if ((*i)->stripable() == s) {
953                         return (*i);
954                 }
955         }
956
957         return 0;
958 }
959
960 AxisView*
961 Mixer_UI::axis_view_by_stripable (boost::shared_ptr<Stripable> s) const
962 {
963         for (list<MixerStrip *>::const_iterator i = strips.begin(); i != strips.end(); ++i) {
964                 if ((*i)->stripable() == s) {
965                         return (*i);
966                 }
967         }
968
969         TreeModel::Children rows = track_model->children();
970         for (TreeModel::Children::const_iterator i = rows.begin(); i != rows.end(); ++i) {
971                 AxisView* av = (*i)[stripable_columns.strip];
972                 VCAMasterStrip* vms = dynamic_cast<VCAMasterStrip*> (av);
973                 if (vms && vms->stripable () == s) {
974                         return av;
975                 }
976         }
977
978         return 0;
979 }
980
981 AxisView*
982 Mixer_UI::axis_view_by_control (boost::shared_ptr<AutomationControl> c) const
983 {
984         for (list<MixerStrip *>::const_iterator i = strips.begin(); i != strips.end(); ++i) {
985                 if ((*i)->control() == c) {
986                         return (*i);
987                 }
988         }
989
990         return 0;
991 }
992
993 bool
994 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
995 {
996         /* Selecting a mixer-strip may also select grouped-tracks, and
997          * presentation_info_changed() being emitted and
998          * _selection.axes.begin() is being moved into view. This may
999          * effectively move the track that was clicked-on out of view.
1000          *
1001          * So here only the track that is actually clicked-on is moved into
1002          * view (in case it's partially visible)
1003          */
1004         PBD::Unwinder<bool> uw (_strip_selection_change_without_scroll, true);
1005         move_stripable_into_view (strip->stripable());
1006
1007         if (ev->button == 1) {
1008                 if (_selection.selected (strip)) {
1009                         /* primary-click: toggle selection state of strip */
1010                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1011                                 _selection.remove (strip, true);
1012                         } else if (_selection.axes.size() > 1) {
1013                                 /* de-select others */
1014                                 _selection.set (strip);
1015                         }
1016                         PublicEditor& pe = PublicEditor::instance();
1017                         TimeAxisView* tav = pe.time_axis_view_from_stripable (strip->stripable());
1018                         if (tav) {
1019                                 pe.set_selected_mixer_strip (*tav);
1020                         }
1021                 } else {
1022                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1023                                 _selection.add (strip, true);
1024                         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::RangeSelectModifier)) {
1025
1026                                 /* extend selection */
1027
1028                                 vector<MixerStrip*> tmp;
1029                                 bool accumulate = false;
1030                                 bool found_another = false;
1031
1032                                 strips.sort (MixerStripSorter());
1033
1034                                 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1035                                         MixerStrip* ms = *i;
1036                                         assert (ms);
1037
1038                                         if (ms == strip) {
1039                                                 /* hit clicked strip, start accumulating till we hit the first
1040                                                    selected strip
1041                                                 */
1042                                                 if (accumulate) {
1043                                                         /* done */
1044                                                         break;
1045                                                 } else {
1046                                                         accumulate = true;
1047                                                 }
1048                                         } else if (_selection.selected (ms)) {
1049                                                 /* hit selected strip. if currently accumulating others,
1050                                                    we're done. if not accumulating others, start doing so.
1051                                                 */
1052                                                 found_another = true;
1053                                                 if (accumulate) {
1054                                                         /* done */
1055                                                         break;
1056                                                 } else {
1057                                                         accumulate = true;
1058                                                 }
1059                                         } else {
1060                                                 if (accumulate) {
1061                                                         tmp.push_back (ms);
1062                                                 }
1063                                         }
1064                                 }
1065
1066                                 tmp.push_back (strip);
1067
1068                                 if (found_another) {
1069                                         PresentationInfo::ChangeSuspender cs;
1070                                         for (vector<MixerStrip*>::iterator i = tmp.begin(); i != tmp.end(); ++i) {
1071                                                 _selection.add (*i, true);
1072                                         }
1073                                 } else {
1074                                         _selection.set (strip);  //user wants to start a range selection, but there aren't any others selected yet
1075                                 }
1076                         } else {
1077                                 _selection.set (strip);
1078                         }
1079                 }
1080         }
1081
1082         return true;
1083 }
1084
1085 bool
1086 Mixer_UI::vca_button_release_event (GdkEventButton *ev, VCAMasterStrip *strip)
1087 {
1088         _selection.set (strip);
1089         return true;
1090 }
1091
1092 void
1093 Mixer_UI::set_session (Session* sess)
1094 {
1095         SessionHandlePtr::set_session (sess);
1096         _monitor_section.set_session (sess);
1097
1098         if (_plugin_selector) {
1099                 _plugin_selector->set_session (_session);
1100         }
1101
1102         _group_tabs->set_session (sess);
1103
1104         if (!_session) {
1105                 _selection.clear ();
1106                 return;
1107         }
1108
1109         refill_favorite_plugins();
1110         refill_tag_combo();
1111
1112         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1113         set_state (*node, 0);
1114
1115         update_title ();
1116
1117         initial_track_display ();
1118
1119         _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_routes, this, _1), gui_context());
1120         _session->route_group_added.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_route_group, this, _1), gui_context());
1121         _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
1122         _session->route_groups_reordered.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
1123         _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
1124         _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
1125         _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
1126
1127         _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_masters, this, _1), gui_context());
1128         _session->vca_manager().VCACreated.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::new_masters_created, this), gui_context());
1129
1130         Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
1131
1132         route_groups_changed ();
1133
1134         if (_visible) {
1135                 show_window();
1136         }
1137
1138         /* catch up on selection state, etc. */
1139
1140         PropertyChange sc;
1141         sc.add (Properties::selected);
1142         _selection.presentation_info_changed (sc);
1143
1144         start_updating ();
1145 }
1146
1147 void
1148 Mixer_UI::session_going_away ()
1149 {
1150         ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away);
1151
1152         _in_group_rebuild_or_clear = true;
1153         group_model->clear ();
1154         _in_group_rebuild_or_clear = false;
1155
1156         _selection.clear ();
1157         track_model->clear ();
1158
1159         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1160                 delete (*i);
1161         }
1162
1163         _monitor_section.tearoff().hide_visible ();
1164         StripableList fb;
1165         _session->get_stripables (fb, PresentationInfo::FoldbackBus);
1166         if (fb.size()) {
1167                 if (foldback_strip) {
1168                         delete foldback_strip;
1169                         foldback_strip = 0;
1170                 }
1171         }
1172
1173         monitor_section_detached ();
1174
1175         strips.clear ();
1176
1177         stop_updating ();
1178
1179         SessionHandlePtr::session_going_away ();
1180
1181         _session = 0;
1182         update_title ();
1183 }
1184
1185 void
1186 Mixer_UI::track_visibility_changed (std::string const & path)
1187 {
1188         if (_session && _session->deletion_in_progress()) {
1189                 return;
1190         }
1191
1192         TreeIter iter;
1193
1194         if ((iter = track_model->get_iter (path))) {
1195
1196                 AxisView* av = (*iter)[stripable_columns.strip];
1197                 bool visible = (*iter)[stripable_columns.visible];
1198
1199                 if (av->set_marked_for_display (!visible)) {
1200                         update_track_visibility ();
1201                 }
1202         }
1203 }
1204
1205 void
1206 Mixer_UI::update_track_visibility ()
1207 {
1208         TreeModel::Children rows = track_model->children();
1209         TreeModel::Children::iterator i;
1210
1211         {
1212                 Unwinder<bool> uw (no_track_list_redisplay, true);
1213
1214                 for (i = rows.begin(); i != rows.end(); ++i) {
1215                         AxisView* av = (*i)[stripable_columns.strip];
1216                         (*i)[stripable_columns.visible] = av->marked_for_display ();
1217                 }
1218
1219                 /* force presentation to catch up with visibility changes */
1220                 sync_presentation_info_from_treeview ();
1221         }
1222
1223         redisplay_track_list ();
1224 }
1225
1226 void
1227 Mixer_UI::show_strip (MixerStrip* ms)
1228 {
1229         TreeModel::Children rows = track_model->children();
1230         TreeModel::Children::iterator i;
1231
1232         for (i = rows.begin(); i != rows.end(); ++i) {
1233
1234                 AxisView* av = (*i)[stripable_columns.strip];
1235                 MixerStrip* strip = dynamic_cast<MixerStrip*> (av);
1236                 if (strip == ms) {
1237                         (*i)[stripable_columns.visible] = true;
1238                         av->set_marked_for_display (true);
1239                         update_track_visibility ();
1240                         break;
1241                 }
1242         }
1243 }
1244
1245 void
1246 Mixer_UI::hide_strip (MixerStrip* ms)
1247 {
1248         TreeModel::Children rows = track_model->children();
1249         TreeModel::Children::iterator i;
1250
1251         for (i = rows.begin(); i != rows.end(); ++i) {
1252
1253                 AxisView* av = (*i)[stripable_columns.strip];
1254                 MixerStrip* strip = dynamic_cast<MixerStrip*> (av);
1255                 if (strip == ms) {
1256                         (*i)[stripable_columns.visible] = false;
1257                         av->set_marked_for_display (false);
1258                         update_track_visibility ();
1259                         break;
1260                 }
1261         }
1262 }
1263
1264 gint
1265 Mixer_UI::start_updating ()
1266 {
1267         fast_screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
1268         return 0;
1269 }
1270
1271 gint
1272 Mixer_UI::stop_updating ()
1273 {
1274         fast_screen_update_connection.disconnect();
1275         return 0;
1276 }
1277
1278 void
1279 Mixer_UI::fast_update_strips ()
1280 {
1281         if (_content.is_mapped () && _session) {
1282                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1283                         (*i)->fast_update ();
1284                 }
1285         }
1286 }
1287
1288 void
1289 Mixer_UI::set_all_strips_visibility (bool yn)
1290 {
1291         TreeModel::Children rows = track_model->children();
1292         TreeModel::Children::iterator i;
1293
1294         {
1295                 Unwinder<bool> uw (no_track_list_redisplay, true);
1296
1297                 for (i = rows.begin(); i != rows.end(); ++i) {
1298
1299                         AxisView* av = (*i)[stripable_columns.strip];
1300                         MixerStrip* strip = dynamic_cast<MixerStrip*> (av);
1301
1302                         if (!strip) {
1303                                 continue;
1304                         }
1305
1306                         if (strip->route()->is_master() || strip->route()->is_monitor()) {
1307                                 continue;
1308                         }
1309
1310                         (*i)[stripable_columns.visible] = yn;
1311                 }
1312
1313                 /* force presentation to catch up with visibility changes */
1314                 sync_presentation_info_from_treeview ();
1315         }
1316
1317         redisplay_track_list ();
1318 }
1319
1320 void
1321 Mixer_UI::set_all_audio_midi_visibility (int tracks, bool yn)
1322 {
1323         TreeModel::Children rows = track_model->children();
1324         TreeModel::Children::iterator i;
1325
1326         {
1327                 Unwinder<bool> uw (no_track_list_redisplay, true);
1328
1329                 for (i = rows.begin(); i != rows.end(); ++i) {
1330
1331                         AxisView* av = (*i)[stripable_columns.strip];
1332                         MixerStrip* strip = dynamic_cast<MixerStrip*> (av);
1333
1334                         if (!strip) {
1335                                 continue;
1336                         }
1337
1338                         if (strip->route()->is_master() || strip->route()->is_monitor()) {
1339                                 continue;
1340                         }
1341
1342                         boost::shared_ptr<AudioTrack> at = strip->audio_track();
1343                         boost::shared_ptr<MidiTrack> mt = strip->midi_track();
1344
1345                         switch (tracks) {
1346                         case 0:
1347                                 (*i)[stripable_columns.visible] = yn;
1348                                 break;
1349
1350                         case 1:
1351                                 if (at) { /* track */
1352                                         (*i)[stripable_columns.visible] = yn;
1353                                 }
1354                                 break;
1355
1356                         case 2:
1357                                 if (!at && !mt) { /* bus */
1358                                         (*i)[stripable_columns.visible] = yn;
1359                                 }
1360                                 break;
1361
1362                         case 3:
1363                                 if (mt) { /* midi-track */
1364                                         (*i)[stripable_columns.visible] = yn;
1365                                 }
1366                                 break;
1367                         }
1368                 }
1369
1370                 /* force presentation to catch up with visibility changes */
1371                 sync_presentation_info_from_treeview ();
1372         }
1373
1374         redisplay_track_list ();
1375 }
1376
1377 void
1378 Mixer_UI::hide_all_routes ()
1379 {
1380         set_all_strips_visibility (false);
1381 }
1382
1383 void
1384 Mixer_UI::show_all_routes ()
1385 {
1386         set_all_strips_visibility (true);
1387 }
1388
1389 void
1390 Mixer_UI::show_all_audiobus ()
1391 {
1392         set_all_audio_midi_visibility (2, true);
1393 }
1394 void
1395 Mixer_UI::hide_all_audiobus ()
1396 {
1397         set_all_audio_midi_visibility (2, false);
1398 }
1399
1400 void
1401 Mixer_UI::show_all_audiotracks()
1402 {
1403         set_all_audio_midi_visibility (1, true);
1404 }
1405 void
1406 Mixer_UI::hide_all_audiotracks ()
1407 {
1408         set_all_audio_midi_visibility (1, false);
1409 }
1410
1411 void
1412 Mixer_UI::show_all_miditracks()
1413 {
1414         set_all_audio_midi_visibility (3, true);
1415 }
1416 void
1417 Mixer_UI::hide_all_miditracks ()
1418 {
1419         set_all_audio_midi_visibility (3, false);
1420 }
1421
1422 void
1423 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
1424 {
1425         DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview reordered\n");
1426         sync_presentation_info_from_treeview ();
1427 }
1428
1429 void
1430 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
1431 {
1432         /* this happens as the second step of a DnD within the treeview as well
1433            as when a row/route is actually deleted.
1434
1435            if it was a deletion then we have to force a redisplay because
1436            order keys may not have changed.
1437         */
1438
1439         DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
1440         sync_presentation_info_from_treeview ();
1441
1442         if (_route_deletion_in_progress) {
1443                 redisplay_track_list ();
1444         }
1445 }
1446
1447 void
1448 Mixer_UI::spill_redisplay (boost::shared_ptr<VCA> vca)
1449 {
1450         TreeModel::Children rows = track_model->children();
1451         std::list<boost::shared_ptr<VCA> > vcas;
1452         vcas.push_back (vca);
1453
1454         for (TreeModel::Children::const_iterator i = rows.begin(); i != rows.end(); ++i) {
1455                 AxisView* av = (*i)[stripable_columns.strip];
1456                 VCAMasterStrip* vms = dynamic_cast<VCAMasterStrip*> (av);
1457                 if (vms && vms->vca()->slaved_to (vca)) {
1458                         vcas.push_back (vms->vca());
1459                 }
1460         }
1461
1462         for (TreeModel::Children::const_iterator i = rows.begin(); i != rows.end(); ++i) {
1463
1464                 AxisView* av = (*i)[stripable_columns.strip];
1465                 MixerStrip* strip = dynamic_cast<MixerStrip*> (av);
1466                 bool const visible = (*i)[stripable_columns.visible];
1467
1468                 if (!strip) {
1469                         /* we're in the middle of changing a row, don't worry */
1470                         continue;
1471                 }
1472
1473                 if (!strip->route()) {
1474                         /* non-route element */
1475                         continue;
1476                 }
1477
1478                 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1479                         continue;
1480                 }
1481
1482                 bool slaved = false;
1483                 for (std::list<boost::shared_ptr<VCA> >::const_iterator m = vcas.begin(); m != vcas.end(); ++m) {
1484                         if (strip->route()->slaved_to (*m)) {
1485                                 slaved = true;
1486                                 break;
1487                         }
1488                 }
1489
1490                 if (slaved && visible) {
1491
1492                         if (strip->packed()) {
1493                                 strip_packer.reorder_child (*strip, -1); /* put at end */
1494                         } else {
1495                                 strip_packer.pack_start (*strip, false, false);
1496                                 strip->set_packed (true);
1497                         }
1498
1499                 } else {
1500
1501                         if (strip->packed()) {
1502                                 strip_packer.remove (*strip);
1503                                 strip->set_packed (false);
1504                         }
1505                 }
1506         }
1507 }
1508
1509 void
1510 Mixer_UI::redisplay_track_list ()
1511 {
1512         if (no_track_list_redisplay) {
1513                 return;
1514         }
1515
1516         boost::shared_ptr<Stripable> ss = spilled_strip.lock ();
1517         if (ss) {
1518                 boost::shared_ptr<VCA> sv = boost::dynamic_pointer_cast<VCA> (ss);
1519                 if (sv) {
1520                         if (_spill_scroll_position <= 0 && scroller.get_hscrollbar()) {
1521                                 _spill_scroll_position = scroller.get_hscrollbar()->get_adjustment()->get_value();
1522                         }
1523                         spill_redisplay (sv);
1524                         return;
1525                 }
1526         }
1527
1528         TreeModel::Children rows = track_model->children();
1529         TreeModel::Children::iterator i;
1530         uint32_t n_masters = 0;
1531
1532         container_clear (vca_hpacker);
1533
1534         vca_hpacker.pack_end (vca_scroller_base, true, true);
1535         vca_hpacker.pack_end (add_vca_button, false, false);
1536
1537         add_vca_button.show ();
1538         vca_scroller_base.show();
1539
1540         for (i = rows.begin(); i != rows.end(); ++i) {
1541
1542                 AxisView* s = (*i)[stripable_columns.strip];
1543                 bool const visible = (*i)[stripable_columns.visible];
1544                 boost::shared_ptr<Stripable> stripable = (*i)[stripable_columns.stripable];
1545
1546                 if (!s) {
1547                         /* we're in the middle of changing a row, don't worry */
1548                         continue;
1549                 }
1550
1551                 VCAMasterStrip* vms;
1552
1553                 if ((vms = dynamic_cast<VCAMasterStrip*> (s))) {
1554                         if (visible) {
1555                                 vca_hpacker.pack_start (*vms, false, false);
1556                                 vms->show ();
1557                                 n_masters++;
1558                         }
1559                         continue;
1560                 }
1561
1562                 MixerStrip* strip = dynamic_cast<MixerStrip*> (s);
1563
1564                 if (!strip) {
1565                         continue;
1566                 }
1567
1568                 if (visible) {
1569
1570                         if (strip->packed()) {
1571                                 strip_packer.reorder_child (*strip, -1); /* put at end */
1572                         } else {
1573                                 strip_packer.pack_start (*strip, false, false);
1574                                 strip->set_packed (true);
1575                         }
1576
1577                 } else {
1578
1579                         if (stripable->is_master() || stripable->is_monitor()) {
1580                                 /* do nothing, these cannot be hidden */
1581                         } else {
1582                                 if (strip->packed()) {
1583                                         strip_packer.remove (*strip);
1584                                         strip->set_packed (false);
1585                                 }
1586                         }
1587                 }
1588         }
1589
1590         /* update visibility of VCA assign buttons */
1591
1592         if (n_masters == 0) {
1593                 //show/hide the channelstrip VCA assign buttons on channelstrips:
1594                 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::remove_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1595
1596                 Glib::RefPtr<Action> act = ActionManager::get_action ("Mixer", "ToggleVCAPane");
1597                 if (act) {
1598                         act->set_sensitive (false);
1599                 }
1600
1601                 //remove the VCA packer, but don't change our prior setting for show/hide:
1602                 vca_vpacker.hide ();
1603         } else {
1604                 //show/hide the channelstrip VCA assign buttons on channelstrips:
1605                 UIConfiguration::instance().set_mixer_strip_visibility (VisibilityGroup::add_element (UIConfiguration::instance().get_mixer_strip_visibility(), X_("VCA")));
1606
1607                 Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action ("Mixer", "ToggleVCAPane");
1608                 act->set_sensitive (true);
1609
1610                 //if we were showing VCAs before, show them now:
1611                 showhide_vcas (act->get_active ());
1612         }
1613
1614         _group_tabs->set_dirty ();
1615
1616         if (_spill_scroll_position > 0 && scroller.get_hscrollbar()) {
1617                 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1618                 adj->set_value (max (adj->get_lower(), min (adj->get_upper(), _spill_scroll_position)));
1619         }
1620         _spill_scroll_position = 0;
1621
1622 }
1623
1624 void
1625 Mixer_UI::strip_width_changed ()
1626 {
1627         _group_tabs->set_dirty ();
1628
1629 #ifdef __APPLE__
1630         TreeModel::Children rows = track_model->children();
1631         TreeModel::Children::iterator i;
1632         long order;
1633
1634         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
1635                 AxisView* av = (*i)[stripable_columns.strip];
1636                 MixerStrip* strip = dynamic_cast<MixerStrip*> (av);
1637
1638                 if (strip == 0) {
1639                         continue;
1640                 }
1641
1642                 bool visible = (*i)[stripable_columns.visible];
1643
1644                 if (visible) {
1645                         strip->queue_draw();
1646                 }
1647         }
1648 #endif
1649
1650 }
1651
1652 struct PresentationInfoMixerSorter
1653 {
1654         bool operator() (boost::shared_ptr<Stripable> a, boost::shared_ptr<Stripable> b) {
1655                 if (a->is_master()) {
1656                         /* master after everything else */
1657                         return false;
1658                 } else if (b->is_master()) {
1659                         /* everything else before master */
1660                         return true;
1661                 }
1662                 return a->presentation_info().order () < b->presentation_info().order ();
1663         }
1664 };
1665
1666 void
1667 Mixer_UI::initial_track_display ()
1668 {
1669         StripableList sl;
1670         StripableList fb;
1671         _session->get_stripables (sl);
1672         _session->get_stripables (fb, PresentationInfo::FoldbackBus);
1673         if (fb.size()) {
1674                 boost::shared_ptr<ARDOUR::Stripable> _current_foldback = *(fb.begin());
1675                 sl.push_back (_current_foldback);
1676         }
1677
1678         sl.sort (PresentationInfoMixerSorter());
1679
1680         {
1681                 /* These are also used inside ::add_stripables() but we need
1682                  *  them here because we're going to clear the track_model also.
1683                  */
1684                 Unwinder<bool> uw1 (no_track_list_redisplay, true);
1685                 Unwinder<bool> uw2 (ignore_reorder, true);
1686
1687                 track_model->clear ();
1688                 add_stripables (sl);
1689         }
1690
1691         sync_treeview_from_presentation_info (Properties::order);
1692 }
1693
1694 bool
1695 Mixer_UI::track_display_button_press (GdkEventButton* ev)
1696 {
1697         if (Keyboard::is_context_menu_event (ev)) {
1698                 if (track_menu == 0) {
1699                         build_track_menu ();
1700                 }
1701                 track_menu->popup (ev->button, ev->time);
1702                 return true;
1703         }
1704         if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 1)) {
1705                 TreeModel::Path path;
1706                 TreeViewColumn* column;
1707                 int cellx, celly;
1708                 if (track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1709                         TreeIter iter = track_model->get_iter (path);
1710                         if ((*iter)[stripable_columns.visible]) {
1711                                 boost::shared_ptr<ARDOUR::Stripable> s = (*iter)[stripable_columns.stripable];
1712                                 move_stripable_into_view (s);
1713                         }
1714                 }
1715         }
1716
1717         return false;
1718 }
1719
1720 void
1721 Mixer_UI::move_vca_into_view (boost::shared_ptr<ARDOUR::Stripable> s)
1722 {
1723         if (!vca_scroller.get_hscrollbar()) {
1724                 return;
1725         }
1726
1727         bool found = false;
1728         int x0 = 0;
1729         Gtk::Allocation alloc;
1730
1731         TreeModel::Children rows = track_model->children();
1732         for (TreeModel::Children::const_iterator i = rows.begin(); i != rows.end(); ++i) {
1733                 AxisView* av = (*i)[stripable_columns.strip];
1734                 VCAMasterStrip* vms = dynamic_cast<VCAMasterStrip*> (av);
1735                 if (vms && vms->stripable () == s) {
1736                         int y;
1737                         found = true;
1738                         vms->translate_coordinates (vca_hpacker, 0, 0, x0, y);
1739                         alloc = vms->get_allocation ();
1740                         break;
1741                 }
1742         }
1743
1744         if (!found) {
1745                 return;
1746         }
1747
1748         Adjustment* adj = vca_scroller.get_hscrollbar()->get_adjustment();
1749
1750         if (x0 < adj->get_value()) {
1751                 adj->set_value (max (adj->get_lower(), min (adj->get_upper(), (double) x0)));
1752         } else if (x0 + alloc.get_width() >= adj->get_value() + adj->get_page_size()) {
1753                 int x1 = x0 + alloc.get_width() - adj->get_page_size();
1754                 adj->set_value (max (adj->get_lower(), min (adj->get_upper(), (double) x1)));
1755         }
1756 }
1757
1758 void
1759 Mixer_UI::move_stripable_into_view (boost::shared_ptr<ARDOUR::Stripable> s)
1760 {
1761         if (!scroller.get_hscrollbar()) {
1762                 return;
1763         }
1764         if (s->presentation_info().special ()) {
1765                 return;
1766         }
1767         if (s->presentation_info().flag_match (PresentationInfo::VCA)) {
1768                 move_vca_into_view (s);
1769         }
1770 #ifdef MIXBUS
1771         if (s->mixbus ()) {
1772                 return;
1773         }
1774 #endif
1775         bool found = false;
1776         int x0 = 0;
1777         Gtk::Allocation alloc;
1778         for (list<MixerStrip *>::const_iterator i = strips.begin(); i != strips.end(); ++i) {
1779                 if ((*i)->route() == s) {
1780                         int y;
1781                         found = true;
1782                         (*i)->translate_coordinates (strip_packer, 0, 0, x0, y);
1783                         alloc = (*i)->get_allocation ();
1784                         break;
1785                 }
1786         }
1787         if (!found) {
1788                 return;
1789         }
1790
1791         Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1792
1793         if (x0 < adj->get_value()) {
1794                 adj->set_value (max (adj->get_lower(), min (adj->get_upper(), (double) x0)));
1795         } else if (x0 + alloc.get_width() >= adj->get_value() + adj->get_page_size()) {
1796                 int x1 = x0 + alloc.get_width() - adj->get_page_size();
1797                 adj->set_value (max (adj->get_lower(), min (adj->get_upper(), (double) x1)));
1798         }
1799 }
1800
1801 void
1802 Mixer_UI::build_track_menu ()
1803 {
1804         using namespace Menu_Helpers;
1805         using namespace Gtk;
1806
1807         track_menu = new Menu;
1808         track_menu->set_name ("ArdourContextMenu");
1809         MenuList& items = track_menu->items();
1810
1811         items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1812         items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1813         items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1814         items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1815         items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::show_all_miditracks)));
1816         items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &Mixer_UI::hide_all_miditracks)));
1817         items.push_back (MenuElem (_("Show All Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1818         items.push_back (MenuElem (_("Hide All Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1819
1820 }
1821
1822 void
1823 Mixer_UI::stripable_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Stripable> ws)
1824 {
1825         if (!what_changed.contains (ARDOUR::Properties::hidden) && !what_changed.contains (ARDOUR::Properties::name)) {
1826                 return;
1827         }
1828
1829         boost::shared_ptr<Stripable> s = ws.lock ();
1830
1831         if (!s) {
1832                 return;
1833         }
1834
1835         TreeModel::Children rows = track_model->children();
1836         TreeModel::Children::iterator i;
1837
1838         for (i = rows.begin(); i != rows.end(); ++i) {
1839                 boost::shared_ptr<Stripable> ss = (*i)[stripable_columns.stripable];
1840
1841                 if (s == ss) {
1842
1843                         if (what_changed.contains (ARDOUR::Properties::name)) {
1844                                 (*i)[stripable_columns.text] = s->name();
1845                         }
1846
1847                         if (what_changed.contains (ARDOUR::Properties::hidden)) {
1848                                 (*i)[stripable_columns.visible] = !s->presentation_info().hidden();
1849                                 redisplay_track_list ();
1850                         }
1851
1852                         return;
1853                 }
1854         }
1855
1856         if (s->is_master ()) {
1857                 return;
1858         }
1859
1860         error << _("track display list item for renamed strip not found!") << endmsg;
1861 }
1862
1863 bool
1864 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1865 {
1866         TreeModel::Path path;
1867         TreeViewColumn* column;
1868         int cellx;
1869         int celly;
1870
1871         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1872                 if (ev->button == 3) {
1873                         _group_tabs->get_menu(0)->popup (ev->button, ev->time);
1874                 }
1875                 return true;
1876         }
1877
1878         TreeIter iter = group_model->get_iter (path);
1879         if (!iter) {
1880                 if (ev->button == 3) {
1881                         _group_tabs->get_menu(0)->popup (ev->button, ev->time);
1882                 }
1883                 return true;
1884         }
1885
1886         RouteGroup* group = (*iter)[group_columns.group];
1887
1888         if (Keyboard::is_context_menu_event (ev)) {
1889                 _group_tabs->get_menu(group)->popup (1, ev->time);
1890                 return true;
1891         }
1892
1893         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1894         case 1:
1895                 if (Keyboard::is_edit_event (ev)) {
1896                         if (group) {
1897                                 // edit_route_group (group);
1898 #ifdef __APPLE__
1899                                 group_display.queue_draw();
1900 #endif
1901                                 return true;
1902                         }
1903                 }
1904                 break;
1905
1906         case 0:
1907         {
1908                 bool visible = (*iter)[group_columns.visible];
1909                 (*iter)[group_columns.visible] = !visible;
1910 #ifdef __APPLE__
1911                 group_display.queue_draw();
1912 #endif
1913                 return true;
1914         }
1915
1916         default:
1917                 break;
1918         }
1919
1920         return false;
1921  }
1922
1923 void
1924 Mixer_UI::activate_all_route_groups ()
1925 {
1926         _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1927 }
1928
1929 void
1930 Mixer_UI::disable_all_route_groups ()
1931 {
1932         _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1933 }
1934
1935 void
1936 Mixer_UI::route_groups_changed ()
1937 {
1938         ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed);
1939
1940         _in_group_rebuild_or_clear = true;
1941
1942         /* just rebuild the while thing */
1943
1944         group_model->clear ();
1945
1946 #if 0
1947         /* this is currently not used,
1948          * Mixer_UI::group_display_button_press() has a case for it,
1949          * and a commented edit_route_group() but that's n/a since 2011.
1950          *
1951          * This code is left as reminder that
1952          * row[group_columns.group] = 0 has special meaning.
1953          */
1954         {
1955                 TreeModel::Row row;
1956                 row = *(group_model->append());
1957                 row[group_columns.visible] = true;
1958                 row[group_columns.text] = (_("-all-"));
1959                 row[group_columns.group] = 0;
1960         }
1961 #endif
1962
1963         _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1964
1965         _group_tabs->set_dirty ();
1966         _in_group_rebuild_or_clear = false;
1967 }
1968
1969 void
1970 Mixer_UI::new_route_group ()
1971 {
1972         _group_tabs->run_new_group_dialog (0, false);
1973 }
1974
1975 void
1976 Mixer_UI::remove_selected_route_group ()
1977 {
1978         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1979         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1980
1981         if (rows.empty()) {
1982                 return;
1983         }
1984
1985         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1986         TreeIter iter;
1987
1988         /* selection mode is single, so rows.begin() is it */
1989
1990         if ((iter = group_model->get_iter (*i))) {
1991
1992                 RouteGroup* rg = (*iter)[group_columns.group];
1993
1994                 if (rg) {
1995                         _session->remove_route_group (*rg);
1996                 }
1997         }
1998 }
1999
2000 void
2001 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
2002 {
2003         if (in_group_row_change) {
2004                 return;
2005         }
2006
2007         /* force an update of any mixer strips that are using this group,
2008            otherwise mix group names don't change in mixer strips
2009         */
2010
2011         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
2012                 if ((*i)->route_group() == group) {
2013                         (*i)->route_group_changed();
2014                 }
2015         }
2016
2017         TreeModel::iterator i;
2018         TreeModel::Children rows = group_model->children();
2019         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
2020
2021         in_group_row_change = true;
2022
2023         for (i = rows.begin(); i != rows.end(); ++i) {
2024                 if ((*i)[group_columns.group] == group) {
2025                         (*i)[group_columns.visible] = !group->is_hidden ();
2026                         (*i)[group_columns.text] = group->name ();
2027                         break;
2028                 }
2029         }
2030
2031         in_group_row_change = false;
2032
2033         if (change.contains (Properties::name)) {
2034                 _group_tabs->set_dirty ();
2035         }
2036
2037         for (list<MixerStrip*>::iterator j = strips.begin(); j != strips.end(); ++j) {
2038                 if ((*j)->route_group() == group) {
2039                         if (group->is_hidden ()) {
2040                                 hide_strip (*j);
2041                         } else {
2042                                 show_strip (*j);
2043                         }
2044                 }
2045         }
2046 }
2047
2048 void
2049 Mixer_UI::toggle_mixer_list ()
2050 {
2051         Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action ("Mixer", "ToggleMixerList");
2052         showhide_mixer_list (act->get_active());
2053 }
2054
2055 void
2056 Mixer_UI::showhide_mixer_list (bool yn)
2057 {
2058         if (yn) {
2059                 list_vpacker.show ();
2060         } else {
2061                 list_vpacker.hide ();
2062         }
2063 }
2064
2065 void
2066 Mixer_UI::toggle_monitor_section ()
2067 {
2068         Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action ("Mixer", "ToggleMonitorSection");
2069         showhide_monitor_section (act->get_active());
2070 }
2071
2072
2073 void
2074 Mixer_UI::showhide_monitor_section (bool yn)
2075 {
2076         if (monitor_section().tearoff().torn_off()) {
2077                 return;
2078         }
2079
2080         if (yn) {
2081                 monitor_section().tearoff().show();
2082         } else {
2083                 monitor_section().tearoff().hide();
2084         }
2085 }
2086
2087 void
2088 Mixer_UI::toggle_foldback_strip ()
2089 {
2090         Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action ("Mixer", "ToggleFoldbackStrip");
2091         showhide_foldback_strip (act->get_active());
2092 }
2093
2094
2095 void
2096 Mixer_UI::showhide_foldback_strip (bool yn)
2097 {
2098         _show_foldback_strip = yn;
2099
2100         if (foldback_strip) {
2101                 if (yn) {
2102                         foldback_strip->show();
2103                 } else {
2104                         foldback_strip->hide();
2105                 }
2106         }
2107 }
2108
2109 void
2110 Mixer_UI::toggle_vcas ()
2111 {
2112         Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action ("Mixer", "ToggleVCAPane");
2113         showhide_vcas (act->get_active());
2114 }
2115
2116 void
2117 Mixer_UI::showhide_vcas (bool yn)
2118 {
2119         if (yn) {
2120                 vca_vpacker.show();
2121         } else {
2122                 vca_vpacker.hide();
2123         }
2124 }
2125
2126 #ifdef MIXBUS
2127 void
2128 Mixer_UI::toggle_mixbuses ()
2129 {
2130         Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action ("Mixer", "ToggleMixbusPane");
2131         showhide_mixbuses (act->get_active());
2132 }
2133
2134 void
2135 Mixer_UI::showhide_mixbuses (bool on)
2136 {
2137         if (on) {
2138                 mb_vpacker.show();
2139         } else {
2140                 mb_vpacker.hide();
2141         }
2142 }
2143 #endif
2144
2145
2146 void
2147 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
2148 {
2149         RouteGroup* group;
2150         TreeIter iter;
2151
2152         if ((iter = group_model->get_iter (path))) {
2153
2154                 if ((group = (*iter)[group_columns.group]) == 0) {
2155                         return;
2156                 }
2157
2158                 if (new_text != group->name()) {
2159                         group->set_name (new_text);
2160                 }
2161         }
2162 }
2163
2164 void
2165 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
2166 {
2167         RouteGroup* group;
2168
2169         if (in_group_row_change) {
2170                 return;
2171         }
2172
2173         if ((group = (*iter)[group_columns.group]) == 0) {
2174                 return;
2175         }
2176
2177         std::string name = (*iter)[group_columns.text];
2178
2179         if (name != group->name()) {
2180                 group->set_name (name);
2181         }
2182
2183         bool hidden = !(*iter)[group_columns.visible];
2184
2185         if (hidden != group->is_hidden ()) {
2186                 group->set_hidden (hidden, this);
2187         }
2188 }
2189
2190 /** Called when a group model row is deleted, but also when the model is
2191  *  reordered by a user drag-and-drop; the latter is what we are
2192  *  interested in here.
2193  */
2194 void
2195 Mixer_UI::route_group_row_deleted (Gtk::TreeModel::Path const &)
2196 {
2197         if (_in_group_rebuild_or_clear) {
2198                 return;
2199         }
2200
2201         /* Re-write the session's route group list so that the new order is preserved */
2202
2203         list<RouteGroup*> new_list;
2204
2205         Gtk::TreeModel::Children children = group_model->children();
2206         for (Gtk::TreeModel::Children::iterator i = children.begin(); i != children.end(); ++i) {
2207                 RouteGroup* g = (*i)[group_columns.group];
2208                 if (g) {
2209                         new_list.push_back (g);
2210                 }
2211         }
2212
2213         _session->reorder_route_groups (new_list);
2214 }
2215
2216
2217 void
2218 Mixer_UI::add_route_group (RouteGroup* group)
2219 {
2220         ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
2221         bool focus = false;
2222
2223         in_group_row_change = true;
2224
2225         TreeModel::Row row = *(group_model->append());
2226         row[group_columns.visible] = !group->is_hidden ();
2227         row[group_columns.group] = group;
2228         if (!group->name().empty()) {
2229                 row[group_columns.text] = group->name();
2230         } else {
2231                 row[group_columns.text] = _("unnamed");
2232                 focus = true;
2233         }
2234
2235         group->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
2236
2237         if (focus) {
2238                 TreeViewColumn* col = group_display.get_column (0);
2239                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (1));
2240                 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
2241         }
2242
2243         _group_tabs->set_dirty ();
2244
2245         in_group_row_change = false;
2246 }
2247
2248 bool
2249 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
2250 {
2251         using namespace Menu_Helpers;
2252
2253         if (Keyboard::is_context_menu_event (ev)) {
2254                 ARDOUR_UI::instance()->add_route ();
2255                 return true;
2256         }
2257
2258         return false;
2259 }
2260
2261 void
2262 Mixer_UI::scroller_drag_data_received (const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& data, guint info, guint time)
2263 {
2264         if (data.get_target() != "PluginFavoritePtr") {
2265                 context->drag_finish (false, false, time);
2266                 return;
2267         }
2268
2269         const void * d = data.get_data();
2270         const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>* tv = reinterpret_cast<const Gtkmm2ext::DnDTreeView<ARDOUR::PluginPresetPtr>*>(d);
2271
2272         PluginPresetList nfos;
2273         TreeView* source;
2274         tv->get_object_drag_data (nfos, &source);
2275
2276         Route::ProcessorList pl;
2277         bool ok = false;
2278
2279         for (list<PluginPresetPtr>::const_iterator i = nfos.begin(); i != nfos.end(); ++i) {
2280                 PluginPresetPtr ppp = (*i);
2281                 PluginInfoPtr pip = ppp->_pip;
2282                 if (!pip->is_instrument ()) {
2283                         continue;
2284                 }
2285                 ARDOUR_UI::instance()->session_add_midi_route (true, (RouteGroup*) 0, 1, _("MIDI"), Config->get_strict_io (), pip, ppp->_preset.valid ? &ppp->_preset : 0, PresentationInfo::max_order);
2286                 ok = true;
2287         }
2288
2289         context->drag_finish (ok, false, time);
2290 }
2291
2292 void
2293 Mixer_UI::set_strip_width (Width w, bool save)
2294 {
2295         _strip_width = w;
2296
2297         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2298                 (*i)->set_width_enum (w, save ? (*i)->width_owner() : this);
2299         }
2300 }
2301
2302
2303 struct PluginStateSorter {
2304 public:
2305         bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
2306                 std::list<std::string>::const_iterator aiter = std::find(_user.begin(), _user.end(), (*a).unique_id);
2307                 std::list<std::string>::const_iterator biter = std::find(_user.begin(), _user.end(), (*b).unique_id);
2308                 if (aiter != _user.end() && biter != _user.end()) {
2309                         return std::distance (_user.begin(), aiter)  < std::distance (_user.begin(), biter);
2310                 }
2311                 if (aiter != _user.end()) {
2312                         return true;
2313                 }
2314                 if (biter != _user.end()) {
2315                         return false;
2316                 }
2317                 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
2318         }
2319
2320         PluginStateSorter(std::list<std::string> user) : _user (user)  {}
2321 private:
2322         std::list<std::string> _user;
2323 };
2324
2325 int
2326 Mixer_UI::set_state (const XMLNode& node, int version)
2327 {
2328         bool yn;
2329
2330         Tabbable::set_state (node, version);
2331
2332         if (node.get_property ("narrow-strips", yn)) {
2333                 if (yn) {
2334                         set_strip_width (Narrow);
2335                 } else {
2336                         set_strip_width (Wide);
2337                 }
2338         }
2339
2340         node.get_property ("show-mixer", _visible);
2341
2342         yn = false;
2343         node.get_property ("maximised", yn);
2344         {
2345                 Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action (X_("Common"), X_("ToggleMaximalMixer"));
2346                 bool fs = act && act->get_active();
2347                 if (yn ^ fs) {
2348                         ActionManager::do_action ("Common", "ToggleMaximalMixer");
2349                 }
2350         }
2351
2352         yn = true;
2353         node.get_property ("show-mixer-list", yn);
2354         {
2355                 Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action (X_("Mixer"), X_("ToggleMixerList"));
2356                 /* do it twice to force the change */
2357                 act->set_active (!yn);
2358                 act->set_active (yn);
2359         }
2360
2361         yn = true;
2362         node.get_property ("monitor-section-visible", yn);
2363         {
2364                 Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action (X_("Mixer"), X_("ToggleMonitorSection"));
2365                 /* do it twice to force the change */
2366                 act->set_active (!yn);
2367                 act->set_active (yn);
2368         }
2369
2370         yn = true;
2371         node.get_property ("foldback-strip-visible", yn);
2372         {
2373                 Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action (X_("Mixer"), X_("ToggleFoldbackStrip"));
2374                 /* do it twice to force the change */
2375                 act->set_active (!yn);
2376                 act->set_active (yn);
2377         }
2378
2379         yn = true;
2380         node.get_property ("show-vca-pane", yn);
2381         {
2382                 Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action (X_("Mixer"), X_("ToggleVCAPane"));
2383                 /* do it twice to force the change */
2384                 act->set_active (!yn);
2385                 act->set_active (yn);
2386         }
2387
2388 #ifdef MIXBUS
2389         yn = true;
2390         node.get_property ("show-mixbus-pane", yn);
2391         {
2392                 Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action (X_("Mixer"), X_("ToggleMixbusPane"));
2393                 /* do it twice to force the change */
2394                 act->set_active (!yn);
2395                 act->set_active (yn);
2396         }
2397 #endif
2398
2399         //check for the user's plugin_order file
2400         XMLNode plugin_order_new(X_("PO"));
2401         if (PluginManager::instance().load_plugin_order_file(plugin_order_new)) {
2402                 store_current_favorite_order ();
2403                 std::list<string> order;
2404                 const XMLNodeList& kids = plugin_order_new.children("PluginInfo");
2405                 XMLNodeConstIterator i;
2406                 for (i = kids.begin(); i != kids.end(); ++i) {
2407                         std::string unique_id;
2408                         if ((*i)->get_property ("unique-id", unique_id)) {
2409                                 order.push_back (unique_id);
2410                                 if ((*i)->get_property ("expanded", yn)) {
2411                                         favorite_ui_state[unique_id] = yn;
2412                                 }
2413                         }
2414                 }
2415                 PluginStateSorter cmp (order);
2416                 favorite_order.sort (cmp);
2417                 sync_treeview_from_favorite_order ();
2418
2419         } else {
2420                 //if there is no user file, then use an existing one from instant.xml
2421                 //NOTE: if you are loading an old session, this might come from the session's instant.xml
2422                 //Todo:  in the next major version, we should probably stop doing the instant.xml check, and just use the new file
2423                 XMLNode* plugin_order;
2424                 if ((plugin_order = find_named_node (node, "PluginOrder")) != 0) {
2425                         store_current_favorite_order ();
2426                         std::list<string> order;
2427                         const XMLNodeList& kids = plugin_order->children("PluginInfo");
2428                         XMLNodeConstIterator i;
2429                         for (i = kids.begin(); i != kids.end(); ++i) {
2430                                 std::string unique_id;
2431                                 if ((*i)->get_property ("unique-id", unique_id)) {
2432                                         order.push_back (unique_id);
2433                                         if ((*i)->get_property ("expanded", yn)) {
2434                                                 favorite_ui_state[unique_id] = yn;
2435                                         }
2436                                 }
2437                         }
2438
2439                         PluginStateSorter cmp (order);
2440                         favorite_order.sort (cmp);
2441                         sync_treeview_from_favorite_order ();
2442                 }
2443         }
2444
2445         return 0;
2446 }
2447
2448 void
2449 Mixer_UI::save_plugin_order_file ()
2450 {
2451         //this writes the plugin order to the user's preference file ( plugin_metadata/plugin_order )
2452
2453         //NOTE:  this replaces the old code that stores info in instant.xml
2454         //why?  because instant.xml prefers the per-session settings, and we want this to be a global pref
2455
2456         store_current_favorite_order ();
2457         XMLNode plugin_order ("PluginOrder");
2458         uint32_t cnt = 0;
2459         for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i, ++cnt) {
2460                 XMLNode* p = new XMLNode ("PluginInfo");
2461                 p->set_property ("sort", cnt);
2462                 p->set_property ("unique-id", (*i)->unique_id);
2463                 if (favorite_ui_state.find ((*i)->unique_id) != favorite_ui_state.end ()) {
2464                         p->set_property ("expanded", favorite_ui_state[(*i)->unique_id]);
2465                 }
2466                 plugin_order.add_child_nocopy (*p);
2467         }
2468         PluginManager::instance().save_plugin_order_file( plugin_order );
2469 }
2470
2471 XMLNode&
2472 Mixer_UI::get_state ()
2473 {
2474         XMLNode* node = new XMLNode (X_("Mixer"));
2475
2476         node->add_child_nocopy (Tabbable::get_state());
2477
2478         node->set_property (X_("mixer-rhs-pane1-pos"), rhs_pane1.get_divider());
2479         node->set_property (X_("mixer-rhs_pane2-pos"), rhs_pane2.get_divider());
2480         node->set_property (X_("mixer-list-hpane-pos"), list_hpane.get_divider());
2481         node->set_property (X_("mixer-inner-pane-pos"),  inner_pane.get_divider());
2482
2483         node->set_property ("narrow-strips", (_strip_width == Narrow));
2484         node->set_property ("show-mixer", _visible);
2485         node->set_property ("maximised", _maximised);
2486
2487         Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action ("Mixer", "ToggleMixerList");
2488         node->set_property ("show-mixer-list", act->get_active ());
2489
2490         act = ActionManager::get_toggle_action ("Mixer", "ToggleMonitorSection");
2491         node->set_property ("monitor-section-visible", act->get_active ());
2492
2493         act = ActionManager::get_toggle_action ("Mixer", "ToggleFoldbackStrip");
2494         node->set_property ("foldback-strip-visible", act->get_active ());
2495
2496         act = ActionManager::get_toggle_action ("Mixer", "ToggleVCAPane");
2497         node->set_property ("show-vca-pane", act->get_active ());
2498
2499 #ifdef MIXBUS
2500         act = ActionManager::get_toggle_action ("Mixer", "ToggleMixbusPane");
2501         node->set_property ("show-mixbus-pane", act->get_active ());
2502 #endif
2503
2504         return *node;
2505 }
2506
2507 void
2508 Mixer_UI::scroll_left ()
2509 {
2510         if (!scroller.get_hscrollbar()) return;
2511         Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2512         int sc_w = scroller.get_width();
2513         int sp_w = strip_packer.get_width();
2514         if (sp_w <= sc_w) {
2515                 return;
2516         }
2517         int lp = adj->get_value();
2518         int lm = 0;
2519         using namespace Gtk::Box_Helpers;
2520         const BoxList& strips = strip_packer.children();
2521         for (BoxList::const_iterator i = strips.begin(); i != strips.end(); ++i) {
2522                 if (i->get_widget() == & add_button) {
2523                         continue;
2524                 }
2525 #ifdef MIXBUS
2526                 if (i->get_widget() == &mb_shadow) {
2527                         continue;
2528                 }
2529 #endif
2530                 lm += i->get_widget()->get_width ();
2531                 if (lm >= lp) {
2532                         lm -= i->get_widget()->get_width ();
2533                         break;
2534                 }
2535         }
2536         scroller.get_hscrollbar()->set_value (max (adj->get_lower(), min (adj->get_upper(), lm - 1.0)));
2537 }
2538
2539 void
2540 Mixer_UI::scroll_right ()
2541 {
2542         if (!scroller.get_hscrollbar()) return;
2543         Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
2544         int sc_w = scroller.get_width();
2545         int sp_w = strip_packer.get_width();
2546         if (sp_w <= sc_w) {
2547                 return;
2548         }
2549         int lp = adj->get_value();
2550         int lm = 0;
2551         using namespace Gtk::Box_Helpers;
2552         const BoxList& strips = strip_packer.children();
2553         for (BoxList::const_iterator i = strips.begin(); i != strips.end(); ++i) {
2554                 if (i->get_widget() == & add_button) {
2555                         continue;
2556                 }
2557 #ifdef MIXBUS
2558                 if (i->get_widget() == &mb_shadow) {
2559                         continue;
2560                 }
2561 #endif
2562                 lm += i->get_widget()->get_width ();
2563                 if (lm > lp + 1) {
2564                         break;
2565                 }
2566         }
2567         scroller.get_hscrollbar()->set_value (max (adj->get_lower(), min (adj->get_upper(), lm - 1.0)));
2568 }
2569
2570 bool
2571 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
2572 {
2573         switch (ev->direction) {
2574         case GDK_SCROLL_LEFT:
2575                 scroll_left ();
2576                 return true;
2577         case GDK_SCROLL_UP:
2578                 if (ev->state & Keyboard::TertiaryModifier) {
2579                         scroll_left ();
2580                         return true;
2581                 }
2582                 return false;
2583
2584         case GDK_SCROLL_RIGHT:
2585                 scroll_right ();
2586                 return true;
2587
2588         case GDK_SCROLL_DOWN:
2589                 if (ev->state & Keyboard::TertiaryModifier) {
2590                         scroll_right ();
2591                         return true;
2592                 }
2593                 return false;
2594         }
2595
2596         return false;
2597 }
2598
2599 void
2600 Mixer_UI::vca_scroll_left ()
2601 {
2602         if (!vca_scroller.get_hscrollbar()) return;
2603         Adjustment* adj = vca_scroller.get_hscrollbar()->get_adjustment();
2604         int sc_w = vca_scroller.get_width();
2605         int sp_w = strip_packer.get_width();
2606         if (sp_w <= sc_w) {
2607                 return;
2608         }
2609         int lp = adj->get_value();
2610         int lm = 0;
2611         using namespace Gtk::Box_Helpers;
2612         const BoxList& strips = vca_hpacker.children();
2613         for (BoxList::const_iterator i = strips.begin(); i != strips.end(); ++i) {
2614                 if (i->get_widget() == &add_vca_button) {
2615                         continue;
2616                 }
2617                 lm += i->get_widget()->get_width ();
2618                 if (lm >= lp) {
2619                         lm -= i->get_widget()->get_width ();
2620                         break;
2621                 }
2622         }
2623         vca_scroller.get_hscrollbar()->set_value (max (adj->get_lower(), min (adj->get_upper(), lm - 1.0)));
2624 }
2625
2626 void
2627 Mixer_UI::vca_scroll_right ()
2628 {
2629         if (!vca_scroller.get_hscrollbar()) return;
2630         Adjustment* adj = vca_scroller.get_hscrollbar()->get_adjustment();
2631         int sc_w = vca_scroller.get_width();
2632         int sp_w = strip_packer.get_width();
2633         if (sp_w <= sc_w) {
2634                 return;
2635         }
2636         int lp = adj->get_value();
2637         int lm = 0;
2638         using namespace Gtk::Box_Helpers;
2639         const BoxList& strips = vca_hpacker.children();
2640         for (BoxList::const_iterator i = strips.begin(); i != strips.end(); ++i) {
2641                 if (i->get_widget() == &add_vca_button) {
2642                         continue;
2643                 }
2644                 lm += i->get_widget()->get_width ();
2645                 if (lm > lp + 1) {
2646                         break;
2647                 }
2648         }
2649         vca_scroller.get_hscrollbar()->set_value (max (adj->get_lower(), min (adj->get_upper(), lm - 1.0)));
2650 }
2651
2652 bool
2653 Mixer_UI::on_vca_scroll_event (GdkEventScroll* ev)
2654 {
2655         switch (ev->direction) {
2656         case GDK_SCROLL_LEFT:
2657                 vca_scroll_left ();
2658                 return true;
2659         case GDK_SCROLL_UP:
2660                 if (ev->state & Keyboard::TertiaryModifier) {
2661                         vca_scroll_left ();
2662                         return true;
2663                 }
2664                 return false;
2665
2666         case GDK_SCROLL_RIGHT:
2667                 vca_scroll_right ();
2668                 return true;
2669
2670         case GDK_SCROLL_DOWN:
2671                 if (ev->state & Keyboard::TertiaryModifier) {
2672                         vca_scroll_right ();
2673                         return true;
2674                 }
2675                 return false;
2676         }
2677
2678         return false;
2679 }
2680
2681 void
2682 Mixer_UI::parameter_changed (string const & p)
2683 {
2684         if (p == "show-group-tabs") {
2685                 bool const s = _session->config.get_show_group_tabs ();
2686                 if (s) {
2687                         _group_tabs->show ();
2688                 } else {
2689                         _group_tabs->hide ();
2690                 }
2691         } else if (p == "default-narrow_ms") {
2692                 bool const s = UIConfiguration::instance().get_default_narrow_ms ();
2693                 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2694                         (*i)->set_width_enum (s ? Narrow : Wide, this);
2695                 }
2696         }
2697 }
2698
2699 void
2700 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
2701 {
2702         g->set_active (a, this);
2703 }
2704
2705 PluginSelector*
2706 Mixer_UI::plugin_selector()
2707 {
2708 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
2709         if (!_plugin_selector)
2710                 _plugin_selector = new PluginSelector (PluginManager::instance());
2711 #endif
2712
2713         return _plugin_selector;
2714 }
2715
2716 void
2717 Mixer_UI::setup_track_display ()
2718 {
2719         track_model = ListStore::create (stripable_columns);
2720         track_display.set_model (track_model);
2721         track_display.append_column (_("Show"), stripable_columns.visible);
2722         track_display.append_column (_("Strips"), stripable_columns.text);
2723         track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
2724         track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
2725         track_display.get_column (0)->set_expand(false);
2726         track_display.get_column (1)->set_expand(true);
2727         track_display.get_column (1)->set_sizing (Gtk::TREE_VIEW_COLUMN_FIXED);
2728         track_display.set_name (X_("EditGroupList"));
2729         track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
2730         track_display.set_reorderable (true);
2731         track_display.set_headers_visible (true);
2732         track_display.set_can_focus(false);
2733
2734         track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
2735         track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
2736
2737         CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (0));
2738         track_list_visible_cell->property_activatable() = true;
2739         track_list_visible_cell->property_radio() = false;
2740         track_list_visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::track_visibility_changed));
2741
2742         track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
2743
2744         track_display_scroller.add (track_display);
2745         track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
2746
2747         VBox* v = manage (new VBox);
2748         v->show ();
2749         v->pack_start (track_display_scroller, true, true);
2750
2751         track_display_frame.set_name("BaseFrame");
2752         track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
2753         track_display_frame.add (*v);
2754
2755         track_display_scroller.show();
2756         track_display_frame.show();
2757         track_display.show();
2758 }
2759
2760 void
2761 Mixer_UI::new_track_or_bus ()
2762 {
2763         ARDOUR_UI::instance()->add_route ();
2764 }
2765
2766 void
2767 Mixer_UI::update_title ()
2768 {
2769         if (!own_window()) {
2770                 return;
2771         }
2772
2773         if (_session) {
2774                 string n;
2775
2776                 if (_session->snap_name() != _session->name()) {
2777                         n = _session->snap_name ();
2778                 } else {
2779                         n = _session->name ();
2780                 }
2781
2782                 if (_session->dirty ()) {
2783                         n = "*" + n;
2784                 }
2785
2786                 WindowTitle title (n);
2787                 title += S_("Window|Mixer");
2788                 title += Glib::get_application_name ();
2789                 own_window()->set_title (title.get_string());
2790
2791         } else {
2792
2793                 WindowTitle title (S_("Window|Mixer"));
2794                 title += Glib::get_application_name ();
2795                 own_window()->set_title (title.get_string());
2796         }
2797 }
2798
2799 MixerStrip*
2800 Mixer_UI::strip_by_x (int x)
2801 {
2802         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
2803                 int x1, x2, y;
2804
2805                 (*i)->translate_coordinates (_content, 0, 0, x1, y);
2806                 x2 = x1 + (*i)->get_width();
2807
2808                 if (x >= x1 && x <= x2) {
2809                         return (*i);
2810                 }
2811         }
2812
2813         return 0;
2814 }
2815
2816 void
2817 Mixer_UI::set_axis_targets_for_operation ()
2818 {
2819         _axis_targets.clear ();
2820
2821         if (!_selection.empty()) {
2822                 _axis_targets = _selection.axes;
2823                 return;
2824         }
2825
2826 //  removed "implicit" selections of strips, after discussion on IRC
2827
2828 }
2829
2830 void
2831 Mixer_UI::monitor_section_going_away ()
2832 {
2833         XMLNode* ui_node = Config->extra_xml(X_("UI"));
2834
2835         /* immediate state save.
2836          *
2837          * Tearoff settings are otherwise only stored during
2838          * save_ardour_state(). The mon-section may or may not
2839          * exist at that point.
2840          */
2841
2842         if (ui_node) {
2843                 XMLNode* tearoff_node = ui_node->child (X_("Tearoffs"));
2844                 if (tearoff_node) {
2845                         tearoff_node->remove_nodes_and_delete (X_("monitor-section"));
2846                         XMLNode* t = new XMLNode (X_("monitor-section"));
2847                         _monitor_section.tearoff().add_state (*t);
2848                         tearoff_node->add_child_nocopy (*t);
2849                 }
2850         }
2851
2852         monitor_section_detached ();
2853         out_packer.remove (_monitor_section.tearoff());
2854 }
2855
2856 void
2857 Mixer_UI::toggle_midi_input_active (bool flip_others)
2858 {
2859         boost::shared_ptr<RouteList> rl (new RouteList);
2860         bool onoff = false;
2861
2862         set_axis_targets_for_operation ();
2863
2864         for (AxisViewSelection::iterator r = _axis_targets.begin(); r != _axis_targets.end(); ++r) {
2865                 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> ((*r)->stripable());
2866
2867                 if (mt) {
2868                         rl->push_back (mt);
2869                         onoff = !mt->input_active();
2870                 }
2871         }
2872
2873         _session->set_exclusive_input_active (rl, onoff, flip_others);
2874 }
2875
2876 void
2877 Mixer_UI::maximise_mixer_space ()
2878 {
2879         if (!own_window()) {
2880                 return;
2881         }
2882
2883         if (_maximised) {
2884                 return;
2885         }
2886
2887         _window->fullscreen ();
2888         _maximised = true;
2889 }
2890
2891 void
2892 Mixer_UI::restore_mixer_space ()
2893 {
2894         if (!own_window()) {
2895                 return;
2896         }
2897
2898         if (!_maximised) {
2899                 return;
2900         }
2901
2902         own_window()->unfullscreen();
2903         _maximised = false;
2904 }
2905
2906 void
2907 Mixer_UI::monitor_section_attached ()
2908 {
2909         Glib::RefPtr<ToggleAction> act = ActionManager::get_toggle_action ("Mixer", "ToggleMonitorSection");
2910         act->set_sensitive (true);
2911         showhide_monitor_section (act->get_active ());
2912 }
2913
2914 void
2915 Mixer_UI::monitor_section_detached ()
2916 {
2917         Glib::RefPtr<Action> act = ActionManager::get_action ("Mixer", "ToggleMonitorSection");
2918         act->set_sensitive (false);
2919 }
2920
2921 void
2922 Mixer_UI::store_current_favorite_order ()
2923 {
2924         typedef Gtk::TreeModel::Children type_children;
2925         type_children children = favorite_plugins_model->children();
2926         favorite_order.clear();
2927         for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
2928         {
2929                 Gtk::TreeModel::Row row = *iter;
2930                 ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2931                 favorite_order.push_back (ppp->_pip);
2932                 std::string name = row[favorite_plugins_columns.name];
2933                 favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2934         }
2935 }
2936
2937 void
2938 Mixer_UI::save_favorite_ui_state (const TreeModel::iterator& iter, const TreeModel::Path& path)
2939 {
2940         Gtk::TreeModel::Row row = *iter;
2941         ARDOUR::PluginPresetPtr ppp = row[favorite_plugins_columns.plugin];
2942         assert (ppp);
2943         favorite_ui_state[(*ppp->_pip).unique_id] = favorite_plugins_display.row_expanded (favorite_plugins_model->get_path(iter));
2944 }
2945
2946 void
2947 Mixer_UI::refiller (PluginInfoList& result, const PluginInfoList& plugs)
2948 {
2949         PluginManager& manager (PluginManager::instance());
2950         for (PluginInfoList::const_iterator i = plugs.begin(); i != plugs.end(); ++i) {
2951
2952                 /* not a Favorite? skip it */
2953                 if (manager.get_status (*i) != PluginManager::Favorite) {
2954                         continue;
2955                 }
2956
2957                 /* Check the tag combo selection, and skip this plugin if it doesn't match the selected tag(s) */
2958                 string test = favorite_plugins_tag_combo.get_active_text();
2959                 if (test != _("Show All")) {
2960                         vector<string> tags = manager.get_tags(*i);
2961
2962                         //does the selected tag match any of the tags in the plugin?
2963                         vector<string>::iterator tt =  find (tags.begin(), tags.end(), test);
2964                         if (tt == tags.end()) {
2965                                 continue;
2966                         }
2967                 }
2968
2969                 result.push_back (*i);
2970         }
2971 }
2972
2973 struct PluginCustomSorter {
2974 public:
2975         bool operator() (PluginInfoPtr a, PluginInfoPtr b) const {
2976                 PluginInfoList::const_iterator aiter = _user.begin();
2977                 PluginInfoList::const_iterator biter = _user.begin();
2978                 while (aiter != _user.end()) { if ((*aiter)->unique_id == a->unique_id) { break; } ++aiter; }
2979                 while (biter != _user.end()) { if ((*biter)->unique_id == b->unique_id) { break; } ++biter; }
2980
2981                 if (aiter != _user.end() && biter != _user.end()) {
2982                         return std::distance (_user.begin(), aiter) < std::distance (_user.begin(), biter);
2983                 }
2984                 if (aiter != _user.end()) {
2985                         return true;
2986                 }
2987                 if (biter != _user.end()) {
2988                         return false;
2989                 }
2990                 return ARDOUR::cmp_nocase((*a).name, (*b).name) == -1;
2991         }
2992         PluginCustomSorter(PluginInfoList user) : _user (user)  {}
2993 private:
2994         PluginInfoList _user;
2995 };
2996
2997 void
2998 Mixer_UI::refill_favorite_plugins ()
2999 {
3000         PluginInfoList plugs;
3001         PluginManager& mgr (PluginManager::instance());
3002
3003 #ifdef LV2_SUPPORT
3004         refiller (plugs, mgr.lv2_plugin_info ());
3005 #endif
3006 #ifdef WINDOWS_VST_SUPPORT
3007         refiller (plugs, mgr.windows_vst_plugin_info ());
3008 #endif
3009 #ifdef LXVST_SUPPORT
3010         refiller (plugs, mgr.lxvst_plugin_info ());
3011 #endif
3012 #ifdef MACVST_SUPPORT
3013         refiller (plugs, mgr.mac_vst_plugin_info ());
3014 #endif
3015 #ifdef AUDIOUNIT_SUPPORT
3016         refiller (plugs, mgr.au_plugin_info ());
3017 #endif
3018         refiller (plugs, mgr.ladspa_plugin_info ());
3019         refiller (plugs, mgr.lua_plugin_info ());
3020
3021         store_current_favorite_order ();
3022
3023         PluginCustomSorter cmp (favorite_order);
3024         plugs.sort (cmp);
3025
3026         favorite_order = plugs;
3027
3028         sync_treeview_from_favorite_order ();
3029 }
3030
3031 void
3032 Mixer_UI::plugin_list_changed ()
3033 {
3034         refill_favorite_plugins();
3035         refill_tag_combo();
3036 }
3037
3038 void
3039 Mixer_UI::refill_tag_combo ()
3040 {
3041         PluginManager& mgr (PluginManager::instance());
3042
3043         std::vector<std::string> tags = mgr.get_all_tags (PluginManager::OnlyFavorites);
3044
3045         favorite_plugins_tag_combo.clear();
3046         favorite_plugins_tag_combo.append_text (_("Show All"));
3047
3048         for (vector<string>::iterator t = tags.begin (); t != tags.end (); ++t) {
3049                 favorite_plugins_tag_combo.append_text (*t);
3050         }
3051
3052         favorite_plugins_tag_combo.set_active_text (_("Show All"));
3053 }
3054
3055 void
3056 Mixer_UI::sync_treeview_favorite_ui_state (const TreeModel::Path& path, const TreeModel::iterator&)
3057 {
3058         TreeIter iter;
3059         if (!(iter = favorite_plugins_model->get_iter (path))) {
3060                 return;
3061         }
3062         ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
3063         if (!ppp) {
3064                 return;
3065         }
3066         PluginInfoPtr pip = ppp->_pip;
3067         if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
3068                 if (favorite_ui_state[pip->unique_id]) {
3069                         favorite_plugins_display.expand_row (path, true);
3070                 }
3071         }
3072 }
3073
3074 void
3075 Mixer_UI::sync_treeview_from_favorite_order ()
3076 {
3077         favorite_plugins_model->clear ();
3078         for (PluginInfoList::const_iterator i = favorite_order.begin(); i != favorite_order.end(); ++i) {
3079                 PluginInfoPtr pip = (*i);
3080
3081                 TreeModel::Row newrow = *(favorite_plugins_model->append());
3082                 newrow[favorite_plugins_columns.name] = (*i)->name;
3083                 newrow[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip));
3084                 if (!_session) {
3085                         continue;
3086                 }
3087
3088                 vector<ARDOUR::Plugin::PresetRecord> presets = (*i)->get_presets (true);
3089                 for (vector<ARDOUR::Plugin::PresetRecord>::const_iterator j = presets.begin(); j != presets.end(); ++j) {
3090                         if (!(*j).user) {
3091                                 continue;
3092                         }
3093                         Gtk::TreeModel::Row child_row = *(favorite_plugins_model->append (newrow.children()));
3094                         child_row[favorite_plugins_columns.name] = (*j).label;
3095                         child_row[favorite_plugins_columns.plugin] = PluginPresetPtr (new PluginPreset(pip, &(*j)));
3096                 }
3097                 if (favorite_ui_state.find (pip->unique_id) != favorite_ui_state.end ()) {
3098                         if (favorite_ui_state[pip->unique_id]) {
3099                                 favorite_plugins_display.expand_row (favorite_plugins_model->get_path(newrow), true);
3100                         }
3101                 }
3102         }
3103 }
3104
3105 void
3106 Mixer_UI::popup_note_context_menu (GdkEventButton *ev)
3107 {
3108         using namespace Gtk::Menu_Helpers;
3109
3110         Gtk::Menu* m = ARDOUR_UI::instance()->shared_popup_menu ();
3111         MenuList& items = m->items ();
3112
3113         if (_selection.axes.empty()) {
3114                 items.push_back (MenuElem (_("No Track/Bus is selected.")));
3115         } else {
3116                 items.push_back (MenuElem (_("Add at the top"),
3117                                         sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddTop)));
3118                 items.push_back (MenuElem (_("Add Pre-Fader"),
3119                                         sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPreFader)));
3120                 items.push_back (MenuElem (_("Add Post-Fader"),
3121                                         sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddPostFader)));
3122                 items.push_back (MenuElem (_("Add at the end"),
3123                                         sigc::bind (sigc::mem_fun (*this, &Mixer_UI::add_selected_processor), AddBottom)));
3124         }
3125
3126         items.push_back (SeparatorElem());
3127
3128         items.push_back (MenuElem (_("Remove from favorites"), sigc::mem_fun (*this, &Mixer_UI::remove_selected_from_favorites)));
3129
3130         ARDOUR::PluginPresetPtr ppp = selected_plugin();
3131         if (ppp && ppp->_preset.valid && ppp->_preset.user) {
3132                 // we cannot currently delete AU presets
3133                 if (!ppp->_pip || ppp->_pip->type != AudioUnit) {
3134                         items.push_back (MenuElem (_("Delete Preset"), sigc::mem_fun (*this, &Mixer_UI::delete_selected_preset)));
3135                 }
3136         }
3137
3138         m->popup (ev->button, ev->time);
3139 }
3140
3141 bool
3142 Mixer_UI::plugin_row_button_press (GdkEventButton *ev)
3143 {
3144         if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3)) {
3145                 TreeModel::Path path;
3146                 TreeViewColumn* column;
3147                 int cellx, celly;
3148                 if (favorite_plugins_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
3149                         Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
3150                         if (selection) {
3151                                 selection->unselect_all();
3152                                 selection->select(path);
3153                         }
3154                 }
3155                 ARDOUR::PluginPresetPtr ppp = selected_plugin();
3156                 if (ppp) {
3157                         popup_note_context_menu (ev);
3158                 }
3159         }
3160         return false;
3161 }
3162
3163
3164 PluginPresetPtr
3165 Mixer_UI::selected_plugin ()
3166 {
3167         Glib::RefPtr<Gtk::TreeView::Selection> selection = favorite_plugins_display.get_selection();
3168         if (!selection) {
3169                 return PluginPresetPtr();
3170         }
3171         Gtk::TreeModel::iterator iter = selection->get_selected();
3172         if (!iter) {
3173                 return PluginPresetPtr();
3174         }
3175         return (*iter)[favorite_plugins_columns.plugin];
3176 }
3177
3178 void
3179 Mixer_UI::add_selected_processor (ProcessorPosition pos)
3180 {
3181         ARDOUR::PluginPresetPtr ppp = selected_plugin();
3182         if (ppp) {
3183                 add_favorite_processor (ppp, pos);
3184         }
3185 }
3186
3187 void
3188 Mixer_UI::delete_selected_preset ()
3189 {
3190         if (!_session) {
3191                 return;
3192         }
3193         ARDOUR::PluginPresetPtr ppp = selected_plugin();
3194         if (!ppp || !ppp->_preset.valid || !ppp->_preset.user) {
3195                 return;
3196         }
3197         PluginPtr plugin = ppp->_pip->load (*_session);
3198         plugin->get_presets();
3199         plugin->remove_preset (ppp->_preset.label);
3200 }
3201
3202 void
3203 Mixer_UI::remove_selected_from_favorites ()
3204 {
3205         ARDOUR::PluginPresetPtr ppp = selected_plugin();
3206         if (!ppp) {
3207                 return;
3208         }
3209         PluginManager::PluginStatusType status = PluginManager::Normal;
3210         PluginManager& manager (PluginManager::instance());
3211
3212         manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
3213         manager.save_statuses ();
3214 }
3215
3216 void
3217 Mixer_UI::plugin_row_activated (const TreeModel::Path& path, TreeViewColumn* column)
3218 {
3219         TreeIter iter;
3220         if (!(iter = favorite_plugins_model->get_iter (path))) {
3221                 return;
3222         }
3223         ARDOUR::PluginPresetPtr ppp = (*iter)[favorite_plugins_columns.plugin];
3224         add_favorite_processor (ppp, AddPreFader); // TODO: preference?!
3225 }
3226
3227 void
3228 Mixer_UI::add_favorite_processor (ARDOUR::PluginPresetPtr ppp, ProcessorPosition pos)
3229 {
3230         if (!_session || _selection.axes.empty()) {
3231                 return;
3232         }
3233
3234         PluginInfoPtr pip = ppp->_pip;
3235         for (AxisViewSelection::iterator i = _selection.axes.begin(); i != _selection.axes.end(); ++i) {
3236                 boost::shared_ptr<ARDOUR::Route> rt = boost::dynamic_pointer_cast<ARDOUR::Route> ((*i)->stripable());
3237
3238                 if (!rt) {
3239                         continue;
3240                 }
3241
3242                 PluginPtr p = pip->load (*_session);
3243
3244                 if (!p) {
3245                         continue;
3246                 }
3247
3248                 if (ppp->_preset.valid) {
3249                         p->load_preset (ppp->_preset);
3250                 }
3251
3252                 Route::ProcessorStreams err;
3253                 boost::shared_ptr<Processor> processor (new PluginInsert (*_session, p));
3254
3255                 switch (pos) {
3256                         case AddTop:
3257                                 rt->add_processor_by_index (processor, 0, &err, Config->get_new_plugins_active ());
3258                                 break;
3259                         case AddPreFader:
3260                                 rt->add_processor (processor, PreFader, &err, Config->get_new_plugins_active ());
3261                                 break;
3262                         case AddPostFader:
3263                                 {
3264                                         int idx = 0;
3265                                         int pos = 0;
3266                                         for (;;++idx) {
3267                                                 boost::shared_ptr<Processor> np = rt->nth_processor (idx);
3268                                                 if (!np) {
3269                                                         break;
3270                                                 }
3271                                                 if (!np->display_to_user()) {
3272                                                         continue;
3273                                                 }
3274                                                 if (boost::dynamic_pointer_cast<Amp> (np) && // Fader, not Trim
3275                                                                 boost::dynamic_pointer_cast<Amp> (np)->gain_control()->parameter().type() == GainAutomation) {
3276                                                         break;
3277                                                 }
3278                                                 ++pos;
3279                                         }
3280                                         rt->add_processor_by_index (processor, ++pos, &err, Config->get_new_plugins_active ());
3281                                 }
3282                                 break;
3283                         case AddBottom:
3284                                 rt->add_processor_by_index (processor, -1, &err, Config->get_new_plugins_active ());
3285                                 break;
3286                 }
3287         }
3288 }
3289
3290 bool
3291 PluginTreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& data) const
3292 {
3293         if (data.get_target() != "GTK_TREE_MODEL_ROW") {
3294                 return false;
3295         }
3296
3297         // only allow to re-order top-level items
3298         TreePath src;
3299         if (TreePath::get_from_selection_data (data, src)) {
3300                 if (src.up() && src.up()) {
3301                         return false;
3302                 }
3303         }
3304
3305         // don't allow to drop as child-rows.
3306         Gtk::TreeModel::Path _dest = dest; // un const
3307         const bool is_child = _dest.up (); // explicit bool for clang
3308         if (!is_child || _dest.empty ()) {
3309                 return true;
3310         }
3311         return false;
3312 }
3313
3314 void
3315 Mixer_UI::plugin_drop (const Glib::RefPtr<Gdk::DragContext>&, const Gtk::SelectionData& data)
3316 {
3317         if (data.get_target() != "PluginPresetPtr") {
3318                 return;
3319         }
3320         if (data.get_length() != sizeof (PluginPresetPtr)) {
3321                 return;
3322         }
3323         const void *d = data.get_data();
3324         const PluginPresetPtr ppp = *(static_cast<const PluginPresetPtr*> (d));
3325
3326         PluginManager::PluginStatusType status = PluginManager::Favorite;
3327         PluginManager& manager (PluginManager::instance());
3328
3329         manager.set_status (ppp->_pip->type, ppp->_pip->unique_id, status);
3330         manager.save_statuses ();
3331 }
3332
3333 void
3334 Mixer_UI::do_vca_assign (boost::shared_ptr<VCA> vca)
3335 {
3336         /* call protected MixerActor:: method */
3337         vca_assign (vca);
3338 }
3339
3340 void
3341 Mixer_UI::do_vca_unassign (boost::shared_ptr<VCA> vca)
3342 {
3343         /* call protected MixerActor:: method */
3344         vca_unassign (vca);
3345 }
3346
3347 void
3348 Mixer_UI::show_spill (boost::shared_ptr<Stripable> s)
3349 {
3350         boost::shared_ptr<Stripable> ss = spilled_strip.lock();
3351         if (ss != s) {
3352                 spilled_strip = s;
3353                 show_spill_change (s); /* EMIT SIGNAL */
3354                 if (s) {
3355                         _group_tabs->hide ();
3356                 } else {
3357                         _group_tabs->show ();
3358                 }
3359                 redisplay_track_list ();
3360         }
3361 }
3362
3363 bool
3364 Mixer_UI::showing_spill_for (boost::shared_ptr<Stripable> s) const
3365 {
3366         return s == spilled_strip.lock();
3367 }
3368
3369 void
3370 Mixer_UI::register_actions ()
3371 {
3372         Glib::RefPtr<ActionGroup> group = ActionManager::create_action_group (bindings, X_("Mixer"));
3373
3374         ActionManager::register_action (group, "solo", _("Toggle Solo on Mixer-Selected Tracks/Busses"), sigc::mem_fun (*this, &Mixer_UI::solo_action));
3375         ActionManager::register_action (group, "mute", _("Toggle Mute on Mixer-Selected Tracks/Busses"), sigc::mem_fun (*this, &Mixer_UI::mute_action));
3376         ActionManager::register_action (group, "recenable", _("Toggle Rec-enable on Mixer-Selected Tracks/Busses"), sigc::mem_fun (*this, &Mixer_UI::rec_enable_action));
3377         ActionManager::register_action (group, "increment-gain", _("Decrease Gain on Mixer-Selected Tracks/Busses"), sigc::mem_fun (*this, &Mixer_UI::step_gain_up_action));
3378         ActionManager::register_action (group, "decrement-gain", _("Increase Gain on Mixer-Selected Tracks/Busses"), sigc::mem_fun (*this, &Mixer_UI::step_gain_down_action));
3379         ActionManager::register_action (group, "unity-gain", _("Set Gain to 0dB on Mixer-Selected Tracks/Busses"), sigc::mem_fun (*this, &Mixer_UI::unity_gain_action));
3380
3381
3382         ActionManager::register_action (group, "copy-processors", _("Copy Selected Processors"), sigc::mem_fun (*this, &Mixer_UI::copy_processors));
3383         ActionManager::register_action (group, "cut-processors", _("Cut Selected Processors"), sigc::mem_fun (*this, &Mixer_UI::cut_processors));
3384         ActionManager::register_action (group, "paste-processors", _("Paste Selected Processors"), sigc::mem_fun (*this, &Mixer_UI::paste_processors));
3385         ActionManager::register_action (group, "delete-processors", _("Delete Selected Processors"), sigc::mem_fun (*this, &Mixer_UI::delete_processors));
3386         ActionManager::register_action (group, "select-all-processors", _("Select All (visible) Processors"), sigc::mem_fun (*this, &Mixer_UI::select_all_processors));
3387         ActionManager::register_action (group, "toggle-processors", _("Toggle Selected Processors"), sigc::mem_fun (*this, &Mixer_UI::toggle_processors));
3388         ActionManager::register_action (group, "ab-plugins", _("Toggle Selected Plugins"), sigc::mem_fun (*this, &Mixer_UI::ab_plugins));
3389         ActionManager::register_action (group, "select-none", _("Deselect all strips and processors"), sigc::mem_fun (*this, &Mixer_UI::select_none));
3390
3391         ActionManager::register_action (group, "select-next-stripable", _("Select Next Mixer Strip"), sigc::mem_fun (*this, &Mixer_UI::select_next_strip));
3392         ActionManager::register_action (group, "select-prev-stripable", _("Scroll Previous Mixer Strip"), sigc::mem_fun (*this, &Mixer_UI::select_prev_strip));
3393
3394         ActionManager::register_action (group, "scroll-left", _("Scroll Mixer Window to the left"), sigc::mem_fun (*this, &Mixer_UI::scroll_left));
3395         ActionManager::register_action (group, "scroll-right", _("Scroll Mixer Window to the right"), sigc::mem_fun (*this, &Mixer_UI::scroll_right));
3396
3397         ActionManager::register_action (group, "toggle-midi-input-active", _("Toggle MIDI Input Active for Mixer-Selected Tracks/Busses"),
3398                                    sigc::bind (sigc::mem_fun (*this, &Mixer_UI::toggle_midi_input_active), false));
3399
3400         ActionManager::register_toggle_action (group, X_("ToggleMixerList"), _("Mixer: Show Mixer List"), sigc::mem_fun (*this, &Mixer_UI::toggle_mixer_list));
3401
3402         ActionManager::register_toggle_action (group, X_("ToggleVCAPane"), _("Mixer: Show VCAs"), sigc::mem_fun (*this, &Mixer_UI::toggle_vcas));
3403
3404 #ifdef MIXBUS
3405         ActionManager::register_toggle_action (group, X_("ToggleMixbusPane"), _("Mixer: Show Mixbusses"), sigc::mem_fun (*this, &Mixer_UI::toggle_mixbuses));
3406 #endif
3407
3408         ActionManager::register_toggle_action (group, X_("ToggleMonitorSection"), _("Mixer: Show Monitor Section"), sigc::mem_fun (*this, &Mixer_UI::toggle_monitor_section));
3409
3410         ActionManager::register_toggle_action (group, X_("ToggleFoldbackStrip"), _("Mixer: Show Foldback Strip"), sigc::mem_fun (*this, &Mixer_UI::toggle_foldback_strip));
3411
3412 }
3413
3414 void
3415 Mixer_UI::load_bindings ()
3416 {
3417         bindings = Bindings::get_bindings (X_("Mixer"));
3418 }
3419
3420 template<class T> void
3421 Mixer_UI::control_action (boost::shared_ptr<T> (Stripable::*get_control)() const)
3422 {
3423         boost::shared_ptr<ControlList> cl (new ControlList);
3424         boost::shared_ptr<AutomationControl> ac;
3425         bool val = false;
3426         bool have_val = false;
3427
3428         set_axis_targets_for_operation ();
3429
3430         BOOST_FOREACH(AxisView* r, _axis_targets) {
3431                 boost::shared_ptr<Stripable> s = r->stripable();
3432                 if (s) {
3433                         ac = (s.get()->*get_control)();
3434                         if (ac) {
3435                                 ac->start_touch (_session->audible_sample ());
3436                                 cl->push_back (ac);
3437                                 if (!have_val) {
3438                                         val = !ac->get_value();
3439                                         have_val = true;
3440                                 }
3441                         }
3442                 }
3443         }
3444
3445         _session->set_controls (cl,  val, Controllable::UseGroup);
3446 }
3447
3448 void
3449 Mixer_UI::solo_action ()
3450 {
3451         control_action (&Stripable::solo_control);
3452 }
3453
3454 void
3455 Mixer_UI::mute_action ()
3456 {
3457         control_action (&Stripable::mute_control);
3458 }
3459
3460 void
3461 Mixer_UI::rec_enable_action ()
3462 {
3463         control_action (&Stripable::rec_enable_control);
3464 }
3465
3466 AutomationControlSet
3467 Mixer_UI::selected_gaincontrols ()
3468 {
3469         set_axis_targets_for_operation ();
3470         AutomationControlSet rv;
3471         BOOST_FOREACH(AxisView* r, _axis_targets) {
3472                 MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
3473                 if (ms) {
3474                         boost::shared_ptr<GainControl> ac (ms->route()->gain_control());
3475                         ControlList cl (ac->grouped_controls());
3476                         for (ControlList::const_iterator c = cl.begin(); c != cl.end (); ++c) {
3477                                 rv.insert (*c);
3478                         }
3479                         rv.insert (ac);
3480                 }
3481         }
3482         return rv;
3483 }
3484
3485 void
3486 Mixer_UI::step_gain_up_action ()
3487 {
3488         AutomationControlSet acs = selected_gaincontrols ();
3489         for (AutomationControlSet::const_iterator i = acs.begin(); i != acs.end (); ++i) {
3490                 boost::shared_ptr<GainControl> ac = boost::dynamic_pointer_cast<GainControl> (*i);
3491                 assert (ac);
3492                 ac->set_value (dB_to_coefficient (accurate_coefficient_to_dB (ac->get_value()) + 0.1), Controllable::NoGroup);
3493         }
3494 }
3495
3496 void
3497 Mixer_UI::step_gain_down_action ()
3498 {
3499         AutomationControlSet acs = selected_gaincontrols ();
3500         for (AutomationControlSet::const_iterator i = acs.begin(); i != acs.end (); ++i) {
3501                 boost::shared_ptr<GainControl> ac = boost::dynamic_pointer_cast<GainControl> (*i);
3502                 assert (ac);
3503                 ac->set_value (dB_to_coefficient (accurate_coefficient_to_dB (ac->get_value()) - 0.1), Controllable::NoGroup);
3504         }
3505 }
3506
3507 void
3508 Mixer_UI::unity_gain_action ()
3509 {
3510         set_axis_targets_for_operation ();
3511
3512         BOOST_FOREACH(AxisView* r, _axis_targets) {
3513                 boost::shared_ptr<Stripable> s = r->stripable();
3514                 if (s) {
3515                         boost::shared_ptr<AutomationControl> ac = s->gain_control();
3516                         if (ac) {
3517                                 ac->set_value (1.0, Controllable::UseGroup);
3518                         }
3519                 }
3520         }
3521 }
3522
3523 void
3524 Mixer_UI::copy_processors ()
3525 {
3526         set_axis_targets_for_operation ();
3527
3528         BOOST_FOREACH(AxisView* r, _axis_targets) {
3529                 MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
3530                 if (ms) {
3531                         ms->copy_processors ();
3532                 }
3533         }
3534 }
3535 void
3536 Mixer_UI::cut_processors ()
3537 {
3538         set_axis_targets_for_operation ();
3539
3540         BOOST_FOREACH(AxisView* r, _axis_targets) {
3541                 MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
3542                 if (ms) {
3543                         ms->cut_processors ();
3544                 }
3545         }
3546 }
3547 void
3548 Mixer_UI::paste_processors ()
3549 {
3550         set_axis_targets_for_operation ();
3551
3552         BOOST_FOREACH(AxisView* r, _axis_targets) {
3553                 MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
3554                 if (ms) {
3555                         ms->paste_processors ();
3556                 }
3557         }
3558 }
3559 void
3560 Mixer_UI::select_all_processors ()
3561 {
3562         set_axis_targets_for_operation ();
3563
3564         BOOST_FOREACH(AxisView* r, _axis_targets) {
3565                 MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
3566                 if (ms) {
3567                         ms->select_all_processors ();
3568                 }
3569         }
3570 }
3571 void
3572 Mixer_UI::toggle_processors ()
3573 {
3574         set_axis_targets_for_operation ();
3575
3576         BOOST_FOREACH(AxisView* r, _axis_targets) {
3577                 MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
3578                 if (ms) {
3579                         ms->toggle_processors ();
3580                 }
3581         }
3582 }
3583 void
3584 Mixer_UI::ab_plugins ()
3585 {
3586         set_axis_targets_for_operation ();
3587
3588         BOOST_FOREACH(AxisView* r, _axis_targets) {
3589                 MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
3590                 if (ms) {
3591                         ms->ab_plugins ();
3592                 }
3593         }
3594 }
3595
3596 void
3597 Mixer_UI::vca_assign (boost::shared_ptr<VCA> vca)
3598 {
3599         set_axis_targets_for_operation ();
3600         BOOST_FOREACH(AxisView* r, _axis_targets) {
3601                 MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
3602                 if (ms) {
3603                         ms->vca_assign (vca);
3604                 }
3605         }
3606 }
3607
3608 void
3609 Mixer_UI::vca_unassign (boost::shared_ptr<VCA> vca)
3610 {
3611         set_axis_targets_for_operation ();
3612         BOOST_FOREACH(AxisView* r, _axis_targets) {
3613                 MixerStrip* ms = dynamic_cast<MixerStrip*> (r);
3614                 if (ms) {
3615                         ms->vca_unassign (vca);
3616                 }
3617         }
3618 }
3619
3620 bool
3621 Mixer_UI::screenshot (std::string const& filename)
3622 {
3623         if (!_session) {
3624                 return false;
3625         }
3626
3627         int height = strip_packer.get_height();
3628         bool with_vca = vca_vpacker.is_visible ();
3629         MixerStrip* master = strip_by_route (_session->master_out ());
3630
3631         Gtk::OffscreenWindow osw;
3632         Gtk::HBox b;
3633         osw.add (b);
3634         b.show ();
3635
3636         /* unpack widgets, add to OffscreenWindow */
3637
3638         strip_group_box.remove (strip_packer);
3639         b.pack_start (strip_packer, false, false);
3640         /* hide extra elements inside strip_packer */
3641         add_button.hide ();
3642         scroller_base.hide ();
3643 #ifdef MIXBUS
3644         mb_shadow.hide();
3645 #endif
3646
3647         if (with_vca) {
3648                 /* work around Gtk::ScrolledWindow */
3649                 Gtk::Viewport* viewport = (Gtk::Viewport*) vca_scroller.get_child();
3650                 viewport->remove (); // << vca_hpacker
3651                 b.pack_start (vca_hpacker, false, false);
3652                 /* hide some growing widgets */
3653                 add_vca_button.hide ();
3654                 vca_scroller_base.hide();
3655         }
3656
3657         if (master) {
3658                 out_packer.remove (*master);
3659                 b.pack_start (*master, false, false);
3660                 master->hide_master_spacer (true);
3661         }
3662
3663         /* prepare the OffscreenWindow for rendering */
3664         osw.set_size_request (-1, height);
3665         osw.show ();
3666         osw.queue_resize ();
3667         osw.queue_draw ();
3668         osw.get_window()->process_updates (true);
3669
3670         /* create screenshot */
3671         Glib::RefPtr<Gdk::Pixbuf> pb = osw.get_pixbuf ();
3672         pb->save (filename, "png");
3673
3674         /* unpack elements before destorying the Box & OffscreenWindow */
3675         list<Gtk::Widget*> children = b.get_children();
3676         for (list<Gtk::Widget*>::iterator child = children.begin(); child != children.end(); ++child) {
3677                 b.remove (**child);
3678         }
3679         osw.remove ();
3680
3681         /* now re-pack the widgets into the main mixer window */
3682         add_button.show ();
3683         scroller_base.show ();
3684 #ifdef MIXBUS
3685         mb_shadow.show();
3686 #endif
3687         strip_group_box.pack_start (strip_packer);
3688         if (with_vca) {
3689                 add_vca_button.show ();
3690                 vca_scroller_base.show();
3691                 vca_scroller.add (vca_hpacker);
3692         }
3693         if (master) {
3694                 master->hide_master_spacer (false);
3695                 out_packer.pack_start (*master, false, false);
3696         }
3697         return true;
3698 }