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