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>
29 #include <pbd/stacktrace.h>
30 #include <pbd/shiva.h>
36 #include "gui_thread.h"
38 #include <ardour/route.h>
39 #include <ardour/audio_track.h>
40 #include <ardour/audio_diskstream.h>
45 using namespace Gtkmm2ext;
46 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 new Shiva<Route,RouteUI> (*_route, *this);
71 _route->active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed));
73 mute_button = manage (new BindableToggleButton (_route->mute_control(), m_name ));
74 solo_button = manage (new BindableToggleButton (_route->solo_control(), s_name ));
77 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
79 t->diskstream()->RecordEnableChanged.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed));
81 _session.RecordStateChanged.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed));
83 rec_enable_button = manage (new BindableToggleButton (t->rec_enable_control(), r_name ));
85 rec_enable_button->unset_flags (Gtk::CAN_FOCUS);
87 update_rec_display ();
90 mute_button->unset_flags (Gtk::CAN_FOCUS);
91 solo_button->unset_flags (Gtk::CAN_FOCUS);
93 /* map the current state */
100 GoingAway (); /* EMIT SIGNAL */
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::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(_session, this);
136 _session.set_all_mute (!_route->muted());
138 _session.add_command(cmd);
139 _session.commit_reversible_command ();
141 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
143 /* ctrl-click applies change to the mix group.
144 ctrl-button2 is MIDI learn.
147 if (ev->button == 1) {
148 set_mix_group_mute (_route, !_route->muted());
153 /* plain click applies change to this route */
155 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this);
166 RouteUI::mute_release(GdkEventButton* ev)
168 if (!ignore_toggle) {
169 if (wait_for_release){
170 wait_for_release = false;
172 // because the press was the last undoable thing we did
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::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
212 _session.set_all_solo (!_route->soloed());
214 _session.add_command (cmd);
215 _session.commit_reversible_command ();
217 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
219 // ctrl-alt-click: exclusively solo this track, not a toggle */
221 _session.begin_reversible_command (_("solo change"));
222 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (_session, this);
223 _session.set_all_solo (false);
224 _route->set_solo (true, this);
226 _session.add_command(cmd);
227 _session.commit_reversible_command ();
229 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
231 // shift-click: set this route to solo safe
233 _route->set_solo_safe (!_route->solo_safe(), this);
234 wait_for_release = false;
236 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
238 /* ctrl-click: solo mix group.
239 ctrl-button2 is MIDI learn.
242 if (ev->button == 1) {
243 set_mix_group_solo (_route, !_route->soloed());
248 /* click: solo this route */
250 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
260 RouteUI::solo_release(GdkEventButton* ev)
262 if (!ignore_toggle) {
263 if (wait_for_release) {
264 wait_for_release = false;
266 // because the press was the last undoable thing we did
276 RouteUI::rec_enable_press(GdkEventButton* ev)
278 if (!ignore_toggle && is_track() && rec_enable_button) {
280 if (ev->button == 2 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
281 // do nothing on midi bind event
283 else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
285 _session.begin_reversible_command (_("rec-enable change"));
286 Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
288 if (rec_enable_button->get_active()) {
289 _session.record_disenable_all ();
291 _session.record_enable_all ();
295 _session.add_command(cmd);
296 _session.commit_reversible_command ();
298 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
300 set_mix_group_rec_enable (_route, !_route->record_enabled());
304 reversibly_apply_audio_track_boolean ("rec-enable change", &AudioTrack::set_record_enable, !audio_track()->record_enabled(), this);
306 ignore_toggle = true;
307 rec_enable_button->set_active(audio_track()->record_enabled());
308 ignore_toggle = false;
311 stop_signal (*rec_enable_button, "button-press-event");
318 RouteUI::solo_changed(void* src)
320 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
324 RouteUI::update_solo_display ()
328 if (solo_button->get_active() != (x = _route->soloed())){
329 ignore_toggle = true;
330 solo_button->set_active(x);
331 ignore_toggle = false;
336 if (_route->solo_safe()){
337 solo_button->set_name(safe_solo_button_name());
339 solo_button->set_name(solo_button_name());
344 RouteUI::mute_changed(void* src)
346 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
350 RouteUI::update_mute_display ()
354 if (mute_button->get_active() != (x = _route->muted())){
355 ignore_toggle = true;
356 mute_button->set_active(x);
357 ignore_toggle = false;
362 RouteUI::route_rec_enable_changed ()
364 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
368 RouteUI::session_rec_enable_changed ()
370 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_rec_display));
374 RouteUI::update_rec_display ()
376 bool model = _route->record_enabled();
377 bool view = rec_enable_button->get_active();
379 /* first make sure the button's "depressed" visual
384 ignore_toggle = true;
385 rec_enable_button->set_active (model);
386 ignore_toggle = false;
389 /* now make sure its color state is correct */
393 switch (_session.record_status ()) {
394 case Session::Disabled:
395 case Session::Enabled:
396 if (rec_enable_button->get_state() != Gtk::STATE_ACTIVE) {
397 rec_enable_button->set_state (Gtk::STATE_ACTIVE);
401 case Session::Recording:
402 if (rec_enable_button->get_state() != Gtk::STATE_SELECTED) {
403 rec_enable_button->set_state (Gtk::STATE_SELECTED);
409 if (rec_enable_button->get_state() != Gtk::STATE_NORMAL) {
410 rec_enable_button->set_state (Gtk::STATE_NORMAL);
416 RouteUI::build_remote_control_menu ()
418 remote_control_menu = manage (new Menu);
419 refresh_remote_control_menu ();
423 RouteUI::refresh_remote_control_menu ()
425 using namespace Menu_Helpers;
427 RadioMenuItem::Group rc_group;
428 CheckMenuItem* rc_active;
429 uint32_t limit = _session.ntracks();
432 MenuList& rc_items = remote_control_menu->items();
435 /* note that this menu list starts at zero, not 1, because zero
436 is a valid, if useless, ID.
439 limit += 4; /* leave some breathing room */
441 rc_items.push_back (RadioMenuElem (rc_group, _("None")));
442 if (_route->remote_control_id() == 0) {
443 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
444 rc_active->set_active ();
447 for (uint32_t i = 1; i < limit; ++i) {
448 snprintf (buf, sizeof (buf), "%u", i);
449 rc_items.push_back (RadioMenuElem (rc_group, buf));
450 rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
451 if (_route->remote_control_id() == i) {
452 rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
453 rc_active->set_active ();
455 rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
460 RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
462 /* this is called when the radio menu item is toggled, and so
463 is actually invoked twice per menu selection. we only
464 care about the invocation for the item that was being
468 if (item->get_active()) {
469 _route->set_remote_control_id (id);
474 RouteUI::build_solo_menu (void)
476 using namespace Menu_Helpers;
478 solo_menu = new Menu;
479 solo_menu->set_name ("ArdourContextMenu");
480 MenuList& items = solo_menu->items();
481 CheckMenuItem* check;
483 check = new CheckMenuItem(_("Solo-safe"));
484 check->set_active (_route->solo_safe());
485 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
486 _route->solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
487 items.push_back (CheckMenuElem(*check));
490 items.push_back (SeparatorElem());
491 // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
496 RouteUI::build_mute_menu(void)
498 using namespace Menu_Helpers;
500 mute_menu = new Menu;
501 mute_menu->set_name ("ArdourContextMenu");
502 MenuList& items = mute_menu->items();
503 CheckMenuItem* check;
505 check = new CheckMenuItem(_("Pre Fader"));
506 init_mute_menu(PRE_FADER, check);
507 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
508 _route->pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
509 items.push_back (CheckMenuElem(*check));
512 check = new CheckMenuItem(_("Post Fader"));
513 init_mute_menu(POST_FADER, check);
514 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
515 _route->post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
516 items.push_back (CheckMenuElem(*check));
519 check = new CheckMenuItem(_("Control Outs"));
520 init_mute_menu(CONTROL_OUTS, check);
521 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
522 _route->control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
523 items.push_back (CheckMenuElem(*check));
526 check = new CheckMenuItem(_("Main Outs"));
527 init_mute_menu(MAIN_OUTS, check);
528 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
529 _route->main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
530 items.push_back (CheckMenuElem(*check));
533 items.push_back (SeparatorElem());
534 // items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
538 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
540 if (_route->get_mute_config (type)) {
541 check->set_active (true);
546 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
548 _route->set_mute_config(type, check->get_active(), this);
552 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
554 _route->set_solo_safe (check->get_active(), this);
558 RouteUI::set_mix_group_solo(boost::shared_ptr<Route> route, bool yn)
560 RouteGroup* mix_group;
562 if((mix_group = route->mix_group()) != 0){
563 _session.begin_reversible_command (_("mix group solo change"));
564 Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
565 mix_group->apply(&Route::set_solo, yn, this);
567 _session.add_command (cmd);
568 _session.commit_reversible_command ();
570 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route->soloed(), this);
575 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
577 _session.begin_reversible_command (name);
578 XMLNode &before = _route->get_state();
579 bind(mem_fun(*_route, func), yn, arg)();
580 XMLNode &after = _route->get_state();
581 _session.add_command (new MementoCommand<Route>(*_route, &before, &after));
582 _session.commit_reversible_command ();
586 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
588 _session.begin_reversible_command (name);
589 XMLNode &before = audio_track()->get_state();
590 bind (mem_fun (*audio_track(), func), yn, arg)();
591 XMLNode &after = audio_track()->get_state();
592 _session.add_command (new MementoCommand<AudioTrack>(*audio_track(), &before, &after));
593 _session.commit_reversible_command ();
597 RouteUI::set_mix_group_mute(boost::shared_ptr<Route> route, bool yn)
599 RouteGroup* mix_group;
601 if((mix_group = route->mix_group()) != 0){
602 _session.begin_reversible_command (_("mix group mute change"));
603 Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand (_session, this);
604 mix_group->apply(&Route::set_mute, yn, this);
606 _session.add_command(cmd);
607 _session.commit_reversible_command ();
609 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route->muted(), this);
614 RouteUI::set_mix_group_rec_enable(boost::shared_ptr<Route> route, bool yn)
616 RouteGroup* mix_group;
618 if((mix_group = route->mix_group()) != 0){
619 _session.begin_reversible_command (_("mix group rec-enable change"));
620 Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
621 mix_group->apply (&Route::set_record_enable, yn, this);
623 _session.add_command(cmd);
624 _session.commit_reversible_command ();
626 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route->record_enabled(), this);
632 RouteUI::choose_color()
637 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, &_color);
647 RouteUI::set_color (const Gdk::Color & c)
654 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
655 xml_node->add_property ("color", buf);
657 _route->gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
662 RouteUI::ensure_xml_node ()
665 if ((xml_node = _route->extra_xml ("GUI")) == 0) {
666 xml_node = new XMLNode ("GUI");
667 _route->add_extra_xml (*xml_node);
673 RouteUI::get_child_xml_node (const string & childname)
680 if ((child = find_named_node (*xml_node, childname)) == 0) {
681 child = new XMLNode (childname);
682 xml_node->add_child_nocopy (*child);
689 RouteUI::set_color_from_route ()
693 RouteUI::ensure_xml_node ();
695 if ((prop = xml_node->property ("color")) != 0) {
697 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
707 RouteUI::remove_this_route ()
709 vector<string> choices;
713 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());
715 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route->name());
718 choices.push_back (_("No, do nothing."));
719 choices.push_back (_("Yes, remove it."));
721 Choice prompter (prompt, choices);
723 if (prompter.run () == 1) {
724 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
729 RouteUI::idle_remove_this_route (RouteUI *rui)
731 rui->_session.remove_route (rui->_route);
736 RouteUI::route_rename ()
738 ArdourPrompter name_prompter (true);
740 name_prompter.set_prompt (_("New Name: "));
741 name_prompter.set_initial_text (_route->name());
742 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
743 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
744 name_prompter.show_all ();
746 switch (name_prompter.run ()) {
748 case Gtk::RESPONSE_ACCEPT:
749 name_prompter.get_result (result);
750 if (result.length()) {
751 _route->set_name (result, this);
761 RouteUI::name_changed (void *src)
763 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
765 name_label.set_text (_route->name());
769 RouteUI::toggle_route_active ()
773 if (route_active_menu_item) {
774 if (route_active_menu_item->get_active() != (yn = _route->active())) {
775 _route->set_active (!yn);
781 RouteUI::route_active_changed ()
783 if (route_active_menu_item) {
784 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route->active()));
789 RouteUI::toggle_polarity ()
791 if (polarity_menu_item) {
795 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
797 if ((x = polarity_menu_item->get_active()) != _route->phase_invert()) {
798 _route->set_phase_invert (x, this);
800 name_label.set_text (X_("Ø ") + name_label.get_text());
802 name_label.set_text (_route->name());
809 RouteUI::polarity_changed ()
811 /* no signal for this yet */
815 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
817 bool yn = _route->solo_safe ();
819 if (check->get_active() != yn) {
820 check->set_active (yn);
824 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
826 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
828 bool yn = _route->get_mute_config(PRE_FADER);
829 if (check->get_active() != yn) {
830 check->set_active (yn);
835 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
837 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
839 bool yn = _route->get_mute_config(POST_FADER);
840 if (check->get_active() != yn) {
841 check->set_active (yn);
846 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
848 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
850 bool yn = _route->get_mute_config(CONTROL_OUTS);
851 if (check->get_active() != yn) {
852 check->set_active (yn);
857 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
859 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
861 bool yn = _route->get_mute_config(MAIN_OUTS);
862 if (check->get_active() != yn) {
863 check->set_active (yn);
868 RouteUI::disconnect_input ()
870 _route->disconnect_inputs (this);
874 RouteUI::disconnect_output ()
876 _route->disconnect_outputs (this);
880 RouteUI::is_track () const
882 return dynamic_cast<Track*>(_route.get()) != 0;
886 RouteUI::track() const
888 return dynamic_cast<Track*>(_route.get());
892 RouteUI::is_audio_track () const
894 return dynamic_cast<AudioTrack*>(_route.get()) != 0;
898 RouteUI::audio_track() const
900 return dynamic_cast<AudioTrack*>(_route.get());
903 boost::shared_ptr<Diskstream>
904 RouteUI::get_diskstream () const
906 boost::shared_ptr<Track> t;
908 if ((t = boost::dynamic_pointer_cast<Track>(_route)) != 0) {
909 return t->diskstream();
911 return boost::shared_ptr<Diskstream> ((Diskstream*) 0);
916 RouteUI::name() const
918 return _route->name();
922 RouteUI::map_frozen ()
924 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
926 AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
929 switch (at->freeze_state()) {
930 case AudioTrack::Frozen:
931 rec_enable_button->set_sensitive (false);
934 rec_enable_button->set_sensitive (true);