Keep the left hand list pane of the mixer the same size when resizing
[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/unwind.h"
32
33 #include <glibmm/threads.h>
34
35 #include <gtkmm2ext/gtk_ui.h>
36 #include <gtkmm2ext/utils.h>
37 #include <gtkmm2ext/tearoff.h>
38 #include <gtkmm2ext/window_title.h>
39
40 #include "ardour/debug.h"
41 #include "ardour/midi_track.h"
42 #include "ardour/plugin_manager.h"
43 #include "ardour/route_group.h"
44 #include "ardour/route_sorters.h"
45 #include "ardour/session.h"
46
47 #include "keyboard.h"
48 #include "mixer_ui.h"
49 #include "mixer_strip.h"
50 #include "monitor_section.h"
51 #include "plugin_selector.h"
52 #include "public_editor.h"
53 #include "ardour_ui.h"
54 #include "prompter.h"
55 #include "utils.h"
56 #include "route_sorter.h"
57 #include "actions.h"
58 #include "gui_thread.h"
59 #include "mixer_group_tabs.h"
60 #include "timers.h"
61
62 #include "i18n.h"
63
64 using namespace ARDOUR;
65 using namespace ARDOUR_UI_UTILS;
66 using namespace PBD;
67 using namespace Gtk;
68 using namespace Glib;
69 using namespace Gtkmm2ext;
70 using namespace std;
71
72 using PBD::atoi;
73 using PBD::Unwinder;
74
75 Mixer_UI* Mixer_UI::_instance = 0;
76
77 Mixer_UI*
78 Mixer_UI::instance () 
79 {
80         if (!_instance) {
81                 _instance  = new Mixer_UI;
82         } 
83
84         return _instance;
85 }
86
87 Mixer_UI::Mixer_UI ()
88         : Window (Gtk::WINDOW_TOPLEVEL)
89         , VisibilityTracker (*((Gtk::Window*) this))
90         , _visible (false)
91         , no_track_list_redisplay (false)
92         , in_group_row_change (false)
93         , track_menu (0)
94         , _monitor_section (0)
95         , _strip_width (ARDOUR_UI::config()->get_default_narrow_ms() ? Narrow : Wide)
96         , ignore_reorder (false)
97         , _in_group_rebuild_or_clear (false)
98         , _route_deletion_in_progress (false)
99         , _following_editor_selection (false)
100         , _maximised (false)
101 {
102         /* allow this window to become the key focus window */
103         set_flags (CAN_FOCUS);
104
105         Route::SyncOrderKeys.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::sync_treeview_from_order_keys, this), gui_context());
106
107         scroller.set_can_default (true);
108         set_default (scroller);
109
110         scroller_base.set_flags (Gtk::CAN_FOCUS);
111         scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
112         scroller_base.set_name ("MixerWindow");
113         scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
114         // add as last item of strip packer
115         strip_packer.pack_end (scroller_base, true, true);
116
117         _group_tabs = new MixerGroupTabs (this);
118         VBox* b = manage (new VBox);
119         b->pack_start (*_group_tabs, PACK_SHRINK);
120         b->pack_start (strip_packer);
121         b->show_all ();
122
123         scroller.add (*b);
124         scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
125
126         setup_track_display ();
127
128         group_model = ListStore::create (group_columns);
129         group_display.set_model (group_model);
130         group_display.append_column (_("Group"), group_columns.text);
131         group_display.append_column (_("Show"), group_columns.visible);
132         group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
133         group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
134         group_display.get_column (0)->set_expand(true);
135         group_display.get_column (1)->set_expand(false);
136         group_display.set_name ("EditGroupList");
137         group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
138         group_display.set_reorderable (true);
139         group_display.set_headers_visible (true);
140         group_display.set_rules_hint (true);
141
142         /* name is directly editable */
143
144         CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
145         name_cell->property_editable() = true;
146         name_cell->signal_edited().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_name_edit));
147
148         /* use checkbox for the active column */
149
150         CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
151         active_cell->property_activatable() = true;
152         active_cell->property_radio() = false;
153
154         group_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_change));
155         /* We use this to notice drag-and-drop reorders of the group list */
156         group_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_deleted));
157         group_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::group_display_button_press), false);
158
159         group_display_scroller.add (group_display);
160         group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
161
162         HBox* route_group_display_button_box = manage (new HBox());
163
164         Button* route_group_add_button = manage (new Button ());
165         Button* route_group_remove_button = manage (new Button ());
166
167         Widget* w;
168
169         w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
170         w->show();
171         route_group_add_button->add (*w);
172
173         w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
174         w->show();
175         route_group_remove_button->add (*w);
176
177         route_group_display_button_box->set_homogeneous (true);
178
179         route_group_add_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_route_group));
180         route_group_remove_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::remove_selected_route_group));
181
182         route_group_display_button_box->add (*route_group_add_button);
183         route_group_display_button_box->add (*route_group_remove_button);
184
185         group_display_vbox.pack_start (group_display_scroller, true, true);
186         group_display_vbox.pack_start (*route_group_display_button_box, false, false);
187
188         group_display_frame.set_name ("BaseFrame");
189         group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
190         group_display_frame.add (group_display_vbox);
191
192         rhs_pane1.pack1 (track_display_frame);
193         rhs_pane1.pack2 (group_display_frame);
194
195         list_vpacker.pack_start (rhs_pane1, true, true);
196
197         global_hpacker.pack_start (scroller, true, true);
198 #ifdef GTKOSX
199         /* current gtk-quartz has dirty updates on borders like this one */
200         global_hpacker.pack_start (out_packer, false, false, 0);
201 #else
202         global_hpacker.pack_start (out_packer, false, false, 12);
203 #endif
204         list_hpane.pack1(list_vpacker, false, false);
205         list_hpane.pack2(global_hpacker, true, false);
206
207         rhs_pane1.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
208                                                         static_cast<Gtk::Paned*> (&rhs_pane1)));
209         list_hpane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
210                                                          static_cast<Gtk::Paned*> (&list_hpane)));
211
212         global_vpacker.pack_start (list_hpane, true, true);
213
214         add (global_vpacker);
215         set_name ("MixerWindow");
216
217         update_title ();
218
219         set_wmclass (X_("ardour_mixer"), PROGRAM_NAME);
220
221         signal_delete_event().connect (sigc::mem_fun (*this, &Mixer_UI::hide_window));
222         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
223
224         signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
225
226         route_group_display_button_box->show();
227         route_group_add_button->show();
228         route_group_remove_button->show();
229
230         global_hpacker.show();
231         global_vpacker.show();
232         scroller.show();
233         scroller_base.show();
234         scroller_hpacker.show();
235         mixer_scroller_vpacker.show();
236         list_vpacker.show();
237         group_display_button_label.show();
238         group_display_button.show();
239         group_display_scroller.show();
240         group_display_vbox.show();
241         group_display_frame.show();
242         rhs_pane1.show();
243         strip_packer.show();
244         out_packer.show();
245         list_hpane.show();
246         group_display.show();
247
248         MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context());
249
250 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
251         _plugin_selector = new PluginSelector (PluginManager::instance ());
252 #endif
253 }
254
255 Mixer_UI::~Mixer_UI ()
256 {
257         if (_monitor_section) {
258                 delete _monitor_section;
259         }
260 }
261
262 void
263 Mixer_UI::track_editor_selection ()
264 {
265         PublicEditor::instance().get_selection().TracksChanged.connect (sigc::mem_fun (*this, &Mixer_UI::follow_editor_selection));
266 }
267
268
269 void
270 Mixer_UI::ensure_float (Window& win)
271 {
272         win.set_transient_for (*this);
273 }
274
275 void
276 Mixer_UI::show_window ()
277 {
278         present ();
279         if (!_visible) {
280                 set_window_pos_and_size ();
281
282                 /* show/hide group tabs as required */
283                 parameter_changed ("show-group-tabs");
284
285                 /* now reset each strips width so the right widgets are shown */
286                 MixerStrip* ms;
287
288                 TreeModel::Children rows = track_model->children();
289                 TreeModel::Children::iterator ri;
290
291                 for (ri = rows.begin(); ri != rows.end(); ++ri) {
292                         ms = (*ri)[track_columns.strip];
293                         ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
294                         /* Fix visibility of mixer strip stuff */
295                         ms->parameter_changed (X_("mixer-element-visibility"));
296                 }
297         }
298         
299         /* force focus into main area */
300         scroller_base.grab_focus ();
301
302         _visible = true;
303 }
304
305 bool
306 Mixer_UI::hide_window (GdkEventAny *ev)
307 {
308         get_window_pos_and_size ();
309
310         _visible = false;
311         return just_hide_it(ev, static_cast<Gtk::Window *>(this));
312 }
313
314
315 void
316 Mixer_UI::add_strips (RouteList& routes)
317 {
318         bool from_scratch = track_model->children().size() == 0;
319         Gtk::TreeModel::Children::iterator insert_iter = track_model->children().end();
320
321         for (Gtk::TreeModel::Children::iterator it = track_model->children().begin(); it != track_model->children().end(); ++it) {
322                 boost::shared_ptr<Route> r = (*it)[track_columns.route];
323
324                 if (r->order_key() == (routes.front()->order_key() + routes.size())) {
325                         insert_iter = it;
326                         break;
327                 }
328         }
329
330         if(!from_scratch) {
331                 _selection.clear_routes ();
332         }
333
334         MixerStrip* strip;
335
336         try {
337                 no_track_list_redisplay = true;
338                 track_display.set_model (Glib::RefPtr<ListStore>());
339
340                 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
341                         boost::shared_ptr<Route> route = (*x);
342                         
343                         if (route->is_auditioner()) {
344                                 continue;
345                         }
346                         
347                         if (route->is_monitor()) {
348                                 
349                                 if (!_monitor_section) {
350                                         _monitor_section = new MonitorSection (_session);
351                                         
352                                         XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
353                                         if (mnode) {
354                                                 _monitor_section->tearoff().set_state (*mnode);
355                                         }
356                                 } 
357                                 
358                                 out_packer.pack_end (_monitor_section->tearoff(), false, false);
359                                 _monitor_section->set_session (_session);
360                                 _monitor_section->tearoff().show_all ();
361                                 
362                                 route->DropReferences.connect (*this, invalidator(*this), boost::bind (&Mixer_UI::monitor_section_going_away, this), gui_context());
363                                 
364                                 /* no regular strip shown for control out */
365                                 
366                                 continue;
367                         }
368                         
369                         strip = new MixerStrip (*this, _session, route);
370                         strips.push_back (strip);
371
372                         ARDOUR_UI::config()->get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
373                         
374                         if (strip->width_owner() != strip) {
375                                 strip->set_width_enum (_strip_width, this);
376                         }
377                         
378                         show_strip (strip);
379                         
380                         TreeModel::Row row = *(track_model->insert(insert_iter));
381                         row[track_columns.text] = route->name();
382                         row[track_columns.visible] = strip->route()->is_master() ? true : strip->marked_for_display();
383                         row[track_columns.route] = route;
384                         row[track_columns.strip] = strip;
385
386                         if (!from_scratch) {
387                                 _selection.add (strip);
388                         }
389                         
390                         route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
391                         
392                         strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
393                         strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
394                 }
395
396         } catch (...) {
397         }
398
399         no_track_list_redisplay = false;
400         track_display.set_model (track_model);
401         
402         sync_order_keys_from_treeview ();
403         redisplay_track_list ();
404 }
405
406 void
407 Mixer_UI::deselect_all_strip_processors ()
408 {
409         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
410                 (*i)->deselect_all_processors();
411         }
412 }
413
414 void
415 Mixer_UI::select_none ()
416 {
417         _selection.clear_routes();
418         deselect_all_strip_processors();
419 }
420
421 void
422 Mixer_UI::delete_processors ()
423 {
424         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
425                 (*i)->delete_processors();
426         }
427 }
428
429
430 void
431 Mixer_UI::remove_strip (MixerStrip* strip)
432 {
433         if (_session && _session->deletion_in_progress()) {
434                 /* its all being taken care of */
435                 return;
436         }
437
438         TreeModel::Children rows = track_model->children();
439         TreeModel::Children::iterator ri;
440         list<MixerStrip *>::iterator i;
441         
442         if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
443                 strips.erase (i);
444         }
445         
446         for (ri = rows.begin(); ri != rows.end(); ++ri) {
447                 if ((*ri)[track_columns.strip] == strip) {
448                         PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
449                         track_model->erase (ri);
450                         break;
451                 }
452         }
453 }
454
455 void
456 Mixer_UI::reset_remote_control_ids ()
457 {
458         if (Config->get_remote_model() == UserOrdered || !_session || _session->deletion_in_progress()) {
459                 return;
460         }
461
462         TreeModel::Children rows = track_model->children();
463         
464         if (rows.empty()) {
465                 return;
466         }
467
468         DEBUG_TRACE (DEBUG::OrderKeys, "mixer resets remote control ids after remote model change\n");
469
470         TreeModel::Children::iterator ri;
471         bool rid_change = false;
472         uint32_t rid = 1;
473         uint32_t invisible_key = UINT32_MAX;
474
475         for (ri = rows.begin(); ri != rows.end(); ++ri) {
476                 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
477                 bool visible = (*ri)[track_columns.visible];
478
479                 if (!route->is_master() && !route->is_monitor()) {
480                         
481                         uint32_t new_rid = (visible ? rid : invisible_key--);
482                         
483                         if (new_rid != route->remote_control_id()) {
484                                 route->set_remote_control_id_explicit (new_rid);        
485                                 rid_change = true;
486                         }
487                         
488                         if (visible) {
489                                 rid++;
490                         }
491                 }
492         }
493
494         if (rid_change) {
495                 /* tell the world that we changed the remote control IDs */
496                 _session->notify_remote_id_change ();
497         }
498 }
499
500 void
501 Mixer_UI::sync_order_keys_from_treeview ()
502 {
503         if (ignore_reorder || !_session || _session->deletion_in_progress()) {
504                 return;
505         }
506
507         TreeModel::Children rows = track_model->children();
508         
509         if (rows.empty()) {
510                 return;
511         }
512
513         DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync order keys from model\n");
514
515         TreeModel::Children::iterator ri;
516         bool changed = false;
517         bool rid_change = false;
518         uint32_t order = 0;
519         uint32_t rid = 1;
520         uint32_t invisible_key = UINT32_MAX;
521
522         for (ri = rows.begin(); ri != rows.end(); ++ri) {
523                 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
524                 bool visible = (*ri)[track_columns.visible];
525
526                 uint32_t old_key = route->order_key ();
527
528                 if (order != old_key) {
529                         route->set_order_key (order);
530                         changed = true;
531                 }
532
533                 if ((Config->get_remote_model() == MixerOrdered) && !route->is_master() && !route->is_monitor()) {
534
535                         uint32_t new_rid = (visible ? rid : invisible_key--);
536
537                         if (new_rid != route->remote_control_id()) {
538                                 route->set_remote_control_id_explicit (new_rid);        
539                                 rid_change = true;
540                         }
541                         
542                         if (visible) {
543                                 rid++;
544                         }
545
546                 }
547
548                 ++order;
549         }
550
551         if (changed) {
552                 /* tell everyone that we changed the mixer sort keys */
553                 _session->sync_order_keys ();
554         }
555
556         if (rid_change) {
557                 /* tell the world that we changed the remote control IDs */
558                 _session->notify_remote_id_change ();
559         }
560 }
561
562 void
563 Mixer_UI::sync_treeview_from_order_keys ()
564 {
565         if (!_session || _session->deletion_in_progress()) {
566                 return;
567         }
568
569         DEBUG_TRACE (DEBUG::OrderKeys, "mixer sync model from order keys.\n");
570
571         /* we could get here after either a change in the Mixer or Editor sort
572          * order, but either way, the mixer order keys reflect the intended
573          * order for the GUI, so reorder the treeview model to match it.
574          */
575
576         vector<int> neworder;
577         TreeModel::Children rows = track_model->children();
578         uint32_t old_order = 0;
579         bool changed = false;
580
581         if (rows.empty()) {
582                 return;
583         }
584
585         OrderKeySortedRoutes sorted_routes;
586
587         for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
588                 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
589                 sorted_routes.push_back (RoutePlusOrderKey (route, old_order, route->order_key ()));
590         }
591
592         SortByNewDisplayOrder cmp;
593
594         sort (sorted_routes.begin(), sorted_routes.end(), cmp);
595         neworder.assign (sorted_routes.size(), 0);
596
597         uint32_t n = 0;
598         
599         for (OrderKeySortedRoutes::iterator sr = sorted_routes.begin(); sr != sorted_routes.end(); ++sr, ++n) {
600
601                 neworder[n] = sr->old_display_order;
602
603                 if (sr->old_display_order != n) {
604                         changed = true;
605                 }
606
607                 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("MIXER change order for %1 from %2 to %3\n",
608                                                                sr->route->name(), sr->old_display_order, n));
609         }
610
611         if (changed) {
612                 Unwinder<bool> uw (ignore_reorder, true);
613                 track_model->reorder (neworder);
614         }
615
616         redisplay_track_list ();
617 }
618
619 void
620 Mixer_UI::follow_editor_selection ()
621 {
622         if (!ARDOUR_UI::config()->get_link_editor_and_mixer_selection() || _following_editor_selection) {
623                 return;
624         }
625
626         _following_editor_selection = true;
627         _selection.block_routes_changed (true);
628         
629         TrackSelection& s (PublicEditor::instance().get_selection().tracks);
630
631         _selection.clear_routes ();
632
633         for (TrackViewList::iterator i = s.begin(); i != s.end(); ++i) {
634                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
635                 if (rtav) {
636                         MixerStrip* ms = strip_by_route (rtav->route());
637                         if (ms) {
638                                 _selection.add (ms);
639                         }
640                 }
641         }
642
643         _following_editor_selection = false;
644         _selection.block_routes_changed (false);
645 }
646
647
648 MixerStrip*
649 Mixer_UI::strip_by_route (boost::shared_ptr<Route> r)
650 {
651         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
652                 if ((*i)->route() == r) {
653                         return (*i);
654                 }
655         }
656
657         return 0;
658 }
659
660 bool
661 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
662 {
663         if (ev->button == 1) {
664                 if (_selection.selected (strip)) {
665                         /* primary-click: toggle selection state of strip */
666                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
667                                 _selection.remove (strip);
668                         } 
669                 } else {
670                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
671                                 _selection.add (strip);
672                         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::RangeSelectModifier)) {
673
674                                 if (!_selection.selected(strip)) {
675                                 
676                                         /* extend selection */
677                                         
678                                         vector<MixerStrip*> tmp;
679                                         bool accumulate = false;
680                                         bool found_another = false;
681                                         
682                                         tmp.push_back (strip);
683
684                                         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
685                                                 if ((*i) == strip) {
686                                                         /* hit clicked strip, start accumulating till we hit the first 
687                                                            selected strip
688                                                         */
689                                                         if (accumulate) {
690                                                                 /* done */
691                                                                 break;
692                                                         } else {
693                                                                 accumulate = true;
694                                                         }
695                                                 } else if (_selection.selected (*i)) {
696                                                         /* hit selected strip. if currently accumulating others,
697                                                            we're done. if not accumulating others, start doing so.
698                                                         */
699                                                         found_another = true;
700                                                         if (accumulate) {
701                                                                 /* done */
702                                                                 break;
703                                                         } else {
704                                                                 accumulate = true;
705                                                         }
706                                                 } else {
707                                                         if (accumulate) {
708                                                                 tmp.push_back (*i);
709                                                         }
710                                                 }
711                                         }
712
713                                         if (found_another) {
714                                                 for (vector<MixerStrip*>::iterator i = tmp.begin(); i != tmp.end(); ++i) {
715                                                         _selection.add (*i);
716                                                 }
717                                         } else
718                                                 _selection.set (strip);  //user wants to start a range selection, but there aren't any others selected yet
719                                 }
720
721                         } else {
722                                 _selection.set (strip);
723                         }
724                 }
725         }
726
727         return true;
728 }
729
730 void
731 Mixer_UI::set_session (Session* sess)
732 {
733         SessionHandlePtr::set_session (sess);
734
735         if (_plugin_selector) {
736                 _plugin_selector->set_session (_session);
737         }
738
739         _group_tabs->set_session (sess);
740
741         if (!_session) {
742                 return;
743         }
744
745         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
746         set_state (*node);
747
748         update_title ();
749
750         initial_track_display ();
751
752         _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_strips, this, _1), gui_context());
753         _session->route_group_added.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::add_route_group, this, _1), gui_context());
754         _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
755         _session->route_groups_reordered.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
756         _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
757         _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
758         _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::update_title, this), gui_context());
759
760         Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
761
762         route_groups_changed ();
763
764         if (_visible) {
765                 show_window();
766
767                 /* Bit of a hack; if we're here, we're opening the mixer because of our
768                    instant XML state having a show-mixer property.  Fix up the corresponding
769                    action state.
770                 */
771                 ActionManager::check_toggleaction ("<Actions>/Common/toggle-mixer");
772         }
773
774         start_updating ();
775 }
776
777 void
778 Mixer_UI::session_going_away ()
779 {
780         ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away);
781
782         _in_group_rebuild_or_clear = true;
783         group_model->clear ();
784         _in_group_rebuild_or_clear = false;
785
786         _selection.clear ();
787         track_model->clear ();
788
789         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
790                 delete (*i);
791         }
792
793         if (_monitor_section) {
794                 _monitor_section->tearoff().hide_visible ();
795         }
796
797         strips.clear ();
798
799         stop_updating ();
800
801         SessionHandlePtr::session_going_away ();
802
803         _session = 0;
804         update_title ();
805 }
806
807 void
808 Mixer_UI::track_visibility_changed (std::string const & path)
809 {
810         if (_session && _session->deletion_in_progress()) {
811                 return;
812         }
813
814         TreeIter iter;
815
816         if ((iter = track_model->get_iter (path))) {
817                 MixerStrip* strip = (*iter)[track_columns.strip];
818                 if (strip) {
819                         bool visible = (*iter)[track_columns.visible];
820
821                         if (strip->set_marked_for_display (!visible)) {
822                                 update_track_visibility ();
823                         }
824                 }
825         }
826 }
827
828 void
829 Mixer_UI::update_track_visibility ()
830 {
831         TreeModel::Children rows = track_model->children();
832         TreeModel::Children::iterator i;
833
834         {
835                 Unwinder<bool> uw (no_track_list_redisplay, true);
836                 
837                 for (i = rows.begin(); i != rows.end(); ++i) {
838                         MixerStrip *strip = (*i)[track_columns.strip];
839                         (*i)[track_columns.visible] = strip->marked_for_display ();
840                 }
841                 
842                 /* force route order keys catch up with visibility changes
843                  */
844                 
845                 sync_order_keys_from_treeview ();
846         }
847
848         redisplay_track_list ();
849 }
850
851 void
852 Mixer_UI::show_strip (MixerStrip* ms)
853 {
854         TreeModel::Children rows = track_model->children();
855         TreeModel::Children::iterator i;
856
857         for (i = rows.begin(); i != rows.end(); ++i) {
858
859                 MixerStrip* strip = (*i)[track_columns.strip];
860                 if (strip == ms) {
861                         (*i)[track_columns.visible] = true;
862                         redisplay_track_list ();
863                         break;
864                 }
865         }
866 }
867
868 void
869 Mixer_UI::hide_strip (MixerStrip* ms)
870 {
871         TreeModel::Children rows = track_model->children();
872         TreeModel::Children::iterator i;
873
874         for (i = rows.begin(); i != rows.end(); ++i) {
875
876                 MixerStrip* strip = (*i)[track_columns.strip];
877                 if (strip == ms) {
878                         (*i)[track_columns.visible] = false;
879                         redisplay_track_list ();
880                         break;
881                 }
882         }
883 }
884
885 gint
886 Mixer_UI::start_updating ()
887 {
888     fast_screen_update_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
889     return 0;
890 }
891
892 gint
893 Mixer_UI::stop_updating ()
894 {
895     fast_screen_update_connection.disconnect();
896     return 0;
897 }
898
899 void
900 Mixer_UI::fast_update_strips ()
901 {
902         if (is_mapped () && _session) {
903                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
904                         (*i)->fast_update ();
905                 }
906         }
907 }
908
909 void
910 Mixer_UI::set_all_strips_visibility (bool yn)
911 {
912         TreeModel::Children rows = track_model->children();
913         TreeModel::Children::iterator i;
914
915         {
916                 Unwinder<bool> uw (no_track_list_redisplay, true);
917                 
918                 for (i = rows.begin(); i != rows.end(); ++i) {
919                         
920                         TreeModel::Row row = (*i);
921                         MixerStrip* strip = row[track_columns.strip];
922                         
923                         if (strip == 0) {
924                                 continue;
925                         }
926                         
927                         if (strip->route()->is_master() || strip->route()->is_monitor()) {
928                                 continue;
929                         }
930                         
931                         (*i)[track_columns.visible] = yn;
932                 }
933         }
934
935         redisplay_track_list ();
936 }
937
938
939 void
940 Mixer_UI::set_all_audio_visibility (int tracks, bool yn)
941 {
942         TreeModel::Children rows = track_model->children();
943         TreeModel::Children::iterator i;
944
945         {
946                 Unwinder<bool> uw (no_track_list_redisplay, true);
947                 
948                 for (i = rows.begin(); i != rows.end(); ++i) {
949                         TreeModel::Row row = (*i);
950                         MixerStrip* strip = row[track_columns.strip];
951                         
952                         if (strip == 0) {
953                                 continue;
954                         }
955                         
956                         if (strip->route()->is_master() || strip->route()->is_monitor()) {
957                                 continue;
958                         }
959                         
960                         boost::shared_ptr<AudioTrack> at = strip->audio_track();
961                         
962                         switch (tracks) {
963                         case 0:
964                                 (*i)[track_columns.visible] = yn;
965                                 break;
966                                 
967                         case 1:
968                                 if (at) { /* track */
969                                         (*i)[track_columns.visible] = yn;
970                                 }
971                                 break;
972                                 
973                         case 2:
974                                 if (!at) { /* bus */
975                                         (*i)[track_columns.visible] = yn;
976                                 }
977                                 break;
978                         }
979                 }
980         }
981
982         redisplay_track_list ();
983 }
984
985 void
986 Mixer_UI::hide_all_routes ()
987 {
988         set_all_strips_visibility (false);
989 }
990
991 void
992 Mixer_UI::show_all_routes ()
993 {
994         set_all_strips_visibility (true);
995 }
996
997 void
998 Mixer_UI::show_all_audiobus ()
999 {
1000         set_all_audio_visibility (2, true);
1001 }
1002 void
1003 Mixer_UI::hide_all_audiobus ()
1004 {
1005         set_all_audio_visibility (2, false);
1006 }
1007
1008 void
1009 Mixer_UI::show_all_audiotracks()
1010 {
1011         set_all_audio_visibility (1, true);
1012 }
1013 void
1014 Mixer_UI::hide_all_audiotracks ()
1015 {
1016         set_all_audio_visibility (1, false);
1017 }
1018
1019 void
1020 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
1021 {
1022         DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview reordered\n");
1023         sync_order_keys_from_treeview ();
1024 }
1025
1026 void
1027 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
1028 {
1029         /* this happens as the second step of a DnD within the treeview as well
1030            as when a row/route is actually deleted.
1031            
1032            if it was a deletion then we have to force a redisplay because
1033            order keys may not have changed.
1034         */
1035
1036         DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
1037         sync_order_keys_from_treeview ();
1038
1039         if (_route_deletion_in_progress) {
1040                 redisplay_track_list ();
1041         }
1042 }
1043
1044 void
1045 Mixer_UI::redisplay_track_list ()
1046 {
1047         TreeModel::Children rows = track_model->children();
1048         TreeModel::Children::iterator i;
1049         
1050         if (no_track_list_redisplay) {
1051                 return;
1052         }
1053
1054         for (i = rows.begin(); i != rows.end(); ++i) {
1055
1056                 MixerStrip* strip = (*i)[track_columns.strip];
1057
1058                 if (strip == 0) {
1059                         /* we're in the middle of changing a row, don't worry */
1060                         continue;
1061                 }
1062
1063                 bool const visible = (*i)[track_columns.visible];
1064
1065                 if (visible) {
1066                         strip->set_gui_property ("visible", true);
1067
1068                         if (strip->packed()) {
1069
1070                                 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1071                                         out_packer.reorder_child (*strip, -1);
1072
1073                                 } else {
1074                                         strip_packer.reorder_child (*strip, -1); /* put at end */
1075                                 }
1076
1077                         } else {
1078
1079                                 if (strip->route()->is_master() || strip->route()->is_monitor()) {
1080                                         out_packer.pack_start (*strip, false, false);
1081                                 } else {
1082                                         strip_packer.pack_start (*strip, false, false);
1083                                 }
1084                                 strip->set_packed (true);
1085                         }
1086
1087                 } else {
1088
1089                         strip->set_gui_property ("visible", false);
1090
1091                         if (strip->route()->is_master() || strip->route()->is_monitor()) {
1092                                 /* do nothing, these cannot be hidden */
1093                         } else {
1094                                 if (strip->packed()) {
1095                                         strip_packer.remove (*strip);
1096                                         strip->set_packed (false);
1097                                 }
1098                         }
1099                 }
1100         }
1101
1102         _group_tabs->set_dirty ();
1103 }
1104
1105 void
1106 Mixer_UI::strip_width_changed ()
1107 {
1108         _group_tabs->set_dirty ();
1109
1110 #ifdef GTKOSX
1111         TreeModel::Children rows = track_model->children();
1112         TreeModel::Children::iterator i;
1113         long order;
1114
1115         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
1116                 MixerStrip* strip = (*i)[track_columns.strip];
1117
1118                 if (strip == 0) {
1119                         continue;
1120                 }
1121
1122                 bool visible = (*i)[track_columns.visible];
1123
1124                 if (visible) {
1125                         strip->queue_draw();
1126                 }
1127         }
1128 #endif
1129
1130 }
1131
1132 void
1133 Mixer_UI::initial_track_display ()
1134 {
1135         boost::shared_ptr<RouteList> routes = _session->get_routes();
1136         RouteList copy (*routes);
1137         ARDOUR::SignalOrderRouteSorter sorter;
1138
1139         copy.sort (sorter);
1140
1141         {
1142                 Unwinder<bool> uw1 (no_track_list_redisplay, true);
1143                 Unwinder<bool> uw2 (ignore_reorder, true);
1144
1145                 track_model->clear ();
1146                 add_strips (copy);
1147         }
1148         
1149         _session->sync_order_keys ();
1150
1151         redisplay_track_list ();
1152 }
1153
1154 void
1155 Mixer_UI::show_track_list_menu ()
1156 {
1157         if (track_menu == 0) {
1158                 build_track_menu ();
1159         }
1160
1161         track_menu->popup (1, gtk_get_current_event_time());
1162 }
1163
1164 bool
1165 Mixer_UI::track_display_button_press (GdkEventButton* ev)
1166 {
1167         if (Keyboard::is_context_menu_event (ev)) {
1168                 show_track_list_menu ();
1169                 return true;
1170         }
1171
1172         return false;
1173 }
1174
1175 void
1176 Mixer_UI::build_track_menu ()
1177 {
1178         using namespace Menu_Helpers;
1179         using namespace Gtk;
1180
1181         track_menu = new Menu;
1182         track_menu->set_name ("ArdourContextMenu");
1183         MenuList& items = track_menu->items();
1184
1185         items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1186         items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1187         items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1188         items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1189         items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1190         items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1191
1192 }
1193
1194 void
1195 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1196 {
1197         if (!what_changed.contains (ARDOUR::Properties::name)) {
1198                 return;
1199         }
1200
1201         ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1202
1203         TreeModel::Children rows = track_model->children();
1204         TreeModel::Children::iterator i;
1205
1206         for (i = rows.begin(); i != rows.end(); ++i) {
1207                 if ((*i)[track_columns.strip] == mx) {
1208                         (*i)[track_columns.text] = mx->route()->name();
1209                         return;
1210                 }
1211         }
1212
1213         error << _("track display list item for renamed strip not found!") << endmsg;
1214 }
1215
1216 bool
1217 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1218 {
1219         TreeModel::Path path;
1220         TreeViewColumn* column;
1221         int cellx;
1222         int celly;
1223
1224         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1225                 return false;
1226         }
1227
1228         TreeIter iter = group_model->get_iter (path);
1229         if (!iter) {
1230                 return false;
1231         }
1232
1233         RouteGroup* group = (*iter)[group_columns.group];
1234
1235         if (Keyboard::is_context_menu_event (ev)) {
1236                 _group_tabs->get_menu(group)->popup (1, ev->time);
1237                 return true;
1238         }
1239
1240         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1241         case 0:
1242                 if (Keyboard::is_edit_event (ev)) {
1243                         if (group) {
1244                                 // edit_route_group (group);
1245 #ifdef GTKOSX
1246                                 group_display.queue_draw();
1247 #endif
1248                                 return true;
1249                         }
1250                 }
1251                 break;
1252
1253         case 1:
1254         {
1255                 bool visible = (*iter)[group_columns.visible];
1256                 (*iter)[group_columns.visible] = !visible;
1257 #ifdef GTKOSX
1258                 group_display.queue_draw();
1259 #endif
1260                 return true;
1261         }
1262
1263         default:
1264                 break;
1265         }
1266
1267         return false;
1268  }
1269
1270 void
1271 Mixer_UI::activate_all_route_groups ()
1272 {
1273         _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1274 }
1275
1276 void
1277 Mixer_UI::disable_all_route_groups ()
1278 {
1279         _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1280 }
1281
1282 void
1283 Mixer_UI::route_groups_changed ()
1284 {
1285         ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed);
1286
1287         _in_group_rebuild_or_clear = true;
1288
1289         /* just rebuild the while thing */
1290
1291         group_model->clear ();
1292
1293 #if 0
1294         /* this is currently not used,
1295          * Mixer_UI::group_display_button_press() has a case for it,
1296          * and a commented edit_route_group() but that's n/a since 2011.
1297          *
1298          * This code is left as reminder that
1299          * row[group_columns.group] = 0 has special meaning.
1300          */
1301         {
1302                 TreeModel::Row row;
1303                 row = *(group_model->append());
1304                 row[group_columns.visible] = true;
1305                 row[group_columns.text] = (_("-all-"));
1306                 row[group_columns.group] = 0;
1307         }
1308 #endif
1309
1310         _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1311
1312         _group_tabs->set_dirty ();
1313         _in_group_rebuild_or_clear = false;
1314 }
1315
1316 void
1317 Mixer_UI::new_route_group ()
1318 {
1319         RouteList rl;
1320
1321         _group_tabs->run_new_group_dialog (rl);
1322 }
1323
1324 void
1325 Mixer_UI::remove_selected_route_group ()
1326 {
1327         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1328         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1329
1330         if (rows.empty()) {
1331                 return;
1332         }
1333
1334         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1335         TreeIter iter;
1336
1337         /* selection mode is single, so rows.begin() is it */
1338
1339         if ((iter = group_model->get_iter (*i))) {
1340
1341                 RouteGroup* rg = (*iter)[group_columns.group];
1342
1343                 if (rg) {
1344                         _session->remove_route_group (*rg);
1345                 }
1346         }
1347 }
1348
1349 void
1350 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1351 {
1352         if (in_group_row_change) {
1353                 return;
1354         }
1355
1356         /* force an update of any mixer strips that are using this group,
1357            otherwise mix group names don't change in mixer strips
1358         */
1359
1360         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1361                 if ((*i)->route_group() == group) {
1362                         (*i)->route_group_changed();
1363                 }
1364         }
1365
1366         TreeModel::iterator i;
1367         TreeModel::Children rows = group_model->children();
1368         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1369
1370         in_group_row_change = true;
1371
1372         for (i = rows.begin(); i != rows.end(); ++i) {
1373                 if ((*i)[group_columns.group] == group) {
1374                         (*i)[group_columns.visible] = !group->is_hidden ();
1375                         (*i)[group_columns.text] = group->name ();
1376                         break;
1377                 }
1378         }
1379
1380         in_group_row_change = false;
1381
1382         if (change.contains (Properties::name)) {
1383                 _group_tabs->set_dirty ();
1384         }
1385
1386         for (list<MixerStrip*>::iterator j = strips.begin(); j != strips.end(); ++j) {
1387                 if ((*j)->route_group() == group) {
1388                         if (group->is_hidden ()) {
1389                                 hide_strip (*j);
1390                         } else {
1391                                 show_strip (*j);
1392                         }
1393                 }
1394         }
1395 }
1396
1397 void
1398 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
1399 {
1400         RouteGroup* group;
1401         TreeIter iter;
1402
1403         if ((iter = group_model->get_iter (path))) {
1404
1405                 if ((group = (*iter)[group_columns.group]) == 0) {
1406                         return;
1407                 }
1408
1409                 if (new_text != group->name()) {
1410                         group->set_name (new_text);
1411                 }
1412         }
1413 }
1414
1415 void
1416 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1417 {
1418         RouteGroup* group;
1419
1420         if (in_group_row_change) {
1421                 return;
1422         }
1423
1424         if ((group = (*iter)[group_columns.group]) == 0) {
1425                 return;
1426         }
1427
1428         std::string name = (*iter)[group_columns.text];
1429
1430         if (name != group->name()) {
1431                 group->set_name (name);
1432         }
1433
1434         bool hidden = !(*iter)[group_columns.visible];
1435
1436         if (hidden != group->is_hidden ()) {
1437                 group->set_hidden (hidden, this);
1438         }
1439 }
1440
1441 /** Called when a group model row is deleted, but also when the model is
1442  *  reordered by a user drag-and-drop; the latter is what we are
1443  *  interested in here.
1444  */
1445 void
1446 Mixer_UI::route_group_row_deleted (Gtk::TreeModel::Path const &)
1447 {
1448         if (_in_group_rebuild_or_clear) {
1449                 return;
1450         }
1451
1452         /* Re-write the session's route group list so that the new order is preserved */
1453
1454         list<RouteGroup*> new_list;
1455
1456         Gtk::TreeModel::Children children = group_model->children();
1457         for (Gtk::TreeModel::Children::iterator i = children.begin(); i != children.end(); ++i) {
1458                 RouteGroup* g = (*i)[group_columns.group];
1459                 if (g) {
1460                         new_list.push_back (g);
1461                 }
1462         }
1463
1464         _session->reorder_route_groups (new_list);
1465 }
1466
1467
1468 void
1469 Mixer_UI::add_route_group (RouteGroup* group)
1470 {
1471         ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1472         bool focus = false;
1473
1474         in_group_row_change = true;
1475
1476         TreeModel::Row row = *(group_model->append());
1477         row[group_columns.visible] = !group->is_hidden ();
1478         row[group_columns.group] = group;
1479         if (!group->name().empty()) {
1480                 row[group_columns.text] = group->name();
1481         } else {
1482                 row[group_columns.text] = _("unnamed");
1483                 focus = true;
1484         }
1485
1486         group->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
1487
1488         if (focus) {
1489                 TreeViewColumn* col = group_display.get_column (0);
1490                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1491                 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1492         }
1493
1494         _group_tabs->set_dirty ();
1495
1496         in_group_row_change = false;
1497 }
1498
1499 bool
1500 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1501 {
1502         using namespace Menu_Helpers;
1503
1504         if (Keyboard::is_context_menu_event (ev)) {
1505                 ARDOUR_UI::instance()->add_route (this);
1506                 return true;
1507         }
1508
1509         return false;
1510 }
1511
1512 void
1513 Mixer_UI::set_strip_width (Width w, bool save)
1514 {
1515         _strip_width = w;
1516
1517         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1518                 (*i)->set_width_enum (w, save ? (*i)->width_owner() : this);
1519         }
1520 }
1521
1522 void
1523 Mixer_UI::set_window_pos_and_size ()
1524 {
1525         resize (m_width, m_height);
1526         move (m_root_x, m_root_y);
1527 }
1528
1529         void
1530 Mixer_UI::get_window_pos_and_size ()
1531 {
1532         get_position(m_root_x, m_root_y);
1533         get_size(m_width, m_height);
1534 }
1535
1536 int
1537 Mixer_UI::set_state (const XMLNode& node)
1538 {
1539         const XMLProperty* prop;
1540         XMLNode* geometry;
1541
1542         m_width = default_width;
1543         m_height = default_height;
1544         m_root_x = 1;
1545         m_root_y = 1;
1546
1547         if ((geometry = find_named_node (node, "geometry")) != 0) {
1548
1549                 XMLProperty* prop;
1550
1551                 if ((prop = geometry->property("x_size")) == 0) {
1552                         prop = geometry->property ("x-size");
1553                 }
1554                 if (prop) {
1555                         m_width = atoi(prop->value());
1556                 }
1557                 if ((prop = geometry->property("y_size")) == 0) {
1558                         prop = geometry->property ("y-size");
1559                 }
1560                 if (prop) {
1561                         m_height = atoi(prop->value());
1562                 }
1563
1564                 if ((prop = geometry->property ("x_pos")) == 0) {
1565                         prop = geometry->property ("x-pos");
1566                 }
1567                 if (prop) {
1568                         m_root_x = atoi (prop->value());
1569
1570                 }
1571                 if ((prop = geometry->property ("y_pos")) == 0) {
1572                         prop = geometry->property ("y-pos");
1573                 }
1574                 if (prop) {
1575                         m_root_y = atoi (prop->value());
1576                 }
1577         }
1578
1579         set_window_pos_and_size ();
1580
1581         if ((prop = node.property ("narrow-strips"))) {
1582                 if (string_is_affirmative (prop->value())) {
1583                         set_strip_width (Narrow);
1584                 } else {
1585                         set_strip_width (Wide);
1586                 }
1587         }
1588
1589         if ((prop = node.property ("show-mixer"))) {
1590                 if (string_is_affirmative (prop->value())) {
1591                        _visible = true;
1592                 }
1593         }
1594
1595         if ((prop = node.property ("maximised"))) {
1596                 bool yn = string_is_affirmative (prop->value());
1597                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleMaximalMixer"));
1598                 assert (act);
1599                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1600                 bool fs = tact && tact->get_active();
1601                 if (yn ^ fs) {
1602                         ActionManager::do_action ("Common",
1603                                         "ToggleMaximalMixer");
1604                 }
1605         }
1606
1607
1608         return 0;
1609 }
1610
1611 XMLNode&
1612 Mixer_UI::get_state (void)
1613 {
1614         XMLNode* node = new XMLNode ("Mixer");
1615
1616         if (is_realized()) {
1617                 Glib::RefPtr<Gdk::Window> win = get_window();
1618
1619                 get_window_pos_and_size ();
1620
1621                 XMLNode* geometry = new XMLNode ("geometry");
1622                 char buf[32];
1623                 snprintf(buf, sizeof(buf), "%d", m_width);
1624                 geometry->add_property(X_("x_size"), string(buf));
1625                 snprintf(buf, sizeof(buf), "%d", m_height);
1626                 geometry->add_property(X_("y_size"), string(buf));
1627                 snprintf(buf, sizeof(buf), "%d", m_root_x);
1628                 geometry->add_property(X_("x_pos"), string(buf));
1629                 snprintf(buf, sizeof(buf), "%d", m_root_y);
1630                 geometry->add_property(X_("y_pos"), string(buf));
1631
1632                 // written only for compatibility, they are not used.
1633                 snprintf(buf, sizeof(buf), "%d", 0);
1634                 geometry->add_property(X_("x_off"), string(buf));
1635                 snprintf(buf, sizeof(buf), "%d", 0);
1636                 geometry->add_property(X_("y_off"), string(buf));
1637
1638                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1639                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1640                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1641                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1642
1643                 node->add_child_nocopy (*geometry);
1644         }
1645
1646         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1647
1648         node->add_property ("show-mixer", _visible ? "yes" : "no");
1649
1650         node->add_property ("maximised", _maximised ? "yes" : "no");
1651
1652         return *node;
1653 }
1654
1655
1656 void
1657 Mixer_UI::pane_allocation_handler (Allocation&, Gtk::Paned* which)
1658 {
1659         int pos;
1660         XMLProperty* prop = 0;
1661         char buf[32];
1662         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1663         XMLNode* geometry;
1664         int height;
1665         static int32_t done[3] = { 0, 0, 0 };
1666
1667         height = default_height;
1668
1669         if ((geometry = find_named_node (*node, "geometry")) != 0) {
1670
1671                 if ((prop = geometry->property ("y_size")) == 0) {
1672                         prop = geometry->property ("y-size");
1673                 }
1674                 if (prop) {
1675                         height = atoi (prop->value());
1676                 }
1677         }
1678
1679         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1680
1681                 if (done[0]) {
1682                         return;
1683                 }
1684
1685                 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1686                         pos = height / 3;
1687                         snprintf (buf, sizeof(buf), "%d", pos);
1688                 } else {
1689                         pos = atoi (prop->value());
1690                 }
1691
1692                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1693                         rhs_pane1.set_position (pos);
1694                 }
1695
1696         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1697
1698                 if (done[2]) {
1699                         return;
1700                 }
1701
1702                 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
1703                         pos = 75;
1704                         snprintf (buf, sizeof(buf), "%d", pos);
1705                 } else {
1706                         pos = atoi (prop->value());
1707                 }
1708
1709                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1710                         list_hpane.set_position (pos);
1711                 }
1712         }
1713 }
1714 void
1715 Mixer_UI::scroll_left ()
1716 {
1717         if (!scroller.get_hscrollbar()) return;
1718         Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1719         /* stupid GTK: can't rely on clamping across versions */
1720         scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1721 }
1722
1723 void
1724 Mixer_UI::scroll_right ()
1725 {
1726         if (!scroller.get_hscrollbar()) return;
1727         Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1728         /* stupid GTK: can't rely on clamping across versions */
1729         scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1730 }
1731
1732 bool
1733 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1734 {
1735         /* focus widget gets first shot, then bindings, otherwise
1736            forward to main window
1737         */
1738
1739         if (gtk_window_propagate_key_event (GTK_WINDOW(gobj()), ev)) {
1740                 return true;
1741         }
1742
1743         KeyboardKey k (ev->state, ev->keyval);
1744
1745         if (bindings.activate (k, Bindings::Press)) {
1746                 return true;
1747         }
1748
1749         return forward_key_press (ev);
1750 }
1751
1752 bool
1753 Mixer_UI::on_key_release_event (GdkEventKey* ev)
1754 {
1755         if (gtk_window_propagate_key_event (GTK_WINDOW(gobj()), ev)) {
1756                 return true;
1757         }
1758
1759         KeyboardKey k (ev->state, ev->keyval);
1760         
1761         if (bindings.activate (k, Bindings::Release)) {
1762                 return true;
1763         }
1764
1765         /* don't forward releases */
1766
1767         return true;
1768 }
1769
1770 bool
1771 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1772 {
1773         switch (ev->direction) {
1774         case GDK_SCROLL_LEFT:
1775                 scroll_left ();
1776                 return true;
1777         case GDK_SCROLL_UP:
1778                 if (ev->state & Keyboard::TertiaryModifier) {
1779                         scroll_left ();
1780                         return true;
1781                 }
1782                 return false;
1783
1784         case GDK_SCROLL_RIGHT:
1785                 scroll_right ();
1786                 return true;
1787
1788         case GDK_SCROLL_DOWN:
1789                 if (ev->state & Keyboard::TertiaryModifier) {
1790                         scroll_right ();
1791                         return true;
1792                 }
1793                 return false;
1794         }
1795
1796         return false;
1797 }
1798
1799
1800 void
1801 Mixer_UI::parameter_changed (string const & p)
1802 {
1803         if (p == "show-group-tabs") {
1804                 bool const s = _session->config.get_show_group_tabs ();
1805                 if (s) {
1806                         _group_tabs->show ();
1807                 } else {
1808                         _group_tabs->hide ();
1809                 }
1810         } else if (p == "default-narrow_ms") {
1811                 bool const s = ARDOUR_UI::config()->get_default_narrow_ms ();
1812                 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1813                         (*i)->set_width_enum (s ? Narrow : Wide, this);
1814                 }
1815         } else if (p == "remote-model") {
1816                 reset_remote_control_ids ();
1817         }
1818 }
1819
1820 void
1821 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
1822 {
1823         g->set_active (a, this);
1824 }
1825
1826 PluginSelector*
1827 Mixer_UI::plugin_selector()
1828 {
1829 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
1830         if (!_plugin_selector)
1831                 _plugin_selector = new PluginSelector (PluginManager::instance());
1832 #endif
1833
1834         return _plugin_selector;
1835 }
1836
1837 void
1838 Mixer_UI::setup_track_display ()
1839 {
1840         track_model = ListStore::create (track_columns);
1841         track_display.set_model (track_model);
1842         track_display.append_column (_("Strips"), track_columns.text);
1843         track_display.append_column (_("Show"), track_columns.visible);
1844         track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
1845         track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
1846         track_display.get_column (0)->set_expand(true);
1847         track_display.get_column (1)->set_expand(false);
1848         track_display.set_name (X_("EditGroupList"));
1849         track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
1850         track_display.set_reorderable (true);
1851         track_display.set_headers_visible (true);
1852
1853         track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
1854         track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
1855
1856         CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
1857         track_list_visible_cell->property_activatable() = true;
1858         track_list_visible_cell->property_radio() = false;
1859         track_list_visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &Mixer_UI::track_visibility_changed));
1860
1861         track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
1862
1863         track_display_scroller.add (track_display);
1864         track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
1865
1866         VBox* v = manage (new VBox);
1867         v->show ();
1868         v->pack_start (track_display_scroller, true, true);
1869
1870         Button* b = manage (new Button);
1871         b->show ();
1872         Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
1873         w->show ();
1874         b->add (*w);
1875
1876         b->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
1877
1878         v->pack_start (*b, false, false);
1879
1880         track_display_frame.set_name("BaseFrame");
1881         track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
1882         track_display_frame.add (*v);
1883
1884         track_display_scroller.show();
1885         track_display_frame.show();
1886         track_display.show();
1887 }
1888
1889 void
1890 Mixer_UI::new_track_or_bus ()
1891 {
1892         ARDOUR_UI::instance()->add_route (this);
1893 }
1894
1895
1896 void
1897 Mixer_UI::update_title ()
1898 {
1899         if (_session) {
1900                 string n;
1901                 
1902                 if (_session->snap_name() != _session->name()) {
1903                         n = _session->snap_name ();
1904                 } else {
1905                         n = _session->name ();
1906                 }
1907
1908                 if (_session->dirty ()) {
1909                         n = "*" + n;
1910                 }
1911                 
1912                 WindowTitle title (n);
1913                 title += S_("Window|Mixer");
1914                 title += Glib::get_application_name ();
1915                 set_title (title.get_string());
1916
1917         } else {
1918                 
1919                 WindowTitle title (S_("Window|Mixer"));
1920                 title += Glib::get_application_name ();
1921                 set_title (title.get_string());
1922         }
1923 }
1924
1925 MixerStrip*
1926 Mixer_UI::strip_by_x (int x)
1927 {
1928         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1929                 int x1, x2, y;
1930
1931                 (*i)->translate_coordinates (*this, 0, 0, x1, y);
1932                 x2 = x1 + (*i)->get_width();
1933
1934                 if (x >= x1 && x <= x2) {
1935                         return (*i);
1936                 }
1937         }
1938
1939         return 0;
1940 }
1941
1942 void
1943 Mixer_UI::set_route_targets_for_operation ()
1944 {
1945         _route_targets.clear ();
1946
1947         if (!_selection.empty()) {
1948                 _route_targets = _selection.routes;
1949                 return;
1950         }
1951
1952 //  removed "implicit" selections of strips, after discussion on IRC
1953
1954 }
1955
1956 void
1957 Mixer_UI::monitor_section_going_away ()
1958 {
1959         if (_monitor_section) {
1960                 out_packer.remove (_monitor_section->tearoff());
1961                 _monitor_section->set_session (0);
1962         }
1963 }
1964
1965 void
1966 Mixer_UI::toggle_midi_input_active (bool flip_others)
1967 {
1968         boost::shared_ptr<RouteList> rl (new RouteList);
1969         bool onoff = false;
1970
1971         set_route_targets_for_operation ();
1972
1973         for (RouteUISelection::iterator r = _route_targets.begin(); r != _route_targets.end(); ++r) {
1974                 boost::shared_ptr<MidiTrack> mt = (*r)->midi_track();
1975
1976                 if (mt) {
1977                         rl->push_back ((*r)->route());
1978                         onoff = !mt->input_active();
1979                 }
1980         }
1981         
1982         _session->set_exclusive_input_active (rl, onoff, flip_others);
1983 }
1984
1985 void
1986 Mixer_UI::maximise_mixer_space ()
1987 {
1988         if (_maximised) {
1989                 return;
1990         }
1991
1992         fullscreen ();
1993
1994         _maximised = true;
1995 }
1996
1997 void
1998 Mixer_UI::restore_mixer_space ()
1999 {
2000         if (!_maximised) {
2001                 return;
2002         }
2003
2004         unfullscreen();
2005
2006         _maximised = false;
2007 }