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