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>
28 #include <pbd/memento_command.h>
34 #include "gui_thread.h"
36 #include <ardour/route.h>
37 #include <ardour/audio_track.h>
38 #include <ardour/audio_diskstream.h>
43 using namespace Gtkmm2ext;
44 using namespace ARDOUR;
48 RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt, ARDOUR::Session& sess, const char* m_name,
49 const char* s_name, const char* r_name)
59 remote_control_menu = 0;
60 ignore_toggle = false;
61 wait_for_release = false;
62 route_active_menu_item = 0;
64 if (set_color_from_route()) {
65 set_color (unique_random_color());
68 _route->GoingAway.connect (mem_fun (*this, &RouteUI::route_removed));
69 _route->active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed));
71 mute_button = manage (new BindableToggleButton (_route->mute_control(), m_name ));
72 solo_button = manage (new BindableToggleButton (_route->solo_control(), s_name ));
75 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
77 t->diskstream()->RecordEnableChanged.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 (t->rec_enable_control(), r_name ));
83 rec_enable_button->unset_flags (Gtk::CAN_FOCUS);
85 update_rec_display ();
88 mute_button->unset_flags (Gtk::CAN_FOCUS);
89 solo_button->unset_flags (Gtk::CAN_FOCUS);
91 /* map the current state */
102 RouteUI::mute_press(GdkEventButton* ev)
104 if (!ignore_toggle) {
106 if (Keyboard::is_context_menu_event (ev)) {
112 mute_menu->popup(0,0);
116 if (ev->button == 2) {
117 // ctrl-button2 click is the midi binding click
118 // button2-click is "momentary"
120 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
121 wait_for_release = true;
125 if (ev->button == 1 || ev->button == 2) {
127 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
129 /* ctrl-shift-click applies change to all routes */
131 _session.begin_reversible_command (_("mute change"));
132 Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(_session, this);
133 _session.set_all_mute (!_route->muted());
135 _session.add_command(cmd);
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::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
209 _session.set_all_solo (!_route->soloed());
211 _session.add_command (cmd);
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::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (_session, this);
220 _session.set_all_solo (false);
221 _route->set_solo (true, this);
223 _session.add_command(cmd);
224 _session.commit_reversible_command ();
226 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
228 // shift-click: set this route to solo safe
230 _route->set_solo_safe (!_route->solo_safe(), this);
231 wait_for_release = false;
233 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
235 /* ctrl-click: solo mix group.
236 ctrl-button2 is MIDI learn.
239 if (ev->button == 1) {
240 set_mix_group_solo (_route, !_route->soloed());
245 /* click: solo this route */
247 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
257 RouteUI::solo_release(GdkEventButton* ev)
259 if (!ignore_toggle) {
260 if (wait_for_release) {
261 wait_for_release = false;
263 // because the press was the last undoable thing we did
273 RouteUI::rec_enable_press(GdkEventButton* ev)
275 if (!ignore_toggle && is_track() && rec_enable_button) {
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::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
285 if (rec_enable_button->get_active()) {
286 _session.record_disenable_all ();
288 _session.record_enable_all ();
292 _session.add_command(cmd);
293 _session.commit_reversible_command ();
295 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
297 set_mix_group_rec_enable (_route, !_route->record_enabled());
301 reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
303 ignore_toggle = true;
304 rec_enable_button->set_active(audio_track()->record_enabled());
305 ignore_toggle = false;
308 stop_signal (*rec_enable_button, "button-press-event");
315 RouteUI::solo_changed(void* src)
317 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
321 RouteUI::update_solo_display ()
325 if (solo_button->get_active() != (x = _route->soloed())){
326 ignore_toggle = true;
327 solo_button->set_active(x);
328 ignore_toggle = false;
333 if (_route->solo_safe()){
334 solo_button->set_name(safe_solo_button_name());
336 solo_button->set_name(solo_button_name());
341 RouteUI::mute_changed(void* src)
343 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
347 RouteUI::update_mute_display ()
351 if (mute_button->get_active() != (x = _route->muted())){
352 ignore_toggle = true;
353 mute_button->set_active(x);
354 ignore_toggle = false;
359 RouteUI::route_rec_enable_changed ()
361 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
365 RouteUI::session_rec_enable_changed ()
367 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
371 RouteUI::update_rec_display ()
373 bool model = _route->record_enabled();
374 bool view = rec_enable_button->get_active();
376 /* first make sure the button's "depressed" visual
381 ignore_toggle = true;
382 rec_enable_button->set_active (model);
383 ignore_toggle = false;
386 /* now make sure its color state is correct */
390 switch (_session.record_status ()) {
391 case Session::Disabled:
392 case Session::Enabled:
393 if (rec_enable_button->get_state() != Gtk::STATE_ACTIVE) {
394 rec_enable_button->set_state (Gtk::STATE_ACTIVE);
398 case Session::Recording:
399 if (rec_enable_button->get_state() != Gtk::STATE_SELECTED) {
400 rec_enable_button->set_state (Gtk::STATE_SELECTED);
406 if (rec_enable_button->get_state() != Gtk::STATE_NORMAL) {
407 rec_enable_button->set_state (Gtk::STATE_NORMAL);
413 RouteUI::build_remote_control_menu ()
415 remote_control_menu = manage (new Menu);
416 refresh_remote_control_menu ();
420 RouteUI::refresh_remote_control_menu ()
422 using namespace Menu_Helpers;
424 RadioMenuItem::Group rc_group;
425 CheckMenuItem* rc_active;
426 uint32_t limit = _session.ntracks();
429 MenuList& rc_items = remote_control_menu->items();
432 /* note that this menu list starts at zero, not 1, because zero
433 is a valid, if useless, ID.
436 limit += 4; /* leave some breathing room */
438 rc_items.push_back (RadioMenuElem (rc_group, _("None")));
439 if (_route->remote_control_id() == 0) {
440 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
441 rc_active->set_active ();
444 for (uint32_t i = 1; i < limit; ++i) {
445 snprintf (buf, sizeof (buf), "%u", i);
446 rc_items.push_back (RadioMenuElem (rc_group, buf));
447 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
448 if (_route->remote_control_id() == i) {
449 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
450 rc_active->set_active ();
452 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
457 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
459 /* this is called when the radio menu item is toggled, and so
460 is actually invoked twice per menu selection. we only
461 care about the invocation for the item that was being
465 if (item->get_active()) {
466 _route->set_remote_control_id (id);
471 RouteUI::build_solo_menu (void)
473 using namespace Menu_Helpers;
475 solo_menu = new Menu;
476 solo_menu->set_name ("ArdourContextMenu");
477 MenuList& items = solo_menu->items();
478 CheckMenuItem* check;
480 check = new CheckMenuItem(_("Solo-safe"));
481 check->set_active (_route->solo_safe());
482 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
483 _route->solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
484 items.push_back (CheckMenuElem(*check));
487 items.push_back (SeparatorElem());
488 // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
493 RouteUI::build_mute_menu(void)
495 using namespace Menu_Helpers;
497 mute_menu = new Menu;
498 mute_menu->set_name ("ArdourContextMenu");
499 MenuList& items = mute_menu->items();
500 CheckMenuItem* check;
502 check = new CheckMenuItem(_("Pre Fader"));
503 init_mute_menu(PRE_FADER, check);
504 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
505 _route->pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
506 items.push_back (CheckMenuElem(*check));
509 check = new CheckMenuItem(_("Post Fader"));
510 init_mute_menu(POST_FADER, check);
511 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
512 _route->post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
513 items.push_back (CheckMenuElem(*check));
516 check = new CheckMenuItem(_("Control Outs"));
517 init_mute_menu(CONTROL_OUTS, check);
518 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
519 _route->control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
520 items.push_back (CheckMenuElem(*check));
523 check = new CheckMenuItem(_("Main Outs"));
524 init_mute_menu(MAIN_OUTS, check);
525 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
526 _route->main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
527 items.push_back (CheckMenuElem(*check));
530 items.push_back (SeparatorElem());
531 // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
535 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
537 if (_route->get_mute_config (type)) {
538 check->set_active (true);
543 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
545 _route->set_mute_config(type, check->get_active(), this);
549 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
551 _route->set_solo_safe (check->get_active(), this);
555 RouteUI::set_mix_group_solo(boost::shared_ptr<Route> route, bool yn)
557 RouteGroup* mix_group;
559 if((mix_group = route->mix_group()) != 0){
560 _session.begin_reversible_command (_("mix group solo change"));
561 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
562 mix_group->apply(&Route::set_solo, yn, this);
564 _session.add_command (cmd);
565 _session.commit_reversible_command ();
567 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route->soloed(), this);
572 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
574 _session.begin_reversible_command (name);
575 XMLNode &before = _route->get_state();
576 bind(mem_fun(*_route, func), yn, arg)();
577 XMLNode &after = _route->get_state();
578 _session.add_command (new MementoCommand<Route>(*_route, &before, &after));
579 _session.commit_reversible_command ();
583 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
585 _session.begin_reversible_command (name);
586 XMLNode &before = audio_track()->get_state();
587 bind (mem_fun (*audio_track(), func), yn, arg)();
588 XMLNode &after = audio_track()->get_state();
589 _session.add_command (new MementoCommand<AudioTrack>(*audio_track(), &before, &after));
590 _session.commit_reversible_command ();
594 RouteUI::set_mix_group_mute(boost::shared_ptr<Route> route, bool yn)
596 RouteGroup* mix_group;
598 if((mix_group = route->mix_group()) != 0){
599 _session.begin_reversible_command (_("mix group mute change"));
600 Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand (_session, this);
601 mix_group->apply(&Route::set_mute, yn, this);
603 _session.add_command(cmd);
604 _session.commit_reversible_command ();
606 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route->muted(), this);
611 RouteUI::set_mix_group_rec_enable(boost::shared_ptr<Route> route, bool yn)
613 RouteGroup* mix_group;
615 if((mix_group = route->mix_group()) != 0){
616 _session.begin_reversible_command (_("mix group rec-enable change"));
617 Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
618 mix_group->apply (&Route::set_record_enable, yn, this);
620 _session.add_command(cmd);
621 _session.commit_reversible_command ();
623 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route->record_enabled(), this);
629 RouteUI::choose_color()
634 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
644 RouteUI::set_color (const Gdk::Color & c)
651 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
652 xml_node->add_property ("color", buf);
654 _route->gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
659 RouteUI::ensure_xml_node ()
662 if ((xml_node = _route->extra_xml ("GUI")) == 0) {
663 xml_node = new XMLNode ("GUI");
664 _route->add_extra_xml (*xml_node);
670 RouteUI::get_child_xml_node (const string & childname)
677 if ((child = find_named_node (*xml_node, childname)) == 0) {
678 child = new XMLNode (childname);
679 xml_node->add_child_nocopy (*child);
686 RouteUI::set_color_from_route ()
690 RouteUI::ensure_xml_node ();
692 if ((prop = xml_node->property ("color")) != 0) {
694 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
704 RouteUI::remove_this_route ()
706 vector<string> choices;
710 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());
712 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route->name());
715 choices.push_back (_("No, do nothing."));
716 choices.push_back (_("Yes, remove it."));
718 Choice prompter (prompt, choices);
720 if (prompter.run () == 1) {
721 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
726 RouteUI::idle_remove_this_route (RouteUI *rui)
728 rui->_session.remove_route (rui->_route);
733 RouteUI::route_removed ()
735 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
736 cerr << "Route UI @ " << this << " destroyed by impending end of route\n";
741 RouteUI::route_rename ()
743 ArdourPrompter name_prompter (true);
745 name_prompter.set_prompt (_("New Name: "));
746 name_prompter.set_initial_text (_route->name());
747 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
748 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
749 name_prompter.show_all ();
751 switch (name_prompter.run ()) {
753 case Gtk::RESPONSE_ACCEPT:
754 name_prompter.get_result (result);
755 if (result.length()) {
756 _route->set_name (result, this);
766 RouteUI::name_changed (void *src)
768 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
770 name_label.set_text (_route->name());
774 RouteUI::toggle_route_active ()
778 if (route_active_menu_item) {
779 if (route_active_menu_item->get_active() != (yn = _route->active())) {
780 _route->set_active (!yn);
786 RouteUI::route_active_changed ()
788 if (route_active_menu_item) {
789 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route->active()));
794 RouteUI::toggle_polarity ()
796 if (polarity_menu_item) {
800 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
802 if ((x = polarity_menu_item->get_active()) != _route->phase_invert()) {
803 _route->set_phase_invert (x, this);
805 name_label.set_text (X_("Ø ") + name_label.get_text());
807 name_label.set_text (_route->name());
814 RouteUI::polarity_changed ()
816 /* no signal for this yet */
820 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
822 bool yn = _route->solo_safe ();
824 if (check->get_active() != yn) {
825 check->set_active (yn);
829 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
831 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
833 bool yn = _route->get_mute_config(PRE_FADER);
834 if (check->get_active() != yn) {
835 check->set_active (yn);
840 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
842 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
844 bool yn = _route->get_mute_config(POST_FADER);
845 if (check->get_active() != yn) {
846 check->set_active (yn);
851 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
853 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
855 bool yn = _route->get_mute_config(CONTROL_OUTS);
856 if (check->get_active() != yn) {
857 check->set_active (yn);
862 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
864 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
866 bool yn = _route->get_mute_config(MAIN_OUTS);
867 if (check->get_active() != yn) {
868 check->set_active (yn);
873 RouteUI::disconnect_input ()
875 _route->disconnect_inputs (this);
879 RouteUI::disconnect_output ()
881 _route->disconnect_outputs (this);
885 RouteUI::is_track () const
887 return dynamic_cast<Track*>(_route.get()) != 0;
891 RouteUI::track() const
893 return dynamic_cast<Track*>(_route.get());
897 RouteUI::is_audio_track () const
899 return dynamic_cast<AudioTrack*>(_route.get()) != 0;
903 RouteUI::audio_track() const
905 return dynamic_cast<AudioTrack*>(_route.get());
908 boost::shared_ptr<Diskstream>
909 RouteUI::get_diskstream () const
911 boost::shared_ptr<Track> t;
913 if ((t = boost::dynamic_pointer_cast<Track>(_route)) != 0) {
914 return t->diskstream();
916 return boost::shared_ptr<Diskstream> ((Diskstream*) 0);
921 RouteUI::name() const
923 return _route->name();
927 RouteUI::map_frozen ()
929 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
931 AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
934 switch (at->freeze_state()) {
935 case AudioTrack::Frozen:
936 rec_enable_button->set_sensitive (false);
939 rec_enable_button->set_sensitive (true);