add (back) PlaySelection to menus
[ardour2.git] / gtk2_ardour / route_ui.cc
blob73a9d59540ce70f0589476b6cc6468ce1adfb5c4
1 /*
2 Copyright (C) 2002-2006 Paul Davis
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.
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.
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.
20 #include <gtkmm2ext/gtk_ui.h>
21 #include <gtkmm2ext/stop_signal.h>
22 #include <gtkmm2ext/choice.h>
23 #include <gtkmm2ext/doi.h>
24 #include <gtkmm2ext/bindable_button.h>
25 #include <gtkmm2ext/gtk_ui.h>
26 #include <gtkmm2ext/prompter.h>
28 #include <ardour/route_group.h>
29 #include <pbd/memento_command.h>
30 #include <pbd/stacktrace.h>
31 #include <pbd/shiva.h>
33 #include "route_ui.h"
34 #include "keyboard.h"
35 #include "utils.h"
36 #include "prompter.h"
37 #include "gui_thread.h"
39 #include <ardour/route.h>
40 #include <ardour/session.h>
41 #include <ardour/audioengine.h>
42 #include <ardour/audio_track.h>
43 #include <ardour/audio_diskstream.h>
44 #include <ardour/profile.h>
45 #include <ardour/utils.h>
47 #include "i18n.h"
48 using namespace sigc;
49 using namespace Gtk;
50 using namespace Gtkmm2ext;
51 using namespace ARDOUR;
52 using namespace PBD;
54 RouteUI::RouteUI (ARDOUR::Session& sess, const char* mute_name, const char* solo_name, const char* rec_name)
55 : AxisView(sess)
57 init ();
58 set_button_names (mute_name, solo_name, rec_name);
61 RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt,
62 ARDOUR::Session& sess, const char* mute_name, const char* solo_name, const char* rec_name)
63 : AxisView(sess)
65 init ();
66 set_button_names (mute_name, solo_name, rec_name);
67 set_route (rt);
70 void
71 RouteUI::init ()
73 self_destruct = true;
74 xml_node = 0;
75 mute_menu = 0;
76 solo_menu = 0;
77 remote_control_menu = 0;
78 ignore_toggle = false;
79 wait_for_release = false;
80 route_active_menu_item = 0;
81 was_solo_safe = false;
82 polarity_menu_item = 0;
83 denormal_menu_item = 0;
84 multiple_mute_change = false;
85 multiple_solo_change = false;
87 mute_button = manage (new BindableToggleButton (0, ""));
88 mute_button->set_self_managed (true);
89 mute_button->set_name ("MuteButton");
90 UI::instance()->set_tip (mute_button, _("Mute this track"), "");
92 solo_button = manage (new BindableToggleButton (0, ""));
93 solo_button->set_self_managed (true);
94 solo_button->set_name ("SoloButton");
95 UI::instance()->set_tip (solo_button, _("Mute other (non-soloed) tracks"), "");
97 rec_enable_button = manage (new BindableToggleButton (0, ""));
98 rec_enable_button->set_name ("RecordEnableButton");
99 rec_enable_button->set_self_managed (true);
100 UI::instance()->set_tip (rec_enable_button, _("Enable recording on this track"), "");
102 _session.SoloChanged.connect (mem_fun(*this, &RouteUI::solo_changed_so_update_mute));
105 void
106 RouteUI::reset ()
108 connections.clear ();
110 if (solo_menu) {
111 delete solo_menu;
112 solo_menu = 0;
115 if (mute_menu) {
116 delete mute_menu;
117 mute_menu = 0;
120 if (xml_node) {
121 /* do not delete the node - its owned by the route */
122 xml_node = 0;
125 route_active_menu_item = 0;
126 polarity_menu_item = 0;
127 denormal_menu_item = 0;
130 void
131 RouteUI::set_button_names (const char* mute, const char* solo, const char* rec)
133 m_name = mute;
134 s_name = solo;
135 r_name = rec;
138 void
139 RouteUI::set_route (boost::shared_ptr<Route> rp)
141 reset ();
143 _route = rp;
145 if (set_color_from_route()) {
146 set_color (unique_random_color());
149 /* no, there is no memory leak here. This object cleans itself (and other stuff)
150 up when the route is destroyed.
153 if (self_destruct) {
154 new PairedShiva<Route,RouteUI> (*_route, *this);
157 mute_button->set_controllable (&_route->mute_control());
158 mute_button->set_label (m_name);
160 solo_button->set_controllable (&_route->solo_control());
161 solo_button->set_label (s_name);
163 connections.push_back (_route->active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed)));
164 connections.push_back (_route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed)));
165 connections.push_back (_route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
166 connections.push_back (_route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
168 /* when solo changes, update mute state too, in case the user wants us to display it */
170 update_solo_display ();
171 update_mute_display ();
173 if (_session.writable() && is_track()) {
174 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
176 connections.push_back (t->diskstream()->RecordEnableChanged.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed)));
178 connections.push_back (_session.RecordStateChanged.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed)));
180 rec_enable_button->set_controllable (&t->rec_enable_control());
181 rec_enable_button->set_label (r_name);
183 update_rec_display ();
186 connections.push_back (_route->RemoteControlIDChanged.connect (mem_fun(*this, &RouteUI::refresh_remote_control_menu)));
188 /* map the current state */
190 map_frozen ();
193 RouteUI::~RouteUI()
195 /* derived classes should emit GoingAway so that they receive the signal
196 when the object is still a legal derived instance.
199 if (solo_menu) {
200 delete solo_menu;
203 if (mute_menu) {
204 delete mute_menu;
207 /* Note: the remote control menu is constructed
208 by derived classes (e.g. MixerStrip or RouteTimeAxis) and
209 is always attached to a context menu. It then becomes
210 owned by that menu, and will deleted along with it. We
211 do not need to take care of it here.
215 bool
216 RouteUI::mute_press(GdkEventButton* ev)
218 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
219 return true;
221 multiple_mute_change = false;
222 if (!ignore_toggle) {
224 if (Keyboard::is_context_menu_event (ev)) {
226 if (mute_menu == 0){
227 build_mute_menu();
230 mute_menu->popup(0,ev->time);
232 } else {
234 if (Keyboard::is_button2_event (ev)) {
236 if (mute_button->on_button_press_event (ev)) {
237 return true;
238 } else {
239 // button2-click is "momentary"
240 wait_for_release = true;
244 if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
246 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
248 /* Primary-Tertiary-click applies change to all routes */
250 _session.begin_reversible_command (_("mute change"));
251 Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(_session, this);
252 _session.set_all_mute (!_route->muted());
253 cmd->mark();
254 _session.add_command(cmd);
255 _session.commit_reversible_command ();
256 multiple_mute_change = true;
258 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
260 /* Primary-button1 applies change to the mix group.
261 NOTE: Primary-button2 is MIDI learn.
264 if (ev->button == 1) {
265 set_mix_group_mute (_route, !_route->muted());
268 } else {
270 /* plain click applies change to this route */
271 if (wait_for_release) {
272 _route->set_mute (!_route->muted(), this);
273 } else {
274 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this);
282 return true;
285 bool
286 RouteUI::mute_release(GdkEventButton* ev)
288 if (!ignore_toggle) {
289 if (wait_for_release){
290 wait_for_release = false;
291 if (multiple_mute_change) {
292 multiple_mute_change = false;
293 // undo the last op
294 // because the press was the last undoable thing we did
295 _session.undo (1U);
296 } else {
297 _route->set_mute (!_route->muted(), this);
301 return true;
304 bool
305 RouteUI::solo_press(GdkEventButton* ev)
307 /* ignore double/triple clicks */
309 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
310 return true;
312 multiple_solo_change = false;
313 if (!ignore_toggle) {
315 if (Keyboard::is_context_menu_event (ev)) {
317 if (solo_menu == 0) {
318 build_solo_menu ();
321 solo_menu->popup (1, ev->time);
323 } else {
325 if (Keyboard::is_button2_event (ev)) {
327 if (solo_button->on_button_press_event (ev)) {
328 return true;
329 } else {
330 // button2-click is "momentary"
331 wait_for_release = true;
335 if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
337 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
339 /* Primary-Tertiary-click applies change to all routes */
340 bool was_not_latched = false;
341 if (!Config->get_solo_latched ()) {
342 was_not_latched = true;
344 XXX it makes no sense to solo all tracks if we're
345 not in latched mode, but doing nothing feels like a bug,
346 so do it anyway
348 Config->set_solo_latched (true);
350 _session.begin_reversible_command (_("solo change"));
351 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
352 _session.set_all_solo (!_route->soloed());
353 cmd->mark();
354 _session.add_command (cmd);
355 _session.commit_reversible_command ();
356 multiple_solo_change = true;
357 if (was_not_latched) {
358 Config->set_solo_latched (false);
361 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
363 // Primary-Secondary-click: exclusively solo this track, not a toggle */
365 _session.begin_reversible_command (_("solo change"));
366 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (_session, this);
367 _session.set_all_solo (false);
368 _route->set_solo (true, this);
369 cmd->mark();
370 _session.add_command(cmd);
371 _session.commit_reversible_command ();
373 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
375 // shift-click: set this route to solo safe
377 if (Profile->get_sae() && ev->button == 1) {
378 // button 1 and shift-click: disables solo_latched for this click
379 if (!Config->get_solo_latched ()) {
380 Config->set_solo_latched (true);
381 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
382 Config->set_solo_latched (false);
384 } else {
385 _route->set_solo_safe (!_route->solo_safe(), this);
386 wait_for_release = false;
389 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
391 /* Primary-button1: solo mix group.
392 NOTE: Primary-button2 is MIDI learn.
395 if (ev->button == 1) {
396 set_mix_group_solo (_route, !_route->soloed());
399 } else {
401 /* click: solo this route */
402 if (wait_for_release) {
403 _route->set_solo (!_route->soloed(), this);
404 } else {
405 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
412 return true;
415 bool
416 RouteUI::solo_release(GdkEventButton* ev)
418 if (!ignore_toggle) {
419 if (wait_for_release) {
420 wait_for_release = false;
421 if (multiple_solo_change) {
422 multiple_solo_change = false;
423 // undo the last op
424 // because the press was the last undoable thing we did
425 _session.undo (1U);
426 } else {
427 // we don't use "undo the last op"
428 // here because its expensive for the GUI
429 _route->set_solo (!_route->soloed(), this);
434 return true;
437 bool
438 RouteUI::rec_enable_press(GdkEventButton* ev)
440 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
441 return true;
444 if (!_session.engine().connected()) {
445 MessageDialog msg (_("Not connected to JACK - cannot engage record"));
446 msg.run ();
447 return true;
450 if (!ignore_toggle && is_track() && rec_enable_button) {
452 if (Keyboard::is_button2_event (ev)) {
454 return rec_enable_button->on_button_press_event (ev);
456 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
458 _session.begin_reversible_command (_("rec-enable change"));
459 Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
461 if (rec_enable_button->get_active()) {
462 _session.record_disenable_all ();
463 } else {
464 _session.record_enable_all ();
467 cmd->mark();
468 _session.add_command(cmd);
469 _session.commit_reversible_command ();
471 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
473 /* Primary-button1 applies change to the mix group.
474 NOTE: Primary-button2 is MIDI learn.
477 set_mix_group_rec_enable (_route, !_route->record_enabled());
479 } else {
481 reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
485 return true;
488 bool
489 RouteUI::rec_enable_release (GdkEventButton* ev)
491 return true;
494 void
495 RouteUI::solo_changed(void* src)
498 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
501 void
502 RouteUI::update_solo_display ()
504 bool x;
505 vector<Gdk::Color> fg_colors;
506 Gdk::Color c;
508 if (solo_button->get_active() != (x = _route->soloed())){
509 ignore_toggle = true;
510 solo_button->set_active(x);
511 ignore_toggle = false;
514 if (_route->solo_safe()) {
515 solo_button->set_visual_state (2);
516 } else if (_route->soloed()) {
517 solo_button->set_visual_state (1);
518 } else {
519 solo_button->set_visual_state (0);
523 void
524 RouteUI::solo_changed_so_update_mute ()
526 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
529 void
530 RouteUI::mute_changed(void* src)
532 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
535 void
536 RouteUI::update_mute_display ()
538 bool model = _route->muted();
539 bool view = mute_button->get_active();
541 /* first make sure the button's "depressed" visual
542 is correct.
545 if (model != view) {
546 ignore_toggle = true;
547 mute_button->set_active (model);
548 ignore_toggle = false;
551 /* now attend to visual state */
553 if (Config->get_show_solo_mutes()) {
554 if (_route->muted()) {
555 mute_button->set_visual_state (2);
556 } else if (!_route->soloed() && _route->solo_muted()) {
558 mute_button->set_visual_state (1);
559 } else {
560 mute_button->set_visual_state (0);
562 } else {
563 if (_route->muted()) {
564 mute_button->set_visual_state (2);
565 } else {
566 mute_button->set_visual_state (0);
572 void
573 RouteUI::route_rec_enable_changed ()
575 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
578 void
579 RouteUI::session_rec_enable_changed ()
581 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
584 void
585 RouteUI::update_rec_display ()
587 bool model = _route->record_enabled();
588 bool view = rec_enable_button->get_active();
590 /* first make sure the button's "depressed" visual
591 is correct.
594 if (model != view) {
595 ignore_toggle = true;
596 rec_enable_button->set_active (model);
597 ignore_toggle = false;
600 /* now make sure its color state is correct */
602 if (model) {
604 switch (_session.record_status ()) {
605 case Session::Recording:
606 rec_enable_button->set_visual_state (1);
607 break;
609 case Session::Disabled:
610 case Session::Enabled:
611 rec_enable_button->set_visual_state (2);
612 break;
616 } else {
617 rec_enable_button->set_visual_state (0);
621 void
622 RouteUI::build_remote_control_menu ()
624 remote_control_menu = new Menu;
625 refresh_remote_control_menu ();
628 void
629 RouteUI::refresh_remote_control_menu ()
631 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::refresh_remote_control_menu));
633 // only refresh the menu if it has been instantiated
635 if (remote_control_menu == 0) {
636 return;
639 using namespace Menu_Helpers;
641 RadioMenuItem::Group rc_group;
642 CheckMenuItem* rc_active;
643 uint32_t limit = _session.ntracks() + _session.nbusses();
644 char buf[32];
646 MenuList& rc_items = remote_control_menu->items();
647 rc_items.clear ();
649 /* note that this menu list starts at zero, not 1, because zero
650 is a valid, if useless, ID.
653 limit += 4; /* leave some breathing room */
655 rc_items.push_back (RadioMenuElem (rc_group, _("None")));
656 if (_route->remote_control_id() == 0) {
657 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
658 rc_active->set_active ();
661 for (uint32_t i = 1; i < limit; ++i) {
662 snprintf (buf, sizeof (buf), "%u", i);
663 rc_items.push_back (RadioMenuElem (rc_group, buf));
664 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
665 if (_route->remote_control_id() == i) {
666 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
667 rc_active->set_active ();
669 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
673 void
674 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
676 /* this is called when the radio menu item is toggled, and so
677 is actually invoked twice per menu selection. we only
678 care about the invocation for the item that was being
679 marked active.
682 if (item->get_active()) {
683 _route->set_remote_control_id (id);
687 void
688 RouteUI::build_solo_menu (void)
690 using namespace Menu_Helpers;
692 solo_menu = new Menu;
693 solo_menu->set_name ("ArdourContextMenu");
694 MenuList& items = solo_menu->items();
695 CheckMenuItem* check;
697 check = new CheckMenuItem(_("Solo Lock"));
698 check->set_active (_route->solo_safe());
699 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
700 _route->solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
701 items.push_back (CheckMenuElem(*check));
702 check->show_all();
704 //items.push_back (SeparatorElem());
705 // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
709 void
710 RouteUI::build_mute_menu(void)
712 using namespace Menu_Helpers;
714 mute_menu = new Menu;
715 mute_menu->set_name ("ArdourContextMenu");
716 MenuList& items = mute_menu->items();
717 CheckMenuItem* check;
719 check = new CheckMenuItem(_("Pre Fader"));
720 init_mute_menu(PRE_FADER, check);
721 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
722 _route->pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
723 items.push_back (CheckMenuElem(*check));
724 check->show_all();
726 check = new CheckMenuItem(_("Post Fader"));
727 init_mute_menu(POST_FADER, check);
728 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
729 _route->post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
730 items.push_back (CheckMenuElem(*check));
731 check->show_all();
733 check = new CheckMenuItem(_("Control Outs"));
734 init_mute_menu(CONTROL_OUTS, check);
735 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
736 _route->control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
737 items.push_back (CheckMenuElem(*check));
738 check->show_all();
740 check = new CheckMenuItem(_("Main Outs"));
741 init_mute_menu(MAIN_OUTS, check);
742 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
743 _route->main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
744 items.push_back (CheckMenuElem(*check));
745 check->show_all();
747 //items.push_back (SeparatorElem());
748 // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
751 void
752 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
754 if (_route->get_mute_config (type)) {
755 check->set_active (true);
759 void
760 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
762 _route->set_mute_config(type, check->get_active(), this);
765 void
766 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
768 _route->set_solo_safe (check->get_active(), this);
771 void
772 RouteUI::set_mix_group_solo(boost::shared_ptr<Route> route, bool yn)
774 RouteGroup* mix_group;
776 if((mix_group = route->mix_group()) != 0){
777 _session.begin_reversible_command (_("mix group solo change"));
778 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
779 mix_group->apply(&Route::set_solo, yn, this);
780 cmd->mark();
781 _session.add_command (cmd);
782 _session.commit_reversible_command ();
783 } else {
784 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route->soloed(), this);
788 void
789 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
791 _session.begin_reversible_command (name);
792 XMLNode &before = _route->get_state();
793 bind(mem_fun(*_route, func), yn, arg)();
794 XMLNode &after = _route->get_state();
795 _session.add_command (new MementoCommand<Route>(*_route, &before, &after));
796 _session.commit_reversible_command ();
799 void
800 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
802 _session.begin_reversible_command (name);
803 XMLNode &before = audio_track()->get_state();
804 bind (mem_fun (*audio_track(), func), yn, arg)();
805 XMLNode &after = audio_track()->get_state();
806 _session.add_command (new MementoCommand<AudioTrack>(*audio_track(), &before, &after));
807 _session.commit_reversible_command ();
810 void
811 RouteUI::set_mix_group_mute(boost::shared_ptr<Route> route, bool yn)
813 RouteGroup* mix_group;
815 if((mix_group = route->mix_group()) != 0){
816 _session.begin_reversible_command (_("mix group mute change"));
817 Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand (_session, this);
818 mix_group->apply(&Route::set_mute, yn, this);
819 cmd->mark();
820 _session.add_command(cmd);
821 _session.commit_reversible_command ();
822 } else {
823 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route->muted(), this);
827 void
828 RouteUI::set_mix_group_rec_enable(boost::shared_ptr<Route> route, bool yn)
830 RouteGroup* mix_group;
832 if((mix_group = route->mix_group()) != 0){
833 _session.begin_reversible_command (_("mix group rec-enable change"));
834 Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
835 mix_group->apply (&Route::set_record_enable, yn, this);
836 cmd->mark();
837 _session.add_command(cmd);
838 _session.commit_reversible_command ();
839 } else {
840 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route->record_enabled(), this);
845 bool
846 RouteUI::choose_color()
848 bool picked;
849 Gdk::Color color;
851 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
853 if (picked) {
854 set_color (color);
857 return picked;
860 void
861 RouteUI::set_color (const Gdk::Color & c)
863 char buf[64];
865 _color = c;
867 ensure_xml_node ();
868 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
869 xml_node->add_property ("color", buf);
871 _route->gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
875 void
876 RouteUI::ensure_xml_node ()
878 if (xml_node == 0) {
879 if ((xml_node = _route->extra_xml ("GUI")) == 0) {
880 xml_node = new XMLNode ("GUI");
881 _route->add_extra_xml (*xml_node);
886 XMLNode*
887 RouteUI::get_child_xml_node (const string & childname)
889 XMLNode* child;
891 ensure_xml_node ();
894 if ((child = find_named_node (*xml_node, childname)) == 0) {
895 child = new XMLNode (childname);
896 xml_node->add_child_nocopy (*child);
899 return child;
903 RouteUI::set_color_from_route ()
905 XMLProperty *prop;
907 RouteUI::ensure_xml_node ();
909 if ((prop = xml_node->property ("color")) != 0) {
910 int r, g, b;
911 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
912 _color.set_red(r);
913 _color.set_green(g);
914 _color.set_blue(b);
915 return 0;
917 return 1;
920 void
921 RouteUI::remove_this_route ()
923 vector<string> choices;
924 string prompt;
926 if (is_track()) {
927 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());
928 } else {
929 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route->name());
932 choices.push_back (_("No, do nothing."));
933 choices.push_back (_("Yes, remove it."));
935 Choice prompter (prompt, choices);
937 if (prompter.run () == 1) {
938 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
942 gint
943 RouteUI::idle_remove_this_route (RouteUI *rui)
945 rui->_session.remove_route (rui->_route);
946 return false;
949 void
950 RouteUI::route_rename ()
952 ArdourPrompter name_prompter (true);
953 string result;
954 name_prompter.set_prompt (_("New Name: "));
955 name_prompter.set_initial_text (_route->name());
956 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
957 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
958 name_prompter.show_all ();
960 switch (name_prompter.run ()) {
962 case Gtk::RESPONSE_ACCEPT:
963 name_prompter.get_result (result);
964 if (result.length()) {
965 _route->set_name (result, this);
967 break;
970 return;
974 void
975 RouteUI::name_changed (void *src)
977 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
979 name_label.set_text (_route->name());
982 void
983 RouteUI::toggle_route_active ()
985 bool yn;
987 if (route_active_menu_item) {
988 if (route_active_menu_item->get_active() != (yn = _route->active())) {
989 _route->set_active (!yn);
994 void
995 RouteUI::route_active_changed ()
997 if (route_active_menu_item) {
998 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route->active()));
1002 void
1003 RouteUI::toggle_polarity ()
1005 if (polarity_menu_item) {
1007 bool x;
1009 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
1011 if ((x = polarity_menu_item->get_active()) != _route->phase_invert()) {
1012 _route->set_phase_invert (x, this);
1013 if (x) {
1014 name_label.set_text (X_("Ø ") + name_label.get_text());
1015 } else {
1016 name_label.set_text (_route->name());
1022 void
1023 RouteUI::polarity_changed ()
1025 /* no signal for this yet */
1028 void
1029 RouteUI::toggle_denormal_protection ()
1031 if (denormal_menu_item) {
1033 bool x;
1035 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_denormal_protection));
1037 if ((x = denormal_menu_item->get_active()) != _route->denormal_protection()) {
1038 _route->set_denormal_protection (x, this);
1043 void
1044 RouteUI::denormal_protection_changed ()
1046 /* no signal for this yet */
1050 void
1051 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
1053 bool yn = _route->solo_safe ();
1055 if (check->get_active() != yn) {
1056 check->set_active (yn);
1059 void
1060 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
1062 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
1064 bool yn = _route->get_mute_config(PRE_FADER);
1065 if (check->get_active() != yn) {
1066 check->set_active (yn);
1070 void
1071 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
1073 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
1075 bool yn = _route->get_mute_config(POST_FADER);
1076 if (check->get_active() != yn) {
1077 check->set_active (yn);
1081 void
1082 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
1084 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
1086 bool yn = _route->get_mute_config(CONTROL_OUTS);
1087 if (check->get_active() != yn) {
1088 check->set_active (yn);
1092 void
1093 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
1095 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
1097 bool yn = _route->get_mute_config(MAIN_OUTS);
1098 if (check->get_active() != yn) {
1099 check->set_active (yn);
1103 void
1104 RouteUI::disconnect_input ()
1106 _route->disconnect_inputs (this);
1109 void
1110 RouteUI::disconnect_output ()
1112 _route->disconnect_outputs (this);
1115 bool
1116 RouteUI::is_track () const
1118 return boost::dynamic_pointer_cast<Track>(_route) != 0;
1121 boost::shared_ptr<Track>
1122 RouteUI::track() const
1124 return boost::dynamic_pointer_cast<Track>(_route);
1127 bool
1128 RouteUI::is_audio_track () const
1130 return boost::dynamic_pointer_cast<AudioTrack>(_route) != 0;
1133 boost::shared_ptr<AudioTrack>
1134 RouteUI::audio_track() const
1136 return boost::dynamic_pointer_cast<AudioTrack>(_route);
1139 boost::shared_ptr<Diskstream>
1140 RouteUI::get_diskstream () const
1142 boost::shared_ptr<Track> t;
1144 if ((t = boost::dynamic_pointer_cast<Track>(_route)) != 0) {
1145 return t->diskstream();
1146 } else {
1147 return boost::shared_ptr<Diskstream> ((Diskstream*) 0);
1151 string
1152 RouteUI::name() const
1154 return _route->name();
1157 void
1158 RouteUI::map_frozen ()
1160 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
1162 AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
1164 if (at) {
1165 switch (at->freeze_state()) {
1166 case AudioTrack::Frozen:
1167 rec_enable_button->set_sensitive (false);
1168 break;
1169 default:
1170 rec_enable_button->set_sensitive (true);
1171 break;
1176 void
1177 RouteUI::save_as_template ()
1179 Glib::ustring path;
1180 Glib::ustring safe_name;
1181 std::string name;
1183 path = Session::route_template_dir();
1185 if (g_mkdir_with_parents (path.c_str(), 0755)) {
1186 error << string_compose (_("Cannot create route template directory %1"), path) << endmsg;
1187 return;
1190 Prompter p (true); // modal
1192 p.set_prompt (_("Template name:"));
1193 switch (p.run()) {
1194 case RESPONSE_ACCEPT:
1195 break;
1196 default:
1197 return;
1200 p.hide ();
1201 p.get_result (name, true);
1203 safe_name = legalize_for_path (name);
1204 safe_name += Session::template_suffix ();
1206 path = Glib::build_filename (path, safe_name);
1208 _route->save_as_template (path, name);