new file
[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         _session->add_route_group (new RouteGroup (*_session, "", RouteGroup::Active, (RouteGroup::Property) (RouteGroup::Gain |RouteGroup::Mute | RouteGroup::Solo)));
1135 }
1136
1137 void
1138 Mixer_UI::remove_selected_route_group ()
1139 {
1140         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1141         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1142
1143         if (rows.empty()) {
1144                 return;
1145         }
1146
1147         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1148         TreeIter iter;
1149
1150         /* selection mode is single, so rows.begin() is it */
1151
1152         if ((iter = group_model->get_iter (*i))) {
1153
1154                 RouteGroup* rg = (*iter)[group_columns.group];
1155
1156                 if (rg) {
1157                         _session->remove_route_group (*rg);
1158                 }
1159         }
1160 }
1161
1162 void
1163 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
1164 {
1165         if (in_group_row_change) {
1166                 return;
1167         }
1168
1169         ENSURE_GUI_THREAD (*this, &Mixer_UI::group_flags_changed, src, group)
1170
1171         /* force an update of any mixer strips that are using this group,
1172            otherwise mix group names don't change in mixer strips
1173         */
1174
1175         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1176                 if ((*i)->route_group() == group) {
1177                         (*i)->route_group_changed();
1178                 }
1179         }
1180
1181         TreeModel::iterator i;
1182         TreeModel::Children rows = group_model->children();
1183         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1184
1185         in_group_row_change = true;
1186
1187         for (i = rows.begin(); i != rows.end(); ++i) {
1188                 if ((*i)[group_columns.group] == group) {
1189                         (*i)[group_columns.visible] = !group->is_hidden ();
1190                         (*i)[group_columns.text] = group->name ();
1191                         break;
1192                 }
1193         }
1194
1195         in_group_row_change = false;
1196
1197         _group_tabs->set_dirty ();
1198 }
1199
1200 void
1201 Mixer_UI::route_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1202 {
1203         RouteGroup* group;
1204         TreeIter iter;
1205
1206         if ((iter = group_model->get_iter (path))) {
1207
1208                 if ((group = (*iter)[group_columns.group]) == 0) {
1209                         return;
1210                 }
1211
1212                 if (new_text != group->name()) {
1213                         group->set_name (new_text);
1214                 }
1215         }
1216 }
1217
1218 void
1219 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1220 {
1221         RouteGroup* group;
1222
1223         if (in_group_row_change) {
1224                 return;
1225         }
1226
1227         if ((group = (*iter)[group_columns.group]) == 0) {
1228                 return;
1229         }
1230
1231         if ((*iter)[group_columns.visible]) {
1232                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1233                         if ((*i)->route_group() == group) {
1234                                 show_strip (*i);
1235                         }
1236                 }
1237         } else {
1238                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1239                         if ((*i)->route_group() == group) {
1240                                 hide_strip (*i);
1241                         }
1242                 }
1243         }
1244
1245         Glib::ustring name = (*iter)[group_columns.text];
1246
1247         if (name != group->name()) {
1248                 group->set_name (name);
1249         }
1250
1251 }
1252
1253 void
1254 Mixer_UI::add_route_group (RouteGroup* group)
1255 {
1256         ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1257         bool focus = false;
1258
1259         in_group_row_change = true;
1260
1261         TreeModel::Row row = *(group_model->append());
1262         row[group_columns.visible] = true;
1263         row[group_columns.group] = group;
1264         if (!group->name().empty()) {
1265                 row[group_columns.text] = group->name();
1266         } else {
1267                 row[group_columns.text] = _("unnamed");
1268                 focus = true;
1269         }
1270
1271         group->FlagsChanged.connect (*this, ui_bind (&Mixer_UI::group_flags_changed, this, _1, group), gui_context());
1272
1273         if (focus) {
1274                 TreeViewColumn* col = group_display.get_column (0);
1275                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1276                 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1277         }
1278
1279         in_group_row_change = false;
1280 }
1281
1282 bool
1283 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1284 {
1285         using namespace Menu_Helpers;
1286
1287         if (Keyboard::is_context_menu_event (ev)) {
1288                 ARDOUR_UI::instance()->add_route (this);
1289                 return true;
1290         }
1291
1292         return false;
1293 }
1294
1295 void
1296 Mixer_UI::set_strip_width (Width w)
1297 {
1298         _strip_width = w;
1299
1300         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1301                 (*i)->set_width_enum (w, this);
1302         }
1303 }
1304
1305 void
1306 Mixer_UI::set_window_pos_and_size ()
1307 {
1308         resize (m_width, m_height);
1309         move (m_root_x, m_root_y);
1310 }
1311
1312         void
1313 Mixer_UI::get_window_pos_and_size ()
1314 {
1315         get_position(m_root_x, m_root_y);
1316         get_size(m_width, m_height);
1317 }
1318
1319 int
1320 Mixer_UI::set_state (const XMLNode& node)
1321 {
1322         const XMLProperty* prop;
1323         XMLNode* geometry;
1324
1325         m_width = default_width;
1326         m_height = default_height;
1327         m_root_x = 1;
1328         m_root_y = 1;
1329
1330         if ((geometry = find_named_node (node, "geometry")) != 0) {
1331
1332                 XMLProperty* prop;
1333
1334                 if ((prop = geometry->property("x_size")) == 0) {
1335                         prop = geometry->property ("x-size");
1336                 }
1337                 if (prop) {
1338                         m_width = atoi(prop->value());
1339                 }
1340                 if ((prop = geometry->property("y_size")) == 0) {
1341                         prop = geometry->property ("y-size");
1342                 }
1343                 if (prop) {
1344                         m_height = atoi(prop->value());
1345                 }
1346
1347                 if ((prop = geometry->property ("x_pos")) == 0) {
1348                         prop = geometry->property ("x-pos");
1349                 }
1350                 if (prop) {
1351                         m_root_x = atoi (prop->value());
1352
1353                 }
1354                 if ((prop = geometry->property ("y_pos")) == 0) {
1355                         prop = geometry->property ("y-pos");
1356                 }
1357                 if (prop) {
1358                         m_root_y = atoi (prop->value());
1359                 }
1360         }
1361
1362         set_window_pos_and_size ();
1363
1364         if ((prop = node.property ("narrow-strips"))) {
1365                 if (string_is_affirmative (prop->value())) {
1366                         set_strip_width (Narrow);
1367                 } else {
1368                         set_strip_width (Wide);
1369                 }
1370         }
1371
1372         if ((prop = node.property ("show-mixer"))) {
1373                 if (string_is_affirmative (prop->value())) {
1374                        _visible = true;
1375                 }
1376         }
1377
1378         return 0;
1379 }
1380
1381 XMLNode&
1382 Mixer_UI::get_state (void)
1383 {
1384         XMLNode* node = new XMLNode ("Mixer");
1385
1386         if (is_realized()) {
1387                 Glib::RefPtr<Gdk::Window> win = get_window();
1388
1389                 get_window_pos_and_size ();
1390
1391                 XMLNode* geometry = new XMLNode ("geometry");
1392                 char buf[32];
1393                 snprintf(buf, sizeof(buf), "%d", m_width);
1394                 geometry->add_property(X_("x_size"), string(buf));
1395                 snprintf(buf, sizeof(buf), "%d", m_height);
1396                 geometry->add_property(X_("y_size"), string(buf));
1397                 snprintf(buf, sizeof(buf), "%d", m_root_x);
1398                 geometry->add_property(X_("x_pos"), string(buf));
1399                 snprintf(buf, sizeof(buf), "%d", m_root_y);
1400                 geometry->add_property(X_("y_pos"), string(buf));
1401
1402                 // written only for compatibility, they are not used.
1403                 snprintf(buf, sizeof(buf), "%d", 0);
1404                 geometry->add_property(X_("x_off"), string(buf));
1405                 snprintf(buf, sizeof(buf), "%d", 0);
1406                 geometry->add_property(X_("y_off"), string(buf));
1407
1408                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1409                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1410                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1411                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1412
1413                 node->add_child_nocopy (*geometry);
1414         }
1415
1416         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1417
1418         node->add_property ("show-mixer", _visible ? "yes" : "no");
1419
1420         return *node;
1421 }
1422
1423
1424 void
1425 Mixer_UI::pane_allocation_handler (Allocation&, Gtk::Paned* which)
1426 {
1427         int pos;
1428         XMLProperty* prop = 0;
1429         char buf[32];
1430         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1431         XMLNode* geometry;
1432         int width, height;
1433         static int32_t done[3] = { 0, 0, 0 };
1434
1435         width = default_width;
1436         height = default_height;
1437
1438         if ((geometry = find_named_node (*node, "geometry")) != 0) {
1439
1440
1441                 if ((prop = geometry->property ("x_size")) == 0) {
1442                         prop = geometry->property ("x-size");
1443                 }
1444                 if (prop) {
1445                         width = atoi (prop->value());
1446                 }
1447                 if ((prop = geometry->property ("y_size")) == 0) {
1448                         prop = geometry->property ("y-size");
1449                 }
1450                 if (prop) {
1451                         height = atoi (prop->value());
1452                 }
1453         }
1454
1455         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1456
1457                 if (done[0]) {
1458                         return;
1459                 }
1460
1461                 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1462                         pos = height / 3;
1463                         snprintf (buf, sizeof(buf), "%d", pos);
1464                 } else {
1465                         pos = atoi (prop->value());
1466                 }
1467
1468                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1469                         rhs_pane1.set_position (pos);
1470                 }
1471
1472         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1473
1474                 if (done[2]) {
1475                         return;
1476                 }
1477
1478                 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
1479                         pos = 75;
1480                         snprintf (buf, sizeof(buf), "%d", pos);
1481                 } else {
1482                         pos = atoi (prop->value());
1483                 }
1484
1485                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1486                         list_hpane.set_position (pos);
1487                 }
1488         }
1489 }
1490 void
1491 Mixer_UI::scroll_left () 
1492 {
1493         Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1494         /* stupid GTK: can't rely on clamping across versions */
1495         scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1496 }
1497
1498 void
1499 Mixer_UI::scroll_right ()
1500 {
1501         Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1502         /* stupid GTK: can't rely on clamping across versions */
1503         scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1504 }
1505
1506 bool
1507 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1508 {
1509         switch (ev->keyval) {
1510         case GDK_Left:
1511                 scroll_left ();
1512                 return true;
1513
1514         case GDK_Right:
1515                 scroll_right ();
1516                 return true;
1517
1518         default:
1519                 break;
1520         }
1521
1522         return key_press_focus_accelerator_handler (*this, ev);
1523 }
1524
1525 bool
1526 Mixer_UI::on_key_release_event (GdkEventKey* ev)
1527 {
1528         return Gtk::Window::on_key_release_event (ev);
1529         // return key_press_focus_accelerator_handler (*this, ev);
1530 }
1531
1532
1533 bool
1534 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1535 {
1536         switch (ev->direction) {
1537         case GDK_SCROLL_LEFT:
1538                 scroll_left ();
1539                 return true;
1540         case GDK_SCROLL_UP:
1541                 if (ev->state & Keyboard::TertiaryModifier) {
1542                         scroll_left ();
1543                         return true;
1544                 }
1545                 return false;
1546
1547         case GDK_SCROLL_RIGHT:
1548                 scroll_right ();
1549                 return true;
1550
1551         case GDK_SCROLL_DOWN:
1552                 if (ev->state & Keyboard::TertiaryModifier) {
1553                         scroll_right ();
1554                         return true;
1555                 }
1556                 return false;
1557         }
1558
1559         return false;
1560 }
1561
1562
1563 void
1564 Mixer_UI::parameter_changed (string const & p)
1565 {
1566         if (p == "show-group-tabs") {
1567                 bool const s = _session->config.get_show_group_tabs ();
1568                 if (s) {
1569                         _group_tabs->show ();
1570                 } else {
1571                         _group_tabs->hide ();
1572                 }
1573         }
1574 }
1575
1576 void
1577 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
1578 {
1579         g->set_active (a, this);
1580 }
1581
1582 PluginSelector*
1583 Mixer_UI::plugin_selector()
1584 {
1585 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
1586         if (!_plugin_selector)
1587                 _plugin_selector = new PluginSelector (PluginManager::the_manager ());
1588 #endif
1589
1590         return _plugin_selector;
1591 }