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