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;
46 RouteUI::RouteUI (ARDOUR::Route& rt, ARDOUR::Session& sess, const char* m_name,
47 const char* s_name, const char* r_name)
57 remote_control_menu = 0;
58 ignore_toggle = false;
59 wait_for_release = false;
60 route_active_menu_item = 0;
62 if (set_color_from_route()) {
63 set_color (unique_random_color());
66 _route.GoingAway.connect (mem_fun (*this, &RouteUI::route_removed));
67 _route.active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed));
69 mute_button = manage (new BindableToggleButton (& _route.midi_mute_control(), m_name ));
70 mute_button->set_bind_button_state (2, GDK_CONTROL_MASK);
71 solo_button = manage (new BindableToggleButton (& _route.midi_solo_control(), s_name ));
72 solo_button->set_bind_button_state (2, GDK_CONTROL_MASK);
74 if (is_audio_track()) {
75 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
77 get_diskstream()->record_enable_changed.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed));
79 _session.RecordStateChanged.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed));
81 rec_enable_button = manage (new BindableToggleButton (& at->midi_rec_enable_control(), r_name ));
82 rec_enable_button->set_bind_button_state (2, GDK_CONTROL_MASK);
85 rec_enable_button = manage (new BindableToggleButton (0, r_name ));
88 mute_button->unset_flags (Gtk::CAN_FOCUS);
89 solo_button->unset_flags (Gtk::CAN_FOCUS);
90 rec_enable_button->unset_flags (Gtk::CAN_FOCUS);
92 /* map the current state */
94 update_rec_display ();
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.add_undo (_session.global_mute_memento(this));
135 _session.set_all_mute (!_route.muted());
136 _session.add_redo_no_execute (_session.global_mute_memento(this));
137 _session.commit_reversible_command ();
139 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
141 /* ctrl-click applies change to the mix group.
142 ctrl-button2 is MIDI learn.
145 if (ev->button == 1) {
146 set_mix_group_mute (_route, !_route.muted());
151 /* plain click applies change to this route */
153 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route.muted(), this);
164 RouteUI::mute_release(GdkEventButton* ev)
166 if (!ignore_toggle) {
167 if (wait_for_release){
168 wait_for_release = false;
170 // because the press was the last undoable thing we did
178 RouteUI::solo_press(GdkEventButton* ev)
180 if (!ignore_toggle) {
182 if (Keyboard::is_context_menu_event (ev)) {
184 if (solo_menu == 0) {
188 solo_menu->popup (1, 0);
192 if (ev->button == 2) {
194 // ctrl-button2 click is the midi binding click
195 // button2-click is "momentary"
197 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
198 wait_for_release = true;
202 if (ev->button == 1 || ev->button == 2) {
204 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
206 /* ctrl-shift-click applies change to all routes */
208 _session.begin_reversible_command (_("solo change"));
209 _session.add_undo (_session.global_solo_memento(this));
210 _session.set_all_solo (!_route.soloed());
211 _session.add_redo_no_execute (_session.global_solo_memento(this));
212 _session.commit_reversible_command ();
214 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
216 // ctrl-alt-click: exclusively solo this track, not a toggle */
218 _session.begin_reversible_command (_("solo change"));
219 _session.add_undo (_session.global_solo_memento(this));
220 _session.set_all_solo (false);
221 _route.set_solo (true, this);
222 _session.add_redo_no_execute (_session.global_solo_memento(this));
223 _session.commit_reversible_command ();
225 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
227 // shift-click: set this route to solo safe
229 _route.set_solo_safe (!_route.solo_safe(), this);
230 wait_for_release = false;
232 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
234 /* ctrl-click: solo mix group.
235 ctrl-button2 is MIDI learn.
238 if (ev->button == 1) {
239 set_mix_group_solo (_route, !_route.soloed());
244 /* click: solo this route */
246 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route.soloed(), this);
256 RouteUI::solo_release(GdkEventButton* ev)
258 if (!ignore_toggle) {
259 if (wait_for_release) {
260 wait_for_release = false;
262 // because the press was the last undoable thing we did
272 RouteUI::rec_enable_press(GdkEventButton* ev)
274 if (!ignore_toggle && is_audio_track()) {
276 if (ev->button == 2 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
277 // do nothing on midi bind event
279 else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
281 _session.begin_reversible_command (_("rec-enable change"));
282 _session.add_undo (_session.global_record_enable_memento(this));
284 if (rec_enable_button->get_active()) {
285 _session.record_disenable_all ();
287 _session.record_enable_all ();
290 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
291 _session.commit_reversible_command ();
293 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
295 set_mix_group_rec_enable (_route, !_route.record_enabled());
299 reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
301 ignore_toggle = true;
302 rec_enable_button->set_active(audio_track()->record_enabled());
303 ignore_toggle = false;
306 stop_signal (*rec_enable_button, "button-press-event");
313 RouteUI::solo_changed(void* src)
315 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
319 RouteUI::update_solo_display ()
323 if (solo_button->get_active() != (x = _route.soloed())){
324 ignore_toggle = true;
325 solo_button->set_active(x);
326 ignore_toggle = false;
331 if (_route.solo_safe()){
332 solo_button->set_name(safe_solo_button_name());
334 solo_button->set_name(solo_button_name());
339 RouteUI::mute_changed(void* src)
341 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
345 RouteUI::update_mute_display ()
349 if (mute_button->get_active() != (x = _route.muted())){
350 ignore_toggle = true;
351 mute_button->set_active(x);
352 ignore_toggle = false;
357 RouteUI::route_rec_enable_changed (void *src)
359 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
363 RouteUI::session_rec_enable_changed ()
365 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
369 RouteUI::update_rec_display ()
371 bool model = _route.record_enabled();
372 bool view = rec_enable_button->get_active();
374 /* first make sure the button's "depressed" visual
379 ignore_toggle = true;
380 rec_enable_button->set_active (model);
381 ignore_toggle = false;
384 /* now make sure its color state is correct */
388 switch (_session.record_status ()) {
389 case Session::Disabled:
390 case Session::Enabled:
391 if (rec_enable_button->get_state() != Gtk::STATE_ACTIVE) {
392 rec_enable_button->set_state (Gtk::STATE_ACTIVE);
396 case Session::Recording:
397 if (rec_enable_button->get_state() != Gtk::STATE_SELECTED) {
398 rec_enable_button->set_state (Gtk::STATE_SELECTED);
404 if (rec_enable_button->get_state() != Gtk::STATE_NORMAL) {
405 rec_enable_button->set_state (Gtk::STATE_NORMAL);
411 RouteUI::build_remote_control_menu ()
413 remote_control_menu = manage (new Menu);
414 refresh_remote_control_menu ();
418 RouteUI::refresh_remote_control_menu ()
420 using namespace Menu_Helpers;
422 RadioMenuItem::Group rc_group;
423 CheckMenuItem* rc_active;
424 uint32_t limit = _session.ntracks();
427 MenuList& rc_items = remote_control_menu->items();
430 /* note that this menu list starts at zero, not 1, because zero
431 is a valid, if useless, ID.
434 limit += 4; /* leave some breathing room */
436 rc_items.push_back (RadioMenuElem (rc_group, _("None")));
437 if (_route.remote_control_id() == 0) {
438 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
439 rc_active->set_active ();
442 for (uint32_t i = 1; i < limit; ++i) {
443 snprintf (buf, sizeof (buf), "%u", i);
444 rc_items.push_back (RadioMenuElem (rc_group, buf));
445 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
446 if (_route.remote_control_id() == i) {
447 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
448 rc_active->set_active ();
450 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
455 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
457 /* this is called when the radio menu item is toggled, and so
458 is actually invoked twice per menu selection. we only
459 care about the invocation for the item that was being
463 if (item->get_active()) {
464 _route.set_remote_control_id (id);
469 RouteUI::build_solo_menu (void)
471 using namespace Menu_Helpers;
473 solo_menu = new Menu;
474 solo_menu->set_name ("ArdourContextMenu");
475 MenuList& items = solo_menu->items();
476 CheckMenuItem* check;
478 check = new CheckMenuItem(_("Solo-safe"));
479 check->set_active (_route.solo_safe());
480 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
481 _route.solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
482 items.push_back (CheckMenuElem(*check));
485 items.push_back (SeparatorElem());
486 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
491 RouteUI::build_mute_menu(void)
493 using namespace Menu_Helpers;
495 mute_menu = new Menu;
496 mute_menu->set_name ("ArdourContextMenu");
497 MenuList& items = mute_menu->items();
498 CheckMenuItem* check;
500 check = new CheckMenuItem(_("Pre Fader"));
501 init_mute_menu(PRE_FADER, check);
502 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
503 _route.pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
504 items.push_back (CheckMenuElem(*check));
507 check = new CheckMenuItem(_("Post Fader"));
508 init_mute_menu(POST_FADER, check);
509 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
510 _route.post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
511 items.push_back (CheckMenuElem(*check));
514 check = new CheckMenuItem(_("Control Outs"));
515 init_mute_menu(CONTROL_OUTS, check);
516 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
517 _route.control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
518 items.push_back (CheckMenuElem(*check));
521 check = new CheckMenuItem(_("Main Outs"));
522 init_mute_menu(MAIN_OUTS, check);
523 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
524 _route.main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
525 items.push_back (CheckMenuElem(*check));
528 items.push_back (SeparatorElem());
529 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
533 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
535 if (_route.get_mute_config (type)) {
536 check->set_active (true);
541 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
543 _route.set_mute_config(type, check->get_active(), this);
547 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
549 _route.set_solo_safe (check->get_active(), this);
553 RouteUI::set_mix_group_solo(Route& route, bool yn)
555 RouteGroup* mix_group;
557 if((mix_group = route.mix_group()) != 0){
558 _session.begin_reversible_command (_("mix group solo change"));
559 _session.add_undo (_session.global_solo_memento (this));
560 mix_group->apply(&Route::set_solo, yn, this);
561 _session.add_redo_no_execute (_session.global_solo_memento(this));
562 _session.commit_reversible_command ();
564 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route.soloed(), this);
569 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
571 _session.begin_reversible_command (name);
572 _session.add_undo (bind (mem_fun (_route, func), !yn, (void *) arg));
573 _session.add_redo (bind (mem_fun (_route, func), yn, (void *) arg));
574 _session.commit_reversible_command ();
578 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
580 _session.begin_reversible_command (name);
581 _session.add_undo (bind (mem_fun (*audio_track(), func), !yn, (void *) arg));
582 _session.add_redo (bind (mem_fun (*audio_track(), func), yn, (void *) arg));
583 _session.commit_reversible_command ();
587 RouteUI::set_mix_group_mute(Route& route, bool yn)
589 RouteGroup* mix_group;
591 if((mix_group = route.mix_group()) != 0){
592 _session.begin_reversible_command (_("mix group mute change"));
593 _session.add_undo (_session.global_mute_memento (this));
594 mix_group->apply(&Route::set_mute, yn, this);
595 _session.add_redo_no_execute (_session.global_mute_memento(this));
596 _session.commit_reversible_command ();
598 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route.muted(), this);
603 RouteUI::set_mix_group_rec_enable(Route& route, bool yn)
605 RouteGroup* mix_group;
607 if((mix_group = route.mix_group()) != 0){
608 _session.begin_reversible_command (_("mix group rec-enable change"));
609 _session.add_undo (_session.global_record_enable_memento (this));
610 mix_group->apply (&Route::set_record_enable, yn, this);
611 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
612 _session.commit_reversible_command ();
614 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route.record_enabled(), this);
620 RouteUI::choose_color()
625 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
635 RouteUI::set_color (const Gdk::Color & c)
642 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
643 xml_node->add_property ("color", buf);
645 _route.gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
650 RouteUI::ensure_xml_node ()
653 if ((xml_node = _route.extra_xml ("GUI")) == 0) {
654 xml_node = new XMLNode ("GUI");
655 _route.add_extra_xml (*xml_node);
661 RouteUI::get_child_xml_node (const string & childname)
668 if ((child = find_named_node (*xml_node, childname)) == 0) {
669 child = new XMLNode (childname);
670 xml_node->add_child_nocopy (*child);
677 RouteUI::set_color_from_route ()
681 RouteUI::ensure_xml_node ();
683 if ((prop = xml_node->property ("color")) != 0) {
685 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
695 RouteUI::remove_this_route ()
697 vector<string> choices;
700 if (is_audio_track()) {
701 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());
703 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route.name());
706 choices.push_back (_("No, do nothing."));
707 choices.push_back (_("Yes, remove it."));
709 Choice prompter (prompt, choices);
711 if (prompter.run () == 1) {
712 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
717 RouteUI::idle_remove_this_route (RouteUI *rui)
719 rui->_session.remove_route (rui->_route);
724 RouteUI::route_removed ()
726 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
732 RouteUI::route_rename ()
734 ArdourPrompter name_prompter (true);
736 name_prompter.set_prompt (_("New Name: "));
737 name_prompter.set_initial_text (_route.name());
738 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
739 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
740 name_prompter.show_all ();
742 switch (name_prompter.run ()) {
744 case Gtk::RESPONSE_ACCEPT:
745 name_prompter.get_result (result);
746 if (result.length()) {
747 _route.set_name (result, this);
757 RouteUI::name_changed (void *src)
759 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
761 name_label.set_text (_route.name());
765 RouteUI::toggle_route_active ()
769 if (route_active_menu_item) {
770 if (route_active_menu_item->get_active() != (yn = _route.active())) {
771 _route.set_active (!yn);
777 RouteUI::route_active_changed ()
779 if (route_active_menu_item) {
780 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route.active()));
785 RouteUI::toggle_polarity ()
787 if (polarity_menu_item) {
791 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
793 if ((x = polarity_menu_item->get_active()) != _route.phase_invert()) {
794 _route.set_phase_invert (x, this);
796 name_label.set_text (X_("Ø ") + name_label.get_text());
798 name_label.set_text (_route.name());
805 RouteUI::polarity_changed ()
807 /* no signal for this yet */
811 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
813 bool yn = _route.solo_safe ();
815 if (check->get_active() != yn) {
816 check->set_active (yn);
820 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
822 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
824 bool yn = _route.get_mute_config(PRE_FADER);
825 if (check->get_active() != yn) {
826 check->set_active (yn);
831 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
833 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
835 bool yn = _route.get_mute_config(POST_FADER);
836 if (check->get_active() != yn) {
837 check->set_active (yn);
842 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
844 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
846 bool yn = _route.get_mute_config(CONTROL_OUTS);
847 if (check->get_active() != yn) {
848 check->set_active (yn);
853 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
855 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
857 bool yn = _route.get_mute_config(MAIN_OUTS);
858 if (check->get_active() != yn) {
859 check->set_active (yn);
864 RouteUI::disconnect_input ()
866 _route.disconnect_inputs (this);
870 RouteUI::disconnect_output ()
872 _route.disconnect_outputs (this);
876 RouteUI::is_audio_track () const
878 return dynamic_cast<AudioTrack*>(&_route) != 0;
882 RouteUI::get_diskstream () const
886 if ((at = dynamic_cast<AudioTrack*>(&_route)) != 0) {
887 return &at->disk_stream();
894 RouteUI::audio_track() const
896 return dynamic_cast<AudioTrack*>(&_route);
899 RouteUI::name() const
901 return _route.name();
905 RouteUI::map_frozen ()
907 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
909 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
912 switch (at->freeze_state()) {
913 case AudioTrack::Frozen:
914 rec_enable_button->set_sensitive (false);
917 rec_enable_button->set_sensitive (true);