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