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