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