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