Remove unnecessary height changed notification for streamviews, now that the summary...
[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/tearoff.h>
33 #include <gtkmm2ext/window_title.h>
34
35 #include "ardour/audio_track.h"
36 #include "ardour/plugin_manager.h"
37 #include "ardour/route_group.h"
38 #include "ardour/session.h"
39 #include "ardour/session_route.h"
40
41 #include "keyboard.h"
42 #include "mixer_ui.h"
43 #include "mixer_strip.h"
44 #include "monitor_section.h"
45 #include "plugin_selector.h"
46 #include "ardour_ui.h"
47 #include "prompter.h"
48 #include "utils.h"
49 #include "actions.h"
50 #include "gui_thread.h"
51 #include "mixer_group_tabs.h"
52
53 #include "i18n.h"
54
55 using namespace ARDOUR;
56 using namespace PBD;
57 using namespace Gtk;
58 using namespace Glib;
59 using namespace Gtkmm2ext;
60 using namespace std;
61
62 using PBD::atoi;
63
64 Mixer_UI::Mixer_UI ()
65         : Window (Gtk::WINDOW_TOPLEVEL)
66 {
67         _strip_width = Config->get_default_narrow_ms() ? Narrow : Wide;
68         track_menu = 0;
69         _monitor_section = 0;
70         route_group_context_menu = 0;
71         no_track_list_redisplay = false;
72         in_group_row_change = false;
73         _visible = false;
74         strip_redisplay_does_not_reset_order_keys = false;
75         strip_redisplay_does_not_sync_order_keys = false;
76         ignore_sync = false;
77
78         Route::SyncOrderKeys.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::sync_order_keys, this, _1), gui_context());
79
80         scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
81         scroller_base.set_name ("MixerWindow");
82         scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
83         // add as last item of strip packer
84         strip_packer.pack_end (scroller_base, true, true);
85
86         _group_tabs = new MixerGroupTabs (this);
87         VBox* b = manage (new VBox);
88         b->pack_start (*_group_tabs, PACK_SHRINK);
89         b->pack_start (strip_packer);
90         b->show_all ();
91
92         scroller.add (*b);
93         scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
94
95         track_model = ListStore::create (track_columns);
96         track_display.set_model (track_model);
97         track_display.append_column (_("Strips"), track_columns.text);
98         track_display.append_column (_("Show"), track_columns.visible);
99         track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
100         track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
101         track_display.get_column (0)->set_expand(true);
102         track_display.get_column (1)->set_expand(false);
103         track_display.set_name (X_("MixerTrackDisplayList"));
104         track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
105         track_display.set_reorderable (true);
106         track_display.set_headers_visible (true);
107
108         track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
109         track_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_change));
110         track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
111
112         CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
113         track_list_visible_cell->property_activatable() = true;
114         track_list_visible_cell->property_radio() = false;
115
116         track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
117
118         track_display_scroller.add (track_display);
119         track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
120
121         group_model = ListStore::create (group_columns);
122         group_display.set_model (group_model);
123         group_display.append_column (_("Group"), group_columns.text);
124         group_display.append_column (_("Show"), group_columns.visible);
125         group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
126         group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
127         group_display.get_column (0)->set_expand(true);
128         group_display.get_column (1)->set_expand(false);
129         group_display.set_name ("MixerGroupList");
130         group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
131         group_display.set_reorderable (true);
132         group_display.set_headers_visible (true);
133         group_display.set_rules_hint (true);
134
135         /* name is directly editable */
136
137         CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
138         name_cell->property_editable() = true;
139         name_cell->signal_edited().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_name_edit));
140
141         /* use checkbox for the active column */
142
143         CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
144         active_cell->property_activatable() = true;
145         active_cell->property_radio() = false;
146
147         group_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_change));
148
149         group_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::group_display_button_press), false);
150
151         group_display_scroller.add (group_display);
152         group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
153
154         HBox* route_group_display_button_box = manage (new HBox());
155
156         Button* route_group_add_button = manage (new Button ());
157         Button* route_group_remove_button = manage (new Button ());
158
159         Widget* w;
160
161         w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
162         w->show();
163         route_group_add_button->add (*w);
164
165         w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
166         w->show();
167         route_group_remove_button->add (*w);
168
169         route_group_display_button_box->set_homogeneous (true);
170
171         route_group_add_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_route_group));
172         route_group_remove_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::remove_selected_route_group));
173
174         route_group_display_button_box->add (*route_group_remove_button);
175         route_group_display_button_box->add (*route_group_add_button);
176
177         group_display_vbox.pack_start (group_display_scroller, true, true);
178         group_display_vbox.pack_start (*route_group_display_button_box, false, false);
179
180         track_display_frame.set_name("BaseFrame");
181         track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
182         track_display_frame.add(track_display_scroller);
183
184         group_display_frame.set_name ("BaseFrame");
185         group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
186         group_display_frame.add (group_display_vbox);
187
188         rhs_pane1.pack1 (track_display_frame);
189         rhs_pane1.pack2 (group_display_frame);
190
191         list_vpacker.pack_start (rhs_pane1, true, true);
192
193         global_hpacker.pack_start (scroller, true, true);
194 #ifdef GTKOSX
195         /* current gtk-quartz has dirty updates on borders like this one */
196         global_hpacker.pack_start (out_packer, false, false, 0);
197 #else
198         global_hpacker.pack_start (out_packer, false, false, 12);
199 #endif
200         list_hpane.add1(list_vpacker);
201         list_hpane.add2(global_hpacker);
202
203         rhs_pane1.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
204                                                         static_cast<Gtk::Paned*> (&rhs_pane1)));
205         list_hpane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
206                                                          static_cast<Gtk::Paned*> (&list_hpane)));
207
208         global_vpacker.pack_start (list_hpane, true, true);
209
210         add (global_vpacker);
211         set_name ("MixerWindow");
212
213         WindowTitle title(Glib::get_application_name());
214         title += _("Mixer");
215         set_title (title.get_string());
216
217         set_wmclass (X_("ardour_mixer"), "Ardour");
218
219         add_accel_group (ActionManager::ui_manager->get_accel_group());
220
221         signal_delete_event().connect (sigc::mem_fun (*this, &Mixer_UI::hide_window));
222         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
223
224         signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
225
226         _selection.RoutesChanged.connect (sigc::mem_fun(*this, &Mixer_UI::follow_strip_selection));
227
228         route_group_display_button_box->show();
229         route_group_add_button->show();
230         route_group_remove_button->show();
231
232         global_hpacker.show();
233         global_vpacker.show();
234         scroller.show();
235         scroller_base.show();
236         scroller_hpacker.show();
237         mixer_scroller_vpacker.show();
238         list_vpacker.show();
239         group_display_button_label.show();
240         group_display_button.show();
241         track_display_scroller.show();
242         group_display_scroller.show();
243         group_display_vbox.show();
244         track_display_frame.show();
245         group_display_frame.show();
246         rhs_pane1.show();
247         strip_packer.show();
248         out_packer.show();
249         list_hpane.show();
250         track_display.show();
251         group_display.show();
252
253         auto_rebinding = FALSE;
254
255         MixerStrip::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::remove_strip, this, _1), gui_context());
256
257         MonitorSection::setup_knob_images ();
258
259 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
260         _plugin_selector = new PluginSelector (PluginManager::the_manager ());
261 #endif
262 }
263
264 Mixer_UI::~Mixer_UI ()
265 {
266 }
267
268 void
269 Mixer_UI::ensure_float (Window& win)
270 {
271         win.set_transient_for (*this);
272 }
273
274 void
275 Mixer_UI::show_window ()
276 {
277         present ();
278         if (!_visible) {
279                 set_window_pos_and_size ();
280
281                 /* now reset each strips width so the right widgets are shown */
282                 MixerStrip* ms;
283
284                 TreeModel::Children rows = track_model->children();
285                 TreeModel::Children::iterator ri;
286
287                 for (ri = rows.begin(); ri != rows.end(); ++ri) {
288                         ms = (*ri)[track_columns.strip];
289                         ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
290                 }
291         }
292         _visible = true;
293 }
294
295 bool
296 Mixer_UI::hide_window (GdkEventAny *ev)
297 {
298         get_window_pos_and_size ();
299
300         _visible = false;
301         return just_hide_it(ev, static_cast<Gtk::Window *>(this));
302 }
303
304
305 void
306 Mixer_UI::add_strip (RouteList& routes)
307 {
308         ENSURE_GUI_THREAD (*this, &Mixer_UI::add_strip, routes)
309
310         MixerStrip* strip;
311
312         no_track_list_redisplay = true;
313         strip_redisplay_does_not_sync_order_keys = true;
314
315         for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
316                 boost::shared_ptr<Route> route = (*x);
317
318                 if (route->is_hidden()) {
319                         continue;
320                 }
321
322                 if (route->is_monitor()) {
323                         if (!_monitor_section) {
324                                 _monitor_section = new MonitorSection (_session);
325                                 out_packer.pack_end (_monitor_section->tearoff(), false, false);
326                         } else {
327                                 _monitor_section->set_session (_session);
328                         }
329
330                         _monitor_section->tearoff().show_all ();
331
332                         XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
333                         if (mnode) {
334                                 _monitor_section->tearoff().set_state (*mnode);
335                         }
336
337                         /* no regular strip shown for control out */
338
339                         continue;
340                 }
341
342                 strip = new MixerStrip (*this, _session, route);
343                 strips.push_back (strip);
344
345                 Config->get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
346
347                 if (strip->width_owner() != strip) {
348                         strip->set_width_enum (_strip_width, this);
349                 }
350
351                 show_strip (strip);
352
353                 TreeModel::Row row = *(track_model->append());
354                 row[track_columns.text] = route->name();
355                 row[track_columns.visible] = strip->marked_for_display();
356                 row[track_columns.route] = route;
357                 row[track_columns.strip] = strip;
358
359                 if (route->order_key (N_("signal")) == -1) {
360                         route->set_order_key (N_("signal"), track_model->children().size()-1);
361                 }
362
363                 route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
364
365                 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
366                 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
367         }
368
369         no_track_list_redisplay = false;
370
371         redisplay_track_list ();
372
373         strip_redisplay_does_not_sync_order_keys = false;
374 }
375
376 void
377 Mixer_UI::remove_strip (MixerStrip* strip)
378 {
379         if (_session && _session->deletion_in_progress()) {
380                 /* its all being taken care of */
381                 return;
382         }
383
384         ENSURE_GUI_THREAD (*this, &Mixer_UI::remove_strip, strip);
385
386         cerr << "Mixer UI removing strip for " << strip << endl;
387
388         TreeModel::Children rows = track_model->children();
389         TreeModel::Children::iterator ri;
390         list<MixerStrip *>::iterator i;
391
392         if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
393                 strips.erase (i);
394         }
395
396         strip_redisplay_does_not_sync_order_keys = true;
397
398         for (ri = rows.begin(); ri != rows.end(); ++ri) {
399                 if ((*ri)[track_columns.strip] == strip) {
400                         track_model->erase (ri);
401                         break;
402                 }
403         }
404
405         strip_redisplay_does_not_sync_order_keys = false;
406 }
407
408 void
409 Mixer_UI::sync_order_keys (string const & src)
410 {
411         TreeModel::Children rows = track_model->children();
412         TreeModel::Children::iterator ri;
413
414         if (src == N_("signal") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
415                 return;
416         }
417
418         std::map<int,int> keys;
419
420         bool changed = false;
421
422         unsigned order = 0;
423         for (ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
424                 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
425                 unsigned int old_key = order;
426                 unsigned int new_key = route->order_key (N_("signal"));
427
428                 keys[new_key] = old_key;
429
430                 if (new_key != old_key) {
431                         changed = true;
432                 }
433         }
434
435         if (keys.size() != rows.size()) {
436                 PBD::stacktrace (cerr, 20);
437         }
438         assert(keys.size() == rows.size());
439
440         // Remove any gaps in keys caused by automation children tracks
441         vector<int> neworder;
442         for (std::map<int,int>::const_iterator i = keys.begin(); i != keys.end(); ++i) {
443                 neworder.push_back(i->second);
444         }
445         assert(neworder.size() == rows.size());
446
447         if (changed) {
448                 strip_redisplay_does_not_reset_order_keys = true;
449                 track_model->reorder (neworder);
450                 strip_redisplay_does_not_reset_order_keys = false;
451         }
452 }
453
454 void
455 Mixer_UI::follow_strip_selection ()
456 {
457         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
458                 (*i)->set_selected (_selection.selected ((*i)->route()));
459         }
460 }
461
462 bool
463 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
464 {
465         if (ev->button == 1) {
466
467                 /* this allows the user to click on the strip to terminate comment
468                    editing. XXX it needs improving so that we don't select the strip
469                    at the same time.
470                 */
471
472                 if (_selection.selected (strip->route())) {
473                         _selection.remove (strip->route());
474                 } else {
475                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
476                                 _selection.add (strip->route());
477                         } else {
478                                 _selection.set (strip->route());
479                         }
480                 }
481         }
482
483         return true;
484 }
485
486 void
487 Mixer_UI::set_session (Session* sess)
488 {
489         SessionHandlePtr::set_session (sess);
490
491         if (_plugin_selector) {
492                 _plugin_selector->set_session (_session);
493         }
494
495         _group_tabs->set_session (sess);
496
497         if (!_session) {
498                 return;
499         }
500
501         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
502         set_state (*node);
503
504         WindowTitle title(_session->name());
505         title += _("Mixer");
506         title += Glib::get_application_name();
507
508         set_title (title.get_string());
509
510         initial_track_display ();
511
512         _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::add_strip, this, _1), gui_context());
513         _session->route_group_added.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::add_route_group, this, _1), gui_context());
514         _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
515         _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
516
517         Config->ParameterChanged.connect (*this, 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         } else if (p == "default-narrow_ms") {
1618                 bool const s = Config->get_default_narrow_ms ();
1619                 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1620                         (*i)->set_width_enum (s ? Narrow : Wide, this);
1621                 }
1622         }
1623 }
1624
1625 void
1626 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
1627 {
1628         g->set_active (a, this);
1629 }
1630
1631 PluginSelector*
1632 Mixer_UI::plugin_selector()
1633 {
1634 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
1635         if (!_plugin_selector)
1636                 _plugin_selector = new PluginSelector (PluginManager::the_manager ());
1637 #endif
1638
1639         return _plugin_selector;
1640 }