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