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.
20 #include <boost/algorithm/string.hpp>
22 #include <gtkmm2ext/gtk_ui.h>
23 #include <gtkmm2ext/choice.h>
24 #include <gtkmm2ext/doi.h>
25 #include <gtkmm2ext/bindable_button.h>
26 #include <gtkmm2ext/barcontroller.h>
27 #include <gtkmm2ext/gtk_ui.h>
28 #include <gtkmm2ext/utils.h>
30 #include "pbd/memento_command.h"
31 #include "pbd/stacktrace.h"
32 #include "pbd/controllable.h"
33 #include "pbd/enumwriter.h"
35 #include "ardour/dB.h"
36 #include "ardour/route_group.h"
37 #include "ardour/solo_isolate_control.h"
38 #include "ardour/vca.h"
39 #include "ardour/vca_manager.h"
40 #include "ardour/audio_track.h"
41 #include "ardour/audioengine.h"
42 #include "ardour/filename_extensions.h"
43 #include "ardour/midi_track.h"
44 #include "ardour/monitor_control.h"
45 #include "ardour/internal_send.h"
46 #include "ardour/profile.h"
47 #include "ardour/phase_control.h"
48 #include "ardour/send.h"
49 #include "ardour/route.h"
50 #include "ardour/session.h"
51 #include "ardour/template_utils.h"
53 #include "ardour_button.h"
54 #include "ardour_dialog.h"
55 #include "ardour_ui.h"
56 #include "automation_time_axis.h"
58 #include "group_tabs.h"
59 #include "gui_object.h"
60 #include "gui_thread.h"
62 #include "latency_gui.h"
63 #include "mixer_strip.h"
64 #include "plugin_pin_dialog.h"
66 #include "rgb_macros.h"
67 #include "route_time_axis.h"
70 #include "ui_config.h"
76 using namespace Gtkmm2ext;
77 using namespace ARDOUR;
78 using namespace ARDOUR_UI_UTILS;
82 uint32_t RouteUI::_max_invert_buttons = 3;
83 PBD::Signal1<void, boost::shared_ptr<Route> > RouteUI::BusSendDisplayChanged;
84 boost::weak_ptr<Route> RouteUI::_showing_sends_to;
85 std::string RouteUI::program_port_prefix;
87 RouteUI::RouteUI (ARDOUR::Session* sess)
98 if (program_port_prefix.empty()) {
99 // compare to gtk2_ardour/port_group.cc
100 string lpn (PROGRAM_NAME);
101 boost::to_lower (lpn);
102 program_port_prefix = lpn + ":"; // e.g. "ardour:"
113 ARDOUR_UI::instance()->gui_object_state->remove_node (route_state_id());
116 _route.reset (); /* drop reference to route, so that it can be cleaned up */
117 route_connections.drop_connections ();
123 delete comment_window;
124 delete input_selector;
125 delete output_selector;
128 send_blink_connection.disconnect ();
129 rec_blink_connection.disconnect ();
135 self_destruct = true;
141 pre_fader_mute_check = 0;
142 post_fader_mute_check = 0;
143 listen_mute_check = 0;
146 solo_isolated_check = 0;
147 solo_isolated_led = 0;
151 denormal_menu_item = 0;
154 multiple_mute_change = false;
155 multiple_solo_change = false;
156 _i_am_the_modifier = 0;
161 setup_invert_buttons ();
163 mute_button = manage (new ArdourButton);
164 mute_button->set_name ("mute button");
165 UI::instance()->set_tip (mute_button, _("Mute this track"), "");
167 solo_button = manage (new ArdourButton);
168 solo_button->set_name ("solo button");
169 UI::instance()->set_tip (solo_button, _("Mute other (non-soloed) tracks"), "");
170 solo_button->set_no_show_all (true);
172 rec_enable_button = manage (new ArdourButton);
173 rec_enable_button->set_name ("record enable button");
174 rec_enable_button->set_icon (ArdourIcon::RecButton);
175 UI::instance()->set_tip (rec_enable_button, _("Enable recording on this track"), "");
177 if (UIConfiguration::instance().get_blink_rec_arm()) {
178 rec_blink_connection = Timers::blink_connect (sigc::mem_fun (*this, &RouteUI::blink_rec_display));
181 show_sends_button = manage (new ArdourButton);
182 show_sends_button->set_name ("send alert button");
183 UI::instance()->set_tip (show_sends_button, _("make mixer strips show sends to this bus"), "");
185 monitor_input_button = manage (new ArdourButton (ArdourButton::default_elements));
186 monitor_input_button->set_name ("monitor button");
187 monitor_input_button->set_text (_("In"));
188 UI::instance()->set_tip (monitor_input_button, _("Monitor input"), "");
189 monitor_input_button->set_no_show_all (true);
191 monitor_disk_button = manage (new ArdourButton (ArdourButton::default_elements));
192 monitor_disk_button->set_name ("monitor button");
193 monitor_disk_button->set_text (_("Disk"));
194 UI::instance()->set_tip (monitor_disk_button, _("Monitor playback"), "");
195 monitor_disk_button->set_no_show_all (true);
197 _session->SoloChanged.connect (_session_connections, invalidator (*this), boost::bind (&RouteUI::solo_changed_so_update_mute, this), gui_context());
198 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&RouteUI::check_rec_enable_sensitivity, this), gui_context());
199 _session->RecordStateChanged.connect (_session_connections, invalidator (*this), boost::bind (&RouteUI::session_rec_enable_changed, this), gui_context());
201 _session->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteUI::parameter_changed, this, _1), gui_context());
202 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteUI::parameter_changed, this, _1), gui_context());
204 rec_enable_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::rec_enable_press), false);
205 rec_enable_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::rec_enable_release), false);
207 show_sends_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::show_sends_press), false);
208 show_sends_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::show_sends_release), false);
210 solo_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::solo_press), false);
211 solo_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::solo_release), false);
212 mute_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::mute_press), false);
213 mute_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::mute_release), false);
215 monitor_input_button->set_distinct_led_click (false);
216 monitor_disk_button->set_distinct_led_click (false);
218 monitor_input_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::monitor_input_press), false);
219 monitor_input_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::monitor_input_release), false);
221 monitor_disk_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::monitor_disk_press), false);
222 monitor_disk_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::monitor_disk_release), false);
224 BusSendDisplayChanged.connect_same_thread (*this, boost::bind(&RouteUI::bus_send_display_changed, this, _1));
230 route_connections.drop_connections ();
238 denormal_menu_item = 0;
242 RouteUI::self_delete ()
248 RouteUI::set_route (boost::shared_ptr<Route> rp)
254 if (set_color_from_route()) {
255 set_color (gdk_color_to_rgba (AxisView::unique_random_color ()));
259 rp->DropReferences.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::self_delete, this), gui_context());
262 delete input_selector;
265 delete output_selector;
268 mute_button->set_controllable (_route->mute_control());
269 solo_button->set_controllable (_route->solo_control());
271 _route->active_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_active_changed, this), gui_context());
273 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::comment_changed, this), gui_context());
275 _route->mute_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_mute_display, this), gui_context());
276 _route->solo_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
277 _route->solo_safe_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
278 _route->solo_isolate_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
279 _route->phase_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::polarity_changed, this), gui_context());
282 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteUI::map_frozen, this), gui_context());
283 track()->TrackModeChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::track_mode_changed, this), gui_context());
284 track_mode_changed();
288 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_property_changed, this, _1), gui_context());
289 _route->presentation_info().PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_gui_changed, this, _1), gui_context ());
291 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::setup_invert_buttons, this), gui_context ());
293 if (_session->writable() && is_track()) {
294 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
296 t->rec_enable_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context());
297 t->rec_safe_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context());
299 rec_enable_button->show();
300 rec_enable_button->set_controllable (t->rec_enable_control());
302 if (is_midi_track()) {
303 midi_track()->StepEditStatusChange.connect (route_connections, invalidator (*this),
304 boost::bind (&RouteUI::step_edit_changed, this, _1), gui_context());
309 /* this will work for busses and tracks, and needs to be called to
310 set up the name entry/name label display.
314 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
315 t->monitoring_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_monitoring_display, this), gui_context());
317 update_monitoring_display ();
320 mute_button->unset_flags (Gtk::CAN_FOCUS);
321 solo_button->unset_flags (Gtk::CAN_FOCUS);
325 if (_route->is_monitor() || _route->is_master()) {
326 solo_button->hide ();
333 setup_invert_buttons ();
334 set_invert_button_state ();
336 boost::shared_ptr<Route> s = _showing_sends_to.lock ();
337 bus_send_display_changed (s);
339 update_mute_display ();
340 update_solo_display ();
342 if (!UIConfiguration::instance().get_blink_rec_arm()) {
343 blink_rec_display(true); // set initial rec-en button state
346 check_rec_enable_sensitivity ();
347 maybe_add_route_print_mgr ();
348 route_color_changed();
349 route_gui_changed (PropertyChange (Properties::selected));
353 RouteUI::polarity_changed ()
359 set_invert_button_state ();
363 RouteUI::mute_press (GdkEventButton* ev)
365 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
369 //if this is a binding action, let the ArdourButton handle it
370 if ( BindingProxy::is_bind_action(ev) )
373 multiple_mute_change = false;
375 if (Keyboard::is_context_menu_event (ev)) {
381 mute_menu->popup(0,ev->time);
387 if (Keyboard::is_button2_event (ev)) {
388 // button2-click is "momentary"
390 _mute_release = new SoloMuteRelease (_route->mute_control()->muted ());
393 if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
395 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
397 /* toggle mute on everything (but
398 * exclude the master and monitor)
400 * because we are going to erase
401 * elements of the list we need to work
405 boost::shared_ptr<RouteList> copy (new RouteList);
407 *copy = *_session->get_routes ();
409 for (RouteList::iterator i = copy->begin(); i != copy->end(); ) {
410 if ((*i)->is_master() || (*i)->is_monitor()) {
418 _mute_release->routes = copy;
422 _session->set_controls (route_list_to_control_list (copy, &Stripable::mute_control), _route->muted_by_self() ? 0.0 : 1.0, Controllable::UseGroup);
424 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
426 /* Primary-button1 inverts the implication of
427 the group being active. If the group is
428 active (for mute), then this modifier means
429 "do not apply to mute". If the group is
430 inactive (for mute), then this modifier
431 means "apply to route". This is all
432 accomplished by passing just the actual
433 route, along with the InverseGroup group
436 NOTE: Primary-button2 is MIDI learn.
439 boost::shared_ptr<RouteList> rl;
441 if (ev->button == 1) {
443 rl.reset (new RouteList);
444 rl->push_back (_route);
447 _mute_release->routes = rl;
451 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), _route->muted_by_self() ? 0.0 : 1.0, Controllable::InverseGroup);
456 /* plain click applies change to this route */
458 boost::shared_ptr<RouteList> rl (new RouteList);
459 rl->push_back (_route);
462 _mute_release->routes = rl;
465 _session->set_control (_route->mute_control(), _route->muted_by_self() ? 0.0 : 1.0, Controllable::UseGroup);
475 RouteUI::mute_release (GdkEventButton* /*ev*/)
479 _session->set_controls (route_list_to_control_list (_mute_release->routes, &Stripable::mute_control), _mute_release->active, Controllable::UseGroup);
480 delete _mute_release;
488 RouteUI::edit_output_configuration ()
490 if (output_selector == 0) {
492 boost::shared_ptr<Send> send;
493 boost::shared_ptr<IO> output;
495 if ((send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0) {
496 if (!boost::dynamic_pointer_cast<InternalSend>(send)) {
497 output = send->output();
499 output = _route->output ();
502 output = _route->output ();
505 output_selector = new IOSelectorWindow (_session, output);
508 if (output_selector->is_visible()) {
509 output_selector->get_toplevel()->get_window()->raise();
511 output_selector->present ();
514 //output_selector->set_keep_above (true);
518 RouteUI::edit_input_configuration ()
520 if (input_selector == 0) {
521 input_selector = new IOSelectorWindow (_session, _route->input());
524 if (input_selector->is_visible()) {
525 input_selector->get_toplevel()->get_window()->raise();
527 input_selector->present ();
530 //input_selector->set_keep_above (true);
534 RouteUI::solo_press(GdkEventButton* ev)
536 /* ignore double/triple clicks */
538 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
542 //if this is a binding action, let the ArdourButton handle it
543 if ( BindingProxy::is_bind_action(ev) )
546 multiple_solo_change = false;
548 if (Keyboard::is_context_menu_event (ev)) {
550 if (! (solo_isolated_led && solo_isolated_led->is_visible()) ||
551 ! (solo_safe_led && solo_safe_led->is_visible())) {
553 if (solo_menu == 0) {
557 solo_menu->popup (1, ev->time);
562 if (Keyboard::is_button2_event (ev)) {
564 // button2-click is "momentary"
565 _solo_release = new SoloMuteRelease (_route->self_soloed());
568 if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
570 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
572 /* Primary-Tertiary-click applies change to all routes */
575 _solo_release->routes = _session->get_routes ();
579 _session->set_controls (route_list_to_control_list (_session->get_routes(), &Stripable::solo_control), !_route->solo_control()->get_value(), Controllable::UseGroup);
581 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
583 // Primary-Secondary-click: exclusively solo this track
586 _solo_release->exclusive = true;
588 boost::shared_ptr<RouteList> routes = _session->get_routes();
590 for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
591 if ((*i)->soloed ()) {
592 _solo_release->routes_on->push_back (*i);
594 _solo_release->routes_off->push_back (*i);
599 if (Config->get_solo_control_is_listen_control()) {
600 /* ??? we need a just_one_listen() method */
603 boost::shared_ptr<ControlList> cl (new ControlList);
604 cl->push_back (_route->solo_control());
605 _session->set_controls (cl, 1.0, Controllable::NoGroup);
608 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
610 // shift-click: toggle solo isolated status
612 _route->solo_isolate_control()->set_value (_route->solo_isolate_control()->get_value() ? 0.0 : 1.0, Controllable::UseGroup);
613 delete _solo_release;
616 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
618 /* Primary-button1: solo mix group.
619 NOTE: Primary-button2 is MIDI learn.
622 /* Primary-button1 applies change to the mix group even if it is not active
623 NOTE: Primary-button2 is MIDI learn.
626 boost::shared_ptr<RouteList> rl;
628 if (ev->button == 1) {
630 /* Primary-button1 inverts the implication of
631 the group being active. If the group is
632 active (for solo), then this modifier means
633 "do not apply to solo". If the group is
634 inactive (for mute), then this modifier
635 means "apply to route". This is all
636 accomplished by passing just the actual
637 route, along with the InverseGroup group
640 NOTE: Primary-button2 is MIDI learn.
643 rl.reset (new RouteList);
644 rl->push_back (_route);
647 _solo_release->routes = rl;
652 _session->set_controls (route_list_to_control_list (rl, &Stripable::solo_control), !_route->self_soloed(), Controllable::InverseGroup);
655 delete _solo_release;
660 /* click: solo this route */
662 boost::shared_ptr<RouteList> rl (new RouteList);
663 rl->push_back (route());
666 _solo_release->routes = rl;
670 _session->set_controls (route_list_to_control_list (rl, &Stripable::solo_control), !_route->self_soloed(), Controllable::UseGroup);
679 RouteUI::solo_release (GdkEventButton* /*ev*/)
683 if (_solo_release->exclusive) {
687 _session->set_controls (route_list_to_control_list (_solo_release->routes, &Stripable::solo_control), _solo_release->active ? 1.0 : 0.0, Controllable::UseGroup);
690 delete _solo_release;
698 RouteUI::rec_enable_press(GdkEventButton* ev)
700 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
704 //if this is a binding action, let the ArdourButton handle it
705 if ( BindingProxy::is_bind_action(ev) )
708 if (!_session->engine().connected()) {
709 MessageDialog msg (_("Not connected to AudioEngine - cannot engage record"));
714 if (is_midi_track()) {
716 /* rec-enable button exits from step editing */
718 if (midi_track()->step_editing()) {
719 midi_track()->set_step_editing (false);
724 if (is_track() && rec_enable_button) {
726 if (Keyboard::is_button2_event (ev)) {
728 //rec arm does not have a momentary mode
731 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
734 _session->set_controls (route_list_to_control_list (_session->get_routes(), &Stripable::rec_enable_control), !track()->rec_enable_control()->get_value(), Controllable::NoGroup);
736 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
738 /* Primary-button1 applies change to the route group (even if it is not active)
739 NOTE: Primary-button2 is MIDI learn.
742 if (ev->button == 1) {
744 boost::shared_ptr<RouteList> rl;
746 rl.reset (new RouteList);
747 rl->push_back (_route);
750 _session->set_controls (route_list_to_control_list (rl, &Stripable::rec_enable_control), !track()->rec_enable_control()->get_value(), Controllable::InverseGroup);
753 } else if (Keyboard::is_context_menu_event (ev)) {
755 /* do this on release */
759 boost::shared_ptr<Track> trk = track();
760 _session->set_control (trk->rec_enable_control(), !trk->rec_enable_control()->get_value(), Controllable::UseGroup);
768 RouteUI::update_monitoring_display ()
774 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
780 MonitorState ms = t->monitoring_state();
782 if (t->monitoring_control()->monitoring_choice() & MonitorInput) {
783 monitor_input_button->set_active_state (Gtkmm2ext::ExplicitActive);
785 if (ms & MonitoringInput) {
786 monitor_input_button->set_active_state (Gtkmm2ext::ImplicitActive);
788 monitor_input_button->unset_active_state ();
792 if (t->monitoring_control()->monitoring_choice() & MonitorDisk) {
793 monitor_disk_button->set_active_state (Gtkmm2ext::ExplicitActive);
795 if (ms & MonitoringDisk) {
796 monitor_disk_button->set_active_state (Gtkmm2ext::ImplicitActive);
798 monitor_disk_button->unset_active_state ();
804 RouteUI::monitor_input_press(GdkEventButton*)
810 RouteUI::monitor_input_release(GdkEventButton* ev)
812 return monitor_release (ev, MonitorInput);
816 RouteUI::monitor_disk_press (GdkEventButton*)
822 RouteUI::monitor_disk_release (GdkEventButton* ev)
824 return monitor_release (ev, MonitorDisk);
828 RouteUI::monitor_release (GdkEventButton* ev, MonitorChoice monitor_choice)
830 if (ev->button != 1) {
834 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
841 boost::shared_ptr<RouteList> rl;
843 /* XXX for now, monitoring choices are orthogonal. cue monitoring
844 will follow in 3.X but requires mixing the input and playback (disk)
845 signal together, which requires yet more buffers.
848 if (t->monitoring_control()->monitoring_choice() & monitor_choice) {
849 mc = MonitorChoice (t->monitoring_control()->monitoring_choice() & ~monitor_choice);
851 /* this line will change when the options are non-orthogonal */
852 // mc = MonitorChoice (t->monitoring_choice() | monitor_choice);
856 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
857 rl = _session->get_routes ();
859 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
860 if (_route->route_group() && _route->route_group()->is_monitoring()) {
861 rl = _route->route_group()->route_list();
863 rl.reset (new RouteList);
864 rl->push_back (route());
867 rl.reset (new RouteList);
868 rl->push_back (route());
872 _session->set_controls (route_list_to_control_list (rl, &Stripable::monitoring_control), (double) mc, Controllable::UseGroup);
878 RouteUI::build_record_menu ()
881 record_menu = new Menu;
882 record_menu->set_name ("ArdourContextMenu");
883 using namespace Menu_Helpers;
884 MenuList& items = record_menu->items();
886 items.push_back (CheckMenuElem (_("Rec-Safe"), sigc::mem_fun (*this, &RouteUI::toggle_rec_safe)));
887 rec_safe_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
889 if (is_midi_track()) {
890 items.push_back (SeparatorElem());
891 items.push_back (CheckMenuElem (_("Step Entry"), sigc::mem_fun (*this, &RouteUI::toggle_step_edit)));
892 step_edit_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
896 if (step_edit_item) {
897 if (track()->rec_enable_control()->get_value()) {
898 step_edit_item->set_sensitive (false);
900 step_edit_item->set_active (midi_track()->step_editing());
903 rec_safe_item->set_sensitive (!_route->rec_enable_control()->get_value());
904 rec_safe_item->set_active (_route->rec_safe_control()->get_value());
909 RouteUI::toggle_step_edit ()
911 if (!is_midi_track() || track()->rec_enable_control()->get_value()) {
915 midi_track()->set_step_editing (step_edit_item->get_active());
919 RouteUI::toggle_rec_safe ()
921 boost::shared_ptr<AutomationControl> rs = _route->rec_safe_control();
927 /* This check is made inside the control too, but dong it here can't
931 if (_route->rec_enable_control()->get_value()) {
935 rs->set_value (rec_safe_item->get_active (), Controllable::UseGroup);
939 RouteUI::step_edit_changed (bool yn)
942 if (rec_enable_button) {
943 rec_enable_button->set_active_state (Gtkmm2ext::ExplicitActive);
946 start_step_editing ();
948 if (step_edit_item) {
949 step_edit_item->set_active (true);
954 if (rec_enable_button) {
955 rec_enable_button->unset_active_state ();
958 stop_step_editing ();
960 if (step_edit_item) {
961 step_edit_item->set_active (false);
967 RouteUI::rec_enable_release (GdkEventButton* ev)
969 if (Keyboard::is_context_menu_event (ev)) {
970 build_record_menu ();
972 record_menu->popup (1, ev->time);
981 RouteUI::build_sends_menu ()
983 using namespace Menu_Helpers;
985 sends_menu = new Menu;
986 sends_menu->set_name ("ArdourContextMenu");
987 MenuList& items = sends_menu->items();
990 MenuElem(_("Assign all tracks (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PreFader, false))
994 MenuElem(_("Assign all tracks and buses (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PreFader, true))
998 MenuElem(_("Assign all tracks (postfader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PostFader, false))
1002 MenuElem(_("Assign all tracks and buses (postfader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PostFader, true))
1006 MenuElem(_("Assign selected tracks (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PreFader, false))
1010 MenuElem(_("Assign selected tracks and buses (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PreFader, true)));
1013 MenuElem(_("Assign selected tracks (postfader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PostFader, false))
1017 MenuElem(_("Assign selected tracks and buses (postfader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PostFader, true))
1020 items.push_back (MenuElem(_("Copy track/bus gains to sends"), sigc::mem_fun (*this, &RouteUI::set_sends_gain_from_track)));
1021 items.push_back (MenuElem(_("Set sends gain to -inf"), sigc::mem_fun (*this, &RouteUI::set_sends_gain_to_zero)));
1022 items.push_back (MenuElem(_("Set sends gain to 0dB"), sigc::mem_fun (*this, &RouteUI::set_sends_gain_to_unity)));
1027 RouteUI::create_sends (Placement p, bool include_buses)
1029 _session->globally_add_internal_sends (_route, p, include_buses);
1033 RouteUI::create_selected_sends (Placement p, bool include_buses)
1035 boost::shared_ptr<RouteList> rlist (new RouteList);
1036 TrackSelection& selected_tracks (ARDOUR_UI::instance()->the_editor().get_selection().tracks);
1038 for (TrackSelection::iterator i = selected_tracks.begin(); i != selected_tracks.end(); ++i) {
1039 RouteTimeAxisView* rtv;
1041 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
1042 if ((rui = dynamic_cast<RouteUI*>(rtv)) != 0) {
1043 if (include_buses || boost::dynamic_pointer_cast<AudioTrack>(rui->route())) {
1044 rlist->push_back (rui->route());
1050 _session->add_internal_sends (_route, p, rlist);
1054 RouteUI::set_sends_gain_from_track ()
1056 _session->globally_set_send_gains_from_track (_route);
1060 RouteUI::set_sends_gain_to_zero ()
1062 _session->globally_set_send_gains_to_zero (_route);
1066 RouteUI::set_sends_gain_to_unity ()
1068 _session->globally_set_send_gains_to_unity (_route);
1072 RouteUI::show_sends_press(GdkEventButton* ev)
1074 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
1078 if (!is_track() && show_sends_button) {
1080 if (Keyboard::is_button2_event (ev) && Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1082 // do nothing on midi sigc::bind event
1085 } else if (Keyboard::is_context_menu_event (ev)) {
1087 if (sends_menu == 0) {
1088 build_sends_menu ();
1091 sends_menu->popup (0, ev->time);
1095 boost::shared_ptr<Route> s = _showing_sends_to.lock ();
1098 set_showing_sends_to (boost::shared_ptr<Route> ());
1100 set_showing_sends_to (_route);
1109 RouteUI::show_sends_release (GdkEventButton*)
1115 RouteUI::send_blink (bool onoff)
1117 if (!show_sends_button) {
1122 show_sends_button->set_active_state (Gtkmm2ext::ExplicitActive);
1124 show_sends_button->unset_active_state ();
1128 Gtkmm2ext::ActiveState
1129 RouteUI::solo_active_state (boost::shared_ptr<Stripable> s)
1131 boost::shared_ptr<SoloControl> sc = s->solo_control();
1134 return Gtkmm2ext::Off;
1137 if (!sc->can_solo()) {
1138 return Gtkmm2ext::Off;
1142 if (sc->self_soloed()) {
1143 return Gtkmm2ext::ExplicitActive;
1144 } else if (sc->soloed_by_others()) {
1145 return Gtkmm2ext::ImplicitActive;
1147 return Gtkmm2ext::Off;
1151 Gtkmm2ext::ActiveState
1152 RouteUI::solo_isolate_active_state (boost::shared_ptr<Stripable> s)
1154 boost::shared_ptr<SoloIsolateControl> sc = s->solo_isolate_control();
1157 return Gtkmm2ext::Off;
1160 if (s->is_master() || s->is_monitor()) {
1161 return Gtkmm2ext::Off;
1164 if (sc->solo_isolated()) {
1165 return Gtkmm2ext::ExplicitActive;
1167 return Gtkmm2ext::Off;
1171 Gtkmm2ext::ActiveState
1172 RouteUI::solo_safe_active_state (boost::shared_ptr<Stripable> s)
1174 boost::shared_ptr<SoloSafeControl> sc = s->solo_safe_control();
1177 return Gtkmm2ext::Off;
1180 if (s->is_master() || s->is_monitor()) {
1181 return Gtkmm2ext::Off;
1184 if (sc->solo_safe()) {
1185 return Gtkmm2ext::ExplicitActive;
1187 return Gtkmm2ext::Off;
1192 RouteUI::update_solo_display ()
1194 bool yn = _route->solo_safe_control()->solo_safe ();
1196 if (solo_safe_check && solo_safe_check->get_active() != yn) {
1197 solo_safe_check->set_active (yn);
1200 yn = _route->solo_isolate_control()->solo_isolated ();
1202 if (solo_isolated_check && solo_isolated_check->get_active() != yn) {
1203 solo_isolated_check->set_active (yn);
1206 set_button_names ();
1208 if (solo_isolated_led) {
1209 if (_route->solo_isolate_control()->solo_isolated()) {
1210 solo_isolated_led->set_active_state (Gtkmm2ext::ExplicitActive);
1212 solo_isolated_led->unset_active_state ();
1216 if (solo_safe_led) {
1217 if (_route->solo_safe_control()->solo_safe()) {
1218 solo_safe_led->set_active_state (Gtkmm2ext::ExplicitActive);
1220 solo_safe_led->unset_active_state ();
1224 solo_button->set_active_state (solo_active_state (_route));
1226 /* some changes to solo status can affect mute display, so catch up
1229 update_mute_display ();
1233 RouteUI::solo_changed_so_update_mute ()
1235 update_mute_display ();
1239 RouteUI::mute_active_state (Session*, boost::shared_ptr<Stripable> s)
1241 boost::shared_ptr<MuteControl> mc = s->mute_control();
1243 if (s->is_monitor()) {
1244 return Gtkmm2ext::Off;
1248 return Gtkmm2ext::Off;
1251 if (Config->get_show_solo_mutes() && !Config->get_solo_control_is_listen_control ()) {
1253 if (mc->muted_by_self ()) {
1255 return Gtkmm2ext::ExplicitActive;
1256 } else if (mc->muted_by_others_soloing () || mc->muted_by_masters ()) {
1257 /* this will reflect both solo mutes AND master mutes */
1258 return Gtkmm2ext::ImplicitActive;
1260 /* no mute at all */
1261 return Gtkmm2ext::Off;
1266 if (mc->muted_by_self()) {
1268 return Gtkmm2ext::ExplicitActive;
1269 } else if (mc->muted_by_masters ()) {
1270 /* this shows only master mutes, not mute-by-others-soloing */
1271 return Gtkmm2ext::ImplicitActive;
1273 /* no mute at all */
1274 return Gtkmm2ext::Off;
1278 return ActiveState(0);
1282 RouteUI::update_mute_display ()
1288 mute_button->set_active_state (mute_active_state (_session, _route));
1293 RouteUI::route_rec_enable_changed ()
1295 blink_rec_display(true); //this lets the button change "immediately" rather than wait for the next blink
1296 update_monitoring_display ();
1300 RouteUI::session_rec_enable_changed ()
1302 blink_rec_display(true); //this lets the button change "immediately" rather than wait for the next blink
1303 update_monitoring_display ();
1307 RouteUI::blink_rec_display (bool blinkOn)
1309 if (!rec_enable_button || !_route) {
1313 if (boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1321 if (track()->rec_enable_control()->get_value()) {
1322 switch (_session->record_status ()) {
1323 case Session::Recording:
1324 rec_enable_button->set_active_state (Gtkmm2ext::ExplicitActive);
1327 case Session::Disabled:
1328 case Session::Enabled:
1329 if (UIConfiguration::instance().get_blink_rec_arm()) {
1330 rec_enable_button->set_active_state ( blinkOn ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off );
1332 rec_enable_button->set_active_state ( ImplicitActive );
1337 if (step_edit_item) {
1338 step_edit_item->set_sensitive (false);
1342 rec_enable_button->unset_active_state ();
1344 if (step_edit_item) {
1345 step_edit_item->set_sensitive (true);
1349 check_rec_enable_sensitivity ();
1353 RouteUI::build_solo_menu (void)
1355 using namespace Menu_Helpers;
1357 solo_menu = new Menu;
1358 solo_menu->set_name ("ArdourContextMenu");
1359 MenuList& items = solo_menu->items();
1360 Gtk::CheckMenuItem* check;
1362 check = new Gtk::CheckMenuItem(_("Solo Isolate"));
1363 check->set_active (_route->solo_isolate_control()->solo_isolated());
1364 check->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_solo_isolated), check));
1365 items.push_back (CheckMenuElem(*check));
1366 solo_isolated_check = dynamic_cast<Gtk::CheckMenuItem*>(&items.back());
1369 check = new Gtk::CheckMenuItem(_("Solo Safe"));
1370 check->set_active (_route->solo_safe_control()->solo_safe());
1371 check->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_solo_safe), check));
1372 items.push_back (CheckMenuElem(*check));
1373 solo_safe_check = dynamic_cast<Gtk::CheckMenuItem*>(&items.back());
1376 //items.push_back (SeparatorElem());
1377 // items.push_back (MenuElem (_("MIDI Bind"), sigc::mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
1382 RouteUI::build_mute_menu(void)
1384 using namespace Menu_Helpers;
1386 mute_menu = new Menu;
1387 mute_menu->set_name ("ArdourContextMenu");
1389 MenuList& items = mute_menu->items();
1391 pre_fader_mute_check = manage (new Gtk::CheckMenuItem(_("Pre Fader Sends")));
1392 init_mute_menu(MuteMaster::PreFader, pre_fader_mute_check);
1393 pre_fader_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::PreFader, pre_fader_mute_check));
1394 items.push_back (CheckMenuElem(*pre_fader_mute_check));
1395 pre_fader_mute_check->show_all();
1397 post_fader_mute_check = manage (new Gtk::CheckMenuItem(_("Post Fader Sends")));
1398 init_mute_menu(MuteMaster::PostFader, post_fader_mute_check);
1399 post_fader_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::PostFader, post_fader_mute_check));
1400 items.push_back (CheckMenuElem(*post_fader_mute_check));
1401 post_fader_mute_check->show_all();
1403 listen_mute_check = manage (new Gtk::CheckMenuItem(_("Control Outs")));
1404 init_mute_menu(MuteMaster::Listen, listen_mute_check);
1405 listen_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::Listen, listen_mute_check));
1406 items.push_back (CheckMenuElem(*listen_mute_check));
1407 listen_mute_check->show_all();
1409 main_mute_check = manage (new Gtk::CheckMenuItem(_("Main Outs")));
1410 init_mute_menu(MuteMaster::Main, main_mute_check);
1411 main_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::Main, main_mute_check));
1412 items.push_back (CheckMenuElem(*main_mute_check));
1413 main_mute_check->show_all();
1415 //items.push_back (SeparatorElem());
1416 // items.push_back (MenuElem (_("MIDI Bind"), sigc::mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
1418 _route->mute_points_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::muting_change, this), gui_context());
1422 RouteUI::init_mute_menu(MuteMaster::MutePoint mp, Gtk::CheckMenuItem* check)
1424 check->set_active (_route->mute_control()->mute_points() & mp);
1428 RouteUI::toggle_mute_menu(MuteMaster::MutePoint mp, Gtk::CheckMenuItem* check)
1430 if (check->get_active()) {
1431 _route->mute_control()->set_mute_points (MuteMaster::MutePoint (_route->mute_control()->mute_points() | mp));
1433 _route->mute_control()->set_mute_points (MuteMaster::MutePoint (_route->mute_control()->mute_points() & ~mp));
1438 RouteUI::muting_change ()
1440 ENSURE_GUI_THREAD (*this, &RouteUI::muting_change)
1443 MuteMaster::MutePoint current = _route->mute_control()->mute_points ();
1445 yn = (current & MuteMaster::PreFader);
1447 if (pre_fader_mute_check->get_active() != yn) {
1448 pre_fader_mute_check->set_active (yn);
1451 yn = (current & MuteMaster::PostFader);
1453 if (post_fader_mute_check->get_active() != yn) {
1454 post_fader_mute_check->set_active (yn);
1457 yn = (current & MuteMaster::Listen);
1459 if (listen_mute_check->get_active() != yn) {
1460 listen_mute_check->set_active (yn);
1463 yn = (current & MuteMaster::Main);
1465 if (main_mute_check->get_active() != yn) {
1466 main_mute_check->set_active (yn);
1471 RouteUI::solo_isolate_button_release (GdkEventButton* ev)
1473 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) {
1477 bool view = solo_isolated_led->active_state();
1478 bool model = _route->solo_isolate_control()->solo_isolated();
1480 /* called BEFORE the view has changed */
1482 if (ev->button == 1) {
1483 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1486 /* disable isolate for all routes */
1487 DisplaySuspender ds;
1488 _session->set_controls (route_list_to_control_list (_session->get_routes(), &Stripable::solo_isolate_control), 0.0, Controllable::NoGroup);
1490 /* enable isolate for all routes */
1491 DisplaySuspender ds;
1492 _session->set_controls (route_list_to_control_list (_session->get_routes(), &Stripable::solo_isolate_control), 1.0, Controllable::NoGroup);
1497 if (model == view) {
1499 /* flip just this route */
1501 boost::shared_ptr<RouteList> rl (new RouteList);
1502 rl->push_back (_route);
1503 DisplaySuspender ds;
1504 _session->set_controls (route_list_to_control_list (rl, &Stripable::solo_isolate_control), view ? 0.0 : 1.0, Controllable::NoGroup);
1513 RouteUI::solo_safe_button_release (GdkEventButton* ev)
1515 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) {
1519 bool view = solo_safe_led->active_state();
1520 bool model = _route->solo_safe_control()->solo_safe();
1522 if (ev->button == 1) {
1523 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1524 boost::shared_ptr<RouteList> rl (_session->get_routes());
1526 /* disable solo safe for all routes */
1527 DisplaySuspender ds;
1528 for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
1529 (*i)->solo_safe_control()->set_value (0.0, Controllable::NoGroup);
1532 /* enable solo safe for all routes */
1533 DisplaySuspender ds;
1534 for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
1535 (*i)->solo_safe_control()->set_value (1.0, Controllable::NoGroup);
1540 if (model == view) {
1541 /* flip just this route */
1542 _route->solo_safe_control()->set_value (view ? 0.0 : 1.0, Controllable::NoGroup);
1551 RouteUI::toggle_solo_isolated (Gtk::CheckMenuItem* check)
1553 bool view = check->get_active();
1554 bool model = _route->solo_isolate_control()->solo_isolated();
1556 /* called AFTER the view has changed */
1558 if (model != view) {
1559 _route->solo_isolate_control()->set_value (view ? 1.0 : 0.0, Controllable::UseGroup);
1564 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
1566 _route->solo_safe_control()->set_value (check->get_active() ? 1.0 : 0.0, Controllable::UseGroup);
1569 /** Ask the user to choose a colour, and then apply that color to my route
1572 RouteUI::choose_color ()
1575 Gdk::Color c (gdk_color_from_rgb (_route->presentation_info().color()));
1576 Gdk::Color const color = Gtkmm2ext::UI::instance()->get_color (_("Color Selection"), picked, &c);
1579 set_color (gdk_color_to_rgba (color));
1583 /** Set the route's own color. This may not be used for display if
1584 * the route is in a group which shares its color with its routes.
1587 RouteUI::set_color (uint32_t c)
1589 _route->presentation_info().set_color (c);
1592 /** @return GUI state ID for things that are common to the route in all its representations */
1594 RouteUI::route_state_id () const
1596 return string_compose (X_("route %1"), _route->id().to_s());
1600 RouteUI::set_color_from_route ()
1605 /** @return true if this name should be used for the route, otherwise false */
1607 RouteUI::verify_new_route_name (const std::string& name)
1609 if (name.find (':') == string::npos) {
1613 MessageDialog colon_msg (
1614 _("The use of colons (':') is discouraged in track and bus names.\nDo you want to use this new name?"),
1615 false, MESSAGE_QUESTION, BUTTONS_NONE
1618 colon_msg.add_button (_("Use the new name"), Gtk::RESPONSE_ACCEPT);
1619 colon_msg.add_button (_("Re-edit the name"), Gtk::RESPONSE_CANCEL);
1621 return (colon_msg.run () == Gtk::RESPONSE_ACCEPT);
1625 RouteUI::route_rename ()
1627 ArdourPrompter name_prompter (true);
1632 name_prompter.set_title (_("Rename Track"));
1634 name_prompter.set_title (_("Rename Bus"));
1636 name_prompter.set_prompt (_("New name:"));
1637 name_prompter.set_initial_text (_route->name());
1638 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1639 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1640 name_prompter.show_all ();
1643 switch (name_prompter.run ()) {
1644 case Gtk::RESPONSE_ACCEPT:
1645 name_prompter.get_result (result);
1646 name_prompter.hide ();
1647 if (result.length()) {
1648 if (verify_new_route_name (result)) {
1649 _route->set_name (result);
1652 /* back to name prompter */
1656 /* nothing entered, just get out of here */
1671 RouteUI::toggle_comment_editor ()
1673 // if (ignore_toggle) {
1677 if (comment_window && comment_window->is_visible ()) {
1678 comment_window->hide ();
1680 open_comment_editor ();
1686 RouteUI::open_comment_editor ()
1688 if (comment_window == 0) {
1689 setup_comment_editor ();
1693 title = _route->name();
1694 title += _(": comment editor");
1696 comment_window->set_title (title);
1697 comment_window->present();
1701 RouteUI::setup_comment_editor ()
1703 comment_window = new ArdourWindow (""); // title will be reset to show route
1704 comment_window->set_skip_taskbar_hint (true);
1705 comment_window->signal_hide().connect (sigc::mem_fun(*this, &MixerStrip::comment_editor_done_editing));
1706 comment_window->set_default_size (400, 200);
1708 comment_area = manage (new TextView());
1709 comment_area->set_name ("MixerTrackCommentArea");
1710 comment_area->set_wrap_mode (WRAP_WORD);
1711 comment_area->set_editable (true);
1712 comment_area->get_buffer()->set_text (_route->comment());
1713 comment_area->show ();
1715 comment_window->add (*comment_area);
1719 RouteUI::comment_changed ()
1721 ignore_comment_edit = true;
1723 comment_area->get_buffer()->set_text (_route->comment());
1725 ignore_comment_edit = false;
1729 RouteUI::comment_editor_done_editing ()
1731 ENSURE_GUI_THREAD (*this, &MixerStrip::comment_editor_done_editing, src)
1733 string const str = comment_area->get_buffer()->get_text();
1734 if (str == _route->comment ()) {
1738 _route->set_comment (str, this);
1742 RouteUI::set_route_active (bool a, bool apply_to_selection)
1744 if (apply_to_selection) {
1745 ARDOUR_UI::instance()->the_editor().get_selection().tracks.foreach_route_ui (boost::bind (&RouteTimeAxisView::set_route_active, _1, a, false));
1747 _route->set_active (a, this);
1752 RouteUI::duplicate_selected_routes ()
1754 ARDOUR_UI::instance()->start_duplicate_routes();
1758 RouteUI::toggle_denormal_protection ()
1760 if (denormal_menu_item) {
1764 ENSURE_GUI_THREAD (*this, &RouteUI::toggle_denormal_protection)
1766 if ((x = denormal_menu_item->get_active()) != _route->denormal_protection()) {
1767 _route->set_denormal_protection (x);
1773 RouteUI::denormal_protection_changed ()
1775 if (denormal_menu_item) {
1776 denormal_menu_item->set_active (_route->denormal_protection());
1781 RouteUI::disconnect_input ()
1783 _route->input()->disconnect (this);
1787 RouteUI::disconnect_output ()
1789 _route->output()->disconnect (this);
1793 RouteUI::is_track () const
1795 return boost::dynamic_pointer_cast<Track>(_route) != 0;
1798 boost::shared_ptr<Track>
1799 RouteUI::track() const
1801 return boost::dynamic_pointer_cast<Track>(_route);
1805 RouteUI::is_audio_track () const
1807 return boost::dynamic_pointer_cast<AudioTrack>(_route) != 0;
1810 boost::shared_ptr<AudioTrack>
1811 RouteUI::audio_track() const
1813 return boost::dynamic_pointer_cast<AudioTrack>(_route);
1817 RouteUI::is_midi_track () const
1819 return boost::dynamic_pointer_cast<MidiTrack>(_route) != 0;
1822 boost::shared_ptr<MidiTrack>
1823 RouteUI::midi_track() const
1825 return boost::dynamic_pointer_cast<MidiTrack>(_route);
1829 RouteUI::has_audio_outputs () const
1831 return (_route->n_outputs().n_audio() > 0);
1835 RouteUI::map_frozen ()
1837 ENSURE_GUI_THREAD (*this, &RouteUI::map_frozen)
1839 AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
1842 check_rec_enable_sensitivity ();
1847 RouteUI::adjust_latency ()
1849 LatencyDialog dialog (_route->name() + _(" latency"), *(_route->output()), _session->frame_rate(), AudioEngine::instance()->samples_per_cycle());
1853 RouteUI::process_save_template_prompter (ArdourPrompter& prompter, const std::string& dir)
1856 std::string safe_name;
1859 prompter.get_result (name, true);
1861 safe_name = legalize_for_path (name);
1862 safe_name += template_suffix;
1864 path = Glib::build_filename (dir, safe_name);
1866 if (Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
1867 bool overwrite = overwrite_file_dialog (prompter,
1868 _("Confirm Template Overwrite"),
1869 _("A template already exists with that name. Do you want to overwrite it?"));
1876 _route->save_as_template (path, name);
1882 RouteUI::save_as_template ()
1886 dir = ARDOUR::user_route_template_directory ();
1888 if (g_mkdir_with_parents (dir.c_str(), 0755)) {
1889 error << string_compose (_("Cannot create route template directory %1"), dir) << endmsg;
1893 ArdourPrompter prompter (true); // modal
1895 prompter.set_title (_("Save As Template"));
1896 prompter.set_prompt (_("Template name:"));
1897 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
1899 bool finished = false;
1901 switch (prompter.run()) {
1902 case RESPONSE_ACCEPT:
1903 finished = process_save_template_prompter (prompter, dir);
1913 RouteUI::check_rec_enable_sensitivity ()
1915 if (!rec_enable_button) {
1916 assert (0); // This should not happen
1919 if (!_session->writable()) {
1920 rec_enable_button->set_sensitive (false);
1924 if (_session->transport_rolling() && rec_enable_button->active_state() && Config->get_disable_disarm_during_roll()) {
1925 rec_enable_button->set_sensitive (false);
1926 } else if (is_audio_track () && track()->freeze_state() == AudioTrack::Frozen) {
1927 rec_enable_button->set_sensitive (false);
1929 rec_enable_button->set_sensitive (true);
1931 if (_route && _route->rec_safe_control () && _route->rec_safe_control()->get_value()) {
1932 rec_enable_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
1934 rec_enable_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
1936 update_monitoring_display ();
1940 RouteUI::parameter_changed (string const & p)
1942 /* this handles RC and per-session parameter changes */
1944 if (p == "disable-disarm-during-roll") {
1945 check_rec_enable_sensitivity ();
1946 } else if (p == "use-monitor-bus" || p == "solo-control-is-listen-control" || p == "listen-position") {
1947 set_button_names ();
1948 } else if (p == "auto-input") {
1949 update_monitoring_display ();
1950 } else if (p == "blink-rec-arm") {
1951 if (UIConfiguration::instance().get_blink_rec_arm()) {
1952 rec_blink_connection.disconnect ();
1953 rec_blink_connection = Timers::blink_connect (sigc::mem_fun (*this, &RouteUI::blink_rec_display));
1955 rec_blink_connection.disconnect ();
1956 RouteUI::blink_rec_display(false);
1962 RouteUI::step_gain_up ()
1964 _route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.1), Controllable::UseGroup);
1968 RouteUI::page_gain_up ()
1970 _route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.5), Controllable::UseGroup);
1974 RouteUI::step_gain_down ()
1976 _route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.1), Controllable::UseGroup);
1980 RouteUI::page_gain_down ()
1982 _route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.5), Controllable::UseGroup);
1986 RouteUI::setup_invert_buttons ()
1988 /* remove old invert buttons */
1989 for (vector<ArdourButton*>::iterator i = _invert_buttons.begin(); i != _invert_buttons.end(); ++i) {
1990 _invert_button_box.remove (**i);
1993 _invert_buttons.clear ();
1995 if (!_route || !_route->input()) {
1999 uint32_t const N = _route->input()->n_ports().n_audio ();
2001 uint32_t const to_add = (N <= _max_invert_buttons) ? N : 1;
2003 for (uint32_t i = 0; i < to_add; ++i) {
2004 ArdourButton* b = manage (new ArdourButton);
2005 b->signal_button_press_event().connect (sigc::mem_fun (*this, &RouteUI::invert_press), false);
2006 b->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::invert_release), i), false);
2008 b->set_name (X_("invert button"));
2011 b->set_text (string_compose (X_("Ø (%1)"), N));
2013 b->set_text (X_("Ø"));
2016 b->set_text (string_compose (X_("Ø%1"), i + 1));
2019 if (N <= _max_invert_buttons) {
2020 UI::instance()->set_tip (*b, string_compose (_("Left-click to invert polarity of channel %1 of this track. Right-click to show menu."), i + 1));
2022 UI::instance()->set_tip (*b, _("Click to show a menu of channels to invert polarity"));
2025 _invert_buttons.push_back (b);
2026 _invert_button_box.pack_start (*b);
2029 _invert_button_box.set_spacing (1);
2030 _invert_button_box.show_all ();
2034 RouteUI::set_invert_button_state ()
2036 uint32_t const N = _route->input()->n_ports().n_audio();
2037 if (N > _max_invert_buttons) {
2039 /* One button for many channels; explicit active if all channels are inverted,
2040 implicit active if some are, off if none are.
2043 ArdourButton* b = _invert_buttons.front ();
2045 if (_route->phase_control()->count() == _route->phase_control()->size()) {
2046 b->set_active_state (Gtkmm2ext::ExplicitActive);
2047 } else if (_route->phase_control()->any()) {
2048 b->set_active_state (Gtkmm2ext::ImplicitActive);
2050 b->set_active_state (Gtkmm2ext::Off);
2055 /* One button per channel; just set active */
2058 for (vector<ArdourButton*>::iterator i = _invert_buttons.begin(); i != _invert_buttons.end(); ++i, ++j) {
2059 (*i)->set_active (_route->phase_control()->inverted (j));
2066 RouteUI::invert_release (GdkEventButton* ev, uint32_t i)
2068 if (ev->button == 1 && i < _invert_buttons.size()) {
2069 uint32_t const N = _route->input()->n_ports().n_audio ();
2070 if (N <= _max_invert_buttons) {
2071 /* left-click inverts phase so long as we have a button per channel */
2072 _route->phase_control()->set_phase_invert (i, !_invert_buttons[i]->get_active());
2081 RouteUI::invert_press (GdkEventButton* ev)
2083 using namespace Menu_Helpers;
2085 uint32_t const N = _route->input()->n_ports().n_audio();
2086 if (N <= _max_invert_buttons && ev->button != 3) {
2087 /* If we have an invert button per channel, we only pop
2088 up a menu on right-click; left click is handled
2094 delete _invert_menu;
2095 _invert_menu = new Menu;
2096 _invert_menu->set_name ("ArdourContextMenu");
2097 MenuList& items = _invert_menu->items ();
2099 for (uint32_t i = 0; i < N; ++i) {
2100 items.push_back (CheckMenuElem (string_compose (X_("Ø%1"), i + 1), sigc::bind (sigc::mem_fun (*this, &RouteUI::invert_menu_toggled), i)));
2101 Gtk::CheckMenuItem* e = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
2102 ++_i_am_the_modifier;
2103 e->set_active (_route->phase_control()->inverted (i));
2104 --_i_am_the_modifier;
2107 _invert_menu->popup (0, ev->time);
2113 RouteUI::invert_menu_toggled (uint32_t c)
2115 if (_i_am_the_modifier) {
2120 _route->phase_control()->set_phase_invert (c, !_route->phase_control()->inverted (c));
2124 RouteUI::set_invert_sensitive (bool yn)
2126 for (vector<ArdourButton*>::iterator b = _invert_buttons.begin(); b != _invert_buttons.end(); ++b) {
2127 (*b)->set_sensitive (yn);
2132 RouteUI::request_redraw ()
2135 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2139 /** The Route's gui_changed signal has been emitted */
2141 RouteUI::route_gui_changed (PropertyChange const& what_changed)
2143 if (what_changed.contains (Properties::color)) {
2144 if (set_color_from_route () == 0) {
2145 route_color_changed ();
2149 if (what_changed.contains (Properties::selected)) {
2155 RouteUI::set_selected (bool yn)
2157 Selectable::set_selected (yn);
2159 _route->presentation_info().set_selected (yn);
2164 RouteUI::selected () const
2166 /* XXX not sure if this is a wise design. Probably don't really want
2167 * the cached _selected value from Selectable.
2174 return _route->presentation_info().selected();
2178 RouteUI::track_mode_changed (void)
2181 switch (track()->mode()) {
2182 case ARDOUR::NonLayered:
2183 case ARDOUR::Normal:
2184 rec_enable_button->set_icon (ArdourIcon::RecButton);
2186 case ARDOUR::Destructive:
2187 rec_enable_button->set_icon (ArdourIcon::RecTapeMode);
2190 rec_enable_button->queue_draw();
2193 /** @return the color that this route should use; it maybe its own,
2194 or it maybe that of its route group.
2198 RouteUI::route_color () const
2201 RouteGroup* g = _route->route_group ();
2204 if (g && g->is_color()) {
2205 set_color_from_rgba (c, GroupTabs::group_color (g));
2208 /* deal with older 4.x color, which was stored in the GUI object state */
2210 string p = ARDOUR_UI::instance()->gui_object_state->get_string (route_state_id(), X_("color"));
2214 /* old v4.x or earlier session. Use this information */
2216 int red, green, blue;
2219 stringstream ss (p);
2221 /* old color format version was:
2223 16bit value for red:16 bit value for green:16 bit value for blue
2238 _route->presentation_info().set_color (RGBA_TO_UINT (red, green, blue, 255));
2241 set_color_from_rgba (c, _route->presentation_info().color());
2248 RouteUI::set_showing_sends_to (boost::shared_ptr<Route> send_to)
2250 _showing_sends_to = send_to;
2251 BusSendDisplayChanged (send_to); /* EMIT SIGNAL */
2255 RouteUI::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2257 if (_route == send_to) {
2258 show_sends_button->set_active (true);
2259 send_blink_connection = Timers::blink_connect (sigc::mem_fun (*this, &RouteUI::send_blink));
2261 show_sends_button->set_active (false);
2262 send_blink_connection.disconnect ();
2267 RouteUI::route_group() const
2269 return _route->route_group();
2273 RoutePinWindowProxy::RoutePinWindowProxy(std::string const &name, boost::shared_ptr<ARDOUR::Route> route)
2274 : WM::ProxyBase (name, string())
2275 , _route (boost::weak_ptr<Route> (route))
2277 route->DropReferences.connect (going_away_connection, MISSING_INVALIDATOR, boost::bind (&RoutePinWindowProxy::route_going_away, this), gui_context());
2280 RoutePinWindowProxy::~RoutePinWindowProxy()
2285 ARDOUR::SessionHandlePtr*
2286 RoutePinWindowProxy::session_handle ()
2288 ArdourWindow* aw = dynamic_cast<ArdourWindow*> (_window);
2289 if (aw) { return aw; }
2294 RoutePinWindowProxy::get (bool create)
2296 boost::shared_ptr<Route> r = _route.lock ();
2305 _window = new PluginPinDialog (r);
2306 ArdourWindow* aw = dynamic_cast<ArdourWindow*> (_window);
2308 aw->set_session (_session);
2310 _window->show_all ();
2316 RoutePinWindowProxy::route_going_away ()
2320 WM::Manager::instance().remove (this);
2321 going_away_connection.disconnect();
2325 RouteUI::maybe_add_route_print_mgr ()
2327 if (_route->pinmgr_proxy ()) {
2330 RoutePinWindowProxy* wp = new RoutePinWindowProxy (
2331 string_compose ("RPM-%1", _route->id()), _route);
2332 wp->set_session (_session);
2334 const XMLNode* ui_xml = _session->extra_xml (X_("UI"));
2336 wp->set_state (*ui_xml, 0);
2340 void* existing_ui = _route->pinmgr_proxy ();
2342 wp->use_window (*(reinterpret_cast<Gtk::Window*>(existing_ui)));
2345 _route->set_pingmgr_proxy (wp);
2347 WM::Manager::instance().register_window (wp);
2351 RouteUI::manage_pins ()
2353 RoutePinWindowProxy* proxy = _route->pinmgr_proxy ();
2361 RouteUI::mark_hidden (bool yn)
2363 if (yn != _route->presentation_info().hidden()) {
2364 _route->presentation_info().set_hidden (yn);
2365 return true; // things changed