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