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