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