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