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::GlobalMuteStateCommand cmd(this);
136 _session.set_all_mute (!_route.muted());
138 _session.add_command(cmd);
139 _session.commit_reversible_command ();
141 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
143 /* ctrl-click applies change to the mix group.
144 ctrl-button2 is MIDI learn.
147 if (ev->button == 1) {
148 set_mix_group_mute (_route, !_route.muted());
153 /* plain click applies change to this route */
155 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route.muted(), this);
166 RouteUI::mute_release(GdkEventButton* ev)
168 if (!ignore_toggle) {
169 if (wait_for_release){
170 wait_for_release = false;
172 // because the press was the last undoable thing we did
180 RouteUI::solo_press(GdkEventButton* ev)
182 if (!ignore_toggle) {
184 if (Keyboard::is_context_menu_event (ev)) {
186 if (solo_menu == 0) {
190 solo_menu->popup (1, 0);
194 if (ev->button == 2) {
196 // ctrl-button2 click is the midi binding click
197 // button2-click is "momentary"
199 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
200 wait_for_release = true;
204 if (ev->button == 1 || ev->button == 2) {
206 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
208 /* ctrl-shift-click applies change to all routes */
210 _session.begin_reversible_command (_("solo change"));
211 Session::GlobalSoloStateCommand cmd(this);
212 _session.set_all_solo (!_route.soloed());
214 _session.add_command (cmd);
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::GlobalSoloStateCommand cmd(this);
223 _session.set_all_solo (false);
224 _route.set_solo (true, this);
226 _session.add_command(cmd);
227 _session.commit_reversible_command ();
229 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
231 // shift-click: set this route to solo safe
233 _route.set_solo_safe (!_route.solo_safe(), this);
234 wait_for_release = false;
236 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
238 /* ctrl-click: solo mix group.
239 ctrl-button2 is MIDI learn.
242 if (ev->button == 1) {
243 set_mix_group_solo (_route, !_route.soloed());
248 /* click: solo this route */
250 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route.soloed(), this);
260 RouteUI::solo_release(GdkEventButton* ev)
262 if (!ignore_toggle) {
263 if (wait_for_release) {
264 wait_for_release = false;
266 // because the press was the last undoable thing we did
276 RouteUI::rec_enable_press(GdkEventButton* ev)
278 if (!ignore_toggle && is_audio_track()) {
280 if (ev->button == 2 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
281 // do nothing on midi bind event
283 else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
285 _session.begin_reversible_command (_("rec-enable change"));
286 Session::GlobalRecordEnableStateCommand cmd(this);
288 if (rec_enable_button->get_active()) {
289 _session.record_disenable_all ();
291 _session.record_enable_all ();
295 _session.add_command(cmd);
296 _session.commit_reversible_command ();
298 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
300 set_mix_group_rec_enable (_route, !_route.record_enabled());
304 reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
306 ignore_toggle = true;
307 rec_enable_button->set_active(audio_track()->record_enabled());
308 ignore_toggle = false;
311 stop_signal (*rec_enable_button, "button-press-event");
318 RouteUI::solo_changed(void* src)
320 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
324 RouteUI::update_solo_display ()
328 if (solo_button->get_active() != (x = _route.soloed())){
329 ignore_toggle = true;
330 solo_button->set_active(x);
331 ignore_toggle = false;
336 if (_route.solo_safe()){
337 solo_button->set_name(safe_solo_button_name());
339 solo_button->set_name(solo_button_name());
344 RouteUI::mute_changed(void* src)
346 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
350 RouteUI::update_mute_display ()
354 if (mute_button->get_active() != (x = _route.muted())){
355 ignore_toggle = true;
356 mute_button->set_active(x);
357 ignore_toggle = false;
362 RouteUI::route_rec_enable_changed (void *src)
364 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
368 RouteUI::session_rec_enable_changed ()
370 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
374 RouteUI::update_rec_display ()
376 bool model = _route.record_enabled();
377 bool view = rec_enable_button->get_active();
379 /* first make sure the button's "depressed" visual
384 ignore_toggle = true;
385 rec_enable_button->set_active (model);
386 ignore_toggle = false;
389 /* now make sure its color state is correct */
393 switch (_session.record_status ()) {
394 case Session::Disabled:
395 case Session::Enabled:
396 if (rec_enable_button->get_state() != Gtk::STATE_ACTIVE) {
397 rec_enable_button->set_state (Gtk::STATE_ACTIVE);
401 case Session::Recording:
402 if (rec_enable_button->get_state() != Gtk::STATE_SELECTED) {
403 rec_enable_button->set_state (Gtk::STATE_SELECTED);
409 if (rec_enable_button->get_state() != Gtk::STATE_NORMAL) {
410 rec_enable_button->set_state (Gtk::STATE_NORMAL);
416 RouteUI::build_remote_control_menu ()
418 remote_control_menu = manage (new Menu);
419 refresh_remote_control_menu ();
423 RouteUI::refresh_remote_control_menu ()
425 using namespace Menu_Helpers;
427 RadioMenuItem::Group rc_group;
428 CheckMenuItem* rc_active;
429 uint32_t limit = _session.ntracks();
432 MenuList& rc_items = remote_control_menu->items();
435 /* note that this menu list starts at zero, not 1, because zero
436 is a valid, if useless, ID.
439 limit += 4; /* leave some breathing room */
441 rc_items.push_back (RadioMenuElem (rc_group, _("None")));
442 if (_route.remote_control_id() == 0) {
443 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
444 rc_active->set_active ();
447 for (uint32_t i = 1; i < limit; ++i) {
448 snprintf (buf, sizeof (buf), "%u", i);
449 rc_items.push_back (RadioMenuElem (rc_group, buf));
450 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
451 if (_route.remote_control_id() == i) {
452 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
453 rc_active->set_active ();
455 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
460 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
462 /* this is called when the radio menu item is toggled, and so
463 is actually invoked twice per menu selection. we only
464 care about the invocation for the item that was being
468 if (item->get_active()) {
469 _route.set_remote_control_id (id);
474 RouteUI::build_solo_menu (void)
476 using namespace Menu_Helpers;
478 solo_menu = new Menu;
479 solo_menu->set_name ("ArdourContextMenu");
480 MenuList& items = solo_menu->items();
481 CheckMenuItem* check;
483 check = new CheckMenuItem(_("Solo-safe"));
484 check->set_active (_route.solo_safe());
485 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
486 _route.solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
487 items.push_back (CheckMenuElem(*check));
490 items.push_back (SeparatorElem());
491 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
496 RouteUI::build_mute_menu(void)
498 using namespace Menu_Helpers;
500 mute_menu = new Menu;
501 mute_menu->set_name ("ArdourContextMenu");
502 MenuList& items = mute_menu->items();
503 CheckMenuItem* check;
505 check = new CheckMenuItem(_("Pre Fader"));
506 init_mute_menu(PRE_FADER, check);
507 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
508 _route.pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
509 items.push_back (CheckMenuElem(*check));
512 check = new CheckMenuItem(_("Post Fader"));
513 init_mute_menu(POST_FADER, check);
514 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
515 _route.post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
516 items.push_back (CheckMenuElem(*check));
519 check = new CheckMenuItem(_("Control Outs"));
520 init_mute_menu(CONTROL_OUTS, check);
521 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
522 _route.control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
523 items.push_back (CheckMenuElem(*check));
526 check = new CheckMenuItem(_("Main Outs"));
527 init_mute_menu(MAIN_OUTS, check);
528 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
529 _route.main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
530 items.push_back (CheckMenuElem(*check));
533 items.push_back (SeparatorElem());
534 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
538 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
540 if (_route.get_mute_config (type)) {
541 check->set_active (true);
546 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
548 _route.set_mute_config(type, check->get_active(), this);
552 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
554 _route.set_solo_safe (check->get_active(), this);
558 RouteUI::set_mix_group_solo(Route& route, bool yn)
560 RouteGroup* mix_group;
562 if((mix_group = route.mix_group()) != 0){
563 _session.begin_reversible_command (_("mix group solo change"));
564 Session::GlobalSoloStateCommand cmd(this);
565 mix_group->apply(&Route::set_solo, yn, this);
567 _session.add_command (cmd);
568 _session.commit_reversible_command ();
570 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route.soloed(), this);
575 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
577 _session.begin_reversible_command (name);
578 XMLNode &before = _route.get_state();
579 bind(mem_fun(_route, func), yn, arg)();
580 XMLNode &after = _route.get_state();
581 _session.add_command (MementoCommand<Route>(_route, before, after));
582 _session.commit_reversible_command ();
586 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
588 _session.begin_reversible_command (name);
589 XMLNode &before = audio_track()->get_state();
590 bind (mem_fun (*audio_track(), func), yn, arg)();
591 XMLNode &after = audio_track()->get_state();
592 _session.add_command (MementoCommand<AudioTrack>(*audio_track(), before, after));
593 _session.commit_reversible_command ();
597 RouteUI::set_mix_group_mute(Route& route, bool yn)
599 RouteGroup* mix_group;
601 if((mix_group = route.mix_group()) != 0){
602 _session.begin_reversible_command (_("mix group mute change"));
603 Session::GlobalMuteStateCommand cmd(this);
604 mix_group->apply(&Route::set_mute, yn, this);
606 _session.add_command(cmd);
607 _session.commit_reversible_command ();
609 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route.muted(), this);
614 RouteUI::set_mix_group_rec_enable(Route& route, bool yn)
616 RouteGroup* mix_group;
618 if((mix_group = route.mix_group()) != 0){
619 _session.begin_reversible_command (_("mix group rec-enable change"));
620 Session::GlobalRecordEnableStateCommand cmd(this);
621 mix_group->apply (&Route::set_record_enable, yn, this);
623 _session.add_command(cmd);
624 _session.commit_reversible_command ();
626 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route.record_enabled(), this);
632 RouteUI::choose_color()
637 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
647 RouteUI::set_color (const Gdk::Color & c)
654 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
655 xml_node->add_property ("color", buf);
657 _route.gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
662 RouteUI::ensure_xml_node ()
665 if ((xml_node = _route.extra_xml ("GUI")) == 0) {
666 xml_node = new XMLNode ("GUI");
667 _route.add_extra_xml (*xml_node);
673 RouteUI::get_child_xml_node (const string & childname)
680 if ((child = find_named_node (*xml_node, childname)) == 0) {
681 child = new XMLNode (childname);
682 xml_node->add_child_nocopy (*child);
689 RouteUI::set_color_from_route ()
693 RouteUI::ensure_xml_node ();
695 if ((prop = xml_node->property ("color")) != 0) {
697 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
707 RouteUI::remove_this_route ()
709 vector<string> choices;
712 if (is_audio_track()) {
713 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());
715 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route.name());
718 choices.push_back (_("No, do nothing."));
719 choices.push_back (_("Yes, remove it."));
721 Choice prompter (prompt, choices);
723 if (prompter.run () == 1) {
724 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
729 RouteUI::idle_remove_this_route (RouteUI *rui)
731 rui->_session.remove_route (rui->_route);
736 RouteUI::route_removed ()
738 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
744 RouteUI::route_rename ()
746 ArdourPrompter name_prompter (true);
748 name_prompter.set_prompt (_("New Name: "));
749 name_prompter.set_initial_text (_route.name());
750 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
751 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
752 name_prompter.show_all ();
754 switch (name_prompter.run ()) {
756 case Gtk::RESPONSE_ACCEPT:
757 name_prompter.get_result (result);
758 if (result.length()) {
759 _route.set_name (result, this);
769 RouteUI::name_changed (void *src)
771 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
773 name_label.set_text (_route.name());
777 RouteUI::toggle_route_active ()
781 if (route_active_menu_item) {
782 if (route_active_menu_item->get_active() != (yn = _route.active())) {
783 _route.set_active (!yn);
789 RouteUI::route_active_changed ()
791 if (route_active_menu_item) {
792 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route.active()));
797 RouteUI::toggle_polarity ()
799 if (polarity_menu_item) {
803 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
805 if ((x = polarity_menu_item->get_active()) != _route.phase_invert()) {
806 _route.set_phase_invert (x, this);
808 name_label.set_text (X_("Ø ") + name_label.get_text());
810 name_label.set_text (_route.name());
817 RouteUI::polarity_changed ()
819 /* no signal for this yet */
823 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
825 bool yn = _route.solo_safe ();
827 if (check->get_active() != yn) {
828 check->set_active (yn);
832 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
834 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
836 bool yn = _route.get_mute_config(PRE_FADER);
837 if (check->get_active() != yn) {
838 check->set_active (yn);
843 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
845 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
847 bool yn = _route.get_mute_config(POST_FADER);
848 if (check->get_active() != yn) {
849 check->set_active (yn);
854 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
856 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
858 bool yn = _route.get_mute_config(CONTROL_OUTS);
859 if (check->get_active() != yn) {
860 check->set_active (yn);
865 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
867 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
869 bool yn = _route.get_mute_config(MAIN_OUTS);
870 if (check->get_active() != yn) {
871 check->set_active (yn);
876 RouteUI::disconnect_input ()
878 _route.disconnect_inputs (this);
882 RouteUI::disconnect_output ()
884 _route.disconnect_outputs (this);
888 RouteUI::is_audio_track () const
890 return dynamic_cast<AudioTrack*>(&_route) != 0;
894 RouteUI::get_diskstream () const
898 if ((at = dynamic_cast<AudioTrack*>(&_route)) != 0) {
899 return &at->disk_stream();
906 RouteUI::audio_track() const
908 return dynamic_cast<AudioTrack*>(&_route);
911 RouteUI::name() const
913 return _route.name();
917 RouteUI::map_frozen ()
919 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
921 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
924 switch (at->freeze_state()) {
925 case AudioTrack::Frozen:
926 rec_enable_button->set_sensitive (false);
929 rec_enable_button->set_sensitive (true);