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