4b4a5b41b18fa135cc9b26ca5dd41c748e3a152f
[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 #include <algorithm>
21 #include <map>
22 #include <sigc++/bind.h>
23
24 #include <gtkmm/accelmap.h>
25
26 #include "pbd/convert.h"
27 #include "pbd/stacktrace.h"
28 #include <glibmm/thread.h>
29
30 #include <gtkmm2ext/gtk_ui.h>
31 #include <gtkmm2ext/utils.h>
32 #include <gtkmm2ext/stop_signal.h>
33 #include <gtkmm2ext/window_title.h>
34
35 #include "ardour/audio_diskstream.h"
36 #include "ardour/audio_track.h"
37 #include "ardour/plugin_manager.h"
38 #include "ardour/route_group.h"
39 #include "ardour/session.h"
40 #include "ardour/session_route.h"
41
42 #include "keyboard.h"
43 #include "mixer_ui.h"
44 #include "mixer_strip.h"
45 #include "plugin_selector.h"
46 #include "ardour_ui.h"
47 #include "prompter.h"
48 #include "utils.h"
49 #include "actions.h"
50 #include "gui_thread.h"
51 #include "mixer_group_tabs.h"
52
53 #include "i18n.h"
54
55 using namespace ARDOUR;
56 using namespace PBD;
57 using namespace Gtk;
58 using namespace Glib;
59 using namespace Gtkmm2ext;
60 using namespace std;
61
62 using PBD::atoi;
63
64 Mixer_UI::Mixer_UI ()
65         : Window (Gtk::WINDOW_TOPLEVEL)
66 {
67         _strip_width = Config->get_default_narrow_ms() ? Narrow : Wide;
68         track_menu = 0;
69         route_group_context_menu = 0;
70         no_track_list_redisplay = false;
71         in_group_row_change = false;
72         _visible = false;
73         strip_redisplay_does_not_reset_order_keys = false;
74         strip_redisplay_does_not_sync_order_keys = false;
75         ignore_sync = false;
76
77         Route::SyncOrderKeys.connect (*this, ui_bind (&Mixer_UI::sync_order_keys, this, _1), gui_context());
78
79         scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
80         scroller_base.set_name ("MixerWindow");
81         scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
82         // add as last item of strip packer
83         strip_packer.pack_end (scroller_base, true, true);
84
85         _group_tabs = new MixerGroupTabs (this);
86         VBox* b = manage (new VBox);
87         b->pack_start (*_group_tabs, PACK_SHRINK);
88         b->pack_start (strip_packer);
89         b->show_all ();
90
91         scroller.add (*b);
92         scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
93
94         track_model = ListStore::create (track_columns);
95         track_display.set_model (track_model);
96         track_display.append_column (_("Strips"), track_columns.text);
97         track_display.append_column (_("Show"), track_columns.visible);
98         track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
99         track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
100         track_display.get_column (0)->set_expand(true);
101         track_display.get_column (1)->set_expand(false);
102         track_display.set_name (X_("MixerTrackDisplayList"));
103         track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
104         track_display.set_reorderable (true);
105         track_display.set_headers_visible (true);
106
107         track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
108         track_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_change));
109         track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
110
111         CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
112         track_list_visible_cell->property_activatable() = true;
113         track_list_visible_cell->property_radio() = false;
114
115         track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
116
117         track_display_scroller.add (track_display);
118         track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
119
120         group_model = ListStore::create (group_columns);
121         group_display.set_model (group_model);
122         group_display.append_column (_("Group"), group_columns.text);
123         group_display.append_column (_("Show"), group_columns.visible);
124         group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
125         group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
126         group_display.get_column (0)->set_expand(true);
127         group_display.get_column (1)->set_expand(false);
128         group_display.set_name ("MixerGroupList");
129         group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
130         group_display.set_reorderable (true);
131         group_display.set_headers_visible (true);
132         group_display.set_rules_hint (true);
133
134         /* name is directly editable */
135
136         CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
137         name_cell->property_editable() = true;
138         name_cell->signal_edited().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_name_edit));
139
140         /* use checkbox for the active column */
141
142         CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
143         active_cell->property_activatable() = true;
144         active_cell->property_radio() = false;
145
146         group_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_change));
147
148         group_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::group_display_button_press), false);
149
150         group_display_scroller.add (group_display);
151         group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
152
153         HBox* route_group_display_button_box = manage (new HBox());
154
155         Button* route_group_add_button = manage (new Button ());
156         Button* route_group_remove_button = manage (new Button ());
157
158         Widget* w;
159
160         w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
161         w->show();
162         route_group_add_button->add (*w);
163
164         w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
165         w->show();
166         route_group_remove_button->add (*w);
167
168         route_group_display_button_box->set_homogeneous (true);
169
170         route_group_add_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_route_group));
171         route_group_remove_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::remove_selected_route_group));
172
173         route_group_display_button_box->add (*route_group_remove_button);
174         route_group_display_button_box->add (*route_group_add_button);
175
176         group_display_vbox.pack_start (group_display_scroller, true, true);
177         group_display_vbox.pack_start (*route_group_display_button_box, false, false);
178
179         track_display_frame.set_name("BaseFrame");
180         track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
181         track_display_frame.add(track_display_scroller);
182
183         group_display_frame.set_name ("BaseFrame");
184         group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
185         group_display_frame.add (group_display_vbox);
186
187         rhs_pane1.pack1 (track_display_frame);
188         rhs_pane1.pack2 (group_display_frame);
189
190         list_vpacker.pack_start (rhs_pane1, true, true);
191
192         global_hpacker.pack_start (scroller, true, true);
193 #ifdef GTKOSX
194         /* current gtk-quartz has dirty updates on borders like this one */
195         global_hpacker.pack_start (out_packer, false, false, 0);
196 #else
197         global_hpacker.pack_start (out_packer, false, false, 12);
198 #endif
199         list_hpane.add1(list_vpacker);
200         list_hpane.add2(global_hpacker);
201
202         rhs_pane1.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
203                                                         static_cast<Gtk::Paned*> (&rhs_pane1)));
204         list_hpane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
205                                                          static_cast<Gtk::Paned*> (&list_hpane)));
206
207         global_vpacker.pack_start (list_hpane, true, true);
208
209         add (global_vpacker);
210         set_name ("MixerWindow");
211
212         WindowTitle title(Glib::get_application_name());
213         title += _("Mixer");
214         set_title (title.get_string());
215
216         set_wmclass (X_("ardour_mixer"), "Ardour");
217
218         add_accel_group (ActionManager::ui_manager->get_accel_group());
219
220         signal_delete_event().connect (sigc::mem_fun (*this, &Mixer_UI::hide_window));
221         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
222
223         signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
224
225         _selection.RoutesChanged.connect (sigc::mem_fun(*this, &Mixer_UI::follow_strip_selection));
226
227         route_group_display_button_box->show();
228         route_group_add_button->show();
229         route_group_remove_button->show();
230
231         global_hpacker.show();
232         global_vpacker.show();
233         scroller.show();
234         scroller_base.show();
235         scroller_hpacker.show();
236         mixer_scroller_vpacker.show();
237         list_vpacker.show();
238         group_display_button_label.show();
239         group_display_button.show();
240         track_display_scroller.show();
241         group_display_scroller.show();
242         group_display_vbox.show();
243         track_display_frame.show();
244         group_display_frame.show();
245         rhs_pane1.show();
246         strip_packer.show();
247         out_packer.show();
248         list_hpane.show();
249         track_display.show();
250         group_display.show();
251
252         auto_rebinding = FALSE;
253
254         MixerStrip::CatchDeletion.connect (*this, ui_bind (&Mixer_UI::remove_strip, this, _1), gui_context());
255
256 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
257         _plugin_selector = new PluginSelector (PluginManager::the_manager ());
258 #endif
259 }
260
261 Mixer_UI::~Mixer_UI ()
262 {
263 }
264
265 void
266 Mixer_UI::ensure_float (Window& win)
267 {
268         win.set_transient_for (*this);
269 }
270
271 void
272 Mixer_UI::show_window ()
273 {
274         present ();
275         if (!_visible) {
276                 set_window_pos_and_size ();
277
278                 /* now reset each strips width so the right widgets are shown */
279                 MixerStrip* ms;
280
281                 TreeModel::Children rows = track_model->children();
282                 TreeModel::Children::iterator ri;
283
284                 for (ri = rows.begin(); ri != rows.end(); ++ri) {
285                         ms = (*ri)[track_columns.strip];
286                         ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
287                 }
288         }
289         _visible = true;
290 }
291
292 bool
293 Mixer_UI::hide_window (GdkEventAny *ev)
294 {
295         get_window_pos_and_size ();
296
297         _visible = false;
298         return just_hide_it(ev, static_cast<Gtk::Window *>(this));
299 }
300
301
302 void
303 Mixer_UI::add_strip (RouteList& routes)
304 {
305         ENSURE_GUI_THREAD (*this, &Mixer_UI::add_strip, routes)
306
307         MixerStrip* strip;
308
309         no_track_list_redisplay = true;
310         strip_redisplay_does_not_sync_order_keys = true;
311
312         for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
313                 boost::shared_ptr<Route> route = (*x);
314
315                 if (route->is_hidden()) {
316                         return;
317                 }
318
319                 strip = new MixerStrip (*this, _session, route);
320                 strips.push_back (strip);
321
322                 Config->get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
323
324                 if (strip->width_owner() != strip) {
325                         strip->set_width_enum (_strip_width, this);
326                 }
327
328                 show_strip (strip);
329
330                 TreeModel::Row row = *(track_model->append());
331                 row[track_columns.text] = route->name();
332                 row[track_columns.visible] = strip->marked_for_display();
333                 row[track_columns.route] = route;
334                 row[track_columns.strip] = strip;
335
336                 if (route->order_key (N_("signal")) == -1) {
337                         route->set_order_key (N_("signal"), track_model->children().size()-1);
338                 }
339
340                 route->PropertyChanged.connect (*this, ui_bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
341
342                 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
343                 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
344         }
345
346         no_track_list_redisplay = false;
347
348         redisplay_track_list ();
349
350         strip_redisplay_does_not_sync_order_keys = false;
351 }
352
353 void
354 Mixer_UI::remove_strip (MixerStrip* strip)
355 {
356         if (_session && _session->deletion_in_progress()) {
357                 /* its all being taken care of */
358                 return;
359         }
360
361         ENSURE_GUI_THREAD (*this, &Mixer_UI::remove_strip, strip);
362
363         cerr << "Mixer UI removing strip for " << strip << endl;
364
365         TreeModel::Children rows = track_model->children();
366         TreeModel::Children::iterator ri;
367         list<MixerStrip *>::iterator i;
368
369         if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
370                 strips.erase (i);
371         }
372
373         strip_redisplay_does_not_sync_order_keys = true;
374
375         for (ri = rows.begin(); ri != rows.end(); ++ri) {
376                 if ((*ri)[track_columns.strip] == strip) {
377                         track_model->erase (ri);
378                         break;
379                 }
380         }
381
382         strip_redisplay_does_not_sync_order_keys = false;
383 }
384
385 void
386 Mixer_UI::sync_order_keys (string const & src)
387 {
388         TreeModel::Children rows = track_model->children();
389         TreeModel::Children::iterator ri;
390
391         if (src == N_("signal") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
392                 return;
393         }
394
395         std::map<int,int> keys;
396
397         bool changed = false;
398
399         unsigned order = 0;
400         for (ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
401                 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
402                 unsigned int old_key = order;
403                 unsigned int new_key = route->order_key (N_("signal"));
404
405                 keys[new_key] = old_key;
406
407                 if (new_key != old_key) {
408                         changed = true;
409                 }
410         }
411
412         if (keys.size() != rows.size()) {
413                 PBD::stacktrace (cerr, 20);
414         }
415         assert(keys.size() == rows.size());
416
417         // Remove any gaps in keys caused by automation children tracks
418         vector<int> neworder;
419         for (std::map<int,int>::const_iterator i = keys.begin(); i != keys.end(); ++i) {
420                 neworder.push_back(i->second);
421         }
422         assert(neworder.size() == rows.size());
423
424         if (changed) {
425                 strip_redisplay_does_not_reset_order_keys = true;
426                 track_model->reorder (neworder);
427                 strip_redisplay_does_not_reset_order_keys = false;
428         }
429 }
430
431 void
432 Mixer_UI::follow_strip_selection ()
433 {
434         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
435                 (*i)->set_selected (_selection.selected ((*i)->route()));
436         }
437 }
438
439 bool
440 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
441 {
442         if (ev->button == 1) {
443
444                 /* this allows the user to click on the strip to terminate comment
445                    editing. XXX it needs improving so that we don't select the strip
446                    at the same time.
447                 */
448
449                 if (_selection.selected (strip->route())) {
450                         _selection.remove (strip->route());
451                 } else {
452                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
453                                 _selection.add (strip->route());
454                         } else {
455                                 _selection.set (strip->route());
456                         }
457                 }
458         }
459
460         return true;
461 }
462
463 void
464 Mixer_UI::set_session (Session* sess)
465 {
466         SessionHandlePtr::set_session (sess);
467
468         if (_plugin_selector) {
469                 _plugin_selector->set_session (_session);
470         }
471
472         _group_tabs->set_session (sess);
473
474         if (!_session) {
475                 return;
476         }
477
478         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
479         set_state (*node);
480
481         WindowTitle title(_session->name());
482         title += _("Mixer");
483         title += Glib::get_application_name();
484
485         set_title (title.get_string());
486
487         initial_track_display ();
488
489         _session->RouteAdded.connect (_session_connections, ui_bind (&Mixer_UI::add_strip, this, _1), gui_context());
490         _session->route_group_added.connect (_session_connections, ui_bind (&Mixer_UI::add_route_group, this, _1), gui_context());
491         _session->route_group_removed.connect (_session_connections, boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
492         _session->config.ParameterChanged.connect (_session_connections, ui_bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
493
494         route_groups_changed ();
495
496         if (_visible) {
497                 show_window();
498         }
499
500         start_updating ();
501 }
502
503 void
504 Mixer_UI::session_going_away ()
505 {
506         ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away)
507
508         group_model->clear ();
509         _selection.clear ();
510         track_model->clear ();
511
512         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
513                 delete (*i);
514         }
515         strips.clear ();
516
517         WindowTitle title(Glib::get_application_name());
518         title += _("Mixer");
519         set_title (title.get_string());
520
521         stop_updating ();
522
523         SessionHandlePtr::session_going_away ();
524 }
525
526 void
527 Mixer_UI::show_strip (MixerStrip* ms)
528 {
529         TreeModel::Children rows = track_model->children();
530         TreeModel::Children::iterator i;
531
532         for (i = rows.begin(); i != rows.end(); ++i) {
533
534                 MixerStrip* strip = (*i)[track_columns.strip];
535                 if (strip == ms) {
536                         (*i)[track_columns.visible] = true;
537                         break;
538                 }
539         }
540 }
541
542 void
543 Mixer_UI::hide_strip (MixerStrip* ms)
544 {
545         TreeModel::Children rows = track_model->children();
546         TreeModel::Children::iterator i;
547
548         for (i = rows.begin(); i != rows.end(); ++i) {
549
550                 MixerStrip* strip = (*i)[track_columns.strip];
551                 if (strip == ms) {
552                         (*i)[track_columns.visible] = false;
553                         break;
554                 }
555         }
556 }
557
558 gint
559 Mixer_UI::start_updating ()
560 {
561     fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
562     return 0;
563 }
564
565 gint
566 Mixer_UI::stop_updating ()
567 {
568     fast_screen_update_connection.disconnect();
569     return 0;
570 }
571
572 void
573 Mixer_UI::fast_update_strips ()
574 {
575         if (is_mapped () && _session) {
576                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
577                         (*i)->fast_update ();
578                 }
579         }
580 }
581
582 void
583 Mixer_UI::set_all_strips_visibility (bool yn)
584 {
585         TreeModel::Children rows = track_model->children();
586         TreeModel::Children::iterator i;
587
588         no_track_list_redisplay = true;
589
590         for (i = rows.begin(); i != rows.end(); ++i) {
591
592                 TreeModel::Row row = (*i);
593                 MixerStrip* strip = row[track_columns.strip];
594
595                 if (strip == 0) {
596                         continue;
597                 }
598
599                 if (strip->route()->is_master() || strip->route()->is_control()) {
600                         continue;
601                 }
602
603                 (*i)[track_columns.visible] = yn;
604         }
605
606         no_track_list_redisplay = false;
607         redisplay_track_list ();
608 }
609
610
611 void
612 Mixer_UI::set_all_audio_visibility (int tracks, bool yn)
613 {
614         TreeModel::Children rows = track_model->children();
615         TreeModel::Children::iterator i;
616
617         no_track_list_redisplay = true;
618
619         for (i = rows.begin(); i != rows.end(); ++i) {
620                 TreeModel::Row row = (*i);
621                 MixerStrip* strip = row[track_columns.strip];
622
623                 if (strip == 0) {
624                         continue;
625                 }
626
627                 if (strip->route()->is_master() || strip->route()->is_control()) {
628                         continue;
629                 }
630
631                 boost::shared_ptr<AudioTrack> at = strip->audio_track();
632
633                 switch (tracks) {
634                 case 0:
635                         (*i)[track_columns.visible] = yn;
636                         break;
637
638                 case 1:
639                         if (at) { /* track */
640                                 (*i)[track_columns.visible] = yn;
641                         }
642                         break;
643
644                 case 2:
645                         if (!at) { /* bus */
646                                 (*i)[track_columns.visible] = yn;
647                         }
648                         break;
649                 }
650         }
651
652         no_track_list_redisplay = false;
653         redisplay_track_list ();
654 }
655
656 void
657 Mixer_UI::hide_all_routes ()
658 {
659         set_all_strips_visibility (false);
660 }
661
662 void
663 Mixer_UI::show_all_routes ()
664 {
665         set_all_strips_visibility (true);
666 }
667
668 void
669 Mixer_UI::show_all_audiobus ()
670 {
671         set_all_audio_visibility (2, true);
672 }
673 void
674 Mixer_UI::hide_all_audiobus ()
675 {
676         set_all_audio_visibility (2, false);
677 }
678
679 void
680 Mixer_UI::show_all_audiotracks()
681 {
682         set_all_audio_visibility (1, true);
683 }
684 void
685 Mixer_UI::hide_all_audiotracks ()
686 {
687         set_all_audio_visibility (1, false);
688 }
689
690 void
691 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
692 {
693         strip_redisplay_does_not_sync_order_keys = true;
694         _session->set_remote_control_ids();
695         redisplay_track_list ();
696         strip_redisplay_does_not_sync_order_keys = false;
697 }
698
699 void
700 Mixer_UI::track_list_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator&)
701 {
702         // never reset order keys because of a property change
703         strip_redisplay_does_not_reset_order_keys = true;
704         _session->set_remote_control_ids();
705         redisplay_track_list ();
706         strip_redisplay_does_not_reset_order_keys = false;
707 }
708
709 void
710 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
711 {
712         /* this could require an order sync */
713         if (_session && !_session->deletion_in_progress()) {
714                 _session->set_remote_control_ids();
715                 redisplay_track_list ();
716         }
717 }
718
719 void
720 Mixer_UI::redisplay_track_list ()
721 {
722         TreeModel::Children rows = track_model->children();
723         TreeModel::Children::iterator i;
724         long order;
725
726         if (no_track_list_redisplay) {
727                 return;
728         }
729
730         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
731                 MixerStrip* strip = (*i)[track_columns.strip];
732
733                 if (strip == 0) {
734                         /* we're in the middle of changing a row, don't worry */
735                         continue;
736                 }
737
738                 bool visible = (*i)[track_columns.visible];
739
740                 if (visible) {
741                         strip->set_marked_for_display (true);
742                         strip->route()->set_order_key (N_("signal"), order);
743
744                         if (!strip_redisplay_does_not_reset_order_keys) {
745                                 strip->route()->set_order_key (N_("signal"), order);
746                         }
747
748                         if (strip->packed()) {
749
750                                 if (strip->route()->is_master() || strip->route()->is_control()) {
751                                         out_packer.reorder_child (*strip, -1);
752                                 } else {
753                                         strip_packer.reorder_child (*strip, -1); /* put at end */
754                                 }
755
756                         } else {
757
758                                 if (strip->route()->is_master() || strip->route()->is_control()) {
759                                         out_packer.pack_start (*strip, false, false);
760                                 } else {
761                                         strip_packer.pack_start (*strip, false, false);
762                                 }
763                                 strip->set_packed (true);
764                                 //strip->show();
765                         }
766
767                 } else {
768
769                         strip->set_marked_for_display (false);
770
771                         if (strip->route()->is_master() || strip->route()->is_control()) {
772                                 /* do nothing, these cannot be hidden */
773                         } else {
774                                 if (strip->packed()) {
775                                         strip_packer.remove (*strip);
776                                         strip->set_packed (false);
777                                 }
778                         }
779                 }
780         }
781
782         if (!strip_redisplay_does_not_reset_order_keys && !strip_redisplay_does_not_sync_order_keys) {
783                 _session->sync_order_keys (N_("signal"));
784         }
785
786         // Resigc::bind all of the midi controls automatically
787
788         if (auto_rebinding)
789                 auto_rebind_midi_controls ();
790
791         _group_tabs->set_dirty ();
792 }
793
794 void
795 Mixer_UI::strip_width_changed ()
796 {
797         _group_tabs->set_dirty ();
798
799 #ifdef GTKOSX
800         TreeModel::Children rows = track_model->children();
801         TreeModel::Children::iterator i;
802         long order;
803
804         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
805                 MixerStrip* strip = (*i)[track_columns.strip];
806
807                 if (strip == 0) {
808                         continue;
809                 }
810
811                 bool visible = (*i)[track_columns.visible];
812
813                 if (visible) {
814                         strip->queue_draw();
815                 }
816         }
817 #endif
818
819 }
820
821 void
822 Mixer_UI::set_auto_rebinding( bool val )
823 {
824         if( val == TRUE )
825         {
826                 auto_rebinding = TRUE;
827                 Session::AutoBindingOff();
828         }
829         else
830         {
831                 auto_rebinding = FALSE;
832                 Session::AutoBindingOn();
833         }
834 }
835
836 void
837 Mixer_UI::toggle_auto_rebinding()
838 {
839         if (auto_rebinding)
840         {
841                 set_auto_rebinding( FALSE );
842         }
843
844         else
845         {
846                 set_auto_rebinding( TRUE );
847         }
848
849         auto_rebind_midi_controls();
850 }
851
852 void
853 Mixer_UI::auto_rebind_midi_controls ()
854 {
855         TreeModel::Children rows = track_model->children();
856         TreeModel::Children::iterator i;
857         int pos;
858
859         // Create bindings for all visible strips and remove those that are not visible
860         pos = 1;  // 0 is reserved for the master strip
861         for (i = rows.begin(); i != rows.end(); ++i) {
862                 MixerStrip* strip = (*i)[track_columns.strip];
863
864                 if ( (*i)[track_columns.visible] == true ) {  // add bindings for
865                         // make the actual binding
866                         //cout<<"Auto Binding:  Visible Strip Found: "<<strip->name()<<endl;
867
868                         int controlValue = pos;
869                         if( strip->route()->is_master() ) {
870                                 controlValue = 0;
871                         }
872                         else {
873                                 pos++;
874                         }
875
876                         PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable().get(), controlValue, 0);
877                         PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable().get(), controlValue, 1);
878
879                         if( strip->is_audio_track() ) {
880                                 PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable().get(), controlValue, 2);
881                         }
882
883                         PBD::Controllable::CreateBinding ( strip->gpm.get_controllable().get(), controlValue, 3);
884                         PBD::Controllable::CreateBinding ( strip->panners.get_controllable().get(), controlValue, 4);
885
886                 }
887                 else {  // Remove any existing binding
888                         PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable().get() );
889                         PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable().get() );
890
891                         if( strip->is_audio_track() ) {
892                                 PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable().get() );
893                         }
894
895                         PBD::Controllable::DeleteBinding ( strip->gpm.get_controllable().get() );
896                         PBD::Controllable::DeleteBinding ( strip->panners.get_controllable().get() ); // This only takes the first panner if there are multiples...
897                 }
898
899         } // for
900
901 }
902
903 struct SignalOrderRouteSorter {
904     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
905             /* use of ">" forces the correct sort order */
906             return a->order_key (N_("signal")) < b->order_key (N_("signal"));
907     }
908 };
909
910 void
911 Mixer_UI::initial_track_display ()
912 {
913         boost::shared_ptr<RouteList> routes = _session->get_routes();
914         RouteList copy (*routes);
915         SignalOrderRouteSorter sorter;
916
917         copy.sort (sorter);
918
919         no_track_list_redisplay = true;
920
921         track_model->clear ();
922
923         add_strip (copy);
924
925         no_track_list_redisplay = false;
926
927         redisplay_track_list ();
928 }
929
930 void
931 Mixer_UI::show_track_list_menu ()
932 {
933         if (track_menu == 0) {
934                 build_track_menu ();
935         }
936
937         track_menu->popup (1, gtk_get_current_event_time());
938 }
939
940 bool
941 Mixer_UI::track_display_button_press (GdkEventButton* ev)
942 {
943         if (Keyboard::is_context_menu_event (ev)) {
944                 show_track_list_menu ();
945                 return true;
946         }
947
948         TreeIter iter;
949         TreeModel::Path path;
950         TreeViewColumn* column;
951         int cellx;
952         int celly;
953
954         if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
955                 return false;
956         }
957
958         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
959         case 0:
960                 /* allow normal processing to occur */
961                 return false;
962
963         case 1: /* visibility */
964
965                 if ((iter = track_model->get_iter (path))) {
966                         MixerStrip* strip = (*iter)[track_columns.strip];
967                         if (strip) {
968
969                                 if (!strip->route()->is_master() && !strip->route()->is_control()) {
970                                         bool visible = (*iter)[track_columns.visible];
971                                         (*iter)[track_columns.visible] = !visible;
972                                 }
973 #ifdef GTKOSX
974                                 track_display.queue_draw();
975 #endif
976                         }
977                 }
978                 return true;
979
980         default:
981                 break;
982         }
983
984         return false;
985 }
986
987
988 void
989 Mixer_UI::build_track_menu ()
990 {
991         using namespace Menu_Helpers;
992         using namespace Gtk;
993
994         track_menu = new Menu;
995         track_menu->set_name ("ArdourContextMenu");
996         MenuList& items = track_menu->items();
997
998         items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
999         items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1000         items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1001         items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1002         items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1003         items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1004
1005 }
1006
1007 void
1008 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1009 {
1010         if (!what_changed.contains (ARDOUR::Properties::name)) {
1011                 return;
1012         }
1013
1014         ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1015
1016         TreeModel::Children rows = track_model->children();
1017         TreeModel::Children::iterator i;
1018
1019         for (i = rows.begin(); i != rows.end(); ++i) {
1020                 if ((*i)[track_columns.strip] == mx) {
1021                         (*i)[track_columns.text] = mx->route()->name();
1022                         return;
1023                 }
1024         }
1025
1026         error << _("track display list item for renamed strip not found!") << endmsg;
1027 }
1028
1029
1030 void
1031 Mixer_UI::build_route_group_context_menu ()
1032 {
1033         using namespace Gtk::Menu_Helpers;
1034
1035         route_group_context_menu = new Menu;
1036         route_group_context_menu->set_name ("ArdourContextMenu");
1037         MenuList& items = route_group_context_menu->items();
1038
1039         items.push_back (MenuElem (_("Activate All"), sigc::mem_fun(*this, &Mixer_UI::activate_all_route_groups)));
1040         items.push_back (MenuElem (_("Disable All"), sigc::mem_fun(*this, &Mixer_UI::disable_all_route_groups)));
1041         items.push_back (SeparatorElem());
1042         items.push_back (MenuElem (_("Add group"), sigc::mem_fun(*this, &Mixer_UI::new_route_group)));
1043
1044 }
1045
1046 bool
1047 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1048 {
1049         if (Keyboard::is_context_menu_event (ev)) {
1050                 if (route_group_context_menu == 0) {
1051                         build_route_group_context_menu ();
1052                 }
1053                 route_group_context_menu->popup (1, ev->time);
1054                 return true;
1055         }
1056
1057
1058         RouteGroup* group;
1059         TreeIter iter;
1060         TreeModel::Path path;
1061         TreeViewColumn* column;
1062         int cellx;
1063         int celly;
1064
1065         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1066                 return false;
1067         }
1068
1069         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1070         case 0:
1071                 if (Keyboard::is_edit_event (ev)) {
1072                         if ((iter = group_model->get_iter (path))) {
1073                                 if ((group = (*iter)[group_columns.group]) != 0) {
1074                                         // edit_route_group (group);
1075 #ifdef GTKOSX
1076                                         group_display.queue_draw();
1077 #endif
1078                                         return true;
1079                                 }
1080                         }
1081
1082                 }
1083                 break;
1084
1085         case 1:
1086                 if ((iter = group_model->get_iter (path))) {
1087                         bool visible = (*iter)[group_columns.visible];
1088                         (*iter)[group_columns.visible] = !visible;
1089 #ifdef GTKOSX
1090                         group_display.queue_draw();
1091 #endif
1092                         return true;
1093                 }
1094                 break;
1095
1096         default:
1097                 break;
1098         }
1099
1100         return false;
1101  }
1102
1103 void
1104 Mixer_UI::activate_all_route_groups ()
1105 {
1106         _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1107 }
1108
1109 void
1110 Mixer_UI::disable_all_route_groups ()
1111 {
1112         _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1113 }
1114
1115 void
1116 Mixer_UI::route_groups_changed ()
1117 {
1118         ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed)
1119
1120         /* just rebuild the while thing */
1121
1122         group_model->clear ();
1123
1124         {
1125                 TreeModel::Row row;
1126                 row = *(group_model->append());
1127                 row[group_columns.visible] = true;
1128                 row[group_columns.text] = (_("-all-"));
1129                 row[group_columns.group] = 0;
1130         }
1131
1132         _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1133 }
1134
1135 void
1136 Mixer_UI::new_route_group ()
1137 {
1138         PropertyList plist;
1139
1140         plist.add (Properties::active, true);
1141         plist.add (Properties::gain, true);
1142         plist.add (Properties::mute, true);
1143         plist.add (Properties::solo, true);
1144
1145         RouteGroup* g = new RouteGroup (*_session, "");
1146         g->set_properties (plist);
1147
1148         _session->add_route_group (g);
1149 }
1150
1151 void
1152 Mixer_UI::remove_selected_route_group ()
1153 {
1154         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1155         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1156
1157         if (rows.empty()) {
1158                 return;
1159         }
1160
1161         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1162         TreeIter iter;
1163
1164         /* selection mode is single, so rows.begin() is it */
1165
1166         if ((iter = group_model->get_iter (*i))) {
1167
1168                 RouteGroup* rg = (*iter)[group_columns.group];
1169
1170                 if (rg) {
1171                         _session->remove_route_group (*rg);
1172                 }
1173         }
1174 }
1175
1176 void
1177 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
1178 {
1179         if (in_group_row_change) {
1180                 return;
1181         }
1182
1183         ENSURE_GUI_THREAD (*this, &Mixer_UI::group_flags_changed, src, group)
1184
1185         /* force an update of any mixer strips that are using this group,
1186            otherwise mix group names don't change in mixer strips
1187         */
1188
1189         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1190                 if ((*i)->route_group() == group) {
1191                         (*i)->route_group_changed();
1192                 }
1193         }
1194
1195         TreeModel::iterator i;
1196         TreeModel::Children rows = group_model->children();
1197         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1198
1199         in_group_row_change = true;
1200
1201         for (i = rows.begin(); i != rows.end(); ++i) {
1202                 if ((*i)[group_columns.group] == group) {
1203                         (*i)[group_columns.visible] = !group->is_hidden ();
1204                         (*i)[group_columns.text] = group->name ();
1205                         break;
1206                 }
1207         }
1208
1209         in_group_row_change = false;
1210
1211         _group_tabs->set_dirty ();
1212 }
1213
1214 void
1215 Mixer_UI::route_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1216 {
1217         RouteGroup* group;
1218         TreeIter iter;
1219
1220         if ((iter = group_model->get_iter (path))) {
1221
1222                 if ((group = (*iter)[group_columns.group]) == 0) {
1223                         return;
1224                 }
1225
1226                 if (new_text != group->name()) {
1227                         group->set_name (new_text);
1228                 }
1229         }
1230 }
1231
1232 void
1233 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1234 {
1235         RouteGroup* group;
1236
1237         if (in_group_row_change) {
1238                 return;
1239         }
1240
1241         if ((group = (*iter)[group_columns.group]) == 0) {
1242                 return;
1243         }
1244
1245         if ((*iter)[group_columns.visible]) {
1246                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1247                         if ((*i)->route_group() == group) {
1248                                 show_strip (*i);
1249                         }
1250                 }
1251         } else {
1252                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1253                         if ((*i)->route_group() == group) {
1254                                 hide_strip (*i);
1255                         }
1256                 }
1257         }
1258
1259         Glib::ustring name = (*iter)[group_columns.text];
1260
1261         if (name != group->name()) {
1262                 group->set_name (name);
1263         }
1264
1265 }
1266
1267 void
1268 Mixer_UI::add_route_group (RouteGroup* group)
1269 {
1270         ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1271         bool focus = false;
1272
1273         in_group_row_change = true;
1274
1275         TreeModel::Row row = *(group_model->append());
1276         row[group_columns.visible] = true;
1277         row[group_columns.group] = group;
1278         if (!group->name().empty()) {
1279                 row[group_columns.text] = group->name();
1280         } else {
1281                 row[group_columns.text] = _("unnamed");
1282                 focus = true;
1283         }
1284
1285         group->FlagsChanged.connect (*this, ui_bind (&Mixer_UI::group_flags_changed, this, _1, group), gui_context());
1286
1287         if (focus) {
1288                 TreeViewColumn* col = group_display.get_column (0);
1289                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1290                 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1291         }
1292
1293         in_group_row_change = false;
1294 }
1295
1296 bool
1297 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1298 {
1299         using namespace Menu_Helpers;
1300
1301         if (Keyboard::is_context_menu_event (ev)) {
1302                 ARDOUR_UI::instance()->add_route (this);
1303                 return true;
1304         }
1305
1306         return false;
1307 }
1308
1309 void
1310 Mixer_UI::set_strip_width (Width w)
1311 {
1312         _strip_width = w;
1313
1314         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1315                 (*i)->set_width_enum (w, this);
1316         }
1317 }
1318
1319 void
1320 Mixer_UI::set_window_pos_and_size ()
1321 {
1322         resize (m_width, m_height);
1323         move (m_root_x, m_root_y);
1324 }
1325
1326         void
1327 Mixer_UI::get_window_pos_and_size ()
1328 {
1329         get_position(m_root_x, m_root_y);
1330         get_size(m_width, m_height);
1331 }
1332
1333 int
1334 Mixer_UI::set_state (const XMLNode& node)
1335 {
1336         const XMLProperty* prop;
1337         XMLNode* geometry;
1338
1339         m_width = default_width;
1340         m_height = default_height;
1341         m_root_x = 1;
1342         m_root_y = 1;
1343
1344         if ((geometry = find_named_node (node, "geometry")) != 0) {
1345
1346                 XMLProperty* prop;
1347
1348                 if ((prop = geometry->property("x_size")) == 0) {
1349                         prop = geometry->property ("x-size");
1350                 }
1351                 if (prop) {
1352                         m_width = atoi(prop->value());
1353                 }
1354                 if ((prop = geometry->property("y_size")) == 0) {
1355                         prop = geometry->property ("y-size");
1356                 }
1357                 if (prop) {
1358                         m_height = atoi(prop->value());
1359                 }
1360
1361                 if ((prop = geometry->property ("x_pos")) == 0) {
1362                         prop = geometry->property ("x-pos");
1363                 }
1364                 if (prop) {
1365                         m_root_x = atoi (prop->value());
1366
1367                 }
1368                 if ((prop = geometry->property ("y_pos")) == 0) {
1369                         prop = geometry->property ("y-pos");
1370                 }
1371                 if (prop) {
1372                         m_root_y = atoi (prop->value());
1373                 }
1374         }
1375
1376         set_window_pos_and_size ();
1377
1378         if ((prop = node.property ("narrow-strips"))) {
1379                 if (string_is_affirmative (prop->value())) {
1380                         set_strip_width (Narrow);
1381                 } else {
1382                         set_strip_width (Wide);
1383                 }
1384         }
1385
1386         if ((prop = node.property ("show-mixer"))) {
1387                 if (string_is_affirmative (prop->value())) {
1388                        _visible = true;
1389                 }
1390         }
1391
1392         return 0;
1393 }
1394
1395 XMLNode&
1396 Mixer_UI::get_state (void)
1397 {
1398         XMLNode* node = new XMLNode ("Mixer");
1399
1400         if (is_realized()) {
1401                 Glib::RefPtr<Gdk::Window> win = get_window();
1402
1403                 get_window_pos_and_size ();
1404
1405                 XMLNode* geometry = new XMLNode ("geometry");
1406                 char buf[32];
1407                 snprintf(buf, sizeof(buf), "%d", m_width);
1408                 geometry->add_property(X_("x_size"), string(buf));
1409                 snprintf(buf, sizeof(buf), "%d", m_height);
1410                 geometry->add_property(X_("y_size"), string(buf));
1411                 snprintf(buf, sizeof(buf), "%d", m_root_x);
1412                 geometry->add_property(X_("x_pos"), string(buf));
1413                 snprintf(buf, sizeof(buf), "%d", m_root_y);
1414                 geometry->add_property(X_("y_pos"), string(buf));
1415
1416                 // written only for compatibility, they are not used.
1417                 snprintf(buf, sizeof(buf), "%d", 0);
1418                 geometry->add_property(X_("x_off"), string(buf));
1419                 snprintf(buf, sizeof(buf), "%d", 0);
1420                 geometry->add_property(X_("y_off"), string(buf));
1421
1422                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1423                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1424                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1425                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1426
1427                 node->add_child_nocopy (*geometry);
1428         }
1429
1430         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1431
1432         node->add_property ("show-mixer", _visible ? "yes" : "no");
1433
1434         return *node;
1435 }
1436
1437
1438 void
1439 Mixer_UI::pane_allocation_handler (Allocation&, Gtk::Paned* which)
1440 {
1441         int pos;
1442         XMLProperty* prop = 0;
1443         char buf[32];
1444         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1445         XMLNode* geometry;
1446         int width, height;
1447         static int32_t done[3] = { 0, 0, 0 };
1448
1449         width = default_width;
1450         height = default_height;
1451
1452         if ((geometry = find_named_node (*node, "geometry")) != 0) {
1453
1454
1455                 if ((prop = geometry->property ("x_size")) == 0) {
1456                         prop = geometry->property ("x-size");
1457                 }
1458                 if (prop) {
1459                         width = atoi (prop->value());
1460                 }
1461                 if ((prop = geometry->property ("y_size")) == 0) {
1462                         prop = geometry->property ("y-size");
1463                 }
1464                 if (prop) {
1465                         height = atoi (prop->value());
1466                 }
1467         }
1468
1469         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1470
1471                 if (done[0]) {
1472                         return;
1473                 }
1474
1475                 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1476                         pos = height / 3;
1477                         snprintf (buf, sizeof(buf), "%d", pos);
1478                 } else {
1479                         pos = atoi (prop->value());
1480                 }
1481
1482                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1483                         rhs_pane1.set_position (pos);
1484                 }
1485
1486         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1487
1488                 if (done[2]) {
1489                         return;
1490                 }
1491
1492                 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
1493                         pos = 75;
1494                         snprintf (buf, sizeof(buf), "%d", pos);
1495                 } else {
1496                         pos = atoi (prop->value());
1497                 }
1498
1499                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1500                         list_hpane.set_position (pos);
1501                 }
1502         }
1503 }
1504 void
1505 Mixer_UI::scroll_left () 
1506 {
1507         Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1508         /* stupid GTK: can't rely on clamping across versions */
1509         scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1510 }
1511
1512 void
1513 Mixer_UI::scroll_right ()
1514 {
1515         Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1516         /* stupid GTK: can't rely on clamping across versions */
1517         scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1518 }
1519
1520 bool
1521 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1522 {
1523         switch (ev->keyval) {
1524         case GDK_Left:
1525                 scroll_left ();
1526                 return true;
1527
1528         case GDK_Right:
1529                 scroll_right ();
1530                 return true;
1531
1532         default:
1533                 break;
1534         }
1535
1536         return key_press_focus_accelerator_handler (*this, ev);
1537 }
1538
1539 bool
1540 Mixer_UI::on_key_release_event (GdkEventKey* ev)
1541 {
1542         return Gtk::Window::on_key_release_event (ev);
1543         // return key_press_focus_accelerator_handler (*this, ev);
1544 }
1545
1546
1547 bool
1548 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1549 {
1550         switch (ev->direction) {
1551         case GDK_SCROLL_LEFT:
1552                 scroll_left ();
1553                 return true;
1554         case GDK_SCROLL_UP:
1555                 if (ev->state & Keyboard::TertiaryModifier) {
1556                         scroll_left ();
1557                         return true;
1558                 }
1559                 return false;
1560
1561         case GDK_SCROLL_RIGHT:
1562                 scroll_right ();
1563                 return true;
1564
1565         case GDK_SCROLL_DOWN:
1566                 if (ev->state & Keyboard::TertiaryModifier) {
1567                         scroll_right ();
1568                         return true;
1569                 }
1570                 return false;
1571         }
1572
1573         return false;
1574 }
1575
1576
1577 void
1578 Mixer_UI::parameter_changed (string const & p)
1579 {
1580         if (p == "show-group-tabs") {
1581                 bool const s = _session->config.get_show_group_tabs ();
1582                 if (s) {
1583                         _group_tabs->show ();
1584                 } else {
1585                         _group_tabs->hide ();
1586                 }
1587         }
1588 }
1589
1590 void
1591 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
1592 {
1593         g->set_active (a, this);
1594 }
1595
1596 PluginSelector*
1597 Mixer_UI::plugin_selector()
1598 {
1599 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
1600         if (!_plugin_selector)
1601                 _plugin_selector = new PluginSelector (PluginManager::the_manager ());
1602 #endif
1603
1604         return _plugin_selector;
1605 }