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