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