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 _session.add_undo (bind (mem_fun (_route, func), !yn, (void *) arg));
574 _session.add_redo (bind (mem_fun (_route, func), yn, (void *) arg));
575 _session.commit_reversible_command ();
579 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
581 _session.begin_reversible_command (name);
582 _session.add_undo (bind (mem_fun (*audio_track(), func), !yn, (void *) arg));
583 _session.add_redo (bind (mem_fun (*audio_track(), func), yn, (void *) arg));
584 _session.commit_reversible_command ();
588 RouteUI::set_mix_group_mute(Route& route, bool yn)
590 RouteGroup* mix_group;
592 if((mix_group = route.mix_group()) != 0){
593 _session.begin_reversible_command (_("mix group mute change"));
594 _session.add_undo (_session.global_mute_memento (this));
595 mix_group->apply(&Route::set_mute, yn, this);
596 _session.add_redo_no_execute (_session.global_mute_memento(this));
597 _session.commit_reversible_command ();
599 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route.muted(), this);
604 RouteUI::set_mix_group_rec_enable(Route& route, bool yn)
606 RouteGroup* mix_group;
608 if((mix_group = route.mix_group()) != 0){
609 _session.begin_reversible_command (_("mix group rec-enable change"));
610 _session.add_undo (_session.global_record_enable_memento (this));
611 mix_group->apply (&Route::set_record_enable, yn, this);
612 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
613 _session.commit_reversible_command ();
615 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route.record_enabled(), this);
621 RouteUI::choose_color()
626 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
636 RouteUI::set_color (const Gdk::Color & c)
643 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
644 xml_node->add_property ("color", buf);
646 _route.gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
651 RouteUI::ensure_xml_node ()
654 if ((xml_node = _route.extra_xml ("GUI")) == 0) {
655 xml_node = new XMLNode ("GUI");
656 _route.add_extra_xml (*xml_node);
662 RouteUI::get_child_xml_node (const string & childname)
669 if ((child = find_named_node (*xml_node, childname)) == 0) {
670 child = new XMLNode (childname);
671 xml_node->add_child_nocopy (*child);
678 RouteUI::set_color_from_route ()
682 RouteUI::ensure_xml_node ();
684 if ((prop = xml_node->property ("color")) != 0) {
686 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
696 RouteUI::remove_this_route ()
698 vector<string> choices;
701 if (is_audio_track()) {
702 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());
704 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route.name());
707 choices.push_back (_("No, do nothing."));
708 choices.push_back (_("Yes, remove it."));
710 Choice prompter (prompt, choices);
712 if (prompter.run () == 1) {
713 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
718 RouteUI::idle_remove_this_route (RouteUI *rui)
720 rui->_session.remove_route (rui->_route);
725 RouteUI::route_removed ()
727 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
733 RouteUI::route_rename ()
735 ArdourPrompter name_prompter (true);
737 name_prompter.set_prompt (_("New Name: "));
738 name_prompter.set_initial_text (_route.name());
739 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
740 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
741 name_prompter.show_all ();
743 switch (name_prompter.run ()) {
745 case Gtk::RESPONSE_ACCEPT:
746 name_prompter.get_result (result);
747 if (result.length()) {
748 _route.set_name (result, this);
758 RouteUI::name_changed (void *src)
760 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
762 name_label.set_text (_route.name());
766 RouteUI::toggle_route_active ()
770 if (route_active_menu_item) {
771 if (route_active_menu_item->get_active() != (yn = _route.active())) {
772 _route.set_active (!yn);
778 RouteUI::route_active_changed ()
780 if (route_active_menu_item) {
781 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route.active()));
786 RouteUI::toggle_polarity ()
788 if (polarity_menu_item) {
792 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
794 if ((x = polarity_menu_item->get_active()) != _route.phase_invert()) {
795 _route.set_phase_invert (x, this);
797 name_label.set_text (X_("Ø ") + name_label.get_text());
799 name_label.set_text (_route.name());
806 RouteUI::polarity_changed ()
808 /* no signal for this yet */
812 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
814 bool yn = _route.solo_safe ();
816 if (check->get_active() != yn) {
817 check->set_active (yn);
821 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
823 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
825 bool yn = _route.get_mute_config(PRE_FADER);
826 if (check->get_active() != yn) {
827 check->set_active (yn);
832 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
834 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
836 bool yn = _route.get_mute_config(POST_FADER);
837 if (check->get_active() != yn) {
838 check->set_active (yn);
843 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
845 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
847 bool yn = _route.get_mute_config(CONTROL_OUTS);
848 if (check->get_active() != yn) {
849 check->set_active (yn);
854 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
856 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
858 bool yn = _route.get_mute_config(MAIN_OUTS);
859 if (check->get_active() != yn) {
860 check->set_active (yn);
865 RouteUI::disconnect_input ()
867 _route.disconnect_inputs (this);
871 RouteUI::disconnect_output ()
873 _route.disconnect_outputs (this);
877 RouteUI::is_audio_track () const
879 return dynamic_cast<AudioTrack*>(&_route) != 0;
883 RouteUI::get_diskstream () const
887 if ((at = dynamic_cast<AudioTrack*>(&_route)) != 0) {
888 return &at->disk_stream();
895 RouteUI::audio_track() const
897 return dynamic_cast<AudioTrack*>(&_route);
900 RouteUI::name() const
902 return _route.name();
906 RouteUI::map_frozen ()
908 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
910 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
913 switch (at->freeze_state()) {
914 case AudioTrack::Frozen:
915 rec_enable_button->set_sensitive (false);
918 rec_enable_button->set_sensitive (true);