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/bindable_button.h>
25 #include <gtkmm2ext/doi.h>
27 #include <ardour/route_group.h>
33 #include "gui_thread.h"
35 #include <ardour/route.h>
36 #include <ardour/audio_track.h>
37 #include <ardour/audio_diskstream.h>
38 #include <ardour/midi_track.h>
39 #include <ardour/midi_diskstream.h>
44 using namespace Gtkmm2ext;
45 using namespace ARDOUR;
49 RouteUI::RouteUI (ARDOUR::Route& rt, ARDOUR::Session& sess, const char* m_name,
50 const char* s_name, const char* r_name)
60 remote_control_menu = 0;
61 ignore_toggle = false;
62 wait_for_release = false;
63 route_active_menu_item = 0;
65 if (set_color_from_route()) {
66 set_color (unique_random_color());
69 _route.GoingAway.connect (mem_fun (*this, &RouteUI::route_removed));
70 _route.active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed));
72 mute_button = manage (new BindableToggleButton (& _route.midi_mute_control(), m_name ));
73 mute_button->set_bind_button_state (2, GDK_CONTROL_MASK);
74 solo_button = manage (new BindableToggleButton (& _route.midi_solo_control(), s_name ));
75 solo_button->set_bind_button_state (2, GDK_CONTROL_MASK);
77 if (is_audio_track()) {
78 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
80 get_diskstream()->record_enable_changed.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed));
82 _session.RecordStateChanged.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed));
84 rec_enable_button = manage (new BindableToggleButton (& at->midi_rec_enable_control(), r_name ));
85 rec_enable_button->set_bind_button_state (2, GDK_CONTROL_MASK);
88 rec_enable_button = manage (new BindableToggleButton (0, r_name ));
91 mute_button->unset_flags (Gtk::CAN_FOCUS);
92 solo_button->unset_flags (Gtk::CAN_FOCUS);
93 rec_enable_button->unset_flags (Gtk::CAN_FOCUS);
95 /* map the current state */
97 update_rec_display ();
107 RouteUI::mute_press(GdkEventButton* ev)
109 if (!ignore_toggle) {
111 if (Keyboard::is_context_menu_event (ev)) {
117 mute_menu->popup(0,0);
121 if (ev->button == 2) {
122 // ctrl-button2 click is the midi binding click
123 // button2-click is "momentary"
125 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
126 wait_for_release = true;
130 if (ev->button == 1 || ev->button == 2) {
132 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
134 /* ctrl-shift-click applies change to all routes */
136 _session.begin_reversible_command (_("mute change"));
137 _session.add_undo (_session.global_mute_memento(this));
138 _session.set_all_mute (!_route.muted());
139 _session.add_redo_no_execute (_session.global_mute_memento(this));
140 _session.commit_reversible_command ();
142 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
144 /* ctrl-click applies change to the mix group.
145 ctrl-button2 is MIDI learn.
148 if (ev->button == 1) {
149 set_mix_group_mute (_route, !_route.muted());
154 /* plain click applies change to this route */
156 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route.muted(), this);
167 RouteUI::mute_release(GdkEventButton* ev)
169 if (!ignore_toggle) {
170 if (wait_for_release){
171 wait_for_release = false;
173 // because the press was the last undoable thing we did
181 RouteUI::solo_press(GdkEventButton* ev)
183 if (!ignore_toggle) {
185 if (Keyboard::is_context_menu_event (ev)) {
187 if (solo_menu == 0) {
191 solo_menu->popup (1, 0);
195 if (ev->button == 2) {
197 // ctrl-button2 click is the midi binding click
198 // button2-click is "momentary"
200 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
201 wait_for_release = true;
205 if (ev->button == 1 || ev->button == 2) {
207 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
209 /* ctrl-shift-click applies change to all routes */
211 _session.begin_reversible_command (_("solo change"));
212 _session.add_undo (_session.global_solo_memento(this));
213 _session.set_all_solo (!_route.soloed());
214 _session.add_redo_no_execute (_session.global_solo_memento(this));
215 _session.commit_reversible_command ();
217 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
219 // ctrl-alt-click: exclusively solo this track, not a toggle */
221 _session.begin_reversible_command (_("solo change"));
222 _session.add_undo (_session.global_solo_memento(this));
223 _session.set_all_solo (false);
224 _route.set_solo (true, this);
225 _session.add_redo_no_execute (_session.global_solo_memento(this));
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_audio_track()) {
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.add_undo (_session.global_record_enable_memento(this));
287 if (rec_enable_button->get_active()) {
288 _session.record_disenable_all ();
290 _session.record_enable_all ();
293 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
294 _session.commit_reversible_command ();
296 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
298 set_mix_group_rec_enable (_route, !_route.record_enabled());
302 reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
304 ignore_toggle = true;
305 rec_enable_button->set_active(audio_track()->record_enabled());
306 ignore_toggle = false;
309 stop_signal (*rec_enable_button, "button-press-event");
316 RouteUI::solo_changed(void* src)
318 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
322 RouteUI::update_solo_display ()
326 if (solo_button->get_active() != (x = _route.soloed())){
327 ignore_toggle = true;
328 solo_button->set_active(x);
329 ignore_toggle = false;
334 if (_route.solo_safe()){
335 solo_button->set_name(safe_solo_button_name());
337 solo_button->set_name(solo_button_name());
342 RouteUI::mute_changed(void* src)
344 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
348 RouteUI::update_mute_display ()
352 if (mute_button->get_active() != (x = _route.muted())){
353 ignore_toggle = true;
354 mute_button->set_active(x);
355 ignore_toggle = false;
360 RouteUI::route_rec_enable_changed (void *src)
362 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
366 RouteUI::session_rec_enable_changed ()
368 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
372 RouteUI::update_rec_display ()
374 bool model = _route.record_enabled();
375 bool view = rec_enable_button->get_active();
377 /* first make sure the button's "depressed" visual
382 ignore_toggle = true;
383 rec_enable_button->set_active (model);
384 ignore_toggle = false;
387 /* now make sure its color state is correct */
391 switch (_session.record_status ()) {
392 case Session::Disabled:
393 case Session::Enabled:
394 if (rec_enable_button->get_state() != Gtk::STATE_ACTIVE) {
395 rec_enable_button->set_state (Gtk::STATE_ACTIVE);
399 case Session::Recording:
400 if (rec_enable_button->get_state() != Gtk::STATE_SELECTED) {
401 rec_enable_button->set_state (Gtk::STATE_SELECTED);
407 if (rec_enable_button->get_state() != Gtk::STATE_NORMAL) {
408 rec_enable_button->set_state (Gtk::STATE_NORMAL);
414 RouteUI::build_remote_control_menu ()
416 remote_control_menu = manage (new Menu);
417 refresh_remote_control_menu ();
421 RouteUI::refresh_remote_control_menu ()
423 using namespace Menu_Helpers;
425 RadioMenuItem::Group rc_group;
426 CheckMenuItem* rc_active;
427 uint32_t limit = _session.ntracks();
430 MenuList& rc_items = remote_control_menu->items();
433 /* note that this menu list starts at zero, not 1, because zero
434 is a valid, if useless, ID.
437 limit += 4; /* leave some breathing room */
439 rc_items.push_back (RadioMenuElem (rc_group, _("None")));
440 if (_route.remote_control_id() == 0) {
441 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
442 rc_active->set_active ();
445 for (uint32_t i = 1; i < limit; ++i) {
446 snprintf (buf, sizeof (buf), "%u", i);
447 rc_items.push_back (RadioMenuElem (rc_group, buf));
448 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
449 if (_route.remote_control_id() == i) {
450 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
451 rc_active->set_active ();
453 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
458 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
460 /* this is called when the radio menu item is toggled, and so
461 is actually invoked twice per menu selection. we only
462 care about the invocation for the item that was being
466 if (item->get_active()) {
467 _route.set_remote_control_id (id);
472 RouteUI::build_solo_menu (void)
474 using namespace Menu_Helpers;
476 solo_menu = new Menu;
477 solo_menu->set_name ("ArdourContextMenu");
478 MenuList& items = solo_menu->items();
479 CheckMenuItem* check;
481 check = new CheckMenuItem(_("Solo-safe"));
482 check->set_active (_route.solo_safe());
483 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
484 _route.solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
485 items.push_back (CheckMenuElem(*check));
488 items.push_back (SeparatorElem());
489 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
494 RouteUI::build_mute_menu(void)
496 using namespace Menu_Helpers;
498 mute_menu = new Menu;
499 mute_menu->set_name ("ArdourContextMenu");
500 MenuList& items = mute_menu->items();
501 CheckMenuItem* check;
503 check = new CheckMenuItem(_("Pre Fader"));
504 init_mute_menu(PRE_FADER, check);
505 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
506 _route.pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
507 items.push_back (CheckMenuElem(*check));
510 check = new CheckMenuItem(_("Post Fader"));
511 init_mute_menu(POST_FADER, check);
512 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
513 _route.post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
514 items.push_back (CheckMenuElem(*check));
517 check = new CheckMenuItem(_("Control Outs"));
518 init_mute_menu(CONTROL_OUTS, check);
519 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
520 _route.control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
521 items.push_back (CheckMenuElem(*check));
524 check = new CheckMenuItem(_("Main Outs"));
525 init_mute_menu(MAIN_OUTS, check);
526 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
527 _route.main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
528 items.push_back (CheckMenuElem(*check));
531 items.push_back (SeparatorElem());
532 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
536 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
538 if (_route.get_mute_config (type)) {
539 check->set_active (true);
544 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
546 _route.set_mute_config(type, check->get_active(), this);
550 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
552 _route.set_solo_safe (check->get_active(), this);
556 RouteUI::set_mix_group_solo(Route& route, bool yn)
558 RouteGroup* mix_group;
560 if((mix_group = route.mix_group()) != 0){
561 _session.begin_reversible_command (_("mix group solo change"));
562 _session.add_undo (_session.global_solo_memento (this));
563 mix_group->apply(&Route::set_solo, yn, this);
564 _session.add_redo_no_execute (_session.global_solo_memento(this));
565 _session.commit_reversible_command ();
567 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route.soloed(), this);
572 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
574 _session.begin_reversible_command (name);
575 _session.add_undo (bind (mem_fun (_route, func), !yn, (void *) arg));
576 _session.add_redo (bind (mem_fun (_route, func), yn, (void *) arg));
577 _session.commit_reversible_command ();
581 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
583 _session.begin_reversible_command (name);
584 _session.add_undo (bind (mem_fun (*audio_track(), func), !yn, (void *) arg));
585 _session.add_redo (bind (mem_fun (*audio_track(), func), yn, (void *) arg));
586 _session.commit_reversible_command ();
590 RouteUI::set_mix_group_mute(Route& route, bool yn)
592 RouteGroup* mix_group;
594 if((mix_group = route.mix_group()) != 0){
595 _session.begin_reversible_command (_("mix group mute change"));
596 _session.add_undo (_session.global_mute_memento (this));
597 mix_group->apply(&Route::set_mute, yn, this);
598 _session.add_redo_no_execute (_session.global_mute_memento(this));
599 _session.commit_reversible_command ();
601 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route.muted(), this);
606 RouteUI::set_mix_group_rec_enable(Route& route, bool yn)
608 RouteGroup* mix_group;
610 if((mix_group = route.mix_group()) != 0){
611 _session.begin_reversible_command (_("mix group rec-enable change"));
612 _session.add_undo (_session.global_record_enable_memento (this));
613 mix_group->apply (&Route::set_record_enable, yn, this);
614 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
615 _session.commit_reversible_command ();
617 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route.record_enabled(), this);
623 RouteUI::choose_color()
628 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
638 RouteUI::set_color (const Gdk::Color & c)
645 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
646 xml_node->add_property ("color", buf);
648 _route.gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
653 RouteUI::ensure_xml_node ()
656 if ((xml_node = _route.extra_xml ("GUI")) == 0) {
657 xml_node = new XMLNode ("GUI");
658 _route.add_extra_xml (*xml_node);
664 RouteUI::get_child_xml_node (const string & childname)
671 if ((child = find_named_node (*xml_node, childname)) == 0) {
672 child = new XMLNode (childname);
673 xml_node->add_child_nocopy (*child);
680 RouteUI::set_color_from_route ()
684 RouteUI::ensure_xml_node ();
686 if ((prop = xml_node->property ("color")) != 0) {
688 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
698 RouteUI::remove_this_route ()
700 vector<string> choices;
703 if (is_audio_track()) {
704 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());
706 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route.name());
709 choices.push_back (_("No, do nothing."));
710 choices.push_back (_("Yes, remove it."));
712 Choice prompter (prompt, choices);
714 if (prompter.run () == 1) {
715 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
720 RouteUI::idle_remove_this_route (RouteUI *rui)
722 rui->_session.remove_route (rui->_route);
727 RouteUI::route_removed ()
729 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
735 RouteUI::route_rename ()
737 ArdourPrompter name_prompter (true);
739 name_prompter.set_prompt (_("New Name: "));
740 name_prompter.set_initial_text (_route.name());
741 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
742 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
743 name_prompter.show_all ();
745 switch (name_prompter.run ()) {
747 case Gtk::RESPONSE_ACCEPT:
748 name_prompter.get_result (result);
749 if (result.length()) {
750 _route.set_name (result, this);
760 RouteUI::name_changed (void *src)
762 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
764 name_label.set_text (_route.name());
768 RouteUI::toggle_route_active ()
772 if (route_active_menu_item) {
773 if (route_active_menu_item->get_active() != (yn = _route.active())) {
774 _route.set_active (!yn);
780 RouteUI::route_active_changed ()
782 if (route_active_menu_item) {
783 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route.active()));
788 RouteUI::toggle_polarity ()
790 if (polarity_menu_item) {
794 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
796 if ((x = polarity_menu_item->get_active()) != _route.phase_invert()) {
797 _route.set_phase_invert (x, this);
799 name_label.set_text (X_("Ø ") + name_label.get_text());
801 name_label.set_text (_route.name());
808 RouteUI::polarity_changed ()
810 /* no signal for this yet */
814 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
816 bool yn = _route.solo_safe ();
818 if (check->get_active() != yn) {
819 check->set_active (yn);
823 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
825 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
827 bool yn = _route.get_mute_config(PRE_FADER);
828 if (check->get_active() != yn) {
829 check->set_active (yn);
834 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
836 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
838 bool yn = _route.get_mute_config(POST_FADER);
839 if (check->get_active() != yn) {
840 check->set_active (yn);
845 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
847 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
849 bool yn = _route.get_mute_config(CONTROL_OUTS);
850 if (check->get_active() != yn) {
851 check->set_active (yn);
856 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
858 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
860 bool yn = _route.get_mute_config(MAIN_OUTS);
861 if (check->get_active() != yn) {
862 check->set_active (yn);
867 RouteUI::disconnect_input ()
869 _route.disconnect_inputs (this);
873 RouteUI::disconnect_output ()
875 _route.disconnect_outputs (this);
879 RouteUI::is_audio_track () const
881 return dynamic_cast<AudioTrack*>(&_route) != 0;
885 RouteUI::is_midi_track () const
887 return dynamic_cast<MidiTrack*>(&_route) != 0;
891 RouteUI::get_diskstream () const
896 if ((at = dynamic_cast<AudioTrack*>(&_route)) != 0) {
897 return &at->disk_stream();
898 } else if ((mt = dynamic_cast<MidiTrack*>(&_route)) != 0) {
899 return &mt->disk_stream();
906 RouteUI::audio_track() const
908 return dynamic_cast<AudioTrack*>(&_route);
912 RouteUI::midi_track() const
914 return dynamic_cast<MidiTrack*>(&_route);
918 RouteUI::name() const
920 return _route.name();
924 RouteUI::map_frozen ()
926 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
928 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
931 switch (at->freeze_state()) {
932 case AudioTrack::Frozen:
933 rec_enable_button->set_sensitive (false);
936 rec_enable_button->set_sensitive (true);