34514d9040ea66e7b95228912871fb4153c30f98
[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 #include <gtkmm2ext/gtk_ui.h>
27
28 #include "ardour/route_group.h"
29 #include "pbd/memento_command.h"
30 #include "pbd/stacktrace.h"
31 #include "pbd/shiva.h"
32 #include "pbd/controllable.h"
33
34 #include "ardour_ui.h"
35 #include "editor.h"
36 #include "route_ui.h"
37 #include "keyboard.h"
38 #include "utils.h"
39 #include "prompter.h"
40 #include "gui_thread.h"
41 #include "ardour_dialog.h"
42 #include "latency_gui.h"
43 #include "mixer_strip.h"
44 #include "automation_time_axis.h"
45
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/audioengine.h"
49 #include "ardour/audio_track.h"
50 #include "ardour/audio_diskstream.h"
51 #include "ardour/midi_track.h"
52 #include "ardour/midi_diskstream.h"
53 #include "ardour/template_utils.h"
54 #include "ardour/filename_extensions.h"
55 #include "ardour/directory_names.h"
56 #include "ardour/profile.h"
57
58 #include "i18n.h"
59 using namespace sigc;
60 using namespace Gtk;
61 using namespace Gtkmm2ext;
62 using namespace ARDOUR;
63 using namespace PBD;
64
65 RouteUI::RouteUI (ARDOUR::Session& sess, const char* mute_name, const char* solo_name, const char* rec_name)
66         : AxisView(sess)
67 {
68         init ();
69         set_button_names (mute_name, solo_name, rec_name);
70 }
71
72 RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt, 
73                   ARDOUR::Session& sess, const char* mute_name, const char* solo_name, const char* rec_name)
74         : AxisView(sess)
75 {
76         init ();
77         set_button_names (mute_name, solo_name, rec_name);
78         set_route (rt);
79 }
80
81 RouteUI::~RouteUI()
82 {
83        /* derived classes should emit GoingAway so that they receive the signal
84           when the object is still a legal derived instance.
85        */
86         
87         delete solo_menu;
88         delete mute_menu;
89         delete remote_control_menu;
90         delete sends_menu;
91 }
92
93 void
94 RouteUI::init ()
95 {
96         self_destruct = true;
97         xml_node = 0;
98         mute_menu = 0;
99         solo_menu = 0;
100         remote_control_menu = 0;
101         sends_menu = 0;
102         ignore_toggle = false;
103         wait_for_release = false;
104         route_active_menu_item = 0;
105         polarity_menu_item = 0;
106         denormal_menu_item = 0;
107         multiple_mute_change = false;
108         multiple_solo_change = false;
109
110         mute_button = manage (new BindableToggleButton (""));
111         mute_button->set_self_managed (true);
112         mute_button->set_name ("MuteButton");
113         UI::instance()->set_tip (mute_button, _("Mute this track"), "");
114
115         solo_button = manage (new BindableToggleButton (""));
116         solo_button->set_self_managed (true);
117         solo_button->set_name ("SoloButton");
118         UI::instance()->set_tip (solo_button, _("Mute other (non-soloed) tracks"), "");
119         solo_button->set_no_show_all (true);
120
121         rec_enable_button = manage (new BindableToggleButton (""));
122         rec_enable_button->set_name ("RecordEnableButton");
123         rec_enable_button->set_self_managed (true);
124         UI::instance()->set_tip (rec_enable_button, _("Enable recording on this track"), "");
125
126         show_sends_button = manage (new BindableToggleButton (""));
127         show_sends_button->set_name ("SendAlert");
128         show_sends_button->set_self_managed (true);
129         UI::instance()->set_tip (show_sends_button, _("make mixer strips show sends to this bus"), "");
130
131         _session.SoloChanged.connect (mem_fun(*this, &RouteUI::solo_changed_so_update_mute));
132         _session.TransportStateChange.connect (mem_fun (*this, &RouteUI::check_rec_enable_sensitivity));
133
134         Config->ParameterChanged.connect (mem_fun (*this, &RouteUI::parameter_changed));
135 }
136
137 void
138 RouteUI::reset ()
139 {
140         //Remove route connections associated with us.
141         for (vector<sigc::connection>::iterator it = connections.begin(); it!=connections.end(); ++it) {
142             (*it).disconnect();
143         }
144
145         connections.clear ();
146
147         delete solo_menu;
148         solo_menu = 0;
149
150         delete mute_menu;
151         mute_menu = 0;
152         
153         if (xml_node) {
154                 /* do not delete the node - its owned by the route */
155                 xml_node = 0;
156         }
157
158         route_active_menu_item = 0;
159         polarity_menu_item = 0;
160         denormal_menu_item = 0;
161 }
162
163 void
164 RouteUI::set_button_names (const char* mute, const char* solo, const char* rec)
165 {
166         m_name = mute;
167         s_name = solo;
168         r_name = rec;
169 }
170
171 void
172 RouteUI::set_route (boost::shared_ptr<Route> rp)
173 {
174         reset ();
175
176         _route = rp;
177
178         if (set_color_from_route()) {
179                 set_color (unique_random_color());
180         }
181
182         /* no, there is no memory leak here. This object cleans itself (and other stuff)
183            up when the route is destroyed.
184         */
185
186         if (self_destruct) {
187                 new PairedShiva<Route,RouteUI> (*_route, *this);
188         }
189
190         mute_button->set_controllable (_route->mute_control());
191         mute_button->set_label (m_name);
192         
193         solo_button->set_controllable (_route->solo_control());
194         solo_button->set_label (s_name);
195   
196         connections.push_back (_route->active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed)));
197         connections.push_back (_route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed)));
198         connections.push_back (_route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
199         connections.push_back (_route->solo_isolated_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
200   
201         if (is_track()) {
202                 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
203
204                 connections.push_back (t->diskstream()->RecordEnableChanged.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed)));
205                 connections.push_back (t->diskstream()->RecordEnableChanged.connect (mem_fun (PublicEditor::instance(), &PublicEditor::update_rec_display)));
206                 connections.push_back (_session.RecordStateChanged.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed)));
207
208                 rec_enable_button->show();
209                 rec_enable_button->set_controllable (t->rec_enable_control());
210                 rec_enable_button->set_label (r_name);
211
212                 update_rec_display ();
213         } 
214
215         mute_button->unset_flags (Gtk::CAN_FOCUS);
216         solo_button->unset_flags (Gtk::CAN_FOCUS);
217         
218         mute_button->show();
219
220         if (_route->is_master()) {
221                 solo_button->hide ();
222         } else {
223                 solo_button->show();
224         }
225
226         connections.push_back (_route->RemoteControlIDChanged.connect (mem_fun(*this, &RouteUI::refresh_remote_control_menu)));
227
228         /* map the current state */
229
230         mute_changed (0);
231         solo_changed (0);
232
233         map_frozen ();
234 }
235
236 bool
237 RouteUI::mute_press(GdkEventButton* ev)
238 {
239         if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
240                 return true;
241         }
242         multiple_mute_change = false;
243         if (!ignore_toggle) {
244
245                 if (Keyboard::is_context_menu_event (ev)) {
246
247                         if (mute_menu == 0){
248                                 build_mute_menu();
249                         }
250
251                         mute_menu->popup(0,ev->time);
252
253                 } else {
254
255                         if (Keyboard::is_button2_event (ev)) {
256                                 // Primary-button2 click is the midi binding click
257                                 // button2-click is "momentary"
258                                 
259                                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier))) {
260                                         wait_for_release = true;
261                                 } else {
262                                         return false;
263                                 }
264                         }
265
266                         if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
267
268                                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
269
270                                         /* Primary-Tertiary-click applies change to all routes */
271
272                                         _session.begin_reversible_command (_("mute change"));
273                                         Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(_session, this);
274                                         _session.set_all_mute (!_route->muted());
275                                         cmd->mark();
276                                         _session.add_command(cmd);
277                                         _session.commit_reversible_command ();
278                                         multiple_mute_change = true;
279
280                                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
281
282                                         /* Primary-button1 applies change to the mix group.
283                                            NOTE: Primary-button2 is MIDI learn.
284                                         */
285
286                                         if (ev->button == 1) {
287                                                 set_route_group_mute (_route, !_route->muted());
288                                         }
289                                         
290                                 } else {
291
292                                         /* plain click applies change to this route */
293                                         if (wait_for_release) {
294                                                 _route->set_mute (!_route->muted(), this);
295                                         } else {
296                                                 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this);
297                                         }
298                                 }
299                         }
300                 }
301
302         }
303
304         return true;
305 }
306
307 bool
308 RouteUI::mute_release(GdkEventButton* ev)
309 {
310         if (!ignore_toggle) {
311                 if (wait_for_release){
312                         wait_for_release = false;
313                         if (multiple_mute_change) {
314                                 multiple_mute_change = false;
315                                 // undo the last op
316                                 // because the press was the last undoable thing we did
317                                 _session.undo (1U);
318                         } else {
319                                 _route->set_mute (!_route->muted(), this);
320                         }
321                 }
322         }
323         return true;
324 }
325
326 bool
327 RouteUI::solo_press(GdkEventButton* ev)
328 {
329         /* ignore double/triple clicks */
330
331         if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
332                 return true;
333         }
334         multiple_solo_change = false;
335         if (!ignore_toggle) {
336
337                 if (Keyboard::is_context_menu_event (ev)) {
338
339                         if (solo_menu == 0) {
340                                 build_solo_menu ();
341                         }
342
343                         solo_menu->popup (1, ev->time);
344
345                 } else {
346
347                         if (Keyboard::is_button2_event (ev)) {
348
349                                 // Primary-button2 click is the midi binding click
350                                 // button2-click is "momentary"
351                                 
352                                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier))) {
353                                         wait_for_release = true;
354                                 } else {
355                                         return false;
356                                 }
357                         }
358
359                         if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
360
361                                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
362
363                                         /* Primary-Tertiary-click applies change to all routes */
364                                         bool was_not_latched = false;
365                                         if (!Config->get_solo_latched ()) {
366                                                 was_not_latched = true;
367                                                 /*
368                                                   XXX it makes no sense to solo all tracks if we're 
369                                                   not in latched mode, but doing nothing feels like a bug, 
370                                                   so do it anyway 
371                                                 */
372                                                 Config->set_solo_latched (true);
373                                         }
374                                         _session.begin_reversible_command (_("solo change"));
375                                         Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
376                                         _session.set_all_solo (!_route->soloed());
377                                         cmd->mark();
378                                         _session.add_command (cmd);
379                                         _session.commit_reversible_command ();
380                                         multiple_solo_change = true;
381                                         if (was_not_latched) {
382                                                 Config->set_solo_latched (false);
383                                         }
384                                         
385                                 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
386
387                                         // Primary-Secondary-click: exclusively solo this track, not a toggle */
388
389                                         _session.begin_reversible_command (_("solo change"));
390                                         Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (_session, this);
391                                         _session.set_all_solo (false);
392                                         _route->set_solo (true, this);
393                                         cmd->mark();
394                                         _session.add_command(cmd);
395                                         _session.commit_reversible_command ();
396
397                                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
398
399                                         // shift-click: set this route to solo safe
400
401                                         if (Profile->get_sae() && ev->button == 1) {
402                                                 // button 1 and shift-click: disables solo_latched for this click
403                                                 if (!Config->get_solo_latched ()) {
404                                                         Config->set_solo_latched (true);
405                                                         reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
406                                                         Config->set_solo_latched (false);
407                                                 }
408                                         } else {
409                                                 _route->set_solo_isolated (!_route->solo_isolated(), this);
410                                                 wait_for_release = false;
411                                         }
412
413                                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
414
415                                         /* Primary-button1: solo mix group.
416                                            NOTE: Primary-button2 is MIDI learn.
417                                         */
418
419                                         if (ev->button == 1) {
420                                                 set_route_group_solo (_route, !_route->soloed());
421                                         }
422
423                                 } else {
424
425                                         /* click: solo this route */
426                                         if (wait_for_release) {
427                                                 _route->set_solo (!_route->soloed(), this);
428                                         } else {
429                                                 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
430                                         }
431                                 }
432                         }
433                 }
434         }
435
436         return true;
437 }
438
439 bool
440 RouteUI::solo_release(GdkEventButton* ev)
441 {
442         if (!ignore_toggle) {
443                 if (wait_for_release) {
444                         wait_for_release = false;
445                         if (multiple_solo_change) {
446                                 multiple_solo_change = false;
447                                 // undo the last op
448                                 // because the press was the last undoable thing we did
449                                 _session.undo (1U);
450                         } else {
451                                 // we don't use "undo the last op"
452                                 // here because its expensive for the GUI
453                                 _route->set_solo (!_route->soloed(), this);
454                         }
455                 }
456         }
457
458         return true;
459 }
460
461 bool
462 RouteUI::rec_enable_press(GdkEventButton* ev)
463 {
464         if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
465                 return true;
466         }
467
468         if (!_session.engine().connected()) {
469                 MessageDialog msg (_("Not connected to JACK - cannot engage record"));
470                 msg.run ();
471                 return true;
472         }
473
474         if (!ignore_toggle && is_track() && rec_enable_button) {
475
476                 if (Keyboard::is_button2_event (ev) && Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
477
478                         // do nothing on midi bind event
479                         return false;
480
481                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
482
483                         _session.begin_reversible_command (_("rec-enable change"));
484                         Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
485
486                         if (rec_enable_button->get_active()) {
487                                 _session.record_disenable_all ();
488                         } else {
489                                 _session.record_enable_all ();
490                                 check_rec_enable_sensitivity ();
491                         }
492
493                         cmd->mark();
494                         _session.add_command(cmd);
495                         _session.commit_reversible_command ();
496
497                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
498
499                         /* Primary-button1 applies change to the mix group.
500                            NOTE: Primary-button2 is MIDI learn.
501                         */
502
503                         set_route_group_rec_enable (_route, !_route->record_enabled());
504
505                 } else {
506                         reversibly_apply_track_boolean ("rec-enable change", &Track::set_record_enable, !track()->record_enabled(), this);
507                         check_rec_enable_sensitivity ();
508                 }
509         }
510
511         return true;
512 }
513
514 bool
515 RouteUI::rec_enable_release (GdkEventButton* ev)
516 {
517         return true;
518 }
519
520 void
521 RouteUI::build_sends_menu ()
522 {
523         using namespace Menu_Helpers;
524         
525         sends_menu = new Menu;
526         sends_menu->set_name ("ArdourContextMenu");
527         MenuList& items = sends_menu->items();
528         
529         items.push_back (MenuElem(_("Copy track gains to sends"), mem_fun (*this, &RouteUI::set_sends_gain_from_track)));
530         items.push_back (MenuElem(_("Set sends gain to -inf"), mem_fun (*this, &RouteUI::set_sends_gain_to_zero)));
531         items.push_back (MenuElem(_("Set sends gain to 0dB"), mem_fun (*this, &RouteUI::set_sends_gain_to_unity)));
532 }
533
534 void
535 RouteUI::set_sends_gain_from_track ()
536 {
537 }
538
539 void
540 RouteUI::set_sends_gain_to_zero ()
541 {
542 }
543
544 void
545 RouteUI::set_sends_gain_to_unity ()
546 {
547 }
548
549 bool
550 RouteUI::show_sends_press(GdkEventButton* ev)
551 {
552         if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
553                 return true;
554         }
555
556         if (!ignore_toggle && !is_track() && show_sends_button) {
557
558                 if (Keyboard::is_button2_event (ev) && Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
559
560                         // do nothing on midi bind event
561                         return false;
562
563                 } else if (Keyboard::is_context_menu_event (ev)) {
564
565                         if (sends_menu == 0) {
566                                 build_sends_menu ();
567                         }
568
569                         sends_menu->popup (0, ev->time);
570
571                 } else {
572
573                         /* change button state */
574
575                         show_sends_button->set_active (!show_sends_button->get_active());
576
577                         /* start blinking */
578
579                         if (show_sends_button->get_active()) {
580                                 /* show sends to this bus */
581                                 MixerStrip::SwitchIO (_route);
582                                 send_blink_connection = ARDOUR_UI::instance()->Blink.connect (mem_fun(*this, &RouteUI::send_blink));
583                         } else {
584                                 /* everybody back to normal */
585                                 send_blink_connection.disconnect ();
586                                 MixerStrip::SwitchIO (boost::shared_ptr<Route>());
587                         }
588
589                 }
590         }
591
592         return true;
593 }
594
595 bool
596 RouteUI::show_sends_release (GdkEventButton* ev)
597 {
598         return true;
599 }
600
601 void
602 RouteUI::send_blink (bool onoff)
603 {
604         if (!show_sends_button) {
605                 return;
606         }
607                 
608         if (onoff) {
609                 show_sends_button->set_state (STATE_ACTIVE);
610         } else {
611                 show_sends_button->set_state (STATE_NORMAL);
612         }
613 }
614
615 void
616 RouteUI::solo_changed(void* src)
617 {
618
619         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
620 }
621
622 void
623 RouteUI::update_solo_display ()
624 {
625         bool x;
626         vector<Gdk::Color> fg_colors;
627         Gdk::Color c;
628         
629         if (solo_button->get_active() != (x = _route->soloed())){
630                 ignore_toggle = true;
631                 solo_button->set_active(x);
632                 ignore_toggle = false;
633         } 
634         
635         if (_route->solo_isolated()) {
636                 solo_button->set_visual_state (2);
637         } else if (_route->soloed()) {
638                 solo_button->set_visual_state (1);
639         } else {
640                 solo_button->set_visual_state (0);
641         }
642 }
643
644 void
645 RouteUI::solo_changed_so_update_mute ()
646 {
647         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
648 }
649
650 void
651 RouteUI::mute_changed(void* src)
652 {
653         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
654 }
655
656 void
657 RouteUI::update_mute_display ()
658 {
659         bool model = _route->muted();
660         bool view = mute_button->get_active();
661
662         /* first make sure the button's "depressed" visual
663            is correct.
664         */
665
666         if (model != view) {
667                 ignore_toggle = true;
668                 mute_button->set_active (model);
669                 ignore_toggle = false;
670         }
671
672         /* now attend to visual state */
673         
674         if (Config->get_show_solo_mutes()) {
675                 if (_route->muted()) {
676                         mute_button->set_visual_state (2);
677                 } else if (!_route->soloed() && _session.soloing()) {
678                         mute_button->set_visual_state (1);
679                 } else {
680                         mute_button->set_visual_state (0);
681                 }
682         } else {
683                 if (_route->muted()) {
684                         mute_button->set_visual_state (2);
685                 } else {
686                         mute_button->set_visual_state (0);
687                 }
688         }
689
690 }
691
692 void
693 RouteUI::route_rec_enable_changed ()
694 {
695         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
696 }
697
698 void
699 RouteUI::session_rec_enable_changed ()
700 {
701         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
702 }
703
704 void
705 RouteUI::update_rec_display ()
706 {
707         bool model = _route->record_enabled();
708         bool view = rec_enable_button->get_active();
709
710         /* first make sure the button's "depressed" visual
711            is correct.
712         */
713
714         if (model != view) {
715                 ignore_toggle = true;
716                 rec_enable_button->set_active (model);
717                 ignore_toggle = false;
718         }
719         else {
720                 return;
721         }
722         
723         /* now make sure its color state is correct */
724
725         if (model) {
726
727                 switch (_session.record_status ()) {
728                 case Session::Recording:
729                         rec_enable_button->set_visual_state (1);
730                         break;
731
732                 case Session::Disabled:
733                 case Session::Enabled:
734                         rec_enable_button->set_visual_state (2);
735                         break;
736
737                 }
738
739         } else {
740                 rec_enable_button->set_visual_state (0);
741         }
742 }
743
744 void
745 RouteUI::build_remote_control_menu ()
746 {
747         remote_control_menu = new Menu;
748         refresh_remote_control_menu ();
749 }
750
751 void
752 RouteUI::refresh_remote_control_menu ()
753 {
754         ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::refresh_remote_control_menu));
755
756         // only refresh the menu if it has been instantiated
757
758         if (remote_control_menu == 0) {
759                 return;
760         }
761
762         using namespace Menu_Helpers;
763
764         RadioMenuItem::Group rc_group;
765         CheckMenuItem* rc_active;
766         uint32_t limit = _session.ntracks() + _session.nbusses();
767         char buf[32];
768
769         MenuList& rc_items = remote_control_menu->items();
770         rc_items.clear ();
771
772         /* note that this menu list starts at zero, not 1, because zero
773            is a valid, if useless, ID.
774         */
775
776         limit += 4; /* leave some breathing room */
777         
778         rc_items.push_back (RadioMenuElem (rc_group, _("None")));
779         if (_route->remote_control_id() == 0) {
780                 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
781                 rc_active->set_active ();
782         }
783                 
784         for (uint32_t i = 1; i < limit; ++i) {
785                 snprintf (buf, sizeof (buf), "%u", i);
786                 rc_items.push_back (RadioMenuElem (rc_group, buf));
787                 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
788                 if (_route->remote_control_id() == i) {
789                         rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
790                         rc_active->set_active ();
791                 }
792                 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
793         }
794 }
795
796 void
797 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
798 {
799         /* this is called when the radio menu item is toggled, and so 
800            is actually invoked twice per menu selection. we only
801            care about the invocation for the item that was being
802            marked active.
803         */
804
805         if (item->get_active()) {
806                 _route->set_remote_control_id (id);
807         }
808 }
809
810 void
811 RouteUI::build_solo_menu (void)
812 {
813         using namespace Menu_Helpers;
814         
815         solo_menu = new Menu;
816         solo_menu->set_name ("ArdourContextMenu");
817         MenuList& items = solo_menu->items();
818         CheckMenuItem* check;
819
820         check = new CheckMenuItem(_("Solo Isolate"));
821         check->set_active (_route->solo_isolated());
822         check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_isolated), check));
823         _route->solo_isolated_changed.connect(bind (mem_fun (*this, &RouteUI::solo_isolated_toggle), check));
824         items.push_back (CheckMenuElem(*check));
825         check->show_all();
826
827         //items.push_back (SeparatorElem());
828         // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
829         
830 }
831
832 void
833 RouteUI::build_mute_menu(void)
834 {
835         using namespace Menu_Helpers;
836         
837         mute_menu = new Menu;
838         mute_menu->set_name ("ArdourContextMenu");
839
840 #if FIX_ME_IN_3_0       
841         MenuList& items = mute_menu->items();
842         CheckMenuItem* check;
843
844         check = new CheckMenuItem(_("Pre Fader"));
845         init_mute_menu(PRE_FADER, check);
846         check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
847         _route->pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
848         items.push_back (CheckMenuElem(*check));
849         check->show_all();
850
851         check = new CheckMenuItem(_("Post Fader"));
852         init_mute_menu(POST_FADER, check);
853         check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
854         _route->post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
855         items.push_back (CheckMenuElem(*check));
856         check->show_all();
857         
858         check = new CheckMenuItem(_("Control Outs"));
859         init_mute_menu(CONTROL_OUTS, check);
860         check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
861         _route->control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
862         items.push_back (CheckMenuElem(*check));
863         check->show_all();
864
865         check = new CheckMenuItem(_("Main Outs"));
866         init_mute_menu(MAIN_OUTS, check);
867         check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
868         _route->main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
869         items.push_back (CheckMenuElem(*check));
870         check->show_all();
871 #endif
872         //items.push_back (SeparatorElem());
873         // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
874 }
875
876 void
877 RouteUI::init_mute_menu(MuteMaster::MutePoint mp, CheckMenuItem* check)
878 {
879         check->set_active (_route->mute_master()->muted_at (mp));
880 }
881
882 void
883 RouteUI::toggle_mute_menu(MuteMaster::MutePoint mp, Gtk::CheckMenuItem* check)
884 {
885         // _route->set_mute_config(type, check->get_active(), this);
886 }
887
888 void
889 RouteUI::toggle_solo_isolated (Gtk::CheckMenuItem* check)
890 {
891         _route->set_solo_isolated (check->get_active(), this);
892 }
893
894 void
895 RouteUI::set_route_group_solo(boost::shared_ptr<Route> route, bool yn)
896 {
897         RouteGroup* route_group;
898
899         if((route_group = route->route_group()) != 0){
900                 _session.begin_reversible_command (_("mix group solo  change"));
901                 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
902                 route_group->apply(&Route::set_solo, yn, this);
903                 cmd->mark();
904                 _session.add_command (cmd);
905                 _session.commit_reversible_command ();
906         } else {
907                 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route->soloed(), this);
908         }
909 }
910
911 void
912 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
913 {
914         _session.begin_reversible_command (name);
915         XMLNode &before = _route->get_state();
916         bind(mem_fun(*_route, func), yn, arg)();
917         XMLNode &after = _route->get_state();
918         _session.add_command (new MementoCommand<Route>(*_route, &before, &after));
919         _session.commit_reversible_command ();
920 }
921
922 void
923 RouteUI::reversibly_apply_track_boolean (string name, void (Track::*func)(bool, void *), bool yn, void *arg)
924 {
925         _session.begin_reversible_command (name);
926         XMLNode &before = track()->get_state();
927         bind (mem_fun (*track(), func), yn, arg)();
928         XMLNode &after = track()->get_state();
929         _session.add_command (new MementoCommand<Track>(*track(), &before, &after));
930         _session.commit_reversible_command ();
931 }
932
933 void
934 RouteUI::set_route_group_mute(boost::shared_ptr<Route> route, bool yn)
935 {
936         RouteGroup* route_group;
937
938         if((route_group = route->route_group()) != 0){
939                 _session.begin_reversible_command (_("mix group mute change"));
940                 Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand (_session, this);
941                 route_group->apply(&Route::set_mute, yn, this);
942                 cmd->mark();
943                 _session.add_command(cmd);
944                 _session.commit_reversible_command ();
945         } else {
946                 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route->muted(), this);
947         }
948 }
949
950 void
951 RouteUI::set_route_group_rec_enable(boost::shared_ptr<Route> route, bool yn)
952 {
953         RouteGroup* route_group;
954
955         if((route_group = route->route_group()) != 0){
956                 _session.begin_reversible_command (_("mix group rec-enable change"));
957                 Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
958                 route_group->apply (&Route::set_record_enable, yn, this);
959                 cmd->mark();
960                 _session.add_command(cmd);
961                 _session.commit_reversible_command ();
962         } else {
963                 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route->record_enabled(), this);
964         }
965 }
966
967
968 bool
969 RouteUI::choose_color()
970 {
971         bool picked;
972         Gdk::Color color;
973
974         color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
975
976         if (picked) {
977                 set_color (color);
978         }
979
980         return picked;
981 }
982
983 void
984 RouteUI::set_color (const Gdk::Color & c)
985 {
986         char buf[64];
987         
988         _color = c;
989         
990         ensure_xml_node ();
991         snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
992         xml_node->add_property ("color", buf);
993
994         _route->gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
995 }
996
997
998 void
999 RouteUI::ensure_xml_node ()
1000 {
1001         if (xml_node == 0) {
1002                 if ((xml_node = _route->extra_xml ("GUI")) == 0) {
1003                         xml_node = new XMLNode ("GUI");
1004                         _route->add_extra_xml (*xml_node);
1005                 }
1006         }
1007 }
1008
1009 XMLNode*
1010 RouteUI::get_automation_child_xml_node (Evoral::Parameter param)
1011 {
1012         ensure_xml_node ();
1013         
1014         XMLNodeList kids = xml_node->children();
1015         XMLNodeConstIterator iter;
1016
1017         const string sym = ARDOUR::EventTypeMap::instance().to_symbol(param);
1018
1019         for (iter = kids.begin(); iter != kids.end(); ++iter) {
1020                 if ((*iter)->name() == AutomationTimeAxisView::state_node_name) {
1021                         XMLProperty* type = (*iter)->property("automation-id");
1022                         if (type && type->value() == sym)
1023                                 return *iter;
1024                 }
1025         }
1026
1027         // Didn't find it, make a new one
1028         XMLNode* child = new XMLNode (AutomationTimeAxisView::state_node_name);
1029         child->add_property("automation-id", sym);
1030         xml_node->add_child_nocopy (*child);
1031
1032         return child;
1033 }
1034
1035 int
1036 RouteUI::set_color_from_route ()
1037 {
1038         XMLProperty *prop;
1039         
1040         RouteUI::ensure_xml_node ();
1041
1042         if ((prop = xml_node->property ("color")) != 0) {
1043                 int r, g, b;
1044                 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
1045                 _color.set_red(r);
1046                 _color.set_green(g);
1047                 _color.set_blue(b);
1048                 return 0;
1049         } 
1050         return 1;
1051 }
1052
1053 void
1054 RouteUI::remove_this_route ()
1055 {
1056         vector<string> choices;
1057         string prompt;
1058
1059         if (is_track()) {
1060                 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());
1061         } else {
1062                 prompt  = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route->name());
1063         }
1064
1065         choices.push_back (_("No, do nothing."));
1066         choices.push_back (_("Yes, remove it."));
1067
1068         Choice prompter (prompt, choices);
1069
1070         if (prompter.run () == 1) {
1071                 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
1072         }
1073 }
1074
1075 gint
1076 RouteUI::idle_remove_this_route (RouteUI *rui)
1077 {
1078         rui->_session.remove_route (rui->_route);
1079         return false;
1080 }
1081
1082 void
1083 RouteUI::route_rename ()
1084 {
1085         ArdourPrompter name_prompter (true);
1086         string result;
1087         name_prompter.set_prompt (_("New Name: "));
1088         name_prompter.set_initial_text (_route->name());
1089         name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1090         name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1091         name_prompter.show_all ();
1092
1093         switch (name_prompter.run ()) {
1094
1095         case Gtk::RESPONSE_ACCEPT:
1096         name_prompter.get_result (result);
1097         if (result.length()) {
1098                         _route->set_name (result);
1099                 }       
1100                 break;
1101         }
1102
1103         return;
1104   
1105 }
1106
1107 void
1108 RouteUI::name_changed ()
1109 {
1110         ENSURE_GUI_THREAD(sigc::mem_fun(*this, &RouteUI::name_changed));
1111
1112         name_label.set_text (_route->name());
1113 }
1114
1115 void
1116 RouteUI::toggle_route_active ()
1117 {
1118         bool yn;
1119
1120         if (route_active_menu_item) {
1121                 if (route_active_menu_item->get_active() != (yn = _route->active())) {
1122                         _route->set_active (!yn);
1123                 }
1124         }
1125 }
1126
1127 void
1128 RouteUI::route_active_changed ()
1129 {
1130         if (route_active_menu_item) {
1131                 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route->active()));
1132         }
1133 }
1134
1135 void
1136 RouteUI::toggle_polarity ()
1137 {
1138         if (polarity_menu_item) {
1139
1140                 bool x;
1141
1142                 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
1143                 
1144                 if ((x = polarity_menu_item->get_active()) != _route->phase_invert()) {
1145                         _route->set_phase_invert (x);
1146                         if (x) {
1147                                 name_label.set_text (X_("Ø ") + name_label.get_text());
1148                         } else {
1149                                 name_label.set_text (_route->name());
1150                         }
1151                 }
1152         }
1153 }
1154
1155 void
1156 RouteUI::polarity_changed ()
1157 {
1158         if (_route->phase_invert()) {
1159                 name_label.set_text (X_("Ø ") + name_label.get_text());
1160         } else {
1161                 name_label.set_text (_route->name());
1162         }
1163 }
1164
1165 void
1166 RouteUI::toggle_denormal_protection ()
1167 {
1168         if (denormal_menu_item) {
1169
1170                 bool x;
1171
1172                 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_denormal_protection));
1173                 
1174                 if ((x = denormal_menu_item->get_active()) != _route->denormal_protection()) {
1175                         _route->set_denormal_protection (x);
1176                 }
1177         }
1178 }
1179
1180 void
1181 RouteUI::denormal_protection_changed ()
1182 {
1183         if (denormal_menu_item) {
1184                 denormal_menu_item->set_active (_route->denormal_protection());
1185         }
1186 }
1187
1188 void
1189 RouteUI::solo_isolated_toggle(void* src, Gtk::CheckMenuItem* check)
1190 {
1191         bool yn = _route->solo_isolated ();
1192
1193         if (check->get_active() != yn) {
1194                 check->set_active (yn);
1195         }
1196 }
1197
1198 #ifdef FIX_THIS_FOR_3_0
1199 void
1200 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
1201 {
1202         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
1203         
1204         bool yn = _route->get_mute_config(PRE_FADER);
1205         if (check->get_active() != yn) {
1206                 check->set_active (yn);
1207         }
1208 }
1209
1210 void
1211 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
1212 {
1213         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
1214         
1215         bool yn = _route->get_mute_config(POST_FADER);
1216         if (check->get_active() != yn) {
1217                 check->set_active (yn);
1218         }
1219 }
1220
1221 void
1222 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
1223 {
1224         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
1225         
1226         bool yn = _route->get_mute_config(CONTROL_OUTS);
1227         if (check->get_active() != yn) {
1228                 check->set_active (yn);
1229         }
1230 }
1231
1232 void
1233 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
1234 {
1235         ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
1236         
1237         bool yn = _route->get_mute_config(MAIN_OUTS);
1238         if (check->get_active() != yn) {
1239                 check->set_active (yn);
1240         }
1241 }
1242 #endif
1243
1244 void
1245 RouteUI::disconnect_input ()
1246 {
1247         _route->input()->disconnect (this);
1248 }
1249
1250 void
1251 RouteUI::disconnect_output ()
1252 {
1253         _route->output()->disconnect (this);
1254 }
1255
1256 bool
1257 RouteUI::is_track () const
1258 {
1259         return boost::dynamic_pointer_cast<Track>(_route) != 0;
1260 }
1261
1262 boost::shared_ptr<Track>
1263 RouteUI::track() const
1264 {
1265         return boost::dynamic_pointer_cast<Track>(_route);
1266 }
1267
1268 bool
1269 RouteUI::is_audio_track () const
1270 {
1271         return boost::dynamic_pointer_cast<AudioTrack>(_route) != 0;
1272 }
1273
1274 boost::shared_ptr<AudioTrack>
1275 RouteUI::audio_track() const
1276 {
1277         return boost::dynamic_pointer_cast<AudioTrack>(_route);
1278 }
1279
1280 bool
1281 RouteUI::is_midi_track () const
1282 {
1283         return boost::dynamic_pointer_cast<MidiTrack>(_route) != 0;
1284 }
1285
1286 boost::shared_ptr<MidiTrack>
1287 RouteUI::midi_track() const
1288 {
1289         return boost::dynamic_pointer_cast<MidiTrack>(_route);
1290 }
1291
1292 boost::shared_ptr<Diskstream>
1293 RouteUI::get_diskstream () const
1294 {
1295         boost::shared_ptr<Track> t;
1296
1297         if ((t = boost::dynamic_pointer_cast<Track>(_route)) != 0) {
1298                 return t->diskstream();
1299         } else {
1300                 return boost::shared_ptr<Diskstream> ((Diskstream*) 0);
1301         }
1302 }
1303
1304 string
1305 RouteUI::name() const
1306 {
1307         return _route->name();
1308 }
1309
1310 void
1311 RouteUI::map_frozen ()
1312 {
1313         ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
1314
1315         AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
1316
1317         if (at) {
1318                 switch (at->freeze_state()) {
1319                 case AudioTrack::Frozen:
1320                         rec_enable_button->set_sensitive (false);
1321                         break;
1322                 default:
1323                         rec_enable_button->set_sensitive (true);
1324                         break;
1325                 }
1326         }
1327 }
1328
1329 void
1330 RouteUI::adjust_latency ()
1331 {
1332         LatencyDialog dialog (_route->name() + _(" latency"), *(_route->output()), _session.frame_rate(), _session.engine().frames_per_cycle());
1333 }
1334
1335 void
1336 RouteUI::save_as_template ()
1337 {
1338         sys::path path;
1339         Glib::ustring safe_name;
1340         string name;
1341         
1342         path = ARDOUR::user_route_template_directory ();
1343         
1344         if (g_mkdir_with_parents (path.to_string().c_str(), 0755)) {
1345                 error << string_compose (_("Cannot create route template directory %1"), path.to_string()) << endmsg;
1346                 return;
1347         }
1348         
1349         Prompter p (true); // modal
1350         
1351         p.set_prompt (_("Template name:"));
1352         switch (p.run()) {
1353         case RESPONSE_ACCEPT:
1354                 break;
1355         default:
1356                 return;
1357         }
1358         
1359         p.hide ();
1360         p.get_result (name, true);
1361         
1362         safe_name = legalize_for_path (name);
1363         safe_name += template_suffix;
1364         
1365         path /= safe_name;
1366         
1367         _route->save_as_template (path.to_string(), name);
1368 }
1369
1370 void
1371 RouteUI::check_rec_enable_sensitivity ()
1372 {
1373         if (_session.transport_rolling() && rec_enable_button->get_active() && Config->get_disable_disarm_during_roll()) {
1374                 rec_enable_button->set_sensitive (false);
1375         } else {
1376                 rec_enable_button->set_sensitive (true);
1377         }
1378 }
1379
1380 void
1381 RouteUI::parameter_changed (string const & p)
1382 {
1383         ENSURE_GUI_THREAD (bind (mem_fun (*this, &RouteUI::parameter_changed), p));
1384         
1385         if (p == "disable-disarm-during-roll") {
1386                 check_rec_enable_sensitivity ();
1387         }
1388 }