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