cc8545ad61e03fab6c5c2efefceee2c9f17005a8
[ardour.git] / gtk2_ardour / route_ui.cc
1 /*
2     Copyright (C) 2002-2006 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/doi.h>
25 #include <gtkmm2ext/bindable_button.h>
26
27 #include <ardour/route_group.h>
28 #include <pbd/memento_command.h>
29 #include <pbd/stacktrace.h>
30 #include <pbd/shiva.h>
31
32 #include "route_ui.h"
33 #include "keyboard.h"
34 #include "utils.h"
35 #include "prompter.h"
36 #include "gui_thread.h"
37
38 #include <ardour/route.h>
39 #include <ardour/session.h>
40 #include <ardour/audioengine.h>
41 #include <ardour/audio_track.h>
42 #include <ardour/audio_diskstream.h>
43
44 #include "i18n.h"
45 using namespace sigc;
46 using namespace Gtk;
47 using namespace Gtkmm2ext;
48 using namespace ARDOUR;
49 using namespace PBD;
50
51 RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt, ARDOUR::Session& sess, const char* m_name,
52                   const char* s_name, const char* r_name)
53         : AxisView(sess),
54           _route(rt),
55           mute_button(0),
56           solo_button(0),
57           rec_enable_button(0)
58 {
59         xml_node = 0;
60         mute_menu = 0;
61         solo_menu = 0;
62         remote_control_menu = 0;
63         ignore_toggle = false;
64         wait_for_release = false;
65         route_active_menu_item = 0;
66         was_solo_safe = false;
67
68         if (set_color_from_route()) {
69                 set_color (unique_random_color());
70         }
71
72         new PairedShiva<Route,RouteUI> (*_route, *this);
73
74         _route->active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed));
75
76         mute_button = manage (new BindableToggleButton (_route->mute_control(), m_name ));
77         mute_button->set_self_managed (true);
78
79         solo_button = manage (new BindableToggleButton (_route->solo_control(), s_name ));
80         solo_button->set_self_managed (true);
81
82         mute_button->set_name ("MuteButton");
83         solo_button->set_name ("SoloButton");
84
85         _route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed));
86         _route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
87         _route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
88
89         /* when solo changes, update mute state too, in case the user wants us to display it */
90
91         _session.SoloChanged.connect (mem_fun(*this, &RouteUI::solo_changed_so_update_mute));
92         
93         update_solo_display ();
94         update_mute_display ();
95
96         if (is_track()) {
97                 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
98
99                 t->diskstream()->RecordEnableChanged.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed));
100
101                 _session.RecordStateChanged.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed));
102
103                 rec_enable_button = manage (new BindableToggleButton (t->rec_enable_control(), r_name ));
104                 rec_enable_button->set_name ("RecordEnableButton");
105                 rec_enable_button->set_self_managed (true);
106
107                 update_rec_display ();
108         } 
109         
110         /* map the current state */
111
112         map_frozen ();
113 }
114
115 RouteUI::~RouteUI()
116 {
117         GoingAway (); /* EMIT SIGNAL */
118         delete mute_menu;
119 }
120
121 bool
122 RouteUI::mute_press(GdkEventButton* ev)
123 {
124         if (!ignore_toggle) {
125
126                 if (Keyboard::is_context_menu_event (ev)) {
127
128                         if (mute_menu == 0){
129                                 build_mute_menu();
130                         }
131
132                         mute_menu->popup(0,ev->time);
133
134                 } else {
135
136                         if (ev->button == 2) {
137                                 // ctrl-button2 click is the midi binding click
138                                 // button2-click is "momentary"
139                                 
140                                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
141                                         wait_for_release = true;
142                                 } else {
143                                         return false;
144                                 }
145                         }
146
147                         if (ev->button == 1 || ev->button == 2) {
148
149                                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
150
151                                         /* ctrl-shift-click applies change to all routes */
152
153                                         _session.begin_reversible_command (_("mute change"));
154                                         Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(_session, this);
155                                         _session.set_all_mute (!_route->muted());
156                                         cmd->mark();
157                                         _session.add_command(cmd);
158                                         _session.commit_reversible_command ();
159
160                                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
161
162                                         /* ctrl-click applies change to the mix group.
163                                            ctrl-button2 is MIDI learn.
164                                         */
165
166                                         if (ev->button == 1) {
167                                                 set_mix_group_mute (_route, !_route->muted());
168                                         }
169                                         
170                                 } else {
171
172                                         /* plain click applies change to this route */
173
174                                         reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this);
175                                 }
176                         }
177                 }
178
179         }
180
181         return true;
182 }
183
184 bool
185 RouteUI::mute_release(GdkEventButton* ev)
186 {
187         if (!ignore_toggle) {
188                 if (wait_for_release){
189                         wait_for_release = false;
190                         // undo the last op
191                         // because the press was the last undoable thing we did
192                         _session.undo (1U);
193                 }
194         }
195         return true;
196 }
197
198 bool
199 RouteUI::solo_press(GdkEventButton* ev)
200 {
201         if (!ignore_toggle) {
202
203                 if (Keyboard::is_context_menu_event (ev)) {
204                         
205                         if (solo_menu == 0) {
206                                 build_solo_menu ();
207                         }
208
209                         solo_menu->popup (1, ev->time);
210
211                 } else {
212
213                         if (ev->button == 2) {
214
215                                 // ctrl-button2 click is the midi binding click
216                                 // button2-click is "momentary"
217                                 
218                                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
219                                         wait_for_release = true;
220                                 } else {
221                                         return false;
222                                 }
223                         }
224
225                         if (ev->button == 1 || ev->button == 2) {
226
227                                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
228
229                                         /* ctrl-shift-click applies change to all routes */
230
231                                         _session.begin_reversible_command (_("solo change"));
232                                         Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
233                                         _session.set_all_solo (!_route->soloed());
234                                         cmd->mark();
235                                         _session.add_command (cmd);
236                                         _session.commit_reversible_command ();
237                                         
238                                 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
239
240                                         // ctrl-alt-click: exclusively solo this track, not a toggle */
241
242                                         _session.begin_reversible_command (_("solo change"));
243                                         Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (_session, this);
244                                         _session.set_all_solo (false);
245                                         _route->set_solo (true, this);
246                                         cmd->mark();
247                                         _session.add_command(cmd);
248                                         _session.commit_reversible_command ();
249
250                                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
251
252                                         // shift-click: set this route to solo safe
253
254                                         _route->set_solo_safe (!_route->solo_safe(), this);
255                                         wait_for_release = false;
256
257                                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
258
259                                         /* ctrl-click: solo mix group.
260                                            ctrl-button2 is MIDI learn.
261                                         */
262
263                                         if (ev->button == 1) {
264                                                 set_mix_group_solo (_route, !_route->soloed());
265                                         }
266
267                                 } else {
268
269                                         /* click: solo this route */
270
271                                         reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
272                                 }
273                         }
274                 }
275         }
276
277         return true;
278 }
279
280 bool
281 RouteUI::solo_release(GdkEventButton* ev)
282 {
283         if (!ignore_toggle) {
284                 if (wait_for_release) {
285                         wait_for_release = false;
286                         // undo the last op
287                         // because the press was the last undoable thing we did
288
289                         _session.undo (1U);
290                 }
291         }
292
293         return true;
294 }
295
296 bool
297 RouteUI::rec_enable_press(GdkEventButton* ev)
298 {
299         if (!_session.engine().connected()) {
300                 MessageDialog msg (_("Not connected to JACK - cannot engage record"));
301                 msg.run ();
302                 return true;
303         }
304
305         if (!ignore_toggle && is_track() && rec_enable_button) {
306
307                 if (ev->button == 2 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
308
309                         // do nothing on midi bind event
310
311                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
312
313                         _session.begin_reversible_command (_("rec-enable change"));
314                         Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
315
316                         if (rec_enable_button->get_active()) {
317                                 _session.record_disenable_all ();
318                         } else {
319                                 _session.record_enable_all ();
320                         }
321
322                         cmd->mark();
323                         _session.add_command(cmd);
324                         _session.commit_reversible_command ();
325
326                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
327
328                         set_mix_group_rec_enable (_route, !_route->record_enabled());
329
330                 } else {
331
332                         reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
333                 }
334         }
335
336         return true;
337 }
338
339 bool
340 RouteUI::rec_enable_release (GdkEventButton* ev)
341 {
342         return true;
343 }
344
345 void
346 RouteUI::solo_changed(void* src)
347 {
348         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
349 }
350
351 void
352 RouteUI::update_solo_display ()
353 {
354         bool x;
355         vector<Gdk::Color> fg_colors;
356         Gdk::Color c;
357         
358         if (solo_button->get_active() != (x = _route->soloed())){
359                 ignore_toggle = true;
360                 solo_button->set_active(x);
361                 ignore_toggle = false;
362         }
363         
364         if (_route->solo_safe()) {
365                 solo_button->set_visual_state (2);
366         } else if (_route->soloed()) {
367                 solo_button->set_visual_state (1);
368         } else {
369                 solo_button->set_visual_state (0);
370         }
371 }
372
373 void
374 RouteUI::solo_changed_so_update_mute ()
375 {
376         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
377 }
378
379 void
380 RouteUI::mute_changed(void* src)
381 {
382         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
383 }
384
385 void
386 RouteUI::update_mute_display ()
387 {
388         bool model = _route->record_enabled();
389         bool view = mute_button->get_active();
390
391         /* first make sure the button's "depressed" visual
392            is correct.
393         */
394
395         if (model != view) {
396                 ignore_toggle = true;
397                 mute_button->set_active (model);
398                 ignore_toggle = false;
399         }
400
401         /* now attend to visual state */
402         
403         if (Config->get_show_solo_mutes()) {
404                 if (_route->muted()) {
405                         mute_button->set_visual_state (2);
406                 } else if (!_route->soloed() && _route->solo_muted()) {
407                         
408                         mute_button->set_visual_state (1);
409                 } else {
410                         mute_button->set_visual_state (0);
411                 }
412         } else {
413                 if (_route->muted()) {
414                         mute_button->set_visual_state (2);
415                 } else {
416                         mute_button->set_visual_state (0);
417                 }
418         }
419
420 }
421
422 void
423 RouteUI::route_rec_enable_changed ()
424 {
425         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
426 }
427
428 void
429 RouteUI::session_rec_enable_changed ()
430 {
431         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
432 }
433
434 void
435 RouteUI::update_rec_display ()
436 {
437         bool model = _route->record_enabled();
438         bool view = rec_enable_button->get_active();
439
440         /* first make sure the button's "depressed" visual
441            is correct.
442         */
443
444         if (model != view) {
445                 ignore_toggle = true;
446                 rec_enable_button->set_active (model);
447                 ignore_toggle = false;
448         }
449
450         /* now make sure its color state is correct */
451
452         if (model) {
453
454                 switch (_session.record_status ()) {
455                 case Session::Recording:
456                         rec_enable_button->set_visual_state (1);
457                         break;
458
459                 case Session::Disabled:
460                 case Session::Enabled:
461                         rec_enable_button->set_visual_state (2);
462                         break;
463
464                 }
465
466         } else {
467                 rec_enable_button->set_visual_state (0);
468         }
469 }
470
471 void
472 RouteUI::build_remote_control_menu ()
473 {
474         remote_control_menu = manage (new Menu);
475         refresh_remote_control_menu ();
476 }
477
478 void
479 RouteUI::refresh_remote_control_menu ()
480 {
481         using namespace Menu_Helpers;
482
483         RadioMenuItem::Group rc_group;
484         CheckMenuItem* rc_active;
485         uint32_t limit = _session.ntracks() + _session.nbusses();
486         char buf[32];
487
488         MenuList& rc_items = remote_control_menu->items();
489         rc_items.clear ();
490
491         /* note that this menu list starts at zero, not 1, because zero
492            is a valid, if useless, ID.
493         */
494
495         limit += 4; /* leave some breathing room */
496         
497         rc_items.push_back (RadioMenuElem (rc_group, _("None")));
498         if (_route->remote_control_id() == 0) {
499                 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
500                 rc_active->set_active ();
501         }
502                 
503         for (uint32_t i = 1; i < limit; ++i) {
504                 snprintf (buf, sizeof (buf), "%u", i);
505                 rc_items.push_back (RadioMenuElem (rc_group, buf));
506                 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
507                 if (_route->remote_control_id() == i) {
508                         rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
509                         rc_active->set_active ();
510                 }
511                 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
512         }
513 }
514
515 void
516 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
517 {
518         /* this is called when the radio menu item is toggled, and so 
519            is actually invoked twice per menu selection. we only
520            care about the invocation for the item that was being
521            marked active.
522         */
523
524         if (item->get_active()) {
525                 _route->set_remote_control_id (id);
526         }
527 }
528
529 void
530 RouteUI::build_solo_menu (void)
531 {
532         using namespace Menu_Helpers;
533         
534         solo_menu = new Menu;
535         solo_menu->set_name ("ArdourContextMenu");
536         MenuList& items = solo_menu->items();
537         CheckMenuItem* check;
538
539         check = new CheckMenuItem(_("Solo-safe"));
540         check->set_active (_route->solo_safe());
541         check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
542         _route->solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
543         items.push_back (CheckMenuElem(*check));
544         check->show_all();
545
546         //items.push_back (SeparatorElem());
547         // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
548         
549 }
550
551 void
552 RouteUI::build_mute_menu(void)
553 {
554         using namespace Menu_Helpers;
555         
556         mute_menu = new Menu;
557         mute_menu->set_name ("ArdourContextMenu");
558         MenuList& items = mute_menu->items();
559         CheckMenuItem* check;
560         
561         check = new CheckMenuItem(_("Pre Fader"));
562         init_mute_menu(PRE_FADER, check);
563         check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
564         _route->pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
565         items.push_back (CheckMenuElem(*check));
566         check->show_all();
567
568         check = new CheckMenuItem(_("Post Fader"));
569         init_mute_menu(POST_FADER, check);
570         check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
571         _route->post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
572         items.push_back (CheckMenuElem(*check));
573         check->show_all();
574         
575         check = new CheckMenuItem(_("Control Outs"));
576         init_mute_menu(CONTROL_OUTS, check);
577         check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
578         _route->control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
579         items.push_back (CheckMenuElem(*check));
580         check->show_all();
581
582         check = new CheckMenuItem(_("Main Outs"));
583         init_mute_menu(MAIN_OUTS, check);
584         check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
585         _route->main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
586         items.push_back (CheckMenuElem(*check));
587         check->show_all();
588
589         //items.push_back (SeparatorElem());
590         // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
591 }
592
593 void
594 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
595 {
596         if (_route->get_mute_config (type)) {
597                 check->set_active (true);
598         }
599 }
600
601 void
602 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
603 {
604         _route->set_mute_config(type, check->get_active(), this);
605 }
606
607 void
608 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
609 {
610         _route->set_solo_safe (check->get_active(), this);
611 }
612
613 void
614 RouteUI::set_mix_group_solo(boost::shared_ptr<Route> route, bool yn)
615 {
616         RouteGroup* mix_group;
617
618         if((mix_group = route->mix_group()) != 0){
619                 _session.begin_reversible_command (_("mix group solo  change"));
620                 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
621                 mix_group->apply(&Route::set_solo, yn, this);
622                 cmd->mark();
623                 _session.add_command (cmd);
624                 _session.commit_reversible_command ();
625         } else {
626                 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route->soloed(), this);
627         }
628 }
629
630 void
631 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
632 {
633         _session.begin_reversible_command (name);
634         XMLNode &before = _route->get_state();
635         bind(mem_fun(*_route, func), yn, arg)();
636         XMLNode &after = _route->get_state();
637         _session.add_command (new MementoCommand<Route>(*_route, &before, &after));
638         _session.commit_reversible_command ();
639 }
640
641 void
642 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
643 {
644         _session.begin_reversible_command (name);
645         XMLNode &before = audio_track()->get_state();
646         bind (mem_fun (*audio_track(), func), yn, arg)();
647         XMLNode &after = audio_track()->get_state();
648         _session.add_command (new MementoCommand<AudioTrack>(*audio_track(), &before, &after));
649         _session.commit_reversible_command ();
650 }
651
652 void
653 RouteUI::set_mix_group_mute(boost::shared_ptr<Route> route, bool yn)
654 {
655         RouteGroup* mix_group;
656
657         if((mix_group = route->mix_group()) != 0){
658                 _session.begin_reversible_command (_("mix group mute change"));
659                 Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand (_session, this);
660                 mix_group->apply(&Route::set_mute, yn, this);
661                 cmd->mark();
662                 _session.add_command(cmd);
663                 _session.commit_reversible_command ();
664         } else {
665                 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route->muted(), this);
666         }
667 }
668
669 void
670 RouteUI::set_mix_group_rec_enable(boost::shared_ptr<Route> route, bool yn)
671 {
672         RouteGroup* mix_group;
673
674         if((mix_group = route->mix_group()) != 0){
675                 _session.begin_reversible_command (_("mix group rec-enable change"));
676                 Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
677                 mix_group->apply (&Route::set_record_enable, yn, this);
678                 cmd->mark();
679                 _session.add_command(cmd);
680                 _session.commit_reversible_command ();
681         } else {
682                 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route->record_enabled(), this);
683         }
684 }
685
686
687 bool
688 RouteUI::choose_color()
689 {
690         bool picked;
691         Gdk::Color color;
692
693         color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
694
695         if (picked) {
696                 set_color (color);
697         }
698
699         return picked;
700 }
701
702 void
703 RouteUI::set_color (const Gdk::Color & c)
704 {
705         char buf[64];
706         
707         _color = c;
708         
709         ensure_xml_node ();
710         snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
711         xml_node->add_property ("color", buf);
712
713         _route->gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
714 }
715
716
717 void
718 RouteUI::ensure_xml_node ()
719 {
720         if (xml_node == 0) {
721                 if ((xml_node = _route->extra_xml ("GUI")) == 0) {
722                         xml_node = new XMLNode ("GUI");
723                         _route->add_extra_xml (*xml_node);
724                 }
725         }
726 }
727
728 XMLNode*
729 RouteUI::get_child_xml_node (const string & childname)
730 {
731         XMLNode* child;
732
733         ensure_xml_node ();
734         
735         
736         if ((child = find_named_node (*xml_node, childname)) == 0) {
737                 child = new XMLNode (childname);
738                 xml_node->add_child_nocopy (*child);
739         }
740
741         return child;
742 }
743
744 int
745 RouteUI::set_color_from_route ()
746 {
747         XMLProperty *prop;
748         
749         RouteUI::ensure_xml_node ();
750
751         if ((prop = xml_node->property ("color")) != 0) {
752                 int r, g, b;
753                 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
754                 _color.set_red(r);
755                 _color.set_green(g);
756                 _color.set_blue(b);
757                 return 0;
758         } 
759         return 1;
760 }
761
762 void
763 RouteUI::remove_this_route ()
764 {
765         vector<string> choices;
766         string prompt;
767
768         if (is_track()) {
769                 prompt  = string_compose (_("Do you really want to remove track \"%1\" ?\n\nYou may also lose the playlist used by this track.\n(cannot be undone)"), _route->name());
770         } else {
771                 prompt  = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route->name());
772         }
773
774         choices.push_back (_("No, do nothing."));
775         choices.push_back (_("Yes, remove it."));
776
777         Choice prompter (prompt, choices);
778
779         if (prompter.run () == 1) {
780                 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
781         }
782 }
783
784 gint
785 RouteUI::idle_remove_this_route (RouteUI *rui)
786 {
787         rui->_session.remove_route (rui->_route);
788         return FALSE;
789 }
790
791 void
792 RouteUI::route_rename ()
793 {
794         ArdourPrompter name_prompter (true);
795         string result;
796         name_prompter.set_prompt (_("New Name: "));
797         name_prompter.set_initial_text (_route->name());
798         name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
799         name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
800         name_prompter.show_all ();
801
802         switch (name_prompter.run ()) {
803
804         case Gtk::RESPONSE_ACCEPT:
805         name_prompter.get_result (result);
806         if (result.length()) {
807                         _route->set_name (result, this);
808                 }       
809                 break;
810         }
811
812         return;
813   
814 }
815
816 void
817 RouteUI::name_changed (void *src)
818 {
819         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
820
821         name_label.set_text (_route->name());
822 }
823
824 void
825 RouteUI::toggle_route_active ()
826 {
827         bool yn;
828
829         if (route_active_menu_item) {
830                 if (route_active_menu_item->get_active() != (yn = _route->active())) {
831                         _route->set_active (!yn);
832                 }
833         }
834 }
835
836 void
837 RouteUI::route_active_changed ()
838 {
839         if (route_active_menu_item) {
840                 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route->active()));
841         }
842 }
843
844 void
845 RouteUI::toggle_polarity ()
846 {
847         if (polarity_menu_item) {
848
849                 bool x;
850
851                 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
852                 
853                 if ((x = polarity_menu_item->get_active()) != _route->phase_invert()) {
854                         _route->set_phase_invert (x, this);
855                         if (x) {
856                                 name_label.set_text (X_("Ø ") + name_label.get_text());
857                         } else {
858                                 name_label.set_text (_route->name());
859                         }
860                 }
861         }
862 }
863
864 void
865 RouteUI::polarity_changed ()
866 {
867         /* no signal for this yet */
868 }
869
870 void
871 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
872 {
873         bool yn = _route->solo_safe ();
874
875         if (check->get_active() != yn) {
876                 check->set_active (yn);
877         }
878 }
879 void
880 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
881 {
882         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
883         
884         bool yn = _route->get_mute_config(PRE_FADER);
885         if (check->get_active() != yn) {
886                 check->set_active (yn);
887         }
888 }
889
890 void
891 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
892 {
893         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
894         
895         bool yn = _route->get_mute_config(POST_FADER);
896         if (check->get_active() != yn) {
897                 check->set_active (yn);
898         }
899 }
900
901 void
902 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
903 {
904         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
905         
906         bool yn = _route->get_mute_config(CONTROL_OUTS);
907         if (check->get_active() != yn) {
908                 check->set_active (yn);
909         }
910 }
911
912 void
913 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
914 {
915         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
916         
917         bool yn = _route->get_mute_config(MAIN_OUTS);
918         if (check->get_active() != yn) {
919                 check->set_active (yn);
920         }
921 }
922
923 void
924 RouteUI::disconnect_input ()
925 {
926         _route->disconnect_inputs (this);
927 }
928
929 void
930 RouteUI::disconnect_output ()
931 {
932         _route->disconnect_outputs (this);
933 }
934
935 bool
936 RouteUI::is_track () const
937 {
938         return dynamic_cast<Track*>(_route.get()) != 0;
939 }
940
941 Track*
942 RouteUI::track() const
943 {
944         return dynamic_cast<Track*>(_route.get());
945 }
946
947 bool
948 RouteUI::is_audio_track () const
949 {
950         return dynamic_cast<AudioTrack*>(_route.get()) != 0;
951 }
952
953 AudioTrack*
954 RouteUI::audio_track() const
955 {
956         return dynamic_cast<AudioTrack*>(_route.get());
957 }
958
959 boost::shared_ptr<Diskstream>
960 RouteUI::get_diskstream () const
961 {
962         boost::shared_ptr<Track> t;
963
964         if ((t = boost::dynamic_pointer_cast<Track>(_route)) != 0) {
965                 return t->diskstream();
966         } else {
967                 return boost::shared_ptr<Diskstream> ((Diskstream*) 0);
968         }
969 }
970
971 string
972 RouteUI::name() const
973 {
974         return _route->name();
975 }
976
977 void
978 RouteUI::map_frozen ()
979 {
980         ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
981
982         AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
983
984         if (at) {
985                 switch (at->freeze_state()) {
986                 case AudioTrack::Frozen:
987                         rec_enable_button->set_sensitive (false);
988                         break;
989                 default:
990                         rec_enable_button->set_sensitive (true);
991                         break;
992                 }
993         }
994 }
995