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