9d918d4e3b08d82f2bce75b1b8516fac61233a92
[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                 keys[new_key] = old_key;
397
398                 if (new_key != old_key) {
399                         changed = true;
400                 }
401         }
402         assert(keys.size() == rows.size());
403
404         // Remove any gaps in keys caused by automation children tracks
405         vector<int> neworder;
406         for (std::map<int,int>::const_iterator i = keys.begin(); i != keys.end(); ++i) {
407                 neworder.push_back(i->second);
408         }
409         assert(neworder.size() == rows.size());
410
411         if (changed) {
412                 strip_redisplay_does_not_reset_order_keys = true;
413                 track_model->reorder (neworder);
414                 strip_redisplay_does_not_reset_order_keys = false;
415         }
416 }
417
418 void
419 Mixer_UI::follow_strip_selection ()
420 {
421         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
422                 (*i)->set_selected (_selection.selected ((*i)->route()));
423         }
424 }
425
426 bool
427 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
428 {
429         if (ev->button == 1) {
430
431                 /* this allows the user to click on the strip to terminate comment
432                    editing. XXX it needs improving so that we don't select the strip
433                    at the same time.
434                 */
435
436                 if (_selection.selected (strip->route())) {
437                         _selection.remove (strip->route());
438                 } else {
439                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
440                                 _selection.add (strip->route());
441                         } else {
442                                 _selection.set (strip->route());
443                         }
444                 }
445         }
446
447         return true;
448 }
449
450 void
451 Mixer_UI::connect_to_session (Session* sess)
452 {
453         session = sess;
454
455         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
456         set_state (*node);
457
458         WindowTitle title(session->name());
459         title += _("Mixer");
460         title += Glib::get_application_name();
461
462         set_title (title.get_string());
463
464         initial_track_display ();
465
466         session->GoingAway.connect (mem_fun(*this, &Mixer_UI::disconnect_from_session));
467         session->RouteAdded.connect (mem_fun(*this, &Mixer_UI::add_strip));
468         session->route_group_added.connect (mem_fun(*this, &Mixer_UI::add_route_group));
469         session->route_group_removed.connect (mem_fun(*this, &Mixer_UI::route_groups_changed));
470         session->config.ParameterChanged.connect (mem_fun (*this, &Mixer_UI::parameter_changed));
471
472         route_groups_changed ();
473
474         if (_plugin_selector)
475                 _plugin_selector->set_session (session);
476
477         if (_visible) {
478                show_window();
479         }
480
481         _group_tabs->connect_to_session (sess);
482
483         start_updating ();
484 }
485
486 void
487 Mixer_UI::disconnect_from_session ()
488 {
489         ENSURE_GUI_THREAD(mem_fun(*this, &Mixer_UI::disconnect_from_session));
490
491         group_model->clear ();
492         _selection.clear ();
493
494         WindowTitle title(Glib::get_application_name());
495         title += _("Mixer");
496         set_title (title.get_string());
497
498         stop_updating ();
499 }
500
501 void
502 Mixer_UI::show_strip (MixerStrip* ms)
503 {
504         TreeModel::Children rows = track_model->children();
505         TreeModel::Children::iterator i;
506
507         for (i = rows.begin(); i != rows.end(); ++i) {
508
509                 MixerStrip* strip = (*i)[track_columns.strip];
510                 if (strip == ms) {
511                         (*i)[track_columns.visible] = true;
512                         break;
513                 }
514         }
515 }
516
517 void
518 Mixer_UI::hide_strip (MixerStrip* ms)
519 {
520         TreeModel::Children rows = track_model->children();
521         TreeModel::Children::iterator i;
522
523         for (i = rows.begin(); i != rows.end(); ++i) {
524
525                 MixerStrip* strip = (*i)[track_columns.strip];
526                 if (strip == ms) {
527                         (*i)[track_columns.visible] = false;
528                         break;
529                 }
530         }
531 }
532
533 gint
534 Mixer_UI::start_updating ()
535 {
536     fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::fast_update_strips));
537     return 0;
538 }
539
540 gint
541 Mixer_UI::stop_updating ()
542 {
543     fast_screen_update_connection.disconnect();
544     return 0;
545 }
546
547 void
548 Mixer_UI::fast_update_strips ()
549 {
550         if (is_mapped () && session) {
551                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
552                         (*i)->fast_update ();
553                 }
554         }
555 }
556
557 void
558 Mixer_UI::set_all_strips_visibility (bool yn)
559 {
560         TreeModel::Children rows = track_model->children();
561         TreeModel::Children::iterator i;
562
563         no_track_list_redisplay = true;
564
565         for (i = rows.begin(); i != rows.end(); ++i) {
566
567                 TreeModel::Row row = (*i);
568                 MixerStrip* strip = row[track_columns.strip];
569
570                 if (strip == 0) {
571                         continue;
572                 }
573
574                 if (strip->route()->is_master() || strip->route()->is_control()) {
575                         continue;
576                 }
577
578                 (*i)[track_columns.visible] = yn;
579         }
580
581         no_track_list_redisplay = false;
582         redisplay_track_list ();
583 }
584
585
586 void
587 Mixer_UI::set_all_audio_visibility (int tracks, bool yn)
588 {
589         TreeModel::Children rows = track_model->children();
590         TreeModel::Children::iterator i;
591
592         no_track_list_redisplay = true;
593
594         for (i = rows.begin(); i != rows.end(); ++i) {
595                 TreeModel::Row row = (*i);
596                 MixerStrip* strip = row[track_columns.strip];
597
598                 if (strip == 0) {
599                         continue;
600                 }
601
602                 if (strip->route()->is_master() || strip->route()->is_control()) {
603                         continue;
604                 }
605
606                 boost::shared_ptr<AudioTrack> at = strip->audio_track();
607
608                 switch (tracks) {
609                 case 0:
610                         (*i)[track_columns.visible] = yn;
611                         break;
612
613                 case 1:
614                         if (at) { /* track */
615                                 (*i)[track_columns.visible] = yn;
616                         }
617                         break;
618
619                 case 2:
620                         if (!at) { /* bus */
621                                 (*i)[track_columns.visible] = yn;
622                         }
623                         break;
624                 }
625         }
626
627         no_track_list_redisplay = false;
628         redisplay_track_list ();
629 }
630
631 void
632 Mixer_UI::hide_all_routes ()
633 {
634         set_all_strips_visibility (false);
635 }
636
637 void
638 Mixer_UI::show_all_routes ()
639 {
640         set_all_strips_visibility (true);
641 }
642
643 void
644 Mixer_UI::show_all_audiobus ()
645 {
646         set_all_audio_visibility (2, true);
647 }
648 void
649 Mixer_UI::hide_all_audiobus ()
650 {
651         set_all_audio_visibility (2, false);
652 }
653
654 void
655 Mixer_UI::show_all_audiotracks()
656 {
657         set_all_audio_visibility (1, true);
658 }
659 void
660 Mixer_UI::hide_all_audiotracks ()
661 {
662         set_all_audio_visibility (1, false);
663 }
664
665 void
666 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
667 {
668         strip_redisplay_does_not_sync_order_keys = true;
669         session->set_remote_control_ids();
670         redisplay_track_list ();
671         strip_redisplay_does_not_sync_order_keys = false;
672 }
673
674 void
675 Mixer_UI::track_list_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator&)
676 {
677         // never reset order keys because of a property change
678         strip_redisplay_does_not_reset_order_keys = true;
679         session->set_remote_control_ids();
680         redisplay_track_list ();
681         strip_redisplay_does_not_reset_order_keys = false;
682 }
683
684 void
685 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
686 {
687         /* this could require an order sync */
688         session->set_remote_control_ids();
689         redisplay_track_list ();
690 }
691
692 void
693 Mixer_UI::redisplay_track_list ()
694 {
695         TreeModel::Children rows = track_model->children();
696         TreeModel::Children::iterator i;
697         long order;
698
699         if (no_track_list_redisplay) {
700                 return;
701         }
702
703         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
704                 MixerStrip* strip = (*i)[track_columns.strip];
705
706                 if (strip == 0) {
707                         /* we're in the middle of changing a row, don't worry */
708                         continue;
709                 }
710
711                 bool visible = (*i)[track_columns.visible];
712
713                 if (visible) {
714                         strip->set_marked_for_display (true);
715                         strip->route()->set_order_key (N_("signal"), order);
716
717                         if (!strip_redisplay_does_not_reset_order_keys) {
718                                 strip->route()->set_order_key (N_("signal"), order);
719                         }
720
721                         if (strip->packed()) {
722
723                                 if (strip->route()->is_master() || strip->route()->is_control()) {
724                                         out_packer.reorder_child (*strip, -1);
725                                 } else {
726                                         strip_packer.reorder_child (*strip, -1); /* put at end */
727                                 }
728
729                         } else {
730
731                                 if (strip->route()->is_master() || strip->route()->is_control()) {
732                                         out_packer.pack_start (*strip, false, false);
733                                 } else {
734                                         strip_packer.pack_start (*strip, false, false);
735                                 }
736                                 strip->set_packed (true);
737                                 //strip->show();
738                         }
739
740                 } else {
741
742                         strip->set_marked_for_display (false);
743
744                         if (strip->route()->is_master() || strip->route()->is_control()) {
745                                 /* do nothing, these cannot be hidden */
746                         } else {
747                                 if (strip->packed()) {
748                                         strip_packer.remove (*strip);
749                                         strip->set_packed (false);
750                                 }
751                         }
752                 }
753         }
754
755         if (!strip_redisplay_does_not_reset_order_keys && !strip_redisplay_does_not_sync_order_keys) {
756                 session->sync_order_keys (N_("signal"));
757         }
758
759         // Rebind all of the midi controls automatically
760
761         if (auto_rebinding)
762                 auto_rebind_midi_controls ();
763
764         _group_tabs->set_dirty ();
765 }
766
767 void
768 Mixer_UI::strip_width_changed ()
769 {
770         _group_tabs->set_dirty ();
771
772 #ifdef GTKOSX
773         TreeModel::Children rows = track_model->children();
774         TreeModel::Children::iterator i;
775         long order;
776
777         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
778                 MixerStrip* strip = (*i)[track_columns.strip];
779
780                 if (strip == 0) {
781                         continue;
782                 }
783
784                 bool visible = (*i)[track_columns.visible];
785
786                 if (visible) {
787                         strip->queue_draw();
788                 }
789         }
790 #endif
791
792 }
793
794 void
795 Mixer_UI::set_auto_rebinding( bool val )
796 {
797         if( val == TRUE )
798         {
799                 auto_rebinding = TRUE;
800                 Session::AutoBindingOff();
801         }
802         else
803         {
804                 auto_rebinding = FALSE;
805                 Session::AutoBindingOn();
806         }
807 }
808
809 void
810 Mixer_UI::toggle_auto_rebinding()
811 {
812         if (auto_rebinding)
813         {
814                 set_auto_rebinding( FALSE );
815         }
816
817         else
818         {
819                 set_auto_rebinding( TRUE );
820         }
821
822         auto_rebind_midi_controls();
823 }
824
825 void
826 Mixer_UI::auto_rebind_midi_controls ()
827 {
828         TreeModel::Children rows = track_model->children();
829         TreeModel::Children::iterator i;
830         int pos;
831
832         // Create bindings for all visible strips and remove those that are not visible
833         pos = 1;  // 0 is reserved for the master strip
834         for (i = rows.begin(); i != rows.end(); ++i) {
835                 MixerStrip* strip = (*i)[track_columns.strip];
836
837                 if ( (*i)[track_columns.visible] == true ) {  // add bindings for
838                         // make the actual binding
839                         //cout<<"Auto Binding:  Visible Strip Found: "<<strip->name()<<endl;
840
841                         int controlValue = pos;
842                         if( strip->route()->is_master() ) {
843                                 controlValue = 0;
844                         }
845                         else {
846                                 pos++;
847                         }
848
849                         PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable().get(), controlValue, 0);
850                         PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable().get(), controlValue, 1);
851
852                         if( strip->is_audio_track() ) {
853                                 PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable().get(), controlValue, 2);
854                         }
855
856                         PBD::Controllable::CreateBinding ( strip->gpm.get_controllable().get(), controlValue, 3);
857                         PBD::Controllable::CreateBinding ( strip->panners.get_controllable().get(), controlValue, 4);
858
859                 }
860                 else {  // Remove any existing binding
861                         PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable().get() );
862                         PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable().get() );
863
864                         if( strip->is_audio_track() ) {
865                                 PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable().get() );
866                         }
867
868                         PBD::Controllable::DeleteBinding ( strip->gpm.get_controllable().get() );
869                         PBD::Controllable::DeleteBinding ( strip->panners.get_controllable().get() ); // This only takes the first panner if there are multiples...
870                 }
871
872         } // for
873
874 }
875
876 struct SignalOrderRouteSorter {
877     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
878             /* use of ">" forces the correct sort order */
879             return a->order_key (N_("signal")) < b->order_key (N_("signal"));
880     }
881 };
882
883 void
884 Mixer_UI::initial_track_display ()
885 {
886         boost::shared_ptr<RouteList> routes = session->get_routes();
887         RouteList copy (*routes);
888         SignalOrderRouteSorter sorter;
889
890         copy.sort (sorter);
891
892         no_track_list_redisplay = true;
893
894         track_model->clear ();
895
896         add_strip (copy);
897
898         no_track_list_redisplay = false;
899
900         redisplay_track_list ();
901 }
902
903 void
904 Mixer_UI::show_track_list_menu ()
905 {
906         if (track_menu == 0) {
907                 build_track_menu ();
908         }
909
910         track_menu->popup (1, gtk_get_current_event_time());
911 }
912
913 bool
914 Mixer_UI::track_display_button_press (GdkEventButton* ev)
915 {
916         if (Keyboard::is_context_menu_event (ev)) {
917                 show_track_list_menu ();
918                 return true;
919         }
920
921         TreeIter iter;
922         TreeModel::Path path;
923         TreeViewColumn* column;
924         int cellx;
925         int celly;
926
927         if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
928                 return false;
929         }
930
931         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
932         case 0:
933                 /* allow normal processing to occur */
934                 return false;
935
936         case 1: /* visibility */
937
938                 if ((iter = track_model->get_iter (path))) {
939                         MixerStrip* strip = (*iter)[track_columns.strip];
940                         if (strip) {
941
942                                 if (!strip->route()->is_master() && !strip->route()->is_control()) {
943                                         bool visible = (*iter)[track_columns.visible];
944                                         (*iter)[track_columns.visible] = !visible;
945                                 }
946 #ifdef GTKOSX
947                                 track_display.queue_draw();
948 #endif
949                         }
950                 }
951                 return true;
952
953         default:
954                 break;
955         }
956
957         return false;
958 }
959
960
961 void
962 Mixer_UI::build_track_menu ()
963 {
964         using namespace Menu_Helpers;
965         using namespace Gtk;
966
967         track_menu = new Menu;
968         track_menu->set_name ("ArdourContextMenu");
969         MenuList& items = track_menu->items();
970
971         items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::show_all_routes)));
972         items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::hide_all_routes)));
973         items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
974         items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
975         items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Mixer_UI::show_all_audiobus)));
976         items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
977
978 }
979
980 void
981 Mixer_UI::strip_name_changed (MixerStrip* mx)
982 {
983         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::strip_name_changed), mx));
984
985         TreeModel::Children rows = track_model->children();
986         TreeModel::Children::iterator i;
987
988         for (i = rows.begin(); i != rows.end(); ++i) {
989                 if ((*i)[track_columns.strip] == mx) {
990                         (*i)[track_columns.text] = mx->route()->name();
991                         return;
992                 }
993         }
994
995         error << _("track display list item for renamed strip not found!") << endmsg;
996 }
997
998
999 void
1000 Mixer_UI::build_route_group_context_menu ()
1001 {
1002         using namespace Gtk::Menu_Helpers;
1003
1004         route_group_context_menu = new Menu;
1005         route_group_context_menu->set_name ("ArdourContextMenu");
1006         MenuList& items = route_group_context_menu->items();
1007
1008         items.push_back (MenuElem (_("Activate All"), mem_fun(*this, &Mixer_UI::activate_all_route_groups)));
1009         items.push_back (MenuElem (_("Disable All"), mem_fun(*this, &Mixer_UI::disable_all_route_groups)));
1010         items.push_back (SeparatorElem());
1011         items.push_back (MenuElem (_("Add group"), mem_fun(*this, &Mixer_UI::new_route_group)));
1012
1013 }
1014
1015 bool
1016 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1017 {
1018         if (Keyboard::is_context_menu_event (ev)) {
1019                 if (route_group_context_menu == 0) {
1020                         build_route_group_context_menu ();
1021                 }
1022                 route_group_context_menu->popup (1, ev->time);
1023                 return true;
1024         }
1025
1026
1027         RouteGroup* group;
1028         TreeIter iter;
1029         TreeModel::Path path;
1030         TreeViewColumn* column;
1031         int cellx;
1032         int celly;
1033
1034         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1035                 return false;
1036         }
1037
1038         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1039         case 0:
1040                 if (Keyboard::is_edit_event (ev)) {
1041                         if ((iter = group_model->get_iter (path))) {
1042                                 if ((group = (*iter)[group_columns.group]) != 0) {
1043                                         // edit_route_group (group);
1044 #ifdef GTKOSX
1045                                         group_display.queue_draw();
1046 #endif
1047                                         return true;
1048                                 }
1049                         }
1050
1051                 }
1052                 break;
1053
1054         case 1:
1055                 if ((iter = group_model->get_iter (path))) {
1056                         bool visible = (*iter)[group_columns.visible];
1057                         (*iter)[group_columns.visible] = !visible;
1058 #ifdef GTKOSX
1059                         group_display.queue_draw();
1060 #endif
1061                         return true;
1062                 }
1063                 break;
1064
1065         default:
1066                 break;
1067         }
1068
1069         return false;
1070  }
1071
1072 void
1073 Mixer_UI::activate_all_route_groups ()
1074 {
1075         session->foreach_route_group (bind (mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1076 }
1077
1078 void
1079 Mixer_UI::disable_all_route_groups ()
1080 {
1081         session->foreach_route_group (bind (mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1082 }
1083
1084 void
1085 Mixer_UI::route_groups_changed ()
1086 {
1087         ENSURE_GUI_THREAD (mem_fun (*this, &Mixer_UI::route_groups_changed));
1088
1089         /* just rebuild the while thing */
1090
1091         group_model->clear ();
1092
1093         {
1094                 TreeModel::Row row;
1095                 row = *(group_model->append());
1096                 row[group_columns.visible] = true;
1097                 row[group_columns.text] = (_("-all-"));
1098                 row[group_columns.group] = 0;
1099         }
1100
1101         session->foreach_route_group (mem_fun (*this, &Mixer_UI::add_route_group));
1102 }
1103
1104 void
1105 Mixer_UI::new_route_group ()
1106 {
1107         session->add_route_group (new RouteGroup (*session, "", RouteGroup::Active, (RouteGroup::Property) (RouteGroup::Gain |RouteGroup::Mute | RouteGroup::Solo)));
1108 }
1109
1110 void
1111 Mixer_UI::remove_selected_route_group ()
1112 {
1113         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1114         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1115
1116         if (rows.empty()) {
1117                 return;
1118         }
1119
1120         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1121         TreeIter iter;
1122
1123         /* selection mode is single, so rows.begin() is it */
1124
1125         if ((iter = group_model->get_iter (*i))) {
1126
1127                 RouteGroup* rg = (*iter)[group_columns.group];
1128
1129                 if (rg) {
1130                         session->remove_route_group (*rg);
1131                 }
1132         }
1133 }
1134
1135 void
1136 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
1137 {
1138         if (in_group_row_change) {
1139                 return;
1140         }
1141
1142         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
1143
1144         /* force an update of any mixer strips that are using this group,
1145            otherwise mix group names don't change in mixer strips
1146         */
1147
1148         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1149                 if ((*i)->route_group() == group) {
1150                         (*i)->route_group_changed(0);
1151                 }
1152         }
1153
1154         TreeModel::iterator i;
1155         TreeModel::Children rows = group_model->children();
1156         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1157
1158         in_group_row_change = true;
1159
1160         for (i = rows.begin(); i != rows.end(); ++i) {
1161                 if ((*i)[group_columns.group] == group) {
1162                         (*i)[group_columns.visible] = !group->is_hidden ();
1163                         (*i)[group_columns.text] = group->name ();
1164                         break;
1165                 }
1166         }
1167
1168         in_group_row_change = false;
1169
1170         _group_tabs->set_dirty ();
1171 }
1172
1173 void
1174 Mixer_UI::route_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1175 {
1176         RouteGroup* group;
1177         TreeIter iter;
1178
1179         if ((iter = group_model->get_iter (path))) {
1180
1181                 if ((group = (*iter)[group_columns.group]) == 0) {
1182                         return;
1183                 }
1184
1185                 if (new_text != group->name()) {
1186                         group->set_name (new_text);
1187                 }
1188         }
1189 }
1190
1191 void
1192 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1193 {
1194         RouteGroup* group;
1195
1196         if (in_group_row_change) {
1197                 return;
1198         }
1199
1200         if ((group = (*iter)[group_columns.group]) == 0) {
1201                 return;
1202         }
1203
1204         if ((*iter)[group_columns.visible]) {
1205                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1206                         if ((*i)->route_group() == group) {
1207                                 show_strip (*i);
1208                         }
1209                 }
1210         } else {
1211                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1212                         if ((*i)->route_group() == group) {
1213                                 hide_strip (*i);
1214                         }
1215                 }
1216         }
1217
1218         Glib::ustring name = (*iter)[group_columns.text];
1219
1220         if (name != group->name()) {
1221                 group->set_name (name);
1222         }
1223
1224 }
1225
1226 void
1227 Mixer_UI::add_route_group (RouteGroup* group)
1228 {
1229         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_route_group), group));
1230         bool focus = false;
1231
1232         in_group_row_change = true;
1233
1234         TreeModel::Row row = *(group_model->append());
1235         row[group_columns.visible] = true;
1236         row[group_columns.group] = group;
1237         if (!group->name().empty()) {
1238                 row[group_columns.text] = group->name();
1239         } else {
1240                 row[group_columns.text] = _("unnamed");
1241                 focus = true;
1242         }
1243
1244         group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
1245
1246         if (focus) {
1247                 TreeViewColumn* col = group_display.get_column (0);
1248                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1249                 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1250         }
1251
1252         in_group_row_change = false;
1253 }
1254
1255 bool
1256 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1257 {
1258         using namespace Menu_Helpers;
1259
1260         if (Keyboard::is_context_menu_event (ev)) {
1261                 ARDOUR_UI::instance()->add_route (this);
1262                 return true;
1263         }
1264
1265         return false;
1266 }
1267
1268 void
1269 Mixer_UI::set_strip_width (Width w)
1270 {
1271         _strip_width = w;
1272
1273         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1274                 (*i)->set_width_enum (w, this);
1275         }
1276 }
1277
1278 void
1279 Mixer_UI::set_window_pos_and_size ()
1280 {
1281         resize (m_width, m_height);
1282         move (m_root_x, m_root_y);
1283 }
1284
1285         void
1286 Mixer_UI::get_window_pos_and_size ()
1287 {
1288         get_position(m_root_x, m_root_y);
1289         get_size(m_width, m_height);
1290 }
1291
1292 int
1293 Mixer_UI::set_state (const XMLNode& node)
1294 {
1295         const XMLProperty* prop;
1296         XMLNode* geometry;
1297
1298         m_width = default_width;
1299         m_height = default_height;
1300         m_root_x = 1;
1301         m_root_y = 1;
1302
1303         if ((geometry = find_named_node (node, "geometry")) != 0) {
1304
1305                 XMLProperty* prop;
1306
1307                 if ((prop = geometry->property("x_size")) == 0) {
1308                         prop = geometry->property ("x-size");
1309                 }
1310                 if (prop) {
1311                         m_width = atoi(prop->value());
1312                 }
1313                 if ((prop = geometry->property("y_size")) == 0) {
1314                         prop = geometry->property ("y-size");
1315                 }
1316                 if (prop) {
1317                         m_height = atoi(prop->value());
1318                 }
1319
1320                 if ((prop = geometry->property ("x_pos")) == 0) {
1321                         prop = geometry->property ("x-pos");
1322                 }
1323                 if (prop) {
1324                         m_root_x = atoi (prop->value());
1325
1326                 }
1327                 if ((prop = geometry->property ("y_pos")) == 0) {
1328                         prop = geometry->property ("y-pos");
1329                 }
1330                 if (prop) {
1331                         m_root_y = atoi (prop->value());
1332                 }
1333         }
1334
1335         set_window_pos_and_size ();
1336
1337         if ((prop = node.property ("narrow-strips"))) {
1338                 if (string_is_affirmative (prop->value())) {
1339                         set_strip_width (Narrow);
1340                 } else {
1341                         set_strip_width (Wide);
1342                 }
1343         }
1344
1345         if ((prop = node.property ("show-mixer"))) {
1346                 if (string_is_affirmative (prop->value())) {
1347                        _visible = true;
1348                 }
1349         }
1350
1351         return 0;
1352 }
1353
1354 XMLNode&
1355 Mixer_UI::get_state (void)
1356 {
1357         XMLNode* node = new XMLNode ("Mixer");
1358
1359         if (is_realized()) {
1360                 Glib::RefPtr<Gdk::Window> win = get_window();
1361
1362                 get_window_pos_and_size ();
1363
1364                 XMLNode* geometry = new XMLNode ("geometry");
1365                 char buf[32];
1366                 snprintf(buf, sizeof(buf), "%d", m_width);
1367                 geometry->add_property(X_("x_size"), string(buf));
1368                 snprintf(buf, sizeof(buf), "%d", m_height);
1369                 geometry->add_property(X_("y_size"), string(buf));
1370                 snprintf(buf, sizeof(buf), "%d", m_root_x);
1371                 geometry->add_property(X_("x_pos"), string(buf));
1372                 snprintf(buf, sizeof(buf), "%d", m_root_y);
1373                 geometry->add_property(X_("y_pos"), string(buf));
1374
1375                 // written only for compatibility, they are not used.
1376                 snprintf(buf, sizeof(buf), "%d", 0);
1377                 geometry->add_property(X_("x_off"), string(buf));
1378                 snprintf(buf, sizeof(buf), "%d", 0);
1379                 geometry->add_property(X_("y_off"), string(buf));
1380
1381                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1382                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1383                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1384                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1385
1386                 node->add_child_nocopy (*geometry);
1387         }
1388
1389         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1390
1391         node->add_property ("show-mixer", _visible ? "yes" : "no");
1392
1393         return *node;
1394 }
1395
1396
1397 void
1398 Mixer_UI::pane_allocation_handler (Allocation&, Gtk::Paned* which)
1399 {
1400         int pos;
1401         XMLProperty* prop = 0;
1402         char buf[32];
1403         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1404         XMLNode* geometry;
1405         int width, height;
1406         static int32_t done[3] = { 0, 0, 0 };
1407
1408         width = default_width;
1409         height = default_height;
1410
1411         if ((geometry = find_named_node (*node, "geometry")) != 0) {
1412
1413
1414                 if ((prop = geometry->property ("x_size")) == 0) {
1415                         prop = geometry->property ("x-size");
1416                 }
1417                 if (prop) {
1418                         width = atoi (prop->value());
1419                 }
1420                 if ((prop = geometry->property ("y_size")) == 0) {
1421                         prop = geometry->property ("y-size");
1422                 }
1423                 if (prop) {
1424                         height = atoi (prop->value());
1425                 }
1426         }
1427
1428         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1429
1430                 if (done[0]) {
1431                         return;
1432                 }
1433
1434                 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1435                         pos = height / 3;
1436                         snprintf (buf, sizeof(buf), "%d", pos);
1437                 } else {
1438                         pos = atoi (prop->value());
1439                 }
1440
1441                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1442                         rhs_pane1.set_position (pos);
1443                 }
1444
1445         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1446
1447                 if (done[2]) {
1448                         return;
1449                 }
1450
1451                 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
1452                         pos = 75;
1453                         snprintf (buf, sizeof(buf), "%d", pos);
1454                 } else {
1455                         pos = atoi (prop->value());
1456                 }
1457
1458                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1459                         list_hpane.set_position (pos);
1460                 }
1461         }
1462 }
1463 void
1464 Mixer_UI::scroll_left () 
1465 {
1466         Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1467         /* stupid GTK: can't rely on clamping across versions */
1468         scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1469 }
1470
1471 void
1472 Mixer_UI::scroll_right ()
1473 {
1474         Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1475         /* stupid GTK: can't rely on clamping across versions */
1476         scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1477 }
1478
1479 bool
1480 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1481 {
1482         switch (ev->keyval) {
1483         case GDK_Left:
1484                 scroll_left ();
1485                 return true;
1486
1487         case GDK_Right:
1488                 scroll_right ();
1489                 return true;
1490
1491         default:
1492                 break;
1493         }
1494
1495         return key_press_focus_accelerator_handler (*this, ev);
1496 }
1497
1498 bool
1499 Mixer_UI::on_key_release_event (GdkEventKey* ev)
1500 {
1501         return Gtk::Window::on_key_release_event (ev);
1502         // return key_press_focus_accelerator_handler (*this, ev);
1503 }
1504
1505
1506 bool
1507 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1508 {
1509         switch (ev->direction) {
1510         case GDK_SCROLL_LEFT:
1511                 scroll_left ();
1512                 return true;
1513         case GDK_SCROLL_UP:
1514                 if (ev->state & Keyboard::TertiaryModifier) {
1515                         scroll_left ();
1516                         return true;
1517                 }
1518                 return false;
1519
1520         case GDK_SCROLL_RIGHT:
1521                 scroll_right ();
1522                 return true;
1523
1524         case GDK_SCROLL_DOWN:
1525                 if (ev->state & Keyboard::TertiaryModifier) {
1526                         scroll_right ();
1527                         return true;
1528                 }
1529                 return false;
1530         }
1531
1532         return false;
1533 }
1534
1535
1536 void
1537 Mixer_UI::parameter_changed (string const & p)
1538 {
1539         if (p == "show-group-tabs") {
1540                 bool const s = session->config.get_show_group_tabs ();
1541                 if (s) {
1542                         _group_tabs->show ();
1543                 } else {
1544                         _group_tabs->hide ();
1545                 }
1546         }
1547 }
1548
1549 void
1550 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
1551 {
1552         g->set_active (a, this);
1553 }
1554
1555 PluginSelector*
1556 Mixer_UI::plugin_selector()
1557 {
1558         return _plugin_selector;
1559 }