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