2 Copyright (C) 2002 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>
42 using namespace Gtkmm2ext;
43 using namespace ARDOUR;
47 RouteUI::RouteUI (ARDOUR::Route& rt, ARDOUR::Session& sess, const char* m_name,
48 const char* s_name, const char* r_name)
58 remote_control_menu = 0;
59 ignore_toggle = false;
60 wait_for_release = false;
61 route_active_menu_item = 0;
63 if (set_color_from_route()) {
64 set_color (unique_random_color());
67 _route.GoingAway.connect (mem_fun (*this, &RouteUI::route_removed));
68 _route.active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed));
70 mute_button = manage (new BindableToggleButton (& _route.midi_mute_control(), m_name ));
71 mute_button->set_bind_button_state (2, GDK_CONTROL_MASK);
72 solo_button = manage (new BindableToggleButton (& _route.midi_solo_control(), s_name ));
73 solo_button->set_bind_button_state (2, GDK_CONTROL_MASK);
75 if (is_audio_track()) {
76 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
78 get_diskstream()->record_enable_changed.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed));
80 _session.RecordStateChanged.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed));
82 rec_enable_button = manage (new BindableToggleButton (& at->midi_rec_enable_control(), r_name ));
83 rec_enable_button->set_bind_button_state (2, GDK_CONTROL_MASK);
86 rec_enable_button = manage (new BindableToggleButton (0, r_name ));
89 mute_button->unset_flags (Gtk::CAN_FOCUS);
90 solo_button->unset_flags (Gtk::CAN_FOCUS);
91 rec_enable_button->unset_flags (Gtk::CAN_FOCUS);
93 /* map the current state */
95 update_rec_display ();
105 RouteUI::mute_press(GdkEventButton* ev)
107 if (!ignore_toggle) {
109 if (Keyboard::is_context_menu_event (ev)) {
115 mute_menu->popup(0,0);
119 if (ev->button == 2) {
120 // ctrl-button2 click is the midi binding click
121 // button2-click is "momentary"
123 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
124 wait_for_release = true;
128 if (ev->button == 1 || ev->button == 2) {
130 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
132 /* ctrl-shift-click applies change to all routes */
134 _session.begin_reversible_command (_("mute change"));
135 _session.add_undo (_session.global_mute_memento(this));
136 _session.set_all_mute (!_route.muted());
137 _session.add_redo_no_execute (_session.global_mute_memento(this));
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.add_undo (_session.global_solo_memento(this));
211 _session.set_all_solo (!_route.soloed());
212 _session.add_redo_no_execute (_session.global_solo_memento(this));
213 _session.commit_reversible_command ();
215 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
217 // ctrl-alt-click: exclusively solo this track, not a toggle */
219 _session.begin_reversible_command (_("solo change"));
220 _session.add_undo (_session.global_solo_memento(this));
221 _session.set_all_solo (false);
222 _route.set_solo (true, this);
223 _session.add_redo_no_execute (_session.global_solo_memento(this));
224 _session.commit_reversible_command ();
226 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
228 // shift-click: set this route to solo safe
230 _route.set_solo_safe (!_route.solo_safe(), this);
231 wait_for_release = false;
233 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
235 /* ctrl-click: solo mix group.
236 ctrl-button2 is MIDI learn.
239 if (ev->button == 1) {
240 set_mix_group_solo (_route, !_route.soloed());
245 /* click: solo this route */
247 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route.soloed(), this);
257 RouteUI::solo_release(GdkEventButton* ev)
259 if (!ignore_toggle) {
260 if (wait_for_release) {
261 wait_for_release = false;
263 // because the press was the last undoable thing we did
273 RouteUI::rec_enable_press(GdkEventButton* ev)
275 if (!ignore_toggle && is_audio_track()) {
277 if (ev->button == 2 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
278 // do nothing on midi bind event
280 else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
282 _session.begin_reversible_command (_("rec-enable change"));
283 _session.add_undo (_session.global_record_enable_memento(this));
285 if (rec_enable_button->get_active()) {
286 _session.record_disenable_all ();
288 _session.record_enable_all ();
291 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
292 _session.commit_reversible_command ();
294 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
296 set_mix_group_rec_enable (_route, !_route.record_enabled());
300 reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
302 ignore_toggle = true;
303 rec_enable_button->set_active(audio_track()->record_enabled());
304 ignore_toggle = false;
307 stop_signal (*rec_enable_button, "button-press-event");
314 RouteUI::solo_changed(void* src)
316 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
320 RouteUI::update_solo_display ()
324 if (solo_button->get_active() != (x = _route.soloed())){
325 ignore_toggle = true;
326 solo_button->set_active(x);
327 ignore_toggle = false;
332 if (_route.solo_safe()){
333 solo_button->set_name(safe_solo_button_name());
335 solo_button->set_name(solo_button_name());
340 RouteUI::mute_changed(void* src)
342 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
346 RouteUI::update_mute_display ()
350 if (mute_button->get_active() != (x = _route.muted())){
351 ignore_toggle = true;
352 mute_button->set_active(x);
353 ignore_toggle = false;
358 RouteUI::route_rec_enable_changed (void *src)
360 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
364 RouteUI::session_rec_enable_changed ()
366 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
370 RouteUI::update_rec_display ()
372 bool model = _route.record_enabled();
373 bool view = rec_enable_button->get_active();
375 /* first make sure the button's "depressed" visual
380 ignore_toggle = true;
381 rec_enable_button->set_active (model);
382 ignore_toggle = false;
385 /* now make sure its color state is correct */
389 switch (_session.record_status ()) {
390 case Session::Disabled:
391 case Session::Enabled:
392 if (rec_enable_button->get_state() != Gtk::STATE_ACTIVE) {
393 rec_enable_button->set_state (Gtk::STATE_ACTIVE);
397 case Session::Recording:
398 if (rec_enable_button->get_state() != Gtk::STATE_SELECTED) {
399 rec_enable_button->set_state (Gtk::STATE_SELECTED);
405 if (rec_enable_button->get_state() != Gtk::STATE_NORMAL) {
406 rec_enable_button->set_state (Gtk::STATE_NORMAL);
412 RouteUI::build_remote_control_menu ()
414 remote_control_menu = manage (new Menu);
415 refresh_remote_control_menu ();
419 RouteUI::refresh_remote_control_menu ()
421 using namespace Menu_Helpers;
423 RadioMenuItem::Group rc_group;
424 CheckMenuItem* rc_active;
425 uint32_t limit = _session.ntracks();
428 MenuList& rc_items = remote_control_menu->items();
431 /* note that this menu list starts at zero, not 1, because zero
432 is a valid, if useless, ID.
435 limit += 4; /* leave some breathing room */
437 rc_items.push_back (RadioMenuElem (rc_group, _("None")));
438 if (_route.remote_control_id() == 0) {
439 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
440 rc_active->set_active ();
443 for (uint32_t i = 1; i < limit; ++i) {
444 snprintf (buf, sizeof (buf), "%u", i);
445 rc_items.push_back (RadioMenuElem (rc_group, buf));
446 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
447 if (_route.remote_control_id() == i) {
448 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
449 rc_active->set_active ();
451 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
456 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
458 /* this is called when the radio menu item is toggled, and so
459 is actually invoked twice per menu selection. we only
460 care about the invocation for the item that was being
464 if (item->get_active()) {
465 _route.set_remote_control_id (id);
470 RouteUI::build_solo_menu (void)
472 using namespace Menu_Helpers;
474 solo_menu = new Menu;
475 solo_menu->set_name ("ArdourContextMenu");
476 MenuList& items = solo_menu->items();
477 CheckMenuItem* check;
479 check = new CheckMenuItem(_("Solo-safe"));
480 check->set_active (_route.solo_safe());
481 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
482 _route.solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
483 items.push_back (CheckMenuElem(*check));
486 items.push_back (SeparatorElem());
487 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
492 RouteUI::build_mute_menu(void)
494 using namespace Menu_Helpers;
496 mute_menu = new Menu;
497 mute_menu->set_name ("ArdourContextMenu");
498 MenuList& items = mute_menu->items();
499 CheckMenuItem* check;
501 check = new CheckMenuItem(_("Pre Fader"));
502 init_mute_menu(PRE_FADER, check);
503 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
504 _route.pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
505 items.push_back (CheckMenuElem(*check));
508 check = new CheckMenuItem(_("Post Fader"));
509 init_mute_menu(POST_FADER, check);
510 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
511 _route.post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
512 items.push_back (CheckMenuElem(*check));
515 check = new CheckMenuItem(_("Control Outs"));
516 init_mute_menu(CONTROL_OUTS, check);
517 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
518 _route.control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
519 items.push_back (CheckMenuElem(*check));
522 check = new CheckMenuItem(_("Main Outs"));
523 init_mute_menu(MAIN_OUTS, check);
524 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
525 _route.main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
526 items.push_back (CheckMenuElem(*check));
529 items.push_back (SeparatorElem());
530 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
534 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
536 if (_route.get_mute_config (type)) {
537 check->set_active (true);
542 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
544 _route.set_mute_config(type, check->get_active(), this);
548 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
550 _route.set_solo_safe (check->get_active(), this);
554 RouteUI::set_mix_group_solo(Route& route, bool yn)
556 RouteGroup* mix_group;
558 if((mix_group = route.mix_group()) != 0){
559 _session.begin_reversible_command (_("mix group solo change"));
560 _session.add_undo (_session.global_solo_memento (this));
561 mix_group->apply(&Route::set_solo, yn, this);
562 _session.add_redo_no_execute (_session.global_solo_memento(this));
563 _session.commit_reversible_command ();
565 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route.soloed(), this);
570 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
572 _session.begin_reversible_command (name);
573 XMLNode &before = _route.get_state();
574 bind(mem_fun(_route, func), yn, arg)();
575 XMLNode &after = _route.get_state();
576 _session.add_command (MementoCommand<Route>(_route, before, after));
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 XMLNode &before = audio_track()->get_state();
585 bind (mem_fun (*audio_track(), func), yn, arg)();
586 XMLNode &after = audio_track()->get_state();
587 _session.add_command (MementoCommand<AudioTrack>(*audio_track(), before, after));
588 _session.commit_reversible_command ();
592 RouteUI::set_mix_group_mute(Route& route, bool yn)
594 RouteGroup* mix_group;
596 if((mix_group = route.mix_group()) != 0){
597 _session.begin_reversible_command (_("mix group mute change"));
598 _session.add_undo (_session.global_mute_memento (this));
599 mix_group->apply(&Route::set_mute, yn, this);
600 _session.add_redo_no_execute (_session.global_mute_memento(this));
601 _session.commit_reversible_command ();
603 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route.muted(), this);
608 RouteUI::set_mix_group_rec_enable(Route& route, bool yn)
610 RouteGroup* mix_group;
612 if((mix_group = route.mix_group()) != 0){
613 _session.begin_reversible_command (_("mix group rec-enable change"));
614 _session.add_undo (_session.global_record_enable_memento (this));
615 mix_group->apply (&Route::set_record_enable, yn, this);
616 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
617 _session.commit_reversible_command ();
619 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route.record_enabled(), this);
625 RouteUI::choose_color()
630 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
640 RouteUI::set_color (const Gdk::Color & c)
647 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
648 xml_node->add_property ("color", buf);
650 _route.gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
655 RouteUI::ensure_xml_node ()
658 if ((xml_node = _route.extra_xml ("GUI")) == 0) {
659 xml_node = new XMLNode ("GUI");
660 _route.add_extra_xml (*xml_node);
666 RouteUI::get_child_xml_node (const string & childname)
673 if ((child = find_named_node (*xml_node, childname)) == 0) {
674 child = new XMLNode (childname);
675 xml_node->add_child_nocopy (*child);
682 RouteUI::set_color_from_route ()
686 RouteUI::ensure_xml_node ();
688 if ((prop = xml_node->property ("color")) != 0) {
690 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
700 RouteUI::remove_this_route ()
702 vector<string> choices;
705 if (is_audio_track()) {
706 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());
708 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route.name());
711 choices.push_back (_("No, do nothing."));
712 choices.push_back (_("Yes, remove it."));
714 Choice prompter (prompt, choices);
716 if (prompter.run () == 1) {
717 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
722 RouteUI::idle_remove_this_route (RouteUI *rui)
724 rui->_session.remove_route (rui->_route);
729 RouteUI::route_removed ()
731 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
737 RouteUI::route_rename ()
739 ArdourPrompter name_prompter (true);
741 name_prompter.set_prompt (_("New Name: "));
742 name_prompter.set_initial_text (_route.name());
743 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
744 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
745 name_prompter.show_all ();
747 switch (name_prompter.run ()) {
749 case Gtk::RESPONSE_ACCEPT:
750 name_prompter.get_result (result);
751 if (result.length()) {
752 _route.set_name (result, this);
762 RouteUI::name_changed (void *src)
764 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
766 name_label.set_text (_route.name());
770 RouteUI::toggle_route_active ()
774 if (route_active_menu_item) {
775 if (route_active_menu_item->get_active() != (yn = _route.active())) {
776 _route.set_active (!yn);
782 RouteUI::route_active_changed ()
784 if (route_active_menu_item) {
785 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route.active()));
790 RouteUI::toggle_polarity ()
792 if (polarity_menu_item) {
796 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
798 if ((x = polarity_menu_item->get_active()) != _route.phase_invert()) {
799 _route.set_phase_invert (x, this);
801 name_label.set_text (X_("Ø ") + name_label.get_text());
803 name_label.set_text (_route.name());
810 RouteUI::polarity_changed ()
812 /* no signal for this yet */
816 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
818 bool yn = _route.solo_safe ();
820 if (check->get_active() != yn) {
821 check->set_active (yn);
825 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
827 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
829 bool yn = _route.get_mute_config(PRE_FADER);
830 if (check->get_active() != yn) {
831 check->set_active (yn);
836 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
838 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
840 bool yn = _route.get_mute_config(POST_FADER);
841 if (check->get_active() != yn) {
842 check->set_active (yn);
847 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
849 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
851 bool yn = _route.get_mute_config(CONTROL_OUTS);
852 if (check->get_active() != yn) {
853 check->set_active (yn);
858 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
860 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
862 bool yn = _route.get_mute_config(MAIN_OUTS);
863 if (check->get_active() != yn) {
864 check->set_active (yn);
869 RouteUI::disconnect_input ()
871 _route.disconnect_inputs (this);
875 RouteUI::disconnect_output ()
877 _route.disconnect_outputs (this);
881 RouteUI::is_audio_track () const
883 return dynamic_cast<AudioTrack*>(&_route) != 0;
887 RouteUI::get_diskstream () const
891 if ((at = dynamic_cast<AudioTrack*>(&_route)) != 0) {
892 return &at->disk_stream();
899 RouteUI::audio_track() const
901 return dynamic_cast<AudioTrack*>(&_route);
904 RouteUI::name() const
906 return _route.name();
910 RouteUI::map_frozen ()
912 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
914 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
917 switch (at->freeze_state()) {
918 case AudioTrack::Frozen:
919 rec_enable_button->set_sensitive (false);
922 rec_enable_button->set_sensitive (true);