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 remote_control_menu = 0;
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.RecordStateChanged.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);
256 RouteUI::solo_release(GdkEventButton* ev)
258 if (!ignore_toggle) {
259 if (wait_for_release) {
260 wait_for_release = false;
262 // because the press was the last undoable thing we did
272 RouteUI::rec_enable_press(GdkEventButton* ev)
274 if (!ignore_toggle && is_audio_track()) {
276 if (ev->button == 2 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
277 // do nothing on midi bind event
279 else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
281 _session.begin_reversible_command (_("rec-enable change"));
282 _session.add_undo (_session.global_record_enable_memento(this));
284 if (rec_enable_button->get_active()) {
285 _session.record_disenable_all ();
287 _session.record_enable_all ();
290 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
291 _session.commit_reversible_command ();
293 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
295 set_mix_group_rec_enable (_route, !_route.record_enabled());
299 reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
301 ignore_toggle = true;
302 rec_enable_button->set_active(audio_track()->record_enabled());
303 ignore_toggle = false;
306 stop_signal (*rec_enable_button, "button-press-event");
313 RouteUI::solo_changed(void* src)
315 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
319 RouteUI::update_solo_display ()
323 if (solo_button->get_active() != (x = _route.soloed())){
324 ignore_toggle = true;
325 solo_button->set_active(x);
326 ignore_toggle = false;
331 if (_route.solo_safe()){
332 solo_button->set_name(safe_solo_button_name());
334 solo_button->set_name(solo_button_name());
339 RouteUI::mute_changed(void* src)
341 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
345 RouteUI::update_mute_display ()
349 if (mute_button->get_active() != (x = _route.muted())){
350 ignore_toggle = true;
351 mute_button->set_active(x);
352 ignore_toggle = false;
357 RouteUI::route_rec_enable_changed (void *src)
359 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
363 RouteUI::session_rec_enable_changed ()
365 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
369 RouteUI::update_rec_display ()
371 bool model = _route.record_enabled();
372 bool view = rec_enable_button->get_active();
374 /* first make sure the button's "depressed" visual
379 ignore_toggle = true;
380 rec_enable_button->set_active (model);
381 ignore_toggle = false;
384 /* now make sure its color state is correct */
388 switch (_session.record_status ()) {
389 case Session::Disabled:
390 case Session::Enabled:
391 if (rec_enable_button->get_state() != Gtk::STATE_ACTIVE) {
392 rec_enable_button->set_state (Gtk::STATE_ACTIVE);
396 case Session::Recording:
397 if (rec_enable_button->get_state() != Gtk::STATE_SELECTED) {
398 rec_enable_button->set_state (Gtk::STATE_SELECTED);
404 if (rec_enable_button->get_state() != Gtk::STATE_NORMAL) {
405 rec_enable_button->set_state (Gtk::STATE_NORMAL);
411 RouteUI::build_remote_control_menu ()
413 remote_control_menu = manage (new Menu);
414 refresh_remote_control_menu ();
418 RouteUI::refresh_remote_control_menu ()
420 using namespace Menu_Helpers;
422 RadioMenuItem::Group rc_group;
423 CheckMenuItem* rc_active;
424 uint32_t limit = _session.ntracks();
427 MenuList& rc_items = remote_control_menu->items();
430 /* note that this menu list starts at zero, not 1, because zero
431 is a valid, if useless, ID.
434 limit += 4; /* leave some breathing room */
436 for (uint32_t i = 0; i < limit; ++i) {
437 snprintf (buf, sizeof (buf), "%u", i);
438 rc_items.push_back (RadioMenuElem (rc_group, buf));
439 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
440 if (_route.remote_control_id() == i) {
441 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
442 rc_active->set_active ();
444 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
449 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
451 /* this is called when the radio menu item is toggled, and so
452 is actually invoked twice per menu selection. we only
453 care about the invocation for the item that was being
457 if (item->get_active()) {
458 _route.set_remote_control_id (id);
463 RouteUI::build_solo_menu (void)
465 using namespace Menu_Helpers;
467 solo_menu = new Menu;
468 solo_menu->set_name ("ArdourContextMenu");
469 MenuList& items = solo_menu->items();
470 CheckMenuItem* check;
472 check = new CheckMenuItem(_("Solo-safe"));
473 check->set_active (_route.solo_safe());
474 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
475 _route.solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
476 items.push_back (CheckMenuElem(*check));
479 items.push_back (SeparatorElem());
480 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
485 RouteUI::build_mute_menu(void)
487 using namespace Menu_Helpers;
489 mute_menu = new Menu;
490 mute_menu->set_name ("ArdourContextMenu");
491 MenuList& items = mute_menu->items();
492 CheckMenuItem* check;
494 check = new CheckMenuItem(_("Pre Fader"));
495 init_mute_menu(PRE_FADER, check);
496 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
497 _route.pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
498 items.push_back (CheckMenuElem(*check));
501 check = new CheckMenuItem(_("Post Fader"));
502 init_mute_menu(POST_FADER, check);
503 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
504 _route.post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
505 items.push_back (CheckMenuElem(*check));
508 check = new CheckMenuItem(_("Control Outs"));
509 init_mute_menu(CONTROL_OUTS, check);
510 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
511 _route.control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
512 items.push_back (CheckMenuElem(*check));
515 check = new CheckMenuItem(_("Main Outs"));
516 init_mute_menu(MAIN_OUTS, check);
517 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
518 _route.main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
519 items.push_back (CheckMenuElem(*check));
522 items.push_back (SeparatorElem());
523 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
527 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
529 if (_route.get_mute_config (type)) {
530 check->set_active (true);
535 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
537 _route.set_mute_config(type, check->get_active(), this);
541 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
543 _route.set_solo_safe (check->get_active(), this);
547 RouteUI::set_mix_group_solo(Route& route, bool yn)
549 RouteGroup* mix_group;
551 if((mix_group = route.mix_group()) != 0){
552 _session.begin_reversible_command (_("mix group solo change"));
553 _session.add_undo (_session.global_solo_memento (this));
554 mix_group->apply(&Route::set_solo, yn, this);
555 _session.add_redo_no_execute (_session.global_solo_memento(this));
556 _session.commit_reversible_command ();
558 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route.soloed(), this);
563 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
565 _session.begin_reversible_command (name);
566 _session.add_undo (bind (mem_fun (_route, func), !yn, (void *) arg));
567 _session.add_redo (bind (mem_fun (_route, func), yn, (void *) arg));
568 _session.commit_reversible_command ();
572 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
574 _session.begin_reversible_command (name);
575 _session.add_undo (bind (mem_fun (*audio_track(), func), !yn, (void *) arg));
576 _session.add_redo (bind (mem_fun (*audio_track(), func), yn, (void *) arg));
577 _session.commit_reversible_command ();
581 RouteUI::set_mix_group_mute(Route& route, bool yn)
583 RouteGroup* mix_group;
585 if((mix_group = route.mix_group()) != 0){
586 _session.begin_reversible_command (_("mix group mute change"));
587 _session.add_undo (_session.global_mute_memento (this));
588 mix_group->apply(&Route::set_mute, yn, this);
589 _session.add_redo_no_execute (_session.global_mute_memento(this));
590 _session.commit_reversible_command ();
592 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route.muted(), this);
597 RouteUI::set_mix_group_rec_enable(Route& route, bool yn)
599 RouteGroup* mix_group;
601 if((mix_group = route.mix_group()) != 0){
602 _session.begin_reversible_command (_("mix group rec-enable change"));
603 _session.add_undo (_session.global_record_enable_memento (this));
604 mix_group->apply (&Route::set_record_enable, yn, this);
605 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
606 _session.commit_reversible_command ();
608 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route.record_enabled(), this);
614 RouteUI::choose_color()
619 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
629 RouteUI::set_color (const Gdk::Color & c)
636 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
637 xml_node->add_property ("color", buf);
639 _route.gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
644 RouteUI::ensure_xml_node ()
647 if ((xml_node = _route.extra_xml ("GUI")) == 0) {
648 xml_node = new XMLNode ("GUI");
649 _route.add_extra_xml (*xml_node);
655 RouteUI::get_child_xml_node (const string & childname)
662 if ((child = find_named_node (*xml_node, childname)) == 0) {
663 child = new XMLNode (childname);
664 xml_node->add_child_nocopy (*child);
671 RouteUI::set_color_from_route ()
675 RouteUI::ensure_xml_node ();
677 if ((prop = xml_node->property ("color")) != 0) {
679 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
689 RouteUI::remove_this_route ()
691 vector<string> choices;
694 if (is_audio_track()) {
695 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());
697 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route.name());
700 choices.push_back (_("Yes, remove it."));
701 choices.push_back (_("No, do nothing."));
703 Choice prompter (prompt, choices);
705 if (prompter.run () == 0) {
706 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
711 RouteUI::idle_remove_this_route (RouteUI *rui)
713 rui->_session.remove_route (rui->_route);
718 RouteUI::route_removed ()
720 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
726 RouteUI::route_rename ()
728 ArdourPrompter name_prompter (true);
730 name_prompter.set_prompt (_("new name: "));
731 name_prompter.set_initial_text (_route.name());
732 name_prompter.show_all ();
734 switch (name_prompter.run ()) {
736 case Gtk::RESPONSE_ACCEPT:
737 name_prompter.get_result (result);
738 if (result.length()) {
739 _route.set_name (result, this);
749 RouteUI::name_changed (void *src)
751 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
753 name_label.set_text (_route.name());
757 RouteUI::toggle_route_active ()
761 if (route_active_menu_item) {
762 if (route_active_menu_item->get_active() != (yn = _route.active())) {
763 _route.set_active (!yn);
769 RouteUI::route_active_changed ()
771 if (route_active_menu_item) {
772 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route.active()));
777 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
779 bool yn = _route.solo_safe ();
781 if (check->get_active() != yn) {
782 check->set_active (yn);
786 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
788 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
790 bool yn = _route.get_mute_config(PRE_FADER);
791 if (check->get_active() != yn) {
792 check->set_active (yn);
797 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
799 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
801 bool yn = _route.get_mute_config(POST_FADER);
802 if (check->get_active() != yn) {
803 check->set_active (yn);
808 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
810 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
812 bool yn = _route.get_mute_config(CONTROL_OUTS);
813 if (check->get_active() != yn) {
814 check->set_active (yn);
819 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
821 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
823 bool yn = _route.get_mute_config(MAIN_OUTS);
824 if (check->get_active() != yn) {
825 check->set_active (yn);
830 RouteUI::disconnect_input ()
832 _route.disconnect_inputs (this);
836 RouteUI::disconnect_output ()
838 _route.disconnect_outputs (this);
842 RouteUI::is_audio_track () const
844 return dynamic_cast<AudioTrack*>(&_route) != 0;
848 RouteUI::get_diskstream () const
852 if ((at = dynamic_cast<AudioTrack*>(&_route)) != 0) {
853 return &at->disk_stream();
860 RouteUI::audio_track() const
862 return dynamic_cast<AudioTrack*>(&_route);
865 RouteUI::name() const
867 return _route.name();
871 RouteUI::map_frozen ()
873 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
875 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
878 switch (at->freeze_state()) {
879 case AudioTrack::Frozen:
880 rec_enable_button->set_sensitive (false);
883 rec_enable_button->set_sensitive (true);