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/barcontroller.h>
26 #include <gtkmm2ext/gtk_ui.h>
28 #include "ardour/route_group.h"
29 #include "pbd/memento_command.h"
30 #include "pbd/stacktrace.h"
31 #include "pbd/shiva.h"
32 #include "pbd/controllable.h"
34 #include "ardour_ui.h"
39 #include "gui_thread.h"
40 #include "ardour_dialog.h"
41 #include "latency_gui.h"
42 #include "mixer_strip.h"
43 #include "automation_time_axis.h"
45 #include "ardour/route.h"
46 #include "ardour/session.h"
47 #include "ardour/audioengine.h"
48 #include "ardour/audio_track.h"
49 #include "ardour/audio_diskstream.h"
50 #include "ardour/midi_track.h"
51 #include "ardour/midi_diskstream.h"
52 #include "ardour/template_utils.h"
53 #include "ardour/filename_extensions.h"
54 #include "ardour/directory_names.h"
55 #include "ardour/profile.h"
60 using namespace Gtkmm2ext;
61 using namespace ARDOUR;
64 RouteUI::RouteUI (ARDOUR::Session& sess, const char* mute_name, const char* solo_name, const char* rec_name)
68 set_button_names (mute_name, solo_name, rec_name);
71 RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt,
72 ARDOUR::Session& sess, const char* mute_name, const char* solo_name, const char* rec_name)
76 set_button_names (mute_name, solo_name, rec_name);
82 /* derived classes should emit GoingAway so that they receive the signal
83 when the object is still a legal derived instance.
88 delete remote_control_menu;
99 remote_control_menu = 0;
101 ignore_toggle = false;
102 wait_for_release = false;
103 route_active_menu_item = 0;
104 was_solo_safe = false;
105 polarity_menu_item = 0;
106 denormal_menu_item = 0;
107 multiple_mute_change = false;
108 multiple_solo_change = false;
110 mute_button = manage (new BindableToggleButton (""));
111 mute_button->set_self_managed (true);
112 mute_button->set_name ("MuteButton");
113 UI::instance()->set_tip (mute_button, _("Mute this track"), "");
115 solo_button = manage (new BindableToggleButton (""));
116 solo_button->set_self_managed (true);
117 solo_button->set_name ("SoloButton");
118 UI::instance()->set_tip (solo_button, _("Mute other (non-soloed) tracks"), "");
120 rec_enable_button = manage (new BindableToggleButton (""));
121 rec_enable_button->set_name ("RecordEnableButton");
122 rec_enable_button->set_self_managed (true);
123 UI::instance()->set_tip (rec_enable_button, _("Enable recording on this track"), "");
125 show_sends_button = manage (new BindableToggleButton (""));
126 show_sends_button->set_name ("SendAlert");
127 show_sends_button->set_self_managed (true);
128 UI::instance()->set_tip (show_sends_button, _("make mixer strips show sends to this bus"), "");
130 _session.SoloChanged.connect (mem_fun(*this, &RouteUI::solo_changed_so_update_mute));
136 //Remove route connections associated with us.
137 for (vector<sigc::connection>::iterator it = connections.begin(); it!=connections.end(); ++it) {
141 connections.clear ();
150 /* do not delete the node - its owned by the route */
154 route_active_menu_item = 0;
155 polarity_menu_item = 0;
156 denormal_menu_item = 0;
160 RouteUI::set_button_names (const char* mute, const char* solo, const char* rec)
168 RouteUI::set_route (boost::shared_ptr<Route> rp)
174 if (set_color_from_route()) {
175 set_color (unique_random_color());
178 /* no, there is no memory leak here. This object cleans itself (and other stuff)
179 up when the route is destroyed.
183 new PairedShiva<Route,RouteUI> (*_route, *this);
186 mute_button->set_controllable (_route->mute_control());
187 mute_button->set_label (m_name);
189 solo_button->set_controllable (_route->solo_control());
190 solo_button->set_label (s_name);
192 connections.push_back (_route->active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed)));
193 connections.push_back (_route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed)));
194 connections.push_back (_route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
195 connections.push_back (_route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
198 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
200 connections.push_back (t->diskstream()->RecordEnableChanged.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed)));
201 connections.push_back (_session.RecordStateChanged.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed)));
203 rec_enable_button->show();
204 rec_enable_button->set_controllable (t->rec_enable_control());
205 rec_enable_button->set_label (r_name);
207 update_rec_display ();
210 mute_button->unset_flags (Gtk::CAN_FOCUS);
211 solo_button->unset_flags (Gtk::CAN_FOCUS);
216 connections.push_back (_route->RemoteControlIDChanged.connect (mem_fun(*this, &RouteUI::refresh_remote_control_menu)));
218 /* map the current state */
227 RouteUI::mute_press(GdkEventButton* ev)
229 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
232 multiple_mute_change = false;
233 if (!ignore_toggle) {
235 if (Keyboard::is_context_menu_event (ev)) {
241 mute_menu->popup(0,ev->time);
245 if (Keyboard::is_button2_event (ev)) {
246 // Primary-button2 click is the midi binding click
247 // button2-click is "momentary"
249 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier))) {
250 wait_for_release = true;
256 if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
258 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
260 /* Primary-Tertiary-click applies change to all routes */
262 _session.begin_reversible_command (_("mute change"));
263 Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(_session, this);
264 _session.set_all_mute (!_route->muted());
266 _session.add_command(cmd);
267 _session.commit_reversible_command ();
268 multiple_mute_change = true;
270 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
272 /* Primary-button1 applies change to the mix group.
273 NOTE: Primary-button2 is MIDI learn.
276 if (ev->button == 1) {
277 set_mix_group_mute (_route, !_route->muted());
282 /* plain click applies change to this route */
283 if (wait_for_release) {
284 _route->set_mute (!_route->muted(), this);
286 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this);
298 RouteUI::mute_release(GdkEventButton* ev)
300 if (!ignore_toggle) {
301 if (wait_for_release){
302 wait_for_release = false;
303 if (multiple_mute_change) {
304 multiple_mute_change = false;
306 // because the press was the last undoable thing we did
309 _route->set_mute (!_route->muted(), this);
317 RouteUI::solo_press(GdkEventButton* ev)
319 /* ignore double/triple clicks */
321 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
324 multiple_solo_change = false;
325 if (!ignore_toggle) {
327 if (Keyboard::is_context_menu_event (ev)) {
329 if (solo_menu == 0) {
333 solo_menu->popup (1, ev->time);
337 if (Keyboard::is_button2_event (ev)) {
339 // Primary-button2 click is the midi binding click
340 // button2-click is "momentary"
342 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier))) {
343 wait_for_release = true;
349 if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
351 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
353 /* Primary-Tertiary-click applies change to all routes */
354 bool was_not_latched = false;
355 if (!Config->get_solo_latched ()) {
356 was_not_latched = true;
358 XXX it makes no sense to solo all tracks if we're
359 not in latched mode, but doing nothing feels like a bug,
362 Config->set_solo_latched (true);
364 _session.begin_reversible_command (_("solo change"));
365 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
366 _session.set_all_solo (!_route->soloed());
368 _session.add_command (cmd);
369 _session.commit_reversible_command ();
370 multiple_solo_change = true;
371 if (was_not_latched) {
372 Config->set_solo_latched (false);
375 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
377 // Primary-Secondary-click: exclusively solo this track, not a toggle */
379 _session.begin_reversible_command (_("solo change"));
380 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (_session, this);
381 _session.set_all_solo (false);
382 _route->set_solo (true, this);
384 _session.add_command(cmd);
385 _session.commit_reversible_command ();
387 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
389 // shift-click: set this route to solo safe
391 if (Profile->get_sae() && ev->button == 1) {
392 // button 1 and shift-click: disables solo_latched for this click
393 if (!Config->get_solo_latched ()) {
394 Config->set_solo_latched (true);
395 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
396 Config->set_solo_latched (false);
399 _route->set_solo_safe (!_route->solo_safe(), this);
400 wait_for_release = false;
403 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
405 /* Primary-button1: solo mix group.
406 NOTE: Primary-button2 is MIDI learn.
409 if (ev->button == 1) {
410 set_mix_group_solo (_route, !_route->soloed());
415 /* click: solo this route */
416 if (wait_for_release) {
417 _route->set_solo (!_route->soloed(), this);
419 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
430 RouteUI::solo_release(GdkEventButton* ev)
432 if (!ignore_toggle) {
433 if (wait_for_release) {
434 wait_for_release = false;
435 if (multiple_solo_change) {
436 multiple_solo_change = false;
438 // because the press was the last undoable thing we did
441 // we don't use "undo the last op"
442 // here because its expensive for the GUI
443 _route->set_solo (!_route->soloed(), this);
452 RouteUI::rec_enable_press(GdkEventButton* ev)
454 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
458 if (!_session.engine().connected()) {
459 MessageDialog msg (_("Not connected to JACK - cannot engage record"));
464 if (!ignore_toggle && is_track() && rec_enable_button) {
466 if (Keyboard::is_button2_event (ev) && Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
468 // do nothing on midi bind event
471 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
473 _session.begin_reversible_command (_("rec-enable change"));
474 Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
476 if (rec_enable_button->get_active()) {
477 _session.record_disenable_all ();
479 _session.record_enable_all ();
483 _session.add_command(cmd);
484 _session.commit_reversible_command ();
486 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
488 /* Primary-button1 applies change to the mix group.
489 NOTE: Primary-button2 is MIDI learn.
492 set_mix_group_rec_enable (_route, !_route->record_enabled());
496 reversibly_apply_track_boolean ("rec-enable change", &Track::set_record_enable, !track()->record_enabled(), this);
504 RouteUI::rec_enable_release (GdkEventButton* ev)
510 RouteUI::build_sends_menu ()
512 using namespace Menu_Helpers;
514 sends_menu = new Menu;
515 sends_menu->set_name ("ArdourContextMenu");
516 MenuList& items = sends_menu->items();
518 items.push_back (MenuElem(_("Copy track gains to sends"), mem_fun (*this, &RouteUI::set_sends_gain_from_track)));
519 items.push_back (MenuElem(_("Set sends gain to -inf"), mem_fun (*this, &RouteUI::set_sends_gain_to_zero)));
520 items.push_back (MenuElem(_("Set sends gain to 0dB"), mem_fun (*this, &RouteUI::set_sends_gain_to_unity)));
524 RouteUI::set_sends_gain_from_track ()
529 RouteUI::set_sends_gain_to_zero ()
534 RouteUI::set_sends_gain_to_unity ()
539 RouteUI::show_sends_press(GdkEventButton* ev)
541 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
545 if (!ignore_toggle && !is_track() && show_sends_button) {
547 if (Keyboard::is_button2_event (ev) && Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
549 // do nothing on midi bind event
552 } else if (Keyboard::is_context_menu_event (ev)) {
554 if (sends_menu == 0) {
558 sends_menu->popup (0, ev->time);
562 /* change button state */
564 show_sends_button->set_active (!show_sends_button->get_active());
568 if (show_sends_button->get_active()) {
569 /* show sends to this bus */
570 MixerStrip::SwitchIO (_route);
571 send_blink_connection = ARDOUR_UI::instance()->Blink.connect (mem_fun(*this, &RouteUI::send_blink));
573 /* everybody back to normal */
574 send_blink_connection.disconnect ();
575 MixerStrip::SwitchIO (boost::shared_ptr<Route>());
585 RouteUI::show_sends_release (GdkEventButton* ev)
591 RouteUI::send_blink (bool onoff)
593 if (!show_sends_button) {
598 show_sends_button->set_state (STATE_ACTIVE);
600 show_sends_button->set_state (STATE_NORMAL);
605 RouteUI::solo_changed(void* src)
608 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
612 RouteUI::update_solo_display ()
615 vector<Gdk::Color> fg_colors;
618 if (solo_button->get_active() != (x = _route->soloed())){
619 ignore_toggle = true;
620 solo_button->set_active(x);
621 ignore_toggle = false;
624 if (_route->solo_safe()) {
625 solo_button->set_visual_state (2);
626 } else if (_route->soloed()) {
627 solo_button->set_visual_state (1);
629 solo_button->set_visual_state (0);
634 RouteUI::solo_changed_so_update_mute ()
636 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
640 RouteUI::mute_changed(void* src)
642 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
646 RouteUI::update_mute_display ()
648 bool model = _route->muted();
649 bool view = mute_button->get_active();
651 /* first make sure the button's "depressed" visual
656 ignore_toggle = true;
657 mute_button->set_active (model);
658 ignore_toggle = false;
661 /* now attend to visual state */
663 if (Config->get_show_solo_mutes()) {
664 if (_route->muted()) {
665 mute_button->set_visual_state (2);
666 } else if (!_route->soloed() && _route->solo_muted()) {
668 mute_button->set_visual_state (1);
670 mute_button->set_visual_state (0);
673 if (_route->muted()) {
674 mute_button->set_visual_state (2);
676 mute_button->set_visual_state (0);
683 RouteUI::route_rec_enable_changed ()
685 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
689 RouteUI::session_rec_enable_changed ()
691 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
695 RouteUI::update_rec_display ()
697 bool model = _route->record_enabled();
698 bool view = rec_enable_button->get_active();
700 /* first make sure the button's "depressed" visual
705 ignore_toggle = true;
706 rec_enable_button->set_active (model);
707 ignore_toggle = false;
710 /* now make sure its color state is correct */
714 switch (_session.record_status ()) {
715 case Session::Recording:
716 rec_enable_button->set_visual_state (1);
719 case Session::Disabled:
720 case Session::Enabled:
721 rec_enable_button->set_visual_state (2);
727 rec_enable_button->set_visual_state (0);
732 RouteUI::build_remote_control_menu ()
734 remote_control_menu = new Menu;
735 refresh_remote_control_menu ();
739 RouteUI::refresh_remote_control_menu ()
741 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::refresh_remote_control_menu));
743 // only refresh the menu if it has been instantiated
745 if (remote_control_menu == 0) {
749 using namespace Menu_Helpers;
751 RadioMenuItem::Group rc_group;
752 CheckMenuItem* rc_active;
753 uint32_t limit = _session.ntracks() + _session.nbusses();
756 MenuList& rc_items = remote_control_menu->items();
759 /* note that this menu list starts at zero, not 1, because zero
760 is a valid, if useless, ID.
763 limit += 4; /* leave some breathing room */
765 rc_items.push_back (RadioMenuElem (rc_group, _("None")));
766 if (_route->remote_control_id() == 0) {
767 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
768 rc_active->set_active ();
771 for (uint32_t i = 1; i < limit; ++i) {
772 snprintf (buf, sizeof (buf), "%u", i);
773 rc_items.push_back (RadioMenuElem (rc_group, buf));
774 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
775 if (_route->remote_control_id() == i) {
776 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
777 rc_active->set_active ();
779 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
784 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
786 /* this is called when the radio menu item is toggled, and so
787 is actually invoked twice per menu selection. we only
788 care about the invocation for the item that was being
792 if (item->get_active()) {
793 _route->set_remote_control_id (id);
798 RouteUI::build_solo_menu (void)
800 using namespace Menu_Helpers;
802 solo_menu = new Menu;
803 solo_menu->set_name ("ArdourContextMenu");
804 MenuList& items = solo_menu->items();
805 CheckMenuItem* check;
807 check = new CheckMenuItem(_("Solo Lock"));
808 check->set_active (_route->solo_safe());
809 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
810 _route->solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
811 items.push_back (CheckMenuElem(*check));
814 //items.push_back (SeparatorElem());
815 // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
820 RouteUI::build_mute_menu(void)
822 using namespace Menu_Helpers;
824 mute_menu = new Menu;
825 mute_menu->set_name ("ArdourContextMenu");
826 MenuList& items = mute_menu->items();
827 CheckMenuItem* check;
829 check = new CheckMenuItem(_("Pre Fader"));
830 init_mute_menu(PRE_FADER, check);
831 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
832 _route->pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
833 items.push_back (CheckMenuElem(*check));
836 check = new CheckMenuItem(_("Post Fader"));
837 init_mute_menu(POST_FADER, check);
838 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
839 _route->post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
840 items.push_back (CheckMenuElem(*check));
843 check = new CheckMenuItem(_("Control Outs"));
844 init_mute_menu(CONTROL_OUTS, check);
845 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
846 _route->control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
847 items.push_back (CheckMenuElem(*check));
850 check = new CheckMenuItem(_("Main Outs"));
851 init_mute_menu(MAIN_OUTS, check);
852 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
853 _route->main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
854 items.push_back (CheckMenuElem(*check));
857 //items.push_back (SeparatorElem());
858 // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
862 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
864 if (_route->get_mute_config (type)) {
865 check->set_active (true);
870 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
872 _route->set_mute_config(type, check->get_active(), this);
876 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
878 _route->set_solo_safe (check->get_active(), this);
882 RouteUI::set_mix_group_solo(boost::shared_ptr<Route> route, bool yn)
884 RouteGroup* mix_group;
886 if((mix_group = route->mix_group()) != 0){
887 _session.begin_reversible_command (_("mix group solo change"));
888 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
889 mix_group->apply(&Route::set_solo, yn, this);
891 _session.add_command (cmd);
892 _session.commit_reversible_command ();
894 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route->soloed(), this);
899 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
901 _session.begin_reversible_command (name);
902 XMLNode &before = _route->get_state();
903 bind(mem_fun(*_route, func), yn, arg)();
904 XMLNode &after = _route->get_state();
905 _session.add_command (new MementoCommand<Route>(*_route, &before, &after));
906 _session.commit_reversible_command ();
910 RouteUI::reversibly_apply_track_boolean (string name, void (Track::*func)(bool, void *), bool yn, void *arg)
912 _session.begin_reversible_command (name);
913 XMLNode &before = track()->get_state();
914 bind (mem_fun (*track(), func), yn, arg)();
915 XMLNode &after = track()->get_state();
916 _session.add_command (new MementoCommand<Track>(*track(), &before, &after));
917 _session.commit_reversible_command ();
921 RouteUI::set_mix_group_mute(boost::shared_ptr<Route> route, bool yn)
923 RouteGroup* mix_group;
925 if((mix_group = route->mix_group()) != 0){
926 _session.begin_reversible_command (_("mix group mute change"));
927 Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand (_session, this);
928 mix_group->apply(&Route::set_mute, yn, this);
930 _session.add_command(cmd);
931 _session.commit_reversible_command ();
933 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route->muted(), this);
938 RouteUI::set_mix_group_rec_enable(boost::shared_ptr<Route> route, bool yn)
940 RouteGroup* mix_group;
942 if((mix_group = route->mix_group()) != 0){
943 _session.begin_reversible_command (_("mix group rec-enable change"));
944 Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
945 mix_group->apply (&Route::set_record_enable, yn, this);
947 _session.add_command(cmd);
948 _session.commit_reversible_command ();
950 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route->record_enabled(), this);
956 RouteUI::choose_color()
961 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
971 RouteUI::set_color (const Gdk::Color & c)
978 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
979 xml_node->add_property ("color", buf);
981 _route->gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
986 RouteUI::ensure_xml_node ()
989 if ((xml_node = _route->extra_xml ("GUI")) == 0) {
990 xml_node = new XMLNode ("GUI");
991 _route->add_extra_xml (*xml_node);
997 RouteUI::get_automation_child_xml_node (Evoral::Parameter param)
1001 XMLNodeList kids = xml_node->children();
1002 XMLNodeConstIterator iter;
1004 const string sym = ARDOUR::EventTypeMap::instance().to_symbol(param);
1006 for (iter = kids.begin(); iter != kids.end(); ++iter) {
1007 if ((*iter)->name() == AutomationTimeAxisView::state_node_name) {
1008 XMLProperty* type = (*iter)->property("automation-id");
1009 if (type && type->value() == sym)
1014 // Didn't find it, make a new one
1015 XMLNode* child = new XMLNode (AutomationTimeAxisView::state_node_name);
1016 child->add_property("automation-id", sym);
1017 xml_node->add_child_nocopy (*child);
1023 RouteUI::set_color_from_route ()
1027 RouteUI::ensure_xml_node ();
1029 if ((prop = xml_node->property ("color")) != 0) {
1031 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
1033 _color.set_green(g);
1041 RouteUI::remove_this_route ()
1043 vector<string> choices;
1047 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());
1049 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route->name());
1052 choices.push_back (_("No, do nothing."));
1053 choices.push_back (_("Yes, remove it."));
1055 Choice prompter (prompt, choices);
1057 if (prompter.run () == 1) {
1058 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
1063 RouteUI::idle_remove_this_route (RouteUI *rui)
1065 rui->_session.remove_route (rui->_route);
1070 RouteUI::route_rename ()
1072 ArdourPrompter name_prompter (true);
1074 name_prompter.set_prompt (_("New Name: "));
1075 name_prompter.set_initial_text (_route->name());
1076 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1077 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1078 name_prompter.show_all ();
1080 switch (name_prompter.run ()) {
1082 case Gtk::RESPONSE_ACCEPT:
1083 name_prompter.get_result (result);
1084 if (result.length()) {
1085 _route->set_name (result);
1095 RouteUI::name_changed ()
1097 ENSURE_GUI_THREAD(sigc::mem_fun(*this, &RouteUI::name_changed));
1099 name_label.set_text (_route->name());
1103 RouteUI::toggle_route_active ()
1107 if (route_active_menu_item) {
1108 if (route_active_menu_item->get_active() != (yn = _route->active())) {
1109 _route->set_active (!yn);
1115 RouteUI::route_active_changed ()
1117 if (route_active_menu_item) {
1118 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route->active()));
1123 RouteUI::toggle_polarity ()
1125 if (polarity_menu_item) {
1129 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
1131 if ((x = polarity_menu_item->get_active()) != _route->phase_invert()) {
1132 _route->set_phase_invert (x, this);
1134 name_label.set_text (X_("Ø ") + name_label.get_text());
1136 name_label.set_text (_route->name());
1143 RouteUI::polarity_changed ()
1145 /* no signal for this yet */
1149 RouteUI::toggle_denormal_protection ()
1151 if (denormal_menu_item) {
1155 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_denormal_protection));
1157 if ((x = denormal_menu_item->get_active()) != _route->denormal_protection()) {
1158 _route->set_denormal_protection (x, this);
1164 RouteUI::denormal_protection_changed ()
1166 /* no signal for this yet */
1171 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
1173 bool yn = _route->solo_safe ();
1175 if (check->get_active() != yn) {
1176 check->set_active (yn);
1180 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
1182 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
1184 bool yn = _route->get_mute_config(PRE_FADER);
1185 if (check->get_active() != yn) {
1186 check->set_active (yn);
1191 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
1193 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
1195 bool yn = _route->get_mute_config(POST_FADER);
1196 if (check->get_active() != yn) {
1197 check->set_active (yn);
1202 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
1204 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
1206 bool yn = _route->get_mute_config(CONTROL_OUTS);
1207 if (check->get_active() != yn) {
1208 check->set_active (yn);
1213 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
1215 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
1217 bool yn = _route->get_mute_config(MAIN_OUTS);
1218 if (check->get_active() != yn) {
1219 check->set_active (yn);
1224 RouteUI::disconnect_input ()
1226 _route->disconnect_inputs (this);
1230 RouteUI::disconnect_output ()
1232 _route->disconnect_outputs (this);
1236 RouteUI::is_track () const
1238 return boost::dynamic_pointer_cast<Track>(_route) != 0;
1241 boost::shared_ptr<Track>
1242 RouteUI::track() const
1244 return boost::dynamic_pointer_cast<Track>(_route);
1248 RouteUI::is_audio_track () const
1250 return boost::dynamic_pointer_cast<AudioTrack>(_route) != 0;
1253 boost::shared_ptr<AudioTrack>
1254 RouteUI::audio_track() const
1256 return boost::dynamic_pointer_cast<AudioTrack>(_route);
1260 RouteUI::is_midi_track () const
1262 return boost::dynamic_pointer_cast<MidiTrack>(_route) != 0;
1265 boost::shared_ptr<MidiTrack>
1266 RouteUI::midi_track() const
1268 return boost::dynamic_pointer_cast<MidiTrack>(_route);
1271 boost::shared_ptr<Diskstream>
1272 RouteUI::get_diskstream () const
1274 boost::shared_ptr<Track> t;
1276 if ((t = boost::dynamic_pointer_cast<Track>(_route)) != 0) {
1277 return t->diskstream();
1279 return boost::shared_ptr<Diskstream> ((Diskstream*) 0);
1284 RouteUI::name() const
1286 return _route->name();
1290 RouteUI::map_frozen ()
1292 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
1294 AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
1297 switch (at->freeze_state()) {
1298 case AudioTrack::Frozen:
1299 rec_enable_button->set_sensitive (false);
1302 rec_enable_button->set_sensitive (true);
1309 RouteUI::adjust_latency ()
1311 LatencyDialog dialog (_route->name() + _("latency"), *(_route.get()), _session.frame_rate(), _session.engine().frames_per_cycle());
1315 RouteUI::save_as_template ()
1318 Glib::ustring safe_name;
1321 path = ARDOUR::user_route_template_directory ();
1323 if (g_mkdir_with_parents (path.to_string().c_str(), 0755)) {
1324 error << string_compose (_("Cannot create route template directory %1"), path.to_string()) << endmsg;
1328 Prompter p (true); // modal
1330 p.set_prompt (_("Template name:"));
1332 case RESPONSE_ACCEPT:
1339 p.get_result (name, true);
1341 safe_name = legalize_for_path (name);
1342 safe_name += template_suffix;
1346 _route->save_as_template (path.to_string(), name);