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/diskstream.h>
40 /* there is a compose() here.. */
41 //using namespace sigc;
43 using namespace Gtkmm2ext;
44 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 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.RecordEnabled.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed));
80 _session.RecordDisabled.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);
161 return stop_signal (*mute_button, "button-press-event");
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
173 stop_signal (*mute_button, "button-release-event");
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.add_undo (_session.global_solo_memento(this));
212 _session.set_all_solo (!_route.soloed());
213 _session.add_redo_no_execute (_session.global_solo_memento(this));
214 _session.commit_reversible_command ();
216 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
218 // ctrl-alt-click: exclusively solo this track, not a toggle */
220 _session.begin_reversible_command (_("solo change"));
221 _session.add_undo (_session.global_solo_memento(this));
222 _session.set_all_solo (false);
223 _route.set_solo (true, this);
224 _session.add_redo_no_execute (_session.global_solo_memento(this));
225 _session.commit_reversible_command ();
227 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
229 // shift-click: set this route to solo safe
231 _route.set_solo_safe (!_route.solo_safe(), this);
232 wait_for_release = false;
234 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
236 /* ctrl-click: solo mix group.
237 ctrl-button2 is MIDI learn.
240 if (ev->button == 1) {
241 set_mix_group_solo (_route, !_route.soloed());
246 /* click: solo this route */
248 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route.soloed(), this);
254 return stop_signal (*solo_button, "button-press-event");
258 RouteUI::solo_release(GdkEventButton* ev)
261 if (wait_for_release){
262 wait_for_release = false;
264 // because the press was the last undoable thing we did
268 stop_signal (*solo_button, "button-release-event");
275 RouteUI::rec_enable_press(GdkEventButton* ev)
277 if (!ignore_toggle && is_audio_track()) {
279 if (ev->button == 2 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
280 // do nothing on midi bind event
282 else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
284 _session.begin_reversible_command (_("rec-enable change"));
285 _session.add_undo (_session.global_record_enable_memento(this));
287 if (rec_enable_button->get_active()) {
288 _session.record_disenable_all ();
290 _session.record_enable_all ();
293 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
294 _session.commit_reversible_command ();
296 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
298 set_mix_group_rec_enable (_route, !_route.record_enabled());
302 reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
304 ignore_toggle = true;
305 rec_enable_button->set_active(audio_track()->record_enabled());
306 ignore_toggle = false;
309 stop_signal (*rec_enable_button, "button-press-event");
316 RouteUI::solo_changed(void* src)
318 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
322 RouteUI::update_solo_display ()
326 if (solo_button->get_active() != (x = _route.soloed())){
327 ignore_toggle = true;
328 solo_button->set_active(x);
329 ignore_toggle = false;
334 if (_route.solo_safe()){
335 solo_button->set_name(safe_solo_button_name());
337 solo_button->set_name(solo_button_name());
342 RouteUI::mute_changed(void* src)
344 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
348 RouteUI::update_mute_display ()
352 if (mute_button->get_active() != (x = _route.muted())){
353 ignore_toggle = true;
354 mute_button->set_active(x);
355 ignore_toggle = false;
360 RouteUI::route_rec_enable_changed (void *src)
362 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
366 RouteUI::session_rec_enable_changed ()
368 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
372 RouteUI::update_rec_display ()
374 bool model = _route.record_enabled();
375 bool view = rec_enable_button->get_active();
377 /* first make sure the button's "depressed" visual
382 ignore_toggle = true;
383 rec_enable_button->set_active (model);
384 ignore_toggle = false;
387 /* now make sure its color state is correct */
391 switch (_session.record_status ()) {
392 case Session::Disabled:
393 case Session::Enabled:
394 if (rec_enable_button->get_state() != Gtk::STATE_ACTIVE) {
395 rec_enable_button->set_state (Gtk::STATE_ACTIVE);
399 case Session::Recording:
400 if (rec_enable_button->get_state() != Gtk::STATE_SELECTED) {
401 rec_enable_button->set_state (Gtk::STATE_SELECTED);
407 if (rec_enable_button->get_state() != Gtk::STATE_NORMAL) {
408 rec_enable_button->set_state (Gtk::STATE_NORMAL);
414 RouteUI::build_solo_menu (void)
416 using namespace Menu_Helpers;
418 solo_menu = new Menu;
419 solo_menu->set_name ("ArdourContextMenu");
420 MenuList& items = solo_menu->items();
421 CheckMenuItem* check;
423 check = new CheckMenuItem(_("Solo-safe"));
424 check->set_active (_route.solo_safe());
425 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
426 _route.solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
427 items.push_back (CheckMenuElem(*check));
430 items.push_back (SeparatorElem());
431 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
436 RouteUI::build_mute_menu(void)
438 using namespace Menu_Helpers;
440 mute_menu = new Menu;
441 mute_menu->set_name ("ArdourContextMenu");
442 MenuList& items = mute_menu->items();
443 CheckMenuItem* check;
445 check = new CheckMenuItem(_("Pre Fader"));
446 init_mute_menu(PRE_FADER, check);
447 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
448 _route.pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
449 items.push_back (CheckMenuElem(*check));
452 check = new CheckMenuItem(_("Post Fader"));
453 init_mute_menu(POST_FADER, check);
454 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
455 _route.post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
456 items.push_back (CheckMenuElem(*check));
459 check = new CheckMenuItem(_("Control Outs"));
460 init_mute_menu(CONTROL_OUTS, check);
461 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
462 _route.control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
463 items.push_back (CheckMenuElem(*check));
466 check = new CheckMenuItem(_("Main Outs"));
467 init_mute_menu(MAIN_OUTS, check);
468 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
469 _route.main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
470 items.push_back (CheckMenuElem(*check));
473 items.push_back (SeparatorElem());
474 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
478 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
480 if (_route.get_mute_config (type)) {
481 check->set_active (true);
486 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
488 _route.set_mute_config(type, check->get_active(), this);
492 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
494 _route.set_solo_safe (check->get_active(), this);
498 RouteUI::set_mix_group_solo(Route& route, bool yn)
500 RouteGroup* mix_group;
502 if((mix_group = route.mix_group()) != 0){
503 _session.begin_reversible_command (_("mix group solo change"));
504 _session.add_undo (_session.global_solo_memento (this));
505 mix_group->apply(&Route::set_solo, yn, this);
506 _session.add_redo_no_execute (_session.global_solo_memento(this));
507 _session.commit_reversible_command ();
509 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route.soloed(), this);
514 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
516 _session.begin_reversible_command (name);
517 _session.add_undo (bind (mem_fun (_route, func), !yn, (void *) arg));
518 _session.add_redo (bind (mem_fun (_route, func), yn, (void *) arg));
519 _session.commit_reversible_command ();
523 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
525 _session.begin_reversible_command (name);
526 _session.add_undo (bind (mem_fun (*audio_track(), func), !yn, (void *) arg));
527 _session.add_redo (bind (mem_fun (*audio_track(), func), yn, (void *) arg));
528 _session.commit_reversible_command ();
532 RouteUI::set_mix_group_mute(Route& route, bool yn)
534 RouteGroup* mix_group;
536 if((mix_group = route.mix_group()) != 0){
537 _session.begin_reversible_command (_("mix group mute change"));
538 _session.add_undo (_session.global_mute_memento (this));
539 mix_group->apply(&Route::set_mute, yn, this);
540 _session.add_redo_no_execute (_session.global_mute_memento(this));
541 _session.commit_reversible_command ();
543 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route.muted(), this);
548 RouteUI::set_mix_group_rec_enable(Route& route, bool yn)
550 RouteGroup* mix_group;
552 if((mix_group = route.mix_group()) != 0){
553 _session.begin_reversible_command (_("mix group rec-enable change"));
554 _session.add_undo (_session.global_record_enable_memento (this));
555 mix_group->apply (&Route::set_record_enable, yn, this);
556 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
557 _session.commit_reversible_command ();
559 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route.record_enabled(), this);
565 RouteUI::choose_color()
571 current[0] = _color.get_red() / 65535.0;
572 current[1] = _color.get_green() / 65535.0;
573 current[2] = _color.get_blue() / 65535.0;
576 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, current);
586 RouteUI::set_color (Gdk::Color c)
593 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
594 xml_node->add_property ("color", buf);
596 _route.gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
601 RouteUI::ensure_xml_node ()
604 if ((xml_node = _route.extra_xml ("GUI")) == 0) {
605 xml_node = new XMLNode ("GUI");
606 _route.add_extra_xml (*xml_node);
612 RouteUI::get_child_xml_node (string childname)
619 if ((child = find_named_node (*xml_node, childname)) == 0) {
620 child = new XMLNode (childname);
621 xml_node->add_child_nocopy (*child);
628 RouteUI::set_color_from_route ()
632 RouteUI::ensure_xml_node ();
634 if ((prop = xml_node->property ("color")) != 0) {
636 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
646 RouteUI::remove_this_route ()
648 vector<string> choices;
651 if (is_audio_track()) {
652 prompt = compose (_("Do you really want to remove track \"%1\" ?\nYou may also lose the playlist used by this track.\n(cannot be undone)"), _route.name());
654 prompt = compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route.name());
657 choices.push_back (_("Yes, remove it."));
658 choices.push_back (_("No, do nothing."));
660 Choice prompter (prompt, choices);
662 prompter.chosen.connect (Gtk::Main::quit.slot());
663 prompter.show_all ();
667 if (prompter.get_choice() == 0) {
668 Glib::signal_idle().connect (bind (mem_fun (&RouteUI::idle_remove_this_route), this));
673 RouteUI::idle_remove_this_route (RouteUI *rui)
675 rui->_session.remove_route (rui->_route);
680 RouteUI::route_removed ()
682 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
688 RouteUI::route_rename ()
690 ArdourPrompter name_prompter (true);
691 name_prompter.set_prompt (_("new name: "));
692 name_prompter.set_initial_text (_route.name());
693 name_prompter.done.connect (Gtk::Main::quit.slot());
694 name_prompter.show_all ();
698 if (name_prompter.status == Gtkmm2ext::Prompter::cancelled) {
703 name_prompter.get_result (result);
705 if (result.length() == 0) {
709 strip_whitespace_edges (result);
710 _route.set_name (result, this);
714 RouteUI::name_changed (void *src)
716 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
718 name_label.set_text (_route.name());
722 RouteUI::toggle_route_active ()
726 if (route_active_menu_item) {
727 if (route_active_menu_item->get_active() != (yn = _route.active())) {
728 _route.set_active (!yn);
734 RouteUI::route_active_changed ()
736 if (route_active_menu_item) {
737 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route.active()));
742 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
744 bool yn = _route.solo_safe ();
746 if (check->get_active() != yn) {
747 check->set_active (yn);
751 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
753 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
755 bool yn = _route.get_mute_config(PRE_FADER);
756 if (check->get_active() != yn) {
757 check->set_active (yn);
762 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
764 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
766 bool yn = _route.get_mute_config(POST_FADER);
767 if (check->get_active() != yn) {
768 check->set_active (yn);
773 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
775 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
777 bool yn = _route.get_mute_config(CONTROL_OUTS);
778 if (check->get_active() != yn) {
779 check->set_active (yn);
784 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
786 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
788 bool yn = _route.get_mute_config(MAIN_OUTS);
789 if (check->get_active() != yn) {
790 check->set_active (yn);
795 RouteUI::disconnect_input ()
797 _route.disconnect_inputs (this);
801 RouteUI::disconnect_output ()
803 _route.disconnect_outputs (this);
807 RouteUI::is_audio_track () const
809 return dynamic_cast<AudioTrack*>(&_route) != 0;
813 RouteUI::get_diskstream () const
817 if ((at = dynamic_cast<AudioTrack*>(&_route)) != 0) {
818 return &at->disk_stream();
825 RouteUI::audio_track() const
827 return dynamic_cast<AudioTrack*>(&_route);
830 RouteUI::name() const
832 return _route.name();
836 RouteUI::map_frozen ()
838 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
840 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
843 switch (at->freeze_state()) {
844 case AudioTrack::Frozen:
845 rec_enable_button->set_sensitive (false);
848 rec_enable_button->set_sensitive (true);