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