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);
160 return stop_signal (*mute_button, "button-press-event");
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
172 stop_signal (*mute_button, "button-release-event");
179 RouteUI::solo_press(GdkEventButton* ev)
181 if (!ignore_toggle) {
183 if (Keyboard::is_context_menu_event (ev)) {
185 if (solo_menu == 0) {
189 solo_menu->popup (1, 0);
193 if (ev->button == 2) {
195 // ctrl-button2 click is the midi binding click
196 // button2-click is "momentary"
198 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
199 wait_for_release = true;
203 if (ev->button == 1 || ev->button == 2) {
205 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
207 /* ctrl-shift-click applies change to all routes */
209 _session.begin_reversible_command (_("solo change"));
210 _session.add_undo (_session.global_solo_memento(this));
211 _session.set_all_solo (!_route.soloed());
212 _session.add_redo_no_execute (_session.global_solo_memento(this));
213 _session.commit_reversible_command ();
215 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
217 // ctrl-alt-click: exclusively solo this track, not a toggle */
219 _session.begin_reversible_command (_("solo change"));
220 _session.add_undo (_session.global_solo_memento(this));
221 _session.set_all_solo (false);
222 _route.set_solo (true, this);
223 _session.add_redo_no_execute (_session.global_solo_memento(this));
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);
253 return stop_signal (*solo_button, "button-press-event");
257 RouteUI::solo_release(GdkEventButton* ev)
260 if (wait_for_release){
261 wait_for_release = false;
263 // because the press was the last undoable thing we did
267 stop_signal (*solo_button, "button-release-event");
274 RouteUI::rec_enable_press(GdkEventButton* ev)
276 if (!ignore_toggle && is_audio_track()) {
278 if (ev->button == 2 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
279 // do nothing on midi bind event
281 else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
283 _session.begin_reversible_command (_("rec-enable change"));
284 _session.add_undo (_session.global_record_enable_memento(this));
286 if (rec_enable_button->get_active()) {
287 _session.record_disenable_all ();
289 _session.record_enable_all ();
292 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
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 (void *src)
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_solo_menu (void)
415 using namespace Menu_Helpers;
417 solo_menu = new Menu;
418 solo_menu->set_name ("ArdourContextMenu");
419 MenuList& items = solo_menu->items();
420 CheckMenuItem* check;
422 check = new CheckMenuItem(_("Solo-safe"));
423 check->set_active (_route.solo_safe());
424 check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
425 _route.solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
426 items.push_back (CheckMenuElem(*check));
429 items.push_back (SeparatorElem());
430 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
435 RouteUI::build_mute_menu(void)
437 using namespace Menu_Helpers;
439 mute_menu = new Menu;
440 mute_menu->set_name ("ArdourContextMenu");
441 MenuList& items = mute_menu->items();
442 CheckMenuItem* check;
444 check = new CheckMenuItem(_("Pre Fader"));
445 init_mute_menu(PRE_FADER, check);
446 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
447 _route.pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
448 items.push_back (CheckMenuElem(*check));
451 check = new CheckMenuItem(_("Post Fader"));
452 init_mute_menu(POST_FADER, check);
453 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
454 _route.post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
455 items.push_back (CheckMenuElem(*check));
458 check = new CheckMenuItem(_("Control Outs"));
459 init_mute_menu(CONTROL_OUTS, check);
460 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
461 _route.control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
462 items.push_back (CheckMenuElem(*check));
465 check = new CheckMenuItem(_("Main Outs"));
466 init_mute_menu(MAIN_OUTS, check);
467 check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
468 _route.main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
469 items.push_back (CheckMenuElem(*check));
472 items.push_back (SeparatorElem());
473 items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
477 RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
479 if (_route.get_mute_config (type)) {
480 check->set_active (true);
485 RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
487 _route.set_mute_config(type, check->get_active(), this);
491 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
493 _route.set_solo_safe (check->get_active(), this);
497 RouteUI::set_mix_group_solo(Route& route, bool yn)
499 RouteGroup* mix_group;
501 if((mix_group = route.mix_group()) != 0){
502 _session.begin_reversible_command (_("mix group solo change"));
503 _session.add_undo (_session.global_solo_memento (this));
504 mix_group->apply(&Route::set_solo, yn, this);
505 _session.add_redo_no_execute (_session.global_solo_memento(this));
506 _session.commit_reversible_command ();
508 reversibly_apply_route_boolean ("solo change", &Route::set_solo, !route.soloed(), this);
513 RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
515 _session.begin_reversible_command (name);
516 _session.add_undo (bind (mem_fun (_route, func), !yn, (void *) arg));
517 _session.add_redo (bind (mem_fun (_route, func), yn, (void *) arg));
518 _session.commit_reversible_command ();
522 RouteUI::reversibly_apply_audio_track_boolean (string name, void (AudioTrack::*func)(bool, void *), bool yn, void *arg)
524 _session.begin_reversible_command (name);
525 _session.add_undo (bind (mem_fun (*audio_track(), func), !yn, (void *) arg));
526 _session.add_redo (bind (mem_fun (*audio_track(), func), yn, (void *) arg));
527 _session.commit_reversible_command ();
531 RouteUI::set_mix_group_mute(Route& route, bool yn)
533 RouteGroup* mix_group;
535 if((mix_group = route.mix_group()) != 0){
536 _session.begin_reversible_command (_("mix group mute change"));
537 _session.add_undo (_session.global_mute_memento (this));
538 mix_group->apply(&Route::set_mute, yn, this);
539 _session.add_redo_no_execute (_session.global_mute_memento(this));
540 _session.commit_reversible_command ();
542 reversibly_apply_route_boolean ("mute change", &Route::set_mute, !route.muted(), this);
547 RouteUI::set_mix_group_rec_enable(Route& route, bool yn)
549 RouteGroup* mix_group;
551 if((mix_group = route.mix_group()) != 0){
552 _session.begin_reversible_command (_("mix group rec-enable change"));
553 _session.add_undo (_session.global_record_enable_memento (this));
554 mix_group->apply (&Route::set_record_enable, yn, this);
555 _session.add_redo_no_execute (_session.global_record_enable_memento(this));
556 _session.commit_reversible_command ();
558 reversibly_apply_route_boolean ("rec-enable change", &Route::set_record_enable, !_route.record_enabled(), this);
564 RouteUI::choose_color()
570 current.set_red ( _color.get_red() / 65535.0);
571 current.set_green (_color.get_green() / 65535.0);
572 current.set_blue (_color.get_blue() / 65535.0);
575 color = Gtkmm2ext::UI::instance()->get_color (_("ardour: color selection"), picked, ¤t);
585 RouteUI::set_color (Gdk::Color c)
592 snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
593 xml_node->add_property ("color", buf);
595 _route.gui_changed ("color", (void *) 0); /* EMIT_SIGNAL */
600 RouteUI::ensure_xml_node ()
603 if ((xml_node = _route.extra_xml ("GUI")) == 0) {
604 xml_node = new XMLNode ("GUI");
605 _route.add_extra_xml (*xml_node);
611 RouteUI::get_child_xml_node (string childname)
618 if ((child = find_named_node (*xml_node, childname)) == 0) {
619 child = new XMLNode (childname);
620 xml_node->add_child_nocopy (*child);
627 RouteUI::set_color_from_route ()
631 RouteUI::ensure_xml_node ();
633 if ((prop = xml_node->property ("color")) != 0) {
635 sscanf (prop->value().c_str(), "%d:%d:%d", &r, &g, &b);
645 RouteUI::remove_this_route ()
647 vector<string> choices;
650 if (is_audio_track()) {
651 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());
653 prompt = string_compose (_("Do you really want to remove bus \"%1\" ?\n(cannot be undone)"), _route.name());
656 choices.push_back (_("Yes, remove it."));
657 choices.push_back (_("No, do nothing."));
659 Choice prompter (prompt, choices);
661 prompter.chosen.connect (Gtk::Main::quit.slot());
662 prompter.show_all ();
666 if (prompter.get_choice() == 0) {
667 Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
672 RouteUI::idle_remove_this_route (RouteUI *rui)
674 rui->_session.remove_route (rui->_route);
679 RouteUI::route_removed ()
681 ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::route_removed));
687 RouteUI::route_rename ()
689 ArdourPrompter name_prompter (true);
691 name_prompter.set_prompt (_("new name: "));
692 name_prompter.set_initial_text (_route.name());
693 name_prompter.show_all ();
695 switch (name_prompter.run ()) {
697 case GTK_RESPONSE_ACCEPT:
698 name_prompter.get_result (result);
699 if (result.length()) {
700 strip_whitespace_edges (result);
701 _route.set_name (result, this);
711 RouteUI::name_changed (void *src)
713 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::name_changed), src));
715 name_label.set_text (_route.name());
719 RouteUI::toggle_route_active ()
723 if (route_active_menu_item) {
724 if (route_active_menu_item->get_active() != (yn = _route.active())) {
725 _route.set_active (!yn);
731 RouteUI::route_active_changed ()
733 if (route_active_menu_item) {
734 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*route_active_menu_item, &CheckMenuItem::set_active), _route.active()));
739 RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
741 bool yn = _route.solo_safe ();
743 if (check->get_active() != yn) {
744 check->set_active (yn);
748 RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
750 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
752 bool yn = _route.get_mute_config(PRE_FADER);
753 if (check->get_active() != yn) {
754 check->set_active (yn);
759 RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
761 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
763 bool yn = _route.get_mute_config(POST_FADER);
764 if (check->get_active() != yn) {
765 check->set_active (yn);
770 RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
772 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
774 bool yn = _route.get_mute_config(CONTROL_OUTS);
775 if (check->get_active() != yn) {
776 check->set_active (yn);
781 RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
783 ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
785 bool yn = _route.get_mute_config(MAIN_OUTS);
786 if (check->get_active() != yn) {
787 check->set_active (yn);
792 RouteUI::disconnect_input ()
794 _route.disconnect_inputs (this);
798 RouteUI::disconnect_output ()
800 _route.disconnect_outputs (this);
804 RouteUI::is_audio_track () const
806 return dynamic_cast<AudioTrack*>(&_route) != 0;
810 RouteUI::get_diskstream () const
814 if ((at = dynamic_cast<AudioTrack*>(&_route)) != 0) {
815 return &at->disk_stream();
822 RouteUI::audio_track() const
824 return dynamic_cast<AudioTrack*>(&_route);
827 RouteUI::name() const
829 return _route.name();
833 RouteUI::map_frozen ()
835 ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::map_frozen));
837 AudioTrack* at = dynamic_cast<AudioTrack*>(&_route);
840 switch (at->freeze_state()) {
841 case AudioTrack::Frozen:
842 rec_enable_button->set_sensitive (false);
845 rec_enable_button->set_sensitive (true);