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