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