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