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