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.
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>
27 #include <ardour/route_group.h>
28 #include <pbd/memento_command.h>
34 #include "gui_thread.h"
36 #include <ardour/route.h>
37 #include <ardour/audio_track.h>
38 #include <ardour/audio_diskstream.h>
39 #include <ardour/midi_track.h>
40 #include <ardour/midi_diskstream.h>
45 using namespace Gtkmm2ext;
46 using namespace ARDOUR;
50 RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt, ARDOUR::Session& sess, const char* m_name,
51 const char* s_name, const char* r_name)
61 remote_control_menu = 0;
62 ignore_toggle = false;
63 wait_for_release = false;
64 route_active_menu_item = 0;
66 if (set_color_from_route()) {
67 set_color (unique_random_color());
70 _route->GoingAway.connect (mem_fun (*this, &RouteUI::route_removed));
71 _route->active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed));
73 mute_button = manage (new BindableToggleButton (_route->mute_control(), m_name ));
74 solo_button = manage (new BindableToggleButton (_route->solo_control(), s_name ));
77 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
79 t->diskstream().RecordEnableChanged.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed));
81 _session.RecordStateChanged.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed));
83 rec_enable_button = manage (new BindableToggleButton (t->rec_enable_control(), r_name ));
85 rec_enable_button->unset_flags (Gtk::CAN_FOCUS);
87 update_rec_display ();
90 mute_button->unset_flags (Gtk::CAN_FOCUS);
91 solo_button->unset_flags (Gtk::CAN_FOCUS);
93 /* map the current state */
104 RouteUI::mute_press(GdkEventButton* ev)
106 if (!ignore_toggle) {
108 if (Keyboard::is_context_menu_event (ev)) {
114 mute_menu->popup(0,0);
118 if (ev->button == 2) {
119 // ctrl-button2 click is the midi binding click
120 // button2-click is "momentary"
122 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
123 wait_for_release = true;
127 if (ev->button == 1 || ev->button == 2) {
129 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
131 /* ctrl-shift-click applies change to all routes */
133 _session.begin_reversible_command (_("mute change"));
134 Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(_session, this);
135 _session.set_all_mute (!_route->muted());
137 _session.add_command(cmd);
138 _session.commit_reversible_command ();
140 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
142 /* ctrl-click applies change to the mix group.
143 ctrl-button2 is MIDI learn.
146 if (ev->button == 1) {
147 set_mix_group_mute (_route, !_route->muted());
152 /* plain click applies change to this route */
154 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this);
165 RouteUI::mute_release(GdkEventButton* ev)
167 if (!ignore_toggle) {
168 if (wait_for_release){
169 wait_for_release = false;
171 // because the press was the last undoable thing we did
179 RouteUI::solo_press(GdkEventButton* ev)
181 if (!ignore_toggle) {
183 if (Keyboard::is_context_menu_event (ev)) {
185 if (solo_menu == 0) {
189 solo_menu->popup (1, 0);
193 if (ev->button == 2) {
195 // ctrl-button2 click is the midi binding click
196 // button2-click is "momentary"
198 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
199 wait_for_release = true;
203 if (ev->button == 1 || ev->button == 2) {
205 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
207 /* ctrl-shift-click applies change to all routes */
209 _session.begin_reversible_command (_("solo change"));
210 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
211 _session.set_all_solo (!_route->soloed());
213 _session.add_command (cmd);
214 _session.commit_reversible_command ();
216 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
218 // ctrl-alt-click: exclusively solo this track, not a toggle */
220 _session.begin_reversible_command (_("solo change"));
221 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (_session, this);
222 _session.set_all_solo (false);
223 _route->set_solo (true, this);
225 _session.add_command(cmd);
226 _session.commit_reversible_command ();
228 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
230 // shift-click: set this route to solo safe
232 _route->set_solo_safe (!_route->solo_safe(), this);
233 wait_for_release = false;
235 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
237 /* ctrl-click: solo mix group.
238 ctrl-button2 is MIDI learn.
241 if (ev->button == 1) {
242 set_mix_group_solo (_route, !_route->soloed());
247 /* click: solo this route */
249 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
259 RouteUI::solo_release(GdkEventButton* ev)
261 if (!ignore_toggle) {
262 if (wait_for_release) {
263 wait_for_release = false;
265 // because the press was the last undoable thing we did
275 RouteUI::rec_enable_press(GdkEventButton* ev)
277 if (!ignore_toggle && is_track() && rec_enable_button) {
279 if (ev->button == 2 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
280 // do nothing on midi bind event
282 else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
284 _session.begin_reversible_command (_("rec-enable change"));
285 Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
287 if (rec_enable_button->get_active()) {
288 _session.record_disenable_all ();
290 _session.record_enable_all ();
294 _session.add_command(cmd);
295 _session.commit_reversible_command ();
297 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
299 set_mix_group_rec_enable (_route, !_route->record_enabled());
303 reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
305 ignore_toggle = true;
306 rec_enable_button->set_active(audio_track()->record_enabled());
307 ignore_toggle = false;
310 stop_signal (*rec_enable_button, "button-press-event");
317 RouteUI::solo_changed(void* src)
319 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
323 RouteUI::update_solo_display ()
327 if (solo_button->get_active() != (x = _route->soloed())){
328 ignore_toggle = true;
329 solo_button->set_active(x);
330 ignore_toggle = false;
335 if (_route->solo_safe()){
336 solo_button->set_name(safe_solo_button_name());
338 solo_button->set_name(solo_button_name());
343 RouteUI::mute_changed(void* src)
345 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
349 RouteUI::update_mute_display ()
353 if (mute_button->get_active() != (x = _route->muted())){
354 ignore_toggle = true;
355 mute_button->set_active(x);
356 ignore_toggle = false;
361 RouteUI::route_rec_enable_changed ()
363 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
367 RouteUI::session_rec_enable_changed ()
369 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
373 RouteUI::update_rec_display ()
375 bool model = _route->record_enabled();
376 bool view = rec_enable_button->get_active();
378 /* first make sure the button's "depressed" visual
383 ignore_toggle = true;
384 rec_enable_button->set_active (model);
385 ignore_toggle = false;
388 /* now make sure its color state is correct */
392 switch (_session.record_status ()) {
393 case Session::Disabled:
394 case Session::Enabled:
395 if (rec_enable_button->get_state() != Gtk::STATE_ACTIVE) {
396 rec_enable_button->set_state (Gtk::STATE_ACTIVE);
400 case Session::Recording:
401 if (rec_enable_button->get_state() != Gtk::STATE_SELECTED) {
402 rec_enable_button->set_state (Gtk::STATE_SELECTED);
408 if (rec_enable_button->get_state() != Gtk::STATE_NORMAL) {
409 rec_enable_button->set_state (Gtk::STATE_NORMAL);
415 RouteUI::build_remote_control_menu ()
417 remote_control_menu = manage (new Menu);
418 refresh_remote_control_menu ();
422 RouteUI::refresh_remote_control_menu ()
424 using namespace Menu_Helpers;
426 RadioMenuItem::Group rc_group;
427 CheckMenuItem* rc_active;
428 uint32_t limit = _session.ntracks();
431 MenuList& rc_items = remote_control_menu->items();
434 /* note that this menu list starts at zero, not 1, because zero
435 is a valid, if useless, ID.
438 limit += 4; /* leave some breathing room */
440 rc_items.push_back (RadioMenuElem (rc_group, _("None")));
441 if (_route->remote_control_id() == 0) {
442 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
443 rc_active->set_active ();
446 for (uint32_t i = 1; i < limit; ++i) {
447 snprintf (buf, sizeof (buf), "%u", i);
448 rc_items.push_back (RadioMenuElem (rc_group, buf));
449 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
450 if (_route->remote_control_id() == i) {
451 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
452 rc_active->set_active ();
454 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
459 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
461 /* this is called when the radio menu item is toggled, and so
462 is actually invoked twice per menu selection. we only
463 care about the invocation for the item that was being
467 if (item->get_active()) {
468 _route->set_remote_control_id (id);
473 RouteUI::build_solo_menu (void)
475 using namespace Menu_Helpers;
477 solo_menu = new Menu;
478 solo_menu->set_name ("ArdourContextMenu");
479 MenuList& items = solo_menu->items();
480 CheckMenuItem* check;
482 check = new CheckMenuItem(_("Solo-safe"));
483 check->set_active (_route->solo_safe());
484 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
485 _route->solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
486 items.push_back (CheckMenuElem(*check));
489 items.push_back (SeparatorElem());
490 // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
495 RouteUI::build_mute_menu(void)
497 using namespace Menu_Helpers;
499 mute_menu = new Menu;
500 mute_menu->set_name ("ArdourContextMenu");
501 MenuList& items = mute_menu->items();
502 CheckMenuItem* check;
504 check = new CheckMenuItem(_("Pre Fader"));
505 init_mute_menu(PRE_FADER, check);
506 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
507 _route->pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
508 items.push_back (CheckMenuElem(*check));
511 check = new CheckMenuItem(_("Post Fader"));
512 init_mute_menu(POST_FADER, check);
513 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
514 _route->post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
515 items.push_back (CheckMenuElem(*check));
518 check = new CheckMenuItem(_("Control Outs"));
519 init_mute_menu(CONTROL_OUTS, check);
520 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
521 _route->control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
522 items.push_back (CheckMenuElem(*check));
525 check = new CheckMenuItem(_("Main Outs"));
526 init_mute_menu(MAIN_OUTS, check);
527 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
528 _route->main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
529 items.push_back (CheckMenuElem(*check));
532 items.push_back (SeparatorElem());
533 // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
537 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
539 if (_route->get_mute_config (type)) {
540 check->set_active (true);
545 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
547 _route->set_mute_config(type, check->get_active(), this);
551 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
553 _route->set_solo_safe (check->get_active(), this);
557 RouteUI::set_mix_group_solo(boost::shared_ptr<Route> route, bool yn)
559 RouteGroup* mix_group;
561 if((mix_group = route->mix_group()) != 0){
562 _session.begin_reversible_command (_("mix group solo change"));
563 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
564 mix_group->apply(&Route::set_solo, yn, this);
566 _session.add_command (cmd);
567 _session.commit_reversible_command ();
569 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route->soloed(), this);
574 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
576 _session.begin_reversible_command (name);
577 XMLNode &before = _route->get_state();
578 bind(mem_fun(*_route, func), yn, arg)();
579 XMLNode &after = _route->get_state();
580 _session.add_command (new MementoCommand<Route>(*_route, before, after));
581 _session.commit_reversible_command ();
585 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
587 _session.begin_reversible_command (name);
588 XMLNode &before = audio_track()->get_state();
589 bind (mem_fun (*audio_track(), func), yn, arg)();
590 XMLNode &after = audio_track()->get_state();
591 _session.add_command (new MementoCommand<AudioTrack>(*audio_track(), before, after));
592 _session.commit_reversible_command ();
596 RouteUI::set_mix_group_mute(boost::shared_ptr<Route> route, bool yn)
598 RouteGroup* mix_group;
600 if((mix_group = route->mix_group()) != 0){
601 _session.begin_reversible_command (_("mix group mute change"));
602 Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand (_session, this);
603 mix_group->apply(&Route::set_mute, yn, this);
605 _session.add_command(cmd);
606 _session.commit_reversible_command ();
608 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route->muted(), this);
613 RouteUI::set_mix_group_rec_enable(boost::shared_ptr<Route> route, bool yn)
615 RouteGroup* mix_group;
617 if((mix_group = route->mix_group()) != 0){
618 _session.begin_reversible_command (_("mix group rec-enable change"));
619 Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
620 mix_group->apply (&Route::set_record_enable, yn, this);
622 _session.add_command(cmd);
623 _session.commit_reversible_command ();
625 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route->record_enabled(), this);
631 RouteUI::choose_color()
636 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
646 RouteUI::set_color (const Gdk::Color & c)
653 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
654 xml_node->add_property ("color", buf);
656 _route->gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
661 RouteUI::ensure_xml_node ()
664 if ((xml_node = _route->extra_xml ("GUI")) == 0) {
665 xml_node = new XMLNode ("GUI");
666 _route->add_extra_xml (*xml_node);
672 RouteUI::get_child_xml_node (const string & childname)
679 if ((child = find_named_node (*xml_node, childname)) == 0) {
680 child = new XMLNode (childname);
681 xml_node->add_child_nocopy (*child);
688 RouteUI::set_color_from_route ()
692 RouteUI::ensure_xml_node ();
694 if ((prop = xml_node->property ("color")) != 0) {
696 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
706 RouteUI::remove_this_route ()
708 vector<string> choices;
712 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());
714 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route->name());
717 choices.push_back (_("No, do nothing."));
718 choices.push_back (_("Yes, remove it."));
720 Choice prompter (prompt, choices);
722 if (prompter.run () == 1) {
723 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
728 RouteUI::idle_remove_this_route (RouteUI *rui)
730 rui->_session.remove_route (rui->_route);
735 RouteUI::route_removed ()
737 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
743 RouteUI::route_rename ()
745 ArdourPrompter name_prompter (true);
747 name_prompter.set_prompt (_("New Name: "));
748 name_prompter.set_initial_text (_route->name());
749 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
750 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
751 name_prompter.show_all ();
753 switch (name_prompter.run ()) {
755 case Gtk::RESPONSE_ACCEPT:
756 name_prompter.get_result (result);
757 if (result.length()) {
758 _route->set_name (result, this);
768 RouteUI::name_changed (void *src)
770 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
772 name_label.set_text (_route->name());
776 RouteUI::toggle_route_active ()
780 if (route_active_menu_item) {
781 if (route_active_menu_item->get_active() != (yn = _route->active())) {
782 _route->set_active (!yn);
788 RouteUI::route_active_changed ()
790 if (route_active_menu_item) {
791 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route->active()));
796 RouteUI::toggle_polarity ()
798 if (polarity_menu_item) {
802 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
804 if ((x = polarity_menu_item->get_active()) != _route->phase_invert()) {
805 _route->set_phase_invert (x, this);
807 name_label.set_text (X_("Ø ") + name_label.get_text());
809 name_label.set_text (_route->name());
816 RouteUI::polarity_changed ()
818 /* no signal for this yet */
822 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
824 bool yn = _route->solo_safe ();
826 if (check->get_active() != yn) {
827 check->set_active (yn);
831 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
833 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
835 bool yn = _route->get_mute_config(PRE_FADER);
836 if (check->get_active() != yn) {
837 check->set_active (yn);
842 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
844 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
846 bool yn = _route->get_mute_config(POST_FADER);
847 if (check->get_active() != yn) {
848 check->set_active (yn);
853 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
855 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
857 bool yn = _route->get_mute_config(CONTROL_OUTS);
858 if (check->get_active() != yn) {
859 check->set_active (yn);
864 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
866 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
868 bool yn = _route->get_mute_config(MAIN_OUTS);
869 if (check->get_active() != yn) {
870 check->set_active (yn);
875 RouteUI::disconnect_input ()
877 _route->disconnect_inputs (this);
881 RouteUI::disconnect_output ()
883 _route->disconnect_outputs (this);
887 RouteUI::is_track () const
889 return dynamic_cast<Track*>(_route.get()) != 0;
893 RouteUI::track() const
895 return dynamic_cast<Track*>(_route.get());
899 RouteUI::is_audio_track () const
901 return dynamic_cast<AudioTrack*>(_route.get()) != 0;
905 RouteUI::audio_track() const
907 return dynamic_cast<AudioTrack*>(_route.get());
911 RouteUI::is_midi_track () const
913 return dynamic_cast<MidiTrack*>(_route.get()) != 0;
917 RouteUI::midi_track() const
919 return dynamic_cast<MidiTrack*>(_route.get());
923 RouteUI::get_diskstream () const
925 boost::shared_ptr<Track> t;
927 if ((t = boost::dynamic_pointer_cast<Track>(_route)) != 0) {
928 return &t->diskstream();
935 RouteUI::name() const
937 return _route->name();
941 RouteUI::map_frozen ()
943 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
945 AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
948 switch (at->freeze_state()) {
949 case AudioTrack::Frozen:
950 rec_enable_button->set_sensitive (false);
953 rec_enable_button->set_sensitive (true);