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