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