option editor and some prompters, misc stuff. this commit message contains no apostro...
[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.0);
571         current.set_green (_color.get_green() / 65535.0);
572         current.set_blue (_color.get_blue() / 65535.0);
573         //current[3] = 1.0;
574
575         color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &current);
576
577         if (picked) {
578                 set_color (color);
579         }
580
581         return picked;
582 }
583
584 void
585 RouteUI::set_color (Gdk::Color c)
586 {
587         char buf[64];
588         
589         _color = c;
590         
591         ensure_xml_node ();
592         snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
593         xml_node->add_property ("color", buf);
594
595          _route.gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
596 }
597
598
599 void
600 RouteUI::ensure_xml_node ()
601 {
602         if (xml_node == 0) {
603                 if ((xml_node = _route.extra_xml ("GUI")) == 0) {
604                         xml_node = new XMLNode ("GUI");
605                         _route.add_extra_xml (*xml_node);
606                 }
607         }
608 }
609
610 XMLNode*
611 RouteUI::get_child_xml_node (string childname)
612 {
613         XMLNode* child;
614
615         ensure_xml_node ();
616         
617         
618         if ((child = find_named_node (*xml_node, childname)) == 0) {
619                 child = new XMLNode (childname);
620                 xml_node->add_child_nocopy (*child);
621         }
622
623         return child;
624 }
625
626 int
627 RouteUI::set_color_from_route ()
628 {
629         XMLProperty *prop;
630         
631         RouteUI::ensure_xml_node ();
632
633         if ((prop = xml_node->property ("color")) != 0) {
634                 int r, g, b;
635                 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
636                 _color.set_red(r);
637                 _color.set_green(g);
638                 _color.set_blue(b);
639                 return 0;
640         } 
641         return 1;
642 }
643
644 void
645 RouteUI::remove_this_route ()
646 {
647         vector<string> choices;
648         string prompt;
649
650         if (is_audio_track()) {
651                 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());
652         } else {
653                 prompt  = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route.name());
654         }
655
656         choices.push_back (_("Yes, remove it."));
657         choices.push_back (_("No, do nothing."));
658
659         Choice prompter (prompt, choices);
660
661         prompter.chosen.connect (Gtk::Main::quit.slot());
662         prompter.show_all ();
663
664         Gtk::Main::run ();
665
666         if (prompter.get_choice() == 0) {
667           Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
668         }
669 }
670
671 gint
672 RouteUI::idle_remove_this_route (RouteUI *rui)
673 {
674         rui->_session.remove_route (rui->_route);
675         return FALSE;
676 }
677
678 void
679 RouteUI::route_removed ()
680 {
681         ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
682         
683         delete this;
684 }
685
686 void
687 RouteUI::route_rename ()
688 {
689         ArdourPrompter name_prompter (true);
690         string result;
691         name_prompter.set_prompt (_("new name: "));
692         name_prompter.set_initial_text (_route.name());
693         name_prompter.show_all ();
694
695         switch (name_prompter.run ()) {
696
697         case GTK_RESPONSE_ACCEPT:
698                 name_prompter.get_result (result);
699                 if (result.length()) {
700                         strip_whitespace_edges (result);
701                         _route.set_name (result, this);
702                 }       
703                 break;
704         }
705
706         return;
707   
708 }
709
710 void
711 RouteUI::name_changed (void *src)
712 {
713         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
714         
715         name_label.set_text (_route.name());
716 }
717
718 void
719 RouteUI::toggle_route_active ()
720 {
721         bool yn;
722
723         if (route_active_menu_item) {
724                 if (route_active_menu_item->get_active() != (yn = _route.active())) {
725                         _route.set_active (!yn);
726                 }
727         }
728 }
729
730 void
731 RouteUI::route_active_changed ()
732 {
733         if (route_active_menu_item) {
734                 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route.active()));
735         }
736 }
737
738 void
739 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
740 {
741         bool yn = _route.solo_safe ();
742
743         if (check->get_active() != yn) {
744                 check->set_active (yn);
745         }
746 }
747 void
748 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
749 {
750         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
751         
752         bool yn = _route.get_mute_config(PRE_FADER);
753         if (check->get_active() != yn) {
754                 check->set_active (yn);
755         }
756 }
757
758 void
759 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
760 {
761         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
762         
763         bool yn = _route.get_mute_config(POST_FADER);
764         if (check->get_active() != yn) {
765                 check->set_active (yn);
766         }
767 }
768
769 void
770 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
771 {
772         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
773         
774         bool yn = _route.get_mute_config(CONTROL_OUTS);
775         if (check->get_active() != yn) {
776                 check->set_active (yn);
777         }
778 }
779
780 void
781 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
782 {
783         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
784         
785         bool yn = _route.get_mute_config(MAIN_OUTS);
786         if (check->get_active() != yn) {
787                 check->set_active (yn);
788         }
789 }
790
791 void
792 RouteUI::disconnect_input ()
793 {
794         _route.disconnect_inputs (this);
795 }
796
797 void
798 RouteUI::disconnect_output ()
799 {
800         _route.disconnect_outputs (this);
801 }
802
803 bool
804 RouteUI::is_audio_track () const
805 {
806         return dynamic_cast<AudioTrack*>(&_route) != 0;
807 }
808
809 DiskStream*
810 RouteUI::get_diskstream () const
811 {
812         AudioTrack *at;
813
814         if ((at = dynamic_cast<AudioTrack*>(&_route)) != 0) {
815                 return &at->disk_stream();
816         } else {
817                 return 0;
818         }
819 }
820
821 AudioTrack*
822 RouteUI::audio_track() const
823 {
824         return dynamic_cast<AudioTrack*>(&_route);
825 }
826 string
827 RouteUI::name() const
828 {
829         return _route.name();
830 }
831
832 void
833 RouteUI::map_frozen ()
834 {
835         ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
836
837         AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
838
839         if (at) {
840                 switch (at->freeze_state()) {
841                 case AudioTrack::Frozen:
842                         rec_enable_button->set_sensitive (false);
843                         break;
844                 default:
845                         rec_enable_button->set_sensitive (true);
846                         break;
847                 }
848         }
849 }