2 Copyright (C) 2002-2006 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/doi.h>
25 #include <gtkmm2ext/bindable_button.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>
38 #include <ardour/midi_track.h>
39 #include <ardour/midi_diskstream.h>
44 using namespace Gtkmm2ext;
45 using namespace ARDOUR;
49 RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt, ARDOUR::Session& sess, const char* m_name,
50 const char* s_name, const char* r_name)
60 remote_control_menu = 0;
61 ignore_toggle = false;
62 wait_for_release = false;
63 route_active_menu_item = 0;
65 if (set_color_from_route()) {
66 set_color (unique_random_color());
69 _route->GoingAway.connect (mem_fun (*this, &RouteUI::route_removed));
70 _route->active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed));
72 mute_button = manage (new BindableToggleButton (_route->mute_control(), m_name ));
73 solo_button = manage (new BindableToggleButton (_route->solo_control(), s_name ));
76 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
78 t->diskstream().RecordEnableChanged.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 (t->rec_enable_control(), r_name ));
84 rec_enable_button->unset_flags (Gtk::CAN_FOCUS);
86 update_rec_display ();
89 mute_button->unset_flags (Gtk::CAN_FOCUS);
90 solo_button->unset_flags (Gtk::CAN_FOCUS);
92 /* map the current state */
103 RouteUI::mute_press(GdkEventButton* ev)
105 if (!ignore_toggle) {
107 if (Keyboard::is_context_menu_event (ev)) {
113 mute_menu->popup(0,0);
117 if (ev->button == 2) {
118 // ctrl-button2 click is the midi binding click
119 // button2-click is "momentary"
121 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
122 wait_for_release = true;
126 if (ev->button == 1 || ev->button == 2) {
128 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
130 /* ctrl-shift-click applies change to all routes */
132 _session.begin_reversible_command (_("mute change"));
133 _session.add_undo (_session.global_mute_memento(this));
134 _session.set_all_mute (!_route->muted());
135 _session.add_redo_no_execute (_session.global_mute_memento(this));
136 _session.commit_reversible_command ();
138 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
140 /* ctrl-click applies change to the mix group.
141 ctrl-button2 is MIDI learn.
144 if (ev->button == 1) {
145 set_mix_group_mute (_route, !_route->muted());
150 /* plain click applies change to this route */
152 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this);
163 RouteUI::mute_release(GdkEventButton* ev)
165 if (!ignore_toggle) {
166 if (wait_for_release){
167 wait_for_release = false;
169 // because the press was the last undoable thing we did
177 RouteUI::solo_press(GdkEventButton* ev)
179 if (!ignore_toggle) {
181 if (Keyboard::is_context_menu_event (ev)) {
183 if (solo_menu == 0) {
187 solo_menu->popup (1, 0);
191 if (ev->button == 2) {
193 // ctrl-button2 click is the midi binding click
194 // button2-click is "momentary"
196 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
197 wait_for_release = true;
201 if (ev->button == 1 || ev->button == 2) {
203 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
205 /* ctrl-shift-click applies change to all routes */
207 _session.begin_reversible_command (_("solo change"));
208 _session.add_undo (_session.global_solo_memento(this));
209 _session.set_all_solo (!_route->soloed());
210 _session.add_redo_no_execute (_session.global_solo_memento(this));
211 _session.commit_reversible_command ();
213 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
215 // ctrl-alt-click: exclusively solo this track, not a toggle */
217 _session.begin_reversible_command (_("solo change"));
218 _session.add_undo (_session.global_solo_memento(this));
219 _session.set_all_solo (false);
220 _route->set_solo (true, this);
221 _session.add_redo_no_execute (_session.global_solo_memento(this));
222 _session.commit_reversible_command ();
224 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
226 // shift-click: set this route to solo safe
228 _route->set_solo_safe (!_route->solo_safe(), this);
229 wait_for_release = false;
231 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
233 /* ctrl-click: solo mix group.
234 ctrl-button2 is MIDI learn.
237 if (ev->button == 1) {
238 set_mix_group_solo (_route, !_route->soloed());
243 /* click: solo this route */
245 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
255 RouteUI::solo_release(GdkEventButton* ev)
257 if (!ignore_toggle) {
258 if (wait_for_release) {
259 wait_for_release = false;
261 // because the press was the last undoable thing we did
271 RouteUI::rec_enable_press(GdkEventButton* ev)
273 if (!ignore_toggle && is_track() && rec_enable_button) {
275 if (ev->button == 2 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
276 // do nothing on midi bind event
278 else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
280 _session.begin_reversible_command (_("rec-enable change"));
281 _session.add_undo (_session.global_record_enable_memento(this));
283 if (rec_enable_button->get_active()) {
284 _session.record_disenable_all ();
286 _session.record_enable_all ();
289 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
290 _session.commit_reversible_command ();
292 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
294 set_mix_group_rec_enable (_route, !_route->record_enabled());
298 reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
300 ignore_toggle = true;
301 rec_enable_button->set_active(audio_track()->record_enabled());
302 ignore_toggle = false;
305 stop_signal (*rec_enable_button, "button-press-event");
312 RouteUI::solo_changed(void* src)
314 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
318 RouteUI::update_solo_display ()
322 if (solo_button->get_active() != (x = _route->soloed())){
323 ignore_toggle = true;
324 solo_button->set_active(x);
325 ignore_toggle = false;
330 if (_route->solo_safe()){
331 solo_button->set_name(safe_solo_button_name());
333 solo_button->set_name(solo_button_name());
338 RouteUI::mute_changed(void* src)
340 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
344 RouteUI::update_mute_display ()
348 if (mute_button->get_active() != (x = _route->muted())){
349 ignore_toggle = true;
350 mute_button->set_active(x);
351 ignore_toggle = false;
356 RouteUI::route_rec_enable_changed (void *src)
358 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
362 RouteUI::session_rec_enable_changed ()
364 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
368 RouteUI::update_rec_display ()
370 bool model = _route->record_enabled();
371 bool view = rec_enable_button->get_active();
373 /* first make sure the button's "depressed" visual
378 ignore_toggle = true;
379 rec_enable_button->set_active (model);
380 ignore_toggle = false;
383 /* now make sure its color state is correct */
387 switch (_session.record_status ()) {
388 case Session::Disabled:
389 case Session::Enabled:
390 if (rec_enable_button->get_state() != Gtk::STATE_ACTIVE) {
391 rec_enable_button->set_state (Gtk::STATE_ACTIVE);
395 case Session::Recording:
396 if (rec_enable_button->get_state() != Gtk::STATE_SELECTED) {
397 rec_enable_button->set_state (Gtk::STATE_SELECTED);
403 if (rec_enable_button->get_state() != Gtk::STATE_NORMAL) {
404 rec_enable_button->set_state (Gtk::STATE_NORMAL);
410 RouteUI::build_remote_control_menu ()
412 remote_control_menu = manage (new Menu);
413 refresh_remote_control_menu ();
417 RouteUI::refresh_remote_control_menu ()
419 using namespace Menu_Helpers;
421 RadioMenuItem::Group rc_group;
422 CheckMenuItem* rc_active;
423 uint32_t limit = _session.ntracks();
426 MenuList& rc_items = remote_control_menu->items();
429 /* note that this menu list starts at zero, not 1, because zero
430 is a valid, if useless, ID.
433 limit += 4; /* leave some breathing room */
435 rc_items.push_back (RadioMenuElem (rc_group, _("None")));
436 if (_route->remote_control_id() == 0) {
437 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
438 rc_active->set_active ();
441 for (uint32_t i = 1; i < limit; ++i) {
442 snprintf (buf, sizeof (buf), "%u", i);
443 rc_items.push_back (RadioMenuElem (rc_group, buf));
444 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
445 if (_route->remote_control_id() == i) {
446 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
447 rc_active->set_active ();
449 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
454 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
456 /* this is called when the radio menu item is toggled, and so
457 is actually invoked twice per menu selection. we only
458 care about the invocation for the item that was being
462 if (item->get_active()) {
463 _route->set_remote_control_id (id);
468 RouteUI::build_solo_menu (void)
470 using namespace Menu_Helpers;
472 solo_menu = new Menu;
473 solo_menu->set_name ("ArdourContextMenu");
474 MenuList& items = solo_menu->items();
475 CheckMenuItem* check;
477 check = new CheckMenuItem(_("Solo-safe"));
478 check->set_active (_route->solo_safe());
479 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
480 _route->solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
481 items.push_back (CheckMenuElem(*check));
484 items.push_back (SeparatorElem());
485 // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
490 RouteUI::build_mute_menu(void)
492 using namespace Menu_Helpers;
494 mute_menu = new Menu;
495 mute_menu->set_name ("ArdourContextMenu");
496 MenuList& items = mute_menu->items();
497 CheckMenuItem* check;
499 check = new CheckMenuItem(_("Pre Fader"));
500 init_mute_menu(PRE_FADER, check);
501 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
502 _route->pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
503 items.push_back (CheckMenuElem(*check));
506 check = new CheckMenuItem(_("Post Fader"));
507 init_mute_menu(POST_FADER, check);
508 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
509 _route->post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
510 items.push_back (CheckMenuElem(*check));
513 check = new CheckMenuItem(_("Control Outs"));
514 init_mute_menu(CONTROL_OUTS, check);
515 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
516 _route->control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
517 items.push_back (CheckMenuElem(*check));
520 check = new CheckMenuItem(_("Main Outs"));
521 init_mute_menu(MAIN_OUTS, check);
522 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
523 _route->main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
524 items.push_back (CheckMenuElem(*check));
527 items.push_back (SeparatorElem());
528 // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
532 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
534 if (_route->get_mute_config (type)) {
535 check->set_active (true);
540 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
542 _route->set_mute_config(type, check->get_active(), this);
546 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
548 _route->set_solo_safe (check->get_active(), this);
552 RouteUI::set_mix_group_solo(boost::shared_ptr<Route> route, bool yn)
554 RouteGroup* mix_group;
556 if((mix_group = route->mix_group()) != 0){
557 _session.begin_reversible_command (_("mix group solo change"));
558 _session.add_undo (_session.global_solo_memento (this));
559 mix_group->apply(&Route::set_solo, yn, this);
560 _session.add_redo_no_execute (_session.global_solo_memento(this));
561 _session.commit_reversible_command ();
563 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route->soloed(), this);
568 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
570 _session.begin_reversible_command (name);
571 _session.add_undo (bind (mem_fun (*_route, func), !yn, (void *) arg));
572 _session.add_redo (bind (mem_fun (*_route, func), yn, (void *) arg));
573 _session.commit_reversible_command ();
577 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
579 _session.begin_reversible_command (name);
580 _session.add_undo (bind (mem_fun (*audio_track(), func), !yn, (void *) arg));
581 _session.add_redo (bind (mem_fun (*audio_track(), func), yn, (void *) arg));
582 _session.commit_reversible_command ();
586 RouteUI::set_mix_group_mute(boost::shared_ptr<Route> route, bool yn)
588 RouteGroup* mix_group;
590 if((mix_group = route->mix_group()) != 0){
591 _session.begin_reversible_command (_("mix group mute change"));
592 _session.add_undo (_session.global_mute_memento (this));
593 mix_group->apply(&Route::set_mute, yn, this);
594 _session.add_redo_no_execute (_session.global_mute_memento(this));
595 _session.commit_reversible_command ();
597 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route->muted(), this);
602 RouteUI::set_mix_group_rec_enable(boost::shared_ptr<Route> route, bool yn)
604 RouteGroup* mix_group;
606 if((mix_group = route->mix_group()) != 0){
607 _session.begin_reversible_command (_("mix group rec-enable change"));
608 _session.add_undo (_session.global_record_enable_memento (this));
609 mix_group->apply (&Route::set_record_enable, yn, this);
610 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
611 _session.commit_reversible_command ();
613 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route->record_enabled(), this);
619 RouteUI::choose_color()
624 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
634 RouteUI::set_color (const Gdk::Color & c)
641 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
642 xml_node->add_property ("color", buf);
644 _route->gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
649 RouteUI::ensure_xml_node ()
652 if ((xml_node = _route->extra_xml ("GUI")) == 0) {
653 xml_node = new XMLNode ("GUI");
654 _route->add_extra_xml (*xml_node);
660 RouteUI::get_child_xml_node (const string & childname)
667 if ((child = find_named_node (*xml_node, childname)) == 0) {
668 child = new XMLNode (childname);
669 xml_node->add_child_nocopy (*child);
676 RouteUI::set_color_from_route ()
680 RouteUI::ensure_xml_node ();
682 if ((prop = xml_node->property ("color")) != 0) {
684 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
694 RouteUI::remove_this_route ()
696 vector<string> choices;
700 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());
702 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route->name());
705 choices.push_back (_("No, do nothing."));
706 choices.push_back (_("Yes, remove it."));
708 Choice prompter (prompt, choices);
710 if (prompter.run () == 1) {
711 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
716 RouteUI::idle_remove_this_route (RouteUI *rui)
718 rui->_session.remove_route (rui->_route);
723 RouteUI::route_removed ()
725 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
731 RouteUI::route_rename ()
733 ArdourPrompter name_prompter (true);
735 name_prompter.set_prompt (_("New Name: "));
736 name_prompter.set_initial_text (_route->name());
737 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
738 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
739 name_prompter.show_all ();
741 switch (name_prompter.run ()) {
743 case Gtk::RESPONSE_ACCEPT:
744 name_prompter.get_result (result);
745 if (result.length()) {
746 _route->set_name (result, this);
756 RouteUI::name_changed (void *src)
758 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
760 name_label.set_text (_route->name());
764 RouteUI::toggle_route_active ()
768 if (route_active_menu_item) {
769 if (route_active_menu_item->get_active() != (yn = _route->active())) {
770 _route->set_active (!yn);
776 RouteUI::route_active_changed ()
778 if (route_active_menu_item) {
779 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route->active()));
784 RouteUI::toggle_polarity ()
786 if (polarity_menu_item) {
790 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
792 if ((x = polarity_menu_item->get_active()) != _route->phase_invert()) {
793 _route->set_phase_invert (x, this);
795 name_label.set_text (X_("Ø ") + name_label.get_text());
797 name_label.set_text (_route->name());
804 RouteUI::polarity_changed ()
806 /* no signal for this yet */
810 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
812 bool yn = _route->solo_safe ();
814 if (check->get_active() != yn) {
815 check->set_active (yn);
819 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
821 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
823 bool yn = _route->get_mute_config(PRE_FADER);
824 if (check->get_active() != yn) {
825 check->set_active (yn);
830 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
832 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
834 bool yn = _route->get_mute_config(POST_FADER);
835 if (check->get_active() != yn) {
836 check->set_active (yn);
841 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
843 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
845 bool yn = _route->get_mute_config(CONTROL_OUTS);
846 if (check->get_active() != yn) {
847 check->set_active (yn);
852 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
854 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
856 bool yn = _route->get_mute_config(MAIN_OUTS);
857 if (check->get_active() != yn) {
858 check->set_active (yn);
863 RouteUI::disconnect_input ()
865 _route->disconnect_inputs (this);
869 RouteUI::disconnect_output ()
871 _route->disconnect_outputs (this);
875 RouteUI::is_track () const
877 return dynamic_cast<Track*>(_route.get()) != 0;
881 RouteUI::track() const
883 return dynamic_cast<Track*>(_route.get());
887 RouteUI::is_audio_track () const
889 return dynamic_cast<AudioTrack*>(_route.get()) != 0;
893 RouteUI::audio_track() const
895 return dynamic_cast<AudioTrack*>(_route.get());
899 RouteUI::is_midi_track () const
901 return dynamic_cast<MidiTrack*>(_route.get()) != 0;
905 RouteUI::midi_track() const
907 return dynamic_cast<MidiTrack*>(_route.get());
911 RouteUI::get_diskstream () const
913 boost::shared_ptr<Track> t;
915 if ((t = boost::dynamic_pointer_cast<Track>(_route)) != 0) {
916 return &t->diskstream();
923 RouteUI::name() const
925 return _route->name();
929 RouteUI::map_frozen ()
931 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
933 AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
936 switch (at->freeze_state()) {
937 case AudioTrack::Frozen:
938 rec_enable_button->set_sensitive (false);
941 rec_enable_button->set_sensitive (true);