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