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