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