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