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>
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 ignore_toggle = false;
58 wait_for_release = false;
59 route_active_menu_item = 0;
61 if (set_color_from_route()) {
62 set_color (unique_random_color());
65 _route.GoingAway.connect (mem_fun (*this, &RouteUI::route_removed));
66 _route.active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed));
68 mute_button = manage (new BindableToggleButton (& _route.midi_mute_control(), m_name ));
69 mute_button->set_bind_button_state (2, GDK_CONTROL_MASK);
70 solo_button = manage (new BindableToggleButton (& _route.midi_solo_control(), s_name ));
71 solo_button->set_bind_button_state (2, GDK_CONTROL_MASK);
73 if (is_audio_track()) {
74 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
76 get_diskstream()->record_enable_changed.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed));
78 _session.RecordEnabled.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed));
79 _session.RecordDisabled.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);
252 return stop_signal (*solo_button, "button-press-event");
256 RouteUI::solo_release(GdkEventButton* ev)
259 if (wait_for_release){
260 wait_for_release = false;
262 // because the press was the last undoable thing we did
266 stop_signal (*solo_button, "button-release-event");
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_solo_menu (void)
414 using namespace Menu_Helpers;
416 solo_menu = new Menu;
417 solo_menu->set_name ("ArdourContextMenu");
418 MenuList& items = solo_menu->items();
419 CheckMenuItem* check;
421 check = new CheckMenuItem(_("Solo-safe"));
422 check->set_active (_route.solo_safe());
423 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
424 _route.solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
425 items.push_back (CheckMenuElem(*check));
428 items.push_back (SeparatorElem());
429 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
434 RouteUI::build_mute_menu(void)
436 using namespace Menu_Helpers;
438 mute_menu = new Menu;
439 mute_menu->set_name ("ArdourContextMenu");
440 MenuList& items = mute_menu->items();
441 CheckMenuItem* check;
443 check = new CheckMenuItem(_("Pre Fader"));
444 init_mute_menu(PRE_FADER, check);
445 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
446 _route.pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
447 items.push_back (CheckMenuElem(*check));
450 check = new CheckMenuItem(_("Post Fader"));
451 init_mute_menu(POST_FADER, check);
452 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
453 _route.post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
454 items.push_back (CheckMenuElem(*check));
457 check = new CheckMenuItem(_("Control Outs"));
458 init_mute_menu(CONTROL_OUTS, check);
459 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
460 _route.control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
461 items.push_back (CheckMenuElem(*check));
464 check = new CheckMenuItem(_("Main Outs"));
465 init_mute_menu(MAIN_OUTS, check);
466 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
467 _route.main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
468 items.push_back (CheckMenuElem(*check));
471 items.push_back (SeparatorElem());
472 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
476 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
478 if (_route.get_mute_config (type)) {
479 check->set_active (true);
484 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
486 _route.set_mute_config(type, check->get_active(), this);
490 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
492 _route.set_solo_safe (check->get_active(), this);
496 RouteUI::set_mix_group_solo(Route& route, bool yn)
498 RouteGroup* mix_group;
500 if((mix_group = route.mix_group()) != 0){
501 _session.begin_reversible_command (_("mix group solo change"));
502 _session.add_undo (_session.global_solo_memento (this));
503 mix_group->apply(&Route::set_solo, yn, this);
504 _session.add_redo_no_execute (_session.global_solo_memento(this));
505 _session.commit_reversible_command ();
507 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route.soloed(), this);
512 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
514 _session.begin_reversible_command (name);
515 _session.add_undo (bind (mem_fun (_route, func), !yn, (void *) arg));
516 _session.add_redo (bind (mem_fun (_route, func), yn, (void *) arg));
517 _session.commit_reversible_command ();
521 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
523 _session.begin_reversible_command (name);
524 _session.add_undo (bind (mem_fun (*audio_track(), func), !yn, (void *) arg));
525 _session.add_redo (bind (mem_fun (*audio_track(), func), yn, (void *) arg));
526 _session.commit_reversible_command ();
530 RouteUI::set_mix_group_mute(Route& route, bool yn)
532 RouteGroup* mix_group;
534 if((mix_group = route.mix_group()) != 0){
535 _session.begin_reversible_command (_("mix group mute change"));
536 _session.add_undo (_session.global_mute_memento (this));
537 mix_group->apply(&Route::set_mute, yn, this);
538 _session.add_redo_no_execute (_session.global_mute_memento(this));
539 _session.commit_reversible_command ();
541 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route.muted(), this);
546 RouteUI::set_mix_group_rec_enable(Route& route, bool yn)
548 RouteGroup* mix_group;
550 if((mix_group = route.mix_group()) != 0){
551 _session.begin_reversible_command (_("mix group rec-enable change"));
552 _session.add_undo (_session.global_record_enable_memento (this));
553 mix_group->apply (&Route::set_record_enable, yn, this);
554 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
555 _session.commit_reversible_command ();
557 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route.record_enabled(), this);
563 RouteUI::choose_color()
569 current.set_red ( _color.get_red() / 65535);
570 current.set_green (_color.get_green() / 65535);
571 current.set_blue (_color.get_blue() / 65535);
573 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, ¤t);
583 RouteUI::set_color (Gdk::Color c)
590 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
591 xml_node->add_property ("color", buf);
593 _route.gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
598 RouteUI::ensure_xml_node ()
601 if ((xml_node = _route.extra_xml ("GUI")) == 0) {
602 xml_node = new XMLNode ("GUI");
603 _route.add_extra_xml (*xml_node);
609 RouteUI::get_child_xml_node (stringcr_t childname)
616 if ((child = find_named_node (*xml_node, childname)) == 0) {
617 child = new XMLNode (childname);
618 xml_node->add_child_nocopy (*child);
625 RouteUI::set_color_from_route ()
629 RouteUI::ensure_xml_node ();
631 if ((prop = xml_node->property ("color")) != 0) {
633 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
643 RouteUI::remove_this_route ()
645 vector<string> choices;
648 if (is_audio_track()) {
649 prompt = string_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());
651 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route.name());
654 choices.push_back (_("Yes, remove it."));
655 choices.push_back (_("No, do nothing."));
657 Choice prompter (prompt, choices);
659 prompter.chosen.connect(sigc::ptr_fun(Gtk::Main::quit));
660 prompter.show_all ();
664 if (prompter.get_choice() == 0) {
665 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
670 RouteUI::idle_remove_this_route (RouteUI *rui)
672 rui->_session.remove_route (rui->_route);
677 RouteUI::route_removed ()
679 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
685 RouteUI::route_rename ()
687 ArdourPrompter name_prompter (true);
689 name_prompter.set_prompt (_("new name: "));
690 name_prompter.set_initial_text (_route.name());
691 name_prompter.show_all ();
693 switch (name_prompter.run ()) {
695 case GTK_RESPONSE_ACCEPT:
696 name_prompter.get_result (result);
697 if (result.length()) {
698 strip_whitespace_edges (result);
699 _route.set_name (result, this);
709 RouteUI::name_changed (void *src)
711 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
713 name_label.set_text (_route.name());
717 RouteUI::toggle_route_active ()
721 if (route_active_menu_item) {
722 if (route_active_menu_item->get_active() != (yn = _route.active())) {
723 _route.set_active (!yn);
729 RouteUI::route_active_changed ()
731 if (route_active_menu_item) {
732 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route.active()));
737 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
739 bool yn = _route.solo_safe ();
741 if (check->get_active() != yn) {
742 check->set_active (yn);
746 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
748 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
750 bool yn = _route.get_mute_config(PRE_FADER);
751 if (check->get_active() != yn) {
752 check->set_active (yn);
757 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
759 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
761 bool yn = _route.get_mute_config(POST_FADER);
762 if (check->get_active() != yn) {
763 check->set_active (yn);
768 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
770 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
772 bool yn = _route.get_mute_config(CONTROL_OUTS);
773 if (check->get_active() != yn) {
774 check->set_active (yn);
779 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
781 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
783 bool yn = _route.get_mute_config(MAIN_OUTS);
784 if (check->get_active() != yn) {
785 check->set_active (yn);
790 RouteUI::disconnect_input ()
792 _route.disconnect_inputs (this);
796 RouteUI::disconnect_output ()
798 _route.disconnect_outputs (this);
802 RouteUI::is_audio_track () const
804 return dynamic_cast<AudioTrack*>(&_route) != 0;
808 RouteUI::get_diskstream () const
812 if ((at = dynamic_cast<AudioTrack*>(&_route)) != 0) {
813 return &at->disk_stream();
820 RouteUI::audio_track() const
822 return dynamic_cast<AudioTrack*>(&_route);
825 RouteUI::name() const
827 return _route.name();
831 RouteUI::map_frozen ()
833 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
835 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
838 switch (at->freeze_state()) {
839 case AudioTrack::Frozen:
840 rec_enable_button->set_sensitive (false);
843 rec_enable_button->set_sensitive (true);