68f6622e35d19a672e13753047aef05e110b41ba
[ardour.git] / gtk2_ardour / route_ui.cc
1 /*
2     Copyright (C) 2002 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     $Id$
19 */
20
21 #include <gtkmm2ext/gtk_ui.h>
22 #include <gtkmm2ext/stop_signal.h>
23 #include <gtkmm2ext/choice.h>
24 #include <gtkmm2ext/bindable_button.h>
25 #include <gtkmm2ext/doi.h>
26
27 #include <ardour/route_group.h>
28
29 #include "route_ui.h"
30 #include "keyboard.h"
31 #include "utils.h"
32 #include "prompter.h"
33 #include "gui_thread.h"
34
35 #include <ardour/route.h>
36 #include <ardour/audio_track.h>
37 #include <ardour/diskstream.h>
38
39 #include "i18n.h"
40 using namespace sigc;
41 using namespace Gtk;
42 using namespace Gtkmm2ext;
43 using namespace ARDOUR;
44
45
46 RouteUI::RouteUI (ARDOUR::Route& rt, ARDOUR::Session& sess, const char* m_name,
47                   const char* s_name, const char* r_name)
48     : AxisView(sess),
49           _route(rt),
50           mute_button(0),
51           solo_button(0),
52           rec_enable_button(0)
53 {
54         xml_node = 0;
55         mute_menu = 0;
56         solo_menu = 0;
57         ignore_toggle = false;
58         wait_for_release = false;
59         route_active_menu_item = 0;
60
61         if (set_color_from_route()) {
62                 set_color (unique_random_color());
63         }
64
65         _route.GoingAway.connect (mem_fun (*this, &RouteUI::route_removed));
66         _route.active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed));
67
68         mute_button = manage (new BindableToggleButton (& _route.midi_mute_control(), m_name ));
69         mute_button->set_bind_button_state (2, GDK_CONTROL_MASK);
70         solo_button = manage (new BindableToggleButton (& _route.midi_solo_control(), s_name ));
71         solo_button->set_bind_button_state (2, GDK_CONTROL_MASK);
72
73         if (is_audio_track()) {
74                 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
75
76                 get_diskstream()->record_enable_changed.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed));
77
78                 _session.RecordEnabled.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed));
79                 _session.RecordDisabled.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed));
80
81                 rec_enable_button = manage (new BindableToggleButton (& at->midi_rec_enable_control(), r_name ));
82                 rec_enable_button->set_bind_button_state (2, GDK_CONTROL_MASK);
83
84         } else {
85                 rec_enable_button = manage (new BindableToggleButton (0, r_name ));
86         }
87         
88         mute_button->unset_flags (Gtk::CAN_FOCUS);
89         solo_button->unset_flags (Gtk::CAN_FOCUS);
90         rec_enable_button->unset_flags (Gtk::CAN_FOCUS);
91
92         /* map the current state */
93
94         update_rec_display ();
95         map_frozen ();
96 }
97
98 RouteUI::~RouteUI()
99 {
100         delete mute_menu;
101 }
102
103 gint
104 RouteUI::mute_press(GdkEventButton* ev)
105 {
106         if (!ignore_toggle) {
107
108                 if (Keyboard::is_context_menu_event (ev)) {
109
110                         if (mute_menu == 0){
111                                 build_mute_menu();
112                         }
113
114                         mute_menu->popup(0,0);
115
116                 } else {
117
118                         if (ev->button == 2) {
119                                 // ctrl-button2 click is the midi binding click
120                                 // button2-click is "momentary"
121                                 
122                                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
123                                         wait_for_release = true;
124                                 }
125                         }
126
127                         if (ev->button == 1 || ev->button == 2) {
128
129                                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
130
131                                         /* ctrl-shift-click applies change to all routes */
132
133                                         _session.begin_reversible_command (_("mute change"));
134                                         _session.add_undo (_session.global_mute_memento(this));
135                                         _session.set_all_mute (!_route.muted());
136                                         _session.add_redo_no_execute (_session.global_mute_memento(this));
137                                         _session.commit_reversible_command ();
138
139                                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
140
141                                         /* ctrl-click applies change to the mix group.
142                                            ctrl-button2 is MIDI learn.
143                                         */
144
145                                         if (ev->button == 1) {
146                                                 set_mix_group_mute (_route, !_route.muted());
147                                         }
148                                         
149                                 } else {
150
151                                         /* plain click applies change to this route */
152
153                                         reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route.muted(), this);
154                                 }
155                         }
156                 }
157
158         }
159
160         return stop_signal (*mute_button, "button-press-event");
161 }
162
163 gint
164 RouteUI::mute_release(GdkEventButton* ev)
165 {
166         if (!ignore_toggle) {
167                 if (wait_for_release){
168                         wait_for_release = false;
169                         // undo the last op
170                         // because the press was the last undoable thing we did
171                         _session.undo (1U);
172                         stop_signal (*mute_button, "button-release-event");
173                 }
174         }
175         return TRUE;
176 }
177
178 gint
179 RouteUI::solo_press(GdkEventButton* ev)
180 {
181         if (!ignore_toggle) {
182
183                 if (Keyboard::is_context_menu_event (ev)) {
184                         
185                         if (solo_menu == 0) {
186                                 build_solo_menu ();
187                         }
188
189                         solo_menu->popup (1, 0);
190
191                 } else {
192
193                         if (ev->button == 2) {
194
195                                 // ctrl-button2 click is the midi binding click
196                                 // button2-click is "momentary"
197                                 
198                                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
199                                         wait_for_release = true;
200                                 }
201                         }
202
203                         if (ev->button == 1 || ev->button == 2) {
204
205                                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
206
207                                         /* ctrl-shift-click applies change to all routes */
208
209                                         _session.begin_reversible_command (_("solo change"));
210                                         _session.add_undo (_session.global_solo_memento(this));
211                                         _session.set_all_solo (!_route.soloed());
212                                         _session.add_redo_no_execute (_session.global_solo_memento(this));
213                                         _session.commit_reversible_command ();
214                                         
215                                 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
216
217                                         // ctrl-alt-click: exclusively solo this track, not a toggle */
218
219                                         _session.begin_reversible_command (_("solo change"));
220                                         _session.add_undo (_session.global_solo_memento(this));
221                                         _session.set_all_solo (false);
222                                         _route.set_solo (true, this);
223                                         _session.add_redo_no_execute (_session.global_solo_memento(this));
224                                         _session.commit_reversible_command ();
225
226                                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
227
228                                         // shift-click: set this route to solo safe
229
230                                         _route.set_solo_safe (!_route.solo_safe(), this);
231                                         wait_for_release = false;
232
233                                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
234
235                                         /* ctrl-click: solo mix group.
236                                            ctrl-button2 is MIDI learn.
237                                         */
238
239                                         if (ev->button == 1) {
240                                                 set_mix_group_solo (_route, !_route.soloed());
241                                         }
242
243                                 } else {
244
245                                         /* click: solo this route */
246
247                                         reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route.soloed(), this);
248                                 }
249                         }
250                 }
251         }
252
253         return stop_signal (*solo_button, "button-press-event");
254 }
255
256 gint
257 RouteUI::solo_release(GdkEventButton* ev)
258 {
259         if(!ignore_toggle){
260                 if (wait_for_release){
261                         wait_for_release = false;
262                         // undo the last op
263                         // because the press was the last undoable thing we did
264
265                         _session.undo (1U);
266
267                         stop_signal (*solo_button, "button-release-event");
268                 }
269         }
270         return TRUE;
271 }
272
273 gint
274 RouteUI::rec_enable_press(GdkEventButton* ev)
275 {
276         if (!ignore_toggle && is_audio_track()) {
277
278                 if (ev->button == 2 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
279                         // do nothing on midi bind event
280                 }
281                 else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
282
283                         _session.begin_reversible_command (_("rec-enable change"));
284                         _session.add_undo (_session.global_record_enable_memento(this));
285
286                         if (rec_enable_button->get_active()) {
287                                 _session.record_disenable_all ();
288                         } else {
289                                 _session.record_enable_all ();
290                         }
291
292                         _session.add_redo_no_execute (_session.global_record_enable_memento(this));
293                         _session.commit_reversible_command ();
294
295                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
296
297                         set_mix_group_rec_enable (_route, !_route.record_enabled());
298
299                 } else {
300
301                         reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
302
303                         ignore_toggle = true;
304                         rec_enable_button->set_active(audio_track()->record_enabled());
305                         ignore_toggle = false;
306                 }
307                 
308                 stop_signal (*rec_enable_button, "button-press-event");
309         }
310
311         return TRUE;
312 }
313
314 void
315 RouteUI::solo_changed(void* src)
316 {
317         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
318 }
319
320 void
321 RouteUI::update_solo_display ()
322 {
323         bool x;
324
325         if (solo_button->get_active() != (x = _route.soloed())){
326                 ignore_toggle = true;
327                 solo_button->set_active(x);
328                 ignore_toggle = false;
329         }
330         
331         /* show solo safe */
332
333         if (_route.solo_safe()){
334                 solo_button->set_name(safe_solo_button_name());
335         } else {
336                 solo_button->set_name(solo_button_name());
337         }
338 }
339
340 void
341 RouteUI::mute_changed(void* src)
342 {
343         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
344 }
345
346 void
347 RouteUI::update_mute_display ()
348 {
349         bool x;
350
351         if (mute_button->get_active() != (x = _route.muted())){
352                 ignore_toggle = true;
353                 mute_button->set_active(x);
354                 ignore_toggle = false;
355         }
356 }
357
358 void
359 RouteUI::route_rec_enable_changed (void *src)
360 {
361         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
362 }
363
364 void
365 RouteUI::session_rec_enable_changed ()
366 {
367         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
368 }
369
370 void
371 RouteUI::update_rec_display ()
372 {
373         bool model = _route.record_enabled();
374         bool view = rec_enable_button->get_active();
375
376         /* first make sure the button's "depressed" visual
377            is correct.
378         */
379         
380         if (model != view) {
381                 ignore_toggle = true;
382                 rec_enable_button->set_active (model);
383                 ignore_toggle = false;
384         }
385
386         /* now make sure its color state is correct */
387
388         if (model) {
389
390                 switch (_session.record_status ()) {
391                 case Session::Disabled:
392                 case Session::Enabled:
393                         if (rec_enable_button->get_state() != Gtk::STATE_ACTIVE) {
394                                 rec_enable_button->set_state (Gtk::STATE_ACTIVE);
395                         }
396                         break;
397
398                 case Session::Recording:
399                         if (rec_enable_button->get_state() != Gtk::STATE_SELECTED) {
400                                 rec_enable_button->set_state (Gtk::STATE_SELECTED);
401                         }
402                         break;
403                 }
404
405         } else {
406                 if (rec_enable_button->get_state() != Gtk::STATE_NORMAL) {
407                         rec_enable_button->set_state (Gtk::STATE_NORMAL);
408                 }
409         }
410 }
411
412 void
413 RouteUI::build_solo_menu (void)
414 {
415         using namespace Menu_Helpers;
416         
417         solo_menu = new Menu;
418         solo_menu->set_name ("ArdourContextMenu");
419         MenuList& items = solo_menu->items();
420         CheckMenuItem* check;
421
422         check = new CheckMenuItem(_("Solo-safe"));
423         check->set_active (_route.solo_safe());
424         check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
425         _route.solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
426         items.push_back (CheckMenuElem(*check));
427         check->show_all();
428
429         items.push_back (SeparatorElem());
430         items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
431         
432 }
433
434 void
435 RouteUI::build_mute_menu(void)
436 {
437         using namespace Menu_Helpers;
438         
439         mute_menu = new Menu;
440         mute_menu->set_name ("ArdourContextMenu");
441         MenuList& items = mute_menu->items();
442         CheckMenuItem* check;
443         
444         check = new CheckMenuItem(_("Pre Fader"));
445         init_mute_menu(PRE_FADER, check);
446         check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
447         _route.pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
448         items.push_back (CheckMenuElem(*check));
449         check->show_all();
450
451         check = new CheckMenuItem(_("Post Fader"));
452         init_mute_menu(POST_FADER, check);
453         check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
454         _route.post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
455         items.push_back (CheckMenuElem(*check));
456         check->show_all();
457         
458         check = new CheckMenuItem(_("Control Outs"));
459         init_mute_menu(CONTROL_OUTS, check);
460         check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
461         _route.control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
462         items.push_back (CheckMenuElem(*check));
463         check->show_all();
464
465         check = new CheckMenuItem(_("Main Outs"));
466         init_mute_menu(MAIN_OUTS, check);
467         check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
468         _route.main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
469         items.push_back (CheckMenuElem(*check));
470         check->show_all();
471
472         items.push_back (SeparatorElem());
473         items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
474 }
475
476 void
477 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
478 {
479         if (_route.get_mute_config (type)) {
480                 check->set_active (true);
481         }
482 }
483
484 void
485 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
486 {
487         _route.set_mute_config(type, check->get_active(), this);
488 }
489
490 void
491 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
492 {
493         _route.set_solo_safe (check->get_active(), this);
494 }
495
496 void
497 RouteUI::set_mix_group_solo(Route& route, bool yn)
498 {
499         RouteGroup* mix_group;
500
501         if((mix_group = route.mix_group()) != 0){
502                 _session.begin_reversible_command (_("mix group solo  change"));
503                 _session.add_undo (_session.global_solo_memento (this));
504                 mix_group->apply(&Route::set_solo, yn, this);
505                 _session.add_redo_no_execute (_session.global_solo_memento(this));
506                 _session.commit_reversible_command ();
507         } else {
508                 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route.soloed(), this);
509         }
510 }
511
512 void
513 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
514 {
515         _session.begin_reversible_command (name);
516         _session.add_undo (bind (mem_fun (_route, func), !yn, (void *) arg));
517         _session.add_redo (bind (mem_fun (_route, func), yn, (void *) arg));
518         _session.commit_reversible_command ();
519 }
520
521 void
522 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
523 {
524         _session.begin_reversible_command (name);
525         _session.add_undo (bind (mem_fun (*audio_track(), func), !yn, (void *) arg));
526         _session.add_redo (bind (mem_fun (*audio_track(), func), yn, (void *) arg));
527         _session.commit_reversible_command ();
528 }
529
530 void
531 RouteUI::set_mix_group_mute(Route& route, bool yn)
532 {
533         RouteGroup* mix_group;
534
535         if((mix_group = route.mix_group()) != 0){
536                 _session.begin_reversible_command (_("mix group mute change"));
537                 _session.add_undo (_session.global_mute_memento (this));
538                 mix_group->apply(&Route::set_mute, yn, this);
539                 _session.add_redo_no_execute (_session.global_mute_memento(this));
540                 _session.commit_reversible_command ();
541         } else {
542                 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route.muted(), this);
543         }
544 }
545
546 void
547 RouteUI::set_mix_group_rec_enable(Route& route, bool yn)
548 {
549         RouteGroup* mix_group;
550
551         if((mix_group = route.mix_group()) != 0){
552                 _session.begin_reversible_command (_("mix group rec-enable change"));
553                 _session.add_undo (_session.global_record_enable_memento (this));
554                 mix_group->apply (&Route::set_record_enable, yn, this);
555                 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
556                 _session.commit_reversible_command ();
557         } else {
558                 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route.record_enabled(), this);
559         }
560 }
561
562
563 bool
564 RouteUI::choose_color()
565 {
566         bool picked;
567         Gdk::Color color;
568         Gdk::Color current;
569
570         current.set_red ( _color.get_red()  / 65535);
571         current.set_green (_color.get_green() / 65535);
572         current.set_blue (_color.get_blue() / 65535);
573
574         color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &current);
575
576         if (picked) {
577                 set_color (color);
578         }
579
580         return picked;
581 }
582
583 void
584 RouteUI::set_color (Gdk::Color c)
585 {
586         char buf[64];
587         
588         _color = c;
589         
590         ensure_xml_node ();
591         snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
592         xml_node->add_property ("color", buf);
593
594          _route.gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
595 }
596
597
598 void
599 RouteUI::ensure_xml_node ()
600 {
601         if (xml_node == 0) {
602                 if ((xml_node = _route.extra_xml ("GUI")) == 0) {
603                         xml_node = new XMLNode ("GUI");
604                         _route.add_extra_xml (*xml_node);
605                 }
606         }
607 }
608
609 XMLNode*
610 RouteUI::get_child_xml_node (string childname)
611 {
612         XMLNode* child;
613
614         ensure_xml_node ();
615         
616         
617         if ((child = find_named_node (*xml_node, childname)) == 0) {
618                 child = new XMLNode (childname);
619                 xml_node->add_child_nocopy (*child);
620         }
621
622         return child;
623 }
624
625 int
626 RouteUI::set_color_from_route ()
627 {
628         XMLProperty *prop;
629         
630         RouteUI::ensure_xml_node ();
631
632         if ((prop = xml_node->property ("color")) != 0) {
633                 int r, g, b;
634                 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
635                 _color.set_red(r);
636                 _color.set_green(g);
637                 _color.set_blue(b);
638                 return 0;
639         } 
640         return 1;
641 }
642
643 void
644 RouteUI::remove_this_route ()
645 {
646         vector<string> choices;
647         string prompt;
648
649         if (is_audio_track()) {
650                 prompt  = string_compose (_("Do you really want to remove track \"%1\" ?\nYou may also lose the playlist used by this track.\n(cannot be undone)"), _route.name());
651         } else {
652                 prompt  = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route.name());
653         }
654
655         choices.push_back (_("Yes, remove it."));
656         choices.push_back (_("No, do nothing."));
657
658         Choice prompter (prompt, choices);
659
660         prompter.chosen.connect(sigc::ptr_fun(Gtk::Main::quit));
661         prompter.show_all ();
662
663         Gtk::Main::run ();
664
665         if (prompter.get_choice() == 0) {
666           Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
667         }
668 }
669
670 gint
671 RouteUI::idle_remove_this_route (RouteUI *rui)
672 {
673         rui->_session.remove_route (rui->_route);
674         return FALSE;
675 }
676
677 void
678 RouteUI::route_removed ()
679 {
680         ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
681         
682         delete this;
683 }
684
685 void
686 RouteUI::route_rename ()
687 {
688         ArdourPrompter name_prompter (true);
689         string result;
690         name_prompter.set_prompt (_("new name: "));
691         name_prompter.set_initial_text (_route.name());
692         name_prompter.show_all ();
693
694         switch (name_prompter.run ()) {
695
696         case GTK_RESPONSE_ACCEPT:
697                 name_prompter.get_result (result);
698                 if (result.length()) {
699                         strip_whitespace_edges (result);
700                         _route.set_name (result, this);
701                 }       
702                 break;
703         }
704
705         return;
706   
707 }
708
709 void
710 RouteUI::name_changed (void *src)
711 {
712         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
713         
714         name_label.set_text (_route.name());
715 }
716
717 void
718 RouteUI::toggle_route_active ()
719 {
720         bool yn;
721
722         if (route_active_menu_item) {
723                 if (route_active_menu_item->get_active() != (yn = _route.active())) {
724                         _route.set_active (!yn);
725                 }
726         }
727 }
728
729 void
730 RouteUI::route_active_changed ()
731 {
732         if (route_active_menu_item) {
733                 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route.active()));
734         }
735 }
736
737 void
738 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
739 {
740         bool yn = _route.solo_safe ();
741
742         if (check->get_active() != yn) {
743                 check->set_active (yn);
744         }
745 }
746 void
747 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
748 {
749         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
750         
751         bool yn = _route.get_mute_config(PRE_FADER);
752         if (check->get_active() != yn) {
753                 check->set_active (yn);
754         }
755 }
756
757 void
758 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
759 {
760         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
761         
762         bool yn = _route.get_mute_config(POST_FADER);
763         if (check->get_active() != yn) {
764                 check->set_active (yn);
765         }
766 }
767
768 void
769 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
770 {
771         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
772         
773         bool yn = _route.get_mute_config(CONTROL_OUTS);
774         if (check->get_active() != yn) {
775                 check->set_active (yn);
776         }
777 }
778
779 void
780 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
781 {
782         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
783         
784         bool yn = _route.get_mute_config(MAIN_OUTS);
785         if (check->get_active() != yn) {
786                 check->set_active (yn);
787         }
788 }
789
790 void
791 RouteUI::disconnect_input ()
792 {
793         _route.disconnect_inputs (this);
794 }
795
796 void
797 RouteUI::disconnect_output ()
798 {
799         _route.disconnect_outputs (this);
800 }
801
802 bool
803 RouteUI::is_audio_track () const
804 {
805         return dynamic_cast<AudioTrack*>(&_route) != 0;
806 }
807
808 DiskStream*
809 RouteUI::get_diskstream () const
810 {
811         AudioTrack *at;
812
813         if ((at = dynamic_cast<AudioTrack*>(&_route)) != 0) {
814                 return &at->disk_stream();
815         } else {
816                 return 0;
817         }
818 }
819
820 AudioTrack*
821 RouteUI::audio_track() const
822 {
823         return dynamic_cast<AudioTrack*>(&_route);
824 }
825 string
826 RouteUI::name() const
827 {
828         return _route.name();
829 }
830
831 void
832 RouteUI::map_frozen ()
833 {
834         ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
835
836         AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
837
838         if (at) {
839                 switch (at->freeze_state()) {
840                 case AudioTrack::Frozen:
841                         rec_enable_button->set_sensitive (false);
842                         break;
843                 default:
844                         rec_enable_button->set_sensitive (true);
845                         break;
846                 }
847         }
848 }