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>
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>
43 using namespace Gtkmm2ext;
44 using namespace ARDOUR;
48 RouteUI::RouteUI (ARDOUR::Route& rt, ARDOUR::Session& sess, const char* m_name,
49 const char* s_name, const char* r_name)
59 remote_control_menu = 0;
60 ignore_toggle = false;
61 wait_for_release = false;
62 route_active_menu_item = 0;
64 if (set_color_from_route()) {
65 set_color (unique_random_color());
68 _route.GoingAway.connect (mem_fun (*this, &RouteUI::route_removed));
69 _route.active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed));
71 mute_button = manage (new BindableToggleButton (& _route.midi_mute_control(), m_name ));
72 mute_button->set_bind_button_state (2, GDK_CONTROL_MASK);
73 solo_button = manage (new BindableToggleButton (& _route.midi_solo_control(), s_name ));
74 solo_button->set_bind_button_state (2, GDK_CONTROL_MASK);
76 if (is_audio_track()) {
77 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
79 get_diskstream()->record_enable_changed.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 (& at->midi_rec_enable_control(), r_name ));
84 rec_enable_button->set_bind_button_state (2, GDK_CONTROL_MASK);
87 rec_enable_button = manage (new BindableToggleButton (0, r_name ));
90 mute_button->unset_flags (Gtk::CAN_FOCUS);
91 solo_button->unset_flags (Gtk::CAN_FOCUS);
92 rec_enable_button->unset_flags (Gtk::CAN_FOCUS);
94 /* map the current state */
96 update_rec_display ();
106 RouteUI::mute_press(GdkEventButton* ev)
108 if (!ignore_toggle) {
110 if (Keyboard::is_context_menu_event (ev)) {
116 mute_menu->popup(0,0);
120 if (ev->button == 2) {
121 // ctrl-button2 click is the midi binding click
122 // button2-click is "momentary"
124 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
125 wait_for_release = true;
129 if (ev->button == 1 || ev->button == 2) {
131 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
133 /* ctrl-shift-click applies change to all routes */
135 _session.begin_reversible_command (_("mute change"));
136 Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(this);
137 _session.set_all_mute (!_route.muted());
139 _session.add_command(cmd);
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::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(this);
213 _session.set_all_solo (!_route.soloed());
215 _session.add_command (cmd);
216 _session.commit_reversible_command ();
218 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
220 // ctrl-alt-click: exclusively solo this track, not a toggle */
222 _session.begin_reversible_command (_("solo change"));
223 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (this);
224 _session.set_all_solo (false);
225 _route.set_solo (true, this);
227 _session.add_command(cmd);
228 _session.commit_reversible_command ();
230 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
232 // shift-click: set this route to solo safe
234 _route.set_solo_safe (!_route.solo_safe(), this);
235 wait_for_release = false;
237 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
239 /* ctrl-click: solo mix group.
240 ctrl-button2 is MIDI learn.
243 if (ev->button == 1) {
244 set_mix_group_solo (_route, !_route.soloed());
249 /* click: solo this route */
251 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route.soloed(), this);
261 RouteUI::solo_release(GdkEventButton* ev)
263 if (!ignore_toggle) {
264 if (wait_for_release) {
265 wait_for_release = false;
267 // because the press was the last undoable thing we did
277 RouteUI::rec_enable_press(GdkEventButton* ev)
279 if (!ignore_toggle && is_audio_track()) {
281 if (ev->button == 2 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
282 // do nothing on midi bind event
284 else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
286 _session.begin_reversible_command (_("rec-enable change"));
287 Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(this);
289 if (rec_enable_button->get_active()) {
290 _session.record_disenable_all ();
292 _session.record_enable_all ();
296 _session.add_command(cmd);
297 _session.commit_reversible_command ();
299 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
301 set_mix_group_rec_enable (_route, !_route.record_enabled());
305 reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
307 ignore_toggle = true;
308 rec_enable_button->set_active(audio_track()->record_enabled());
309 ignore_toggle = false;
312 stop_signal (*rec_enable_button, "button-press-event");
319 RouteUI::solo_changed(void* src)
321 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
325 RouteUI::update_solo_display ()
329 if (solo_button->get_active() != (x = _route.soloed())){
330 ignore_toggle = true;
331 solo_button->set_active(x);
332 ignore_toggle = false;
337 if (_route.solo_safe()){
338 solo_button->set_name(safe_solo_button_name());
340 solo_button->set_name(solo_button_name());
345 RouteUI::mute_changed(void* src)
347 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
351 RouteUI::update_mute_display ()
355 if (mute_button->get_active() != (x = _route.muted())){
356 ignore_toggle = true;
357 mute_button->set_active(x);
358 ignore_toggle = false;
363 RouteUI::route_rec_enable_changed (void *src)
365 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
369 RouteUI::session_rec_enable_changed ()
371 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
375 RouteUI::update_rec_display ()
377 bool model = _route.record_enabled();
378 bool view = rec_enable_button->get_active();
380 /* first make sure the button's "depressed" visual
385 ignore_toggle = true;
386 rec_enable_button->set_active (model);
387 ignore_toggle = false;
390 /* now make sure its color state is correct */
394 switch (_session.record_status ()) {
395 case Session::Disabled:
396 case Session::Enabled:
397 if (rec_enable_button->get_state() != Gtk::STATE_ACTIVE) {
398 rec_enable_button->set_state (Gtk::STATE_ACTIVE);
402 case Session::Recording:
403 if (rec_enable_button->get_state() != Gtk::STATE_SELECTED) {
404 rec_enable_button->set_state (Gtk::STATE_SELECTED);
410 if (rec_enable_button->get_state() != Gtk::STATE_NORMAL) {
411 rec_enable_button->set_state (Gtk::STATE_NORMAL);
417 RouteUI::build_remote_control_menu ()
419 remote_control_menu = manage (new Menu);
420 refresh_remote_control_menu ();
424 RouteUI::refresh_remote_control_menu ()
426 using namespace Menu_Helpers;
428 RadioMenuItem::Group rc_group;
429 CheckMenuItem* rc_active;
430 uint32_t limit = _session.ntracks();
433 MenuList& rc_items = remote_control_menu->items();
436 /* note that this menu list starts at zero, not 1, because zero
437 is a valid, if useless, ID.
440 limit += 4; /* leave some breathing room */
442 rc_items.push_back (RadioMenuElem (rc_group, _("None")));
443 if (_route.remote_control_id() == 0) {
444 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
445 rc_active->set_active ();
448 for (uint32_t i = 1; i < limit; ++i) {
449 snprintf (buf, sizeof (buf), "%u", i);
450 rc_items.push_back (RadioMenuElem (rc_group, buf));
451 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
452 if (_route.remote_control_id() == i) {
453 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
454 rc_active->set_active ();
456 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
461 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
463 /* this is called when the radio menu item is toggled, and so
464 is actually invoked twice per menu selection. we only
465 care about the invocation for the item that was being
469 if (item->get_active()) {
470 _route.set_remote_control_id (id);
475 RouteUI::build_solo_menu (void)
477 using namespace Menu_Helpers;
479 solo_menu = new Menu;
480 solo_menu->set_name ("ArdourContextMenu");
481 MenuList& items = solo_menu->items();
482 CheckMenuItem* check;
484 check = new CheckMenuItem(_("Solo-safe"));
485 check->set_active (_route.solo_safe());
486 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
487 _route.solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
488 items.push_back (CheckMenuElem(*check));
491 items.push_back (SeparatorElem());
492 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
497 RouteUI::build_mute_menu(void)
499 using namespace Menu_Helpers;
501 mute_menu = new Menu;
502 mute_menu->set_name ("ArdourContextMenu");
503 MenuList& items = mute_menu->items();
504 CheckMenuItem* check;
506 check = new CheckMenuItem(_("Pre Fader"));
507 init_mute_menu(PRE_FADER, check);
508 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
509 _route.pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
510 items.push_back (CheckMenuElem(*check));
513 check = new CheckMenuItem(_("Post Fader"));
514 init_mute_menu(POST_FADER, check);
515 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
516 _route.post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
517 items.push_back (CheckMenuElem(*check));
520 check = new CheckMenuItem(_("Control Outs"));
521 init_mute_menu(CONTROL_OUTS, check);
522 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
523 _route.control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
524 items.push_back (CheckMenuElem(*check));
527 check = new CheckMenuItem(_("Main Outs"));
528 init_mute_menu(MAIN_OUTS, check);
529 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
530 _route.main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
531 items.push_back (CheckMenuElem(*check));
534 items.push_back (SeparatorElem());
535 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
539 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
541 if (_route.get_mute_config (type)) {
542 check->set_active (true);
547 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
549 _route.set_mute_config(type, check->get_active(), this);
553 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
555 _route.set_solo_safe (check->get_active(), this);
559 RouteUI::set_mix_group_solo(Route& route, bool yn)
561 RouteGroup* mix_group;
563 if((mix_group = route.mix_group()) != 0){
564 _session.begin_reversible_command (_("mix group solo change"));
565 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(this);
566 mix_group->apply(&Route::set_solo, yn, this);
568 _session.add_command (cmd);
569 _session.commit_reversible_command ();
571 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route.soloed(), this);
576 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
578 _session.begin_reversible_command (name);
579 XMLNode &before = _route.get_state();
580 bind(mem_fun(_route, func), yn, arg)();
581 XMLNode &after = _route.get_state();
582 _session.add_command (new MementoCommand<Route>(_route, before, after));
583 _session.commit_reversible_command ();
587 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
589 _session.begin_reversible_command (name);
590 XMLNode &before = audio_track()->get_state();
591 bind (mem_fun (*audio_track(), func), yn, arg)();
592 XMLNode &after = audio_track()->get_state();
593 _session.add_command (new MementoCommand<AudioTrack>(*audio_track(), before, after));
594 _session.commit_reversible_command ();
598 RouteUI::set_mix_group_mute(Route& route, bool yn)
600 RouteGroup* mix_group;
602 if((mix_group = route.mix_group()) != 0){
603 _session.begin_reversible_command (_("mix group mute change"));
604 Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand (this);
605 mix_group->apply(&Route::set_mute, yn, this);
607 _session.add_command(cmd);
608 _session.commit_reversible_command ();
610 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route.muted(), this);
615 RouteUI::set_mix_group_rec_enable(Route& route, bool yn)
617 RouteGroup* mix_group;
619 if((mix_group = route.mix_group()) != 0){
620 _session.begin_reversible_command (_("mix group rec-enable change"));
621 Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(this);
622 mix_group->apply (&Route::set_record_enable, yn, this);
624 _session.add_command(cmd);
625 _session.commit_reversible_command ();
627 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route.record_enabled(), this);
633 RouteUI::choose_color()
638 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
648 RouteUI::set_color (const Gdk::Color & c)
655 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
656 xml_node->add_property ("color", buf);
658 _route.gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
663 RouteUI::ensure_xml_node ()
666 if ((xml_node = _route.extra_xml ("GUI")) == 0) {
667 xml_node = new XMLNode ("GUI");
668 _route.add_extra_xml (*xml_node);
674 RouteUI::get_child_xml_node (const string & childname)
681 if ((child = find_named_node (*xml_node, childname)) == 0) {
682 child = new XMLNode (childname);
683 xml_node->add_child_nocopy (*child);
690 RouteUI::set_color_from_route ()
694 RouteUI::ensure_xml_node ();
696 if ((prop = xml_node->property ("color")) != 0) {
698 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
708 RouteUI::remove_this_route ()
710 vector<string> choices;
713 if (is_audio_track()) {
714 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());
716 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route.name());
719 choices.push_back (_("No, do nothing."));
720 choices.push_back (_("Yes, remove it."));
722 Choice prompter (prompt, choices);
724 if (prompter.run () == 1) {
725 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
730 RouteUI::idle_remove_this_route (RouteUI *rui)
732 rui->_session.remove_route (rui->_route);
737 RouteUI::route_removed ()
739 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
745 RouteUI::route_rename ()
747 ArdourPrompter name_prompter (true);
749 name_prompter.set_prompt (_("New Name: "));
750 name_prompter.set_initial_text (_route.name());
751 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
752 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
753 name_prompter.show_all ();
755 switch (name_prompter.run ()) {
757 case Gtk::RESPONSE_ACCEPT:
758 name_prompter.get_result (result);
759 if (result.length()) {
760 _route.set_name (result, this);
770 RouteUI::name_changed (void *src)
772 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
774 name_label.set_text (_route.name());
778 RouteUI::toggle_route_active ()
782 if (route_active_menu_item) {
783 if (route_active_menu_item->get_active() != (yn = _route.active())) {
784 _route.set_active (!yn);
790 RouteUI::route_active_changed ()
792 if (route_active_menu_item) {
793 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route.active()));
798 RouteUI::toggle_polarity ()
800 if (polarity_menu_item) {
804 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
806 if ((x = polarity_menu_item->get_active()) != _route.phase_invert()) {
807 _route.set_phase_invert (x, this);
809 name_label.set_text (X_("Ø ") + name_label.get_text());
811 name_label.set_text (_route.name());
818 RouteUI::polarity_changed ()
820 /* no signal for this yet */
824 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
826 bool yn = _route.solo_safe ();
828 if (check->get_active() != yn) {
829 check->set_active (yn);
833 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
835 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
837 bool yn = _route.get_mute_config(PRE_FADER);
838 if (check->get_active() != yn) {
839 check->set_active (yn);
844 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
846 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
848 bool yn = _route.get_mute_config(POST_FADER);
849 if (check->get_active() != yn) {
850 check->set_active (yn);
855 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
857 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
859 bool yn = _route.get_mute_config(CONTROL_OUTS);
860 if (check->get_active() != yn) {
861 check->set_active (yn);
866 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
868 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
870 bool yn = _route.get_mute_config(MAIN_OUTS);
871 if (check->get_active() != yn) {
872 check->set_active (yn);
877 RouteUI::disconnect_input ()
879 _route.disconnect_inputs (this);
883 RouteUI::disconnect_output ()
885 _route.disconnect_outputs (this);
889 RouteUI::is_audio_track () const
891 return dynamic_cast<AudioTrack*>(&_route) != 0;
895 RouteUI::get_diskstream () const
899 if ((at = dynamic_cast<AudioTrack*>(&_route)) != 0) {
900 return &at->disk_stream();
907 RouteUI::audio_track() const
909 return dynamic_cast<AudioTrack*>(&_route);
912 RouteUI::name() const
914 return _route.name();
918 RouteUI::map_frozen ()
920 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
922 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
925 switch (at->freeze_state()) {
926 case AudioTrack::Frozen:
927 rec_enable_button->set_sensitive (false);
930 rec_enable_button->set_sensitive (true);