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