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 _route->mute_control()->set_value (!_route->muted_by_self(), Controllable::UseGroup);
474 RouteUI::mute_release (GdkEventButton* /*ev*/)
478 _session->set_controls (route_list_to_control_list (_mute_release->routes, &Stripable::mute_control), _mute_release->active, Controllable::UseGroup);
479 delete _mute_release;
487 RouteUI::edit_output_configuration ()
489 if (output_selector == 0) {
491 boost::shared_ptr<Send> send;
492 boost::shared_ptr<IO> output;
494 if ((send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0) {
495 if (!boost::dynamic_pointer_cast<InternalSend>(send)) {
496 output = send->output();
498 output = _route->output ();
501 output = _route->output ();
504 output_selector = new IOSelectorWindow (_session, output);
507 if (output_selector->is_visible()) {
508 output_selector->get_toplevel()->get_window()->raise();
510 output_selector->present ();
513 //output_selector->set_keep_above (true);
517 RouteUI::edit_input_configuration ()
519 if (input_selector == 0) {
520 input_selector = new IOSelectorWindow (_session, _route->input());
523 if (input_selector->is_visible()) {
524 input_selector->get_toplevel()->get_window()->raise();
526 input_selector->present ();
529 //input_selector->set_keep_above (true);
533 RouteUI::solo_press(GdkEventButton* ev)
535 /* ignore double/triple clicks */
537 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
541 //if this is a binding action, let the ArdourButton handle it
542 if ( BindingProxy::is_bind_action(ev) )
545 multiple_solo_change = false;
547 if (Keyboard::is_context_menu_event (ev)) {
549 if (! (solo_isolated_led && solo_isolated_led->is_visible()) ||
550 ! (solo_safe_led && solo_safe_led->is_visible())) {
552 if (solo_menu == 0) {
556 solo_menu->popup (1, ev->time);
561 if (Keyboard::is_button2_event (ev)) {
563 // button2-click is "momentary"
564 _solo_release = new SoloMuteRelease (_route->self_soloed());
567 if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
569 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
571 /* Primary-Tertiary-click applies change to all routes */
574 _solo_release->routes = _session->get_routes ();
578 _session->set_controls (route_list_to_control_list (_session->get_routes(), &Stripable::solo_control), !_route->solo_control()->get_value(), Controllable::UseGroup);
580 } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
582 // Primary-Secondary-click: exclusively solo this track
585 _solo_release->exclusive = true;
587 boost::shared_ptr<RouteList> routes = _session->get_routes();
589 for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
590 if ((*i)->soloed ()) {
591 _solo_release->routes_on->push_back (*i);
593 _solo_release->routes_off->push_back (*i);
598 if (Config->get_solo_control_is_listen_control()) {
599 /* ??? we need a just_one_listen() method */
602 _route->solo_control()->set_value (1.0, Controllable::NoGroup);
605 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
607 // shift-click: toggle solo isolated status
609 _route->solo_isolate_control()->set_value (_route->solo_isolate_control()->get_value() ? 0.0 : 1.0, Controllable::UseGroup);
610 delete _solo_release;
613 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
615 /* Primary-button1: solo mix group.
616 NOTE: Primary-button2 is MIDI learn.
619 /* Primary-button1 applies change to the mix group even if it is not active
620 NOTE: Primary-button2 is MIDI learn.
623 boost::shared_ptr<RouteList> rl;
625 if (ev->button == 1) {
627 /* Primary-button1 inverts the implication of
628 the group being active. If the group is
629 active (for solo), then this modifier means
630 "do not apply to solo". If the group is
631 inactive (for mute), then this modifier
632 means "apply to route". This is all
633 accomplished by passing just the actual
634 route, along with the InverseGroup group
637 NOTE: Primary-button2 is MIDI learn.
640 rl.reset (new RouteList);
641 rl->push_back (_route);
644 _solo_release->routes = rl;
649 _session->set_controls (route_list_to_control_list (rl, &Stripable::solo_control), !_route->self_soloed(), Controllable::InverseGroup);
652 delete _solo_release;
657 /* click: solo this route */
659 boost::shared_ptr<RouteList> rl (new RouteList);
660 rl->push_back (route());
663 _solo_release->routes = rl;
667 _session->set_controls (route_list_to_control_list (rl, &Stripable::solo_control), !_route->self_soloed(), Controllable::UseGroup);
676 RouteUI::solo_release (GdkEventButton* /*ev*/)
680 if (_solo_release->exclusive) {
684 _session->set_controls (route_list_to_control_list (_solo_release->routes, &Stripable::solo_control), _solo_release->active ? 1.0 : 0.0, Controllable::UseGroup);
687 delete _solo_release;
695 RouteUI::rec_enable_press(GdkEventButton* ev)
697 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
701 //if this is a binding action, let the ArdourButton handle it
702 if ( BindingProxy::is_bind_action(ev) )
705 if (!_session->engine().connected()) {
706 MessageDialog msg (_("Not connected to AudioEngine - cannot engage record"));
711 if (is_midi_track()) {
713 /* rec-enable button exits from step editing */
715 if (midi_track()->step_editing()) {
716 midi_track()->set_step_editing (false);
721 if (is_track() && rec_enable_button) {
723 if (Keyboard::is_button2_event (ev)) {
725 //rec arm does not have a momentary mode
728 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
731 _session->set_controls (route_list_to_control_list (_session->get_routes(), &Stripable::rec_enable_control), !track()->rec_enable_control()->get_value(), Controllable::NoGroup);
733 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
735 /* Primary-button1 applies change to the route group (even if it is not active)
736 NOTE: Primary-button2 is MIDI learn.
739 if (ev->button == 1) {
741 boost::shared_ptr<RouteList> rl;
743 rl.reset (new RouteList);
744 rl->push_back (_route);
747 _session->set_controls (route_list_to_control_list (rl, &Stripable::rec_enable_control), !track()->rec_enable_control()->get_value(), Controllable::InverseGroup);
750 } else if (Keyboard::is_context_menu_event (ev)) {
752 /* do this on release */
756 boost::shared_ptr<Track> trk = track();
757 trk->rec_enable_control()->set_value (!trk->rec_enable_control()->get_value(), Controllable::UseGroup);
765 RouteUI::update_monitoring_display ()
771 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
777 MonitorState ms = t->monitoring_state();
779 if (t->monitoring_control()->monitoring_choice() & MonitorInput) {
780 monitor_input_button->set_active_state (Gtkmm2ext::ExplicitActive);
782 if (ms & MonitoringInput) {
783 monitor_input_button->set_active_state (Gtkmm2ext::ImplicitActive);
785 monitor_input_button->unset_active_state ();
789 if (t->monitoring_control()->monitoring_choice() & MonitorDisk) {
790 monitor_disk_button->set_active_state (Gtkmm2ext::ExplicitActive);
792 if (ms & MonitoringDisk) {
793 monitor_disk_button->set_active_state (Gtkmm2ext::ImplicitActive);
795 monitor_disk_button->unset_active_state ();
801 RouteUI::monitor_input_press(GdkEventButton*)
807 RouteUI::monitor_input_release(GdkEventButton* ev)
809 return monitor_release (ev, MonitorInput);
813 RouteUI::monitor_disk_press (GdkEventButton*)
819 RouteUI::monitor_disk_release (GdkEventButton* ev)
821 return monitor_release (ev, MonitorDisk);
825 RouteUI::monitor_release (GdkEventButton* ev, MonitorChoice monitor_choice)
827 if (ev->button != 1) {
831 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
838 boost::shared_ptr<RouteList> rl;
840 /* XXX for now, monitoring choices are orthogonal. cue monitoring
841 will follow in 3.X but requires mixing the input and playback (disk)
842 signal together, which requires yet more buffers.
845 if (t->monitoring_control()->monitoring_choice() & monitor_choice) {
846 mc = MonitorChoice (t->monitoring_control()->monitoring_choice() & ~monitor_choice);
848 /* this line will change when the options are non-orthogonal */
849 // mc = MonitorChoice (t->monitoring_choice() | monitor_choice);
853 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
854 rl = _session->get_routes ();
856 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
857 if (_route->route_group() && _route->route_group()->is_monitoring()) {
858 rl = _route->route_group()->route_list();
860 rl.reset (new RouteList);
861 rl->push_back (route());
864 rl.reset (new RouteList);
865 rl->push_back (route());
869 _session->set_controls (route_list_to_control_list (rl, &Stripable::monitoring_control), (double) mc, Controllable::UseGroup);
875 RouteUI::build_record_menu ()
878 record_menu = new Menu;
879 record_menu->set_name ("ArdourContextMenu");
880 using namespace Menu_Helpers;
881 MenuList& items = record_menu->items();
883 items.push_back (CheckMenuElem (_("Rec-Safe"), sigc::mem_fun (*this, &RouteUI::toggle_rec_safe)));
884 rec_safe_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
886 if (is_midi_track()) {
887 items.push_back (SeparatorElem());
888 items.push_back (CheckMenuElem (_("Step Entry"), sigc::mem_fun (*this, &RouteUI::toggle_step_edit)));
889 step_edit_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
893 if (step_edit_item) {
894 if (track()->rec_enable_control()->get_value()) {
895 step_edit_item->set_sensitive (false);
897 step_edit_item->set_active (midi_track()->step_editing());
900 rec_safe_item->set_sensitive (!_route->rec_enable_control()->get_value());
901 rec_safe_item->set_active (_route->rec_safe_control()->get_value());
906 RouteUI::toggle_step_edit ()
908 if (!is_midi_track() || track()->rec_enable_control()->get_value()) {
912 midi_track()->set_step_editing (step_edit_item->get_active());
916 RouteUI::toggle_rec_safe ()
918 boost::shared_ptr<AutomationControl> rs = _route->rec_safe_control();
924 /* This check is made inside the control too, but dong it here can't
928 if (_route->rec_enable_control()->get_value()) {
932 rs->set_value (rec_safe_item->get_active (), Controllable::UseGroup);
936 RouteUI::step_edit_changed (bool yn)
939 if (rec_enable_button) {
940 rec_enable_button->set_active_state (Gtkmm2ext::ExplicitActive);
943 start_step_editing ();
945 if (step_edit_item) {
946 step_edit_item->set_active (true);
951 if (rec_enable_button) {
952 rec_enable_button->unset_active_state ();
955 stop_step_editing ();
957 if (step_edit_item) {
958 step_edit_item->set_active (false);
964 RouteUI::rec_enable_release (GdkEventButton* ev)
966 if (Keyboard::is_context_menu_event (ev)) {
967 build_record_menu ();
969 record_menu->popup (1, ev->time);
978 RouteUI::build_sends_menu ()
980 using namespace Menu_Helpers;
982 sends_menu = new Menu;
983 sends_menu->set_name ("ArdourContextMenu");
984 MenuList& items = sends_menu->items();
987 MenuElem(_("Assign all tracks (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PreFader, false))
991 MenuElem(_("Assign all tracks and buses (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PreFader, true))
995 MenuElem(_("Assign all tracks (postfader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PostFader, false))
999 MenuElem(_("Assign all tracks and buses (postfader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PostFader, true))
1003 MenuElem(_("Assign selected tracks (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PreFader, false))
1007 MenuElem(_("Assign selected tracks and buses (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PreFader, true)));
1010 MenuElem(_("Assign selected tracks (postfader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PostFader, false))
1014 MenuElem(_("Assign selected tracks and buses (postfader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PostFader, true))
1017 items.push_back (MenuElem(_("Copy track/bus gains to sends"), sigc::mem_fun (*this, &RouteUI::set_sends_gain_from_track)));
1018 items.push_back (MenuElem(_("Set sends gain to -inf"), sigc::mem_fun (*this, &RouteUI::set_sends_gain_to_zero)));
1019 items.push_back (MenuElem(_("Set sends gain to 0dB"), sigc::mem_fun (*this, &RouteUI::set_sends_gain_to_unity)));
1024 RouteUI::create_sends (Placement p, bool include_buses)
1026 _session->globally_add_internal_sends (_route, p, include_buses);
1030 RouteUI::create_selected_sends (Placement p, bool include_buses)
1032 boost::shared_ptr<RouteList> rlist (new RouteList);
1033 TrackSelection& selected_tracks (ARDOUR_UI::instance()->the_editor().get_selection().tracks);
1035 for (TrackSelection::iterator i = selected_tracks.begin(); i != selected_tracks.end(); ++i) {
1036 RouteTimeAxisView* rtv;
1038 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
1039 if ((rui = dynamic_cast<RouteUI*>(rtv)) != 0) {
1040 if (include_buses || boost::dynamic_pointer_cast<AudioTrack>(rui->route())) {
1041 rlist->push_back (rui->route());
1047 _session->add_internal_sends (_route, p, rlist);
1051 RouteUI::set_sends_gain_from_track ()
1053 _session->globally_set_send_gains_from_track (_route);
1057 RouteUI::set_sends_gain_to_zero ()
1059 _session->globally_set_send_gains_to_zero (_route);
1063 RouteUI::set_sends_gain_to_unity ()
1065 _session->globally_set_send_gains_to_unity (_route);
1069 RouteUI::show_sends_press(GdkEventButton* ev)
1071 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
1075 if (!is_track() && show_sends_button) {
1077 if (Keyboard::is_button2_event (ev) && Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1079 // do nothing on midi sigc::bind event
1082 } else if (Keyboard::is_context_menu_event (ev)) {
1084 if (sends_menu == 0) {
1085 build_sends_menu ();
1088 sends_menu->popup (0, ev->time);
1092 boost::shared_ptr<Route> s = _showing_sends_to.lock ();
1095 set_showing_sends_to (boost::shared_ptr<Route> ());
1097 set_showing_sends_to (_route);
1106 RouteUI::show_sends_release (GdkEventButton*)
1112 RouteUI::send_blink (bool onoff)
1114 if (!show_sends_button) {
1119 show_sends_button->set_active_state (Gtkmm2ext::ExplicitActive);
1121 show_sends_button->unset_active_state ();
1125 Gtkmm2ext::ActiveState
1126 RouteUI::solo_active_state (boost::shared_ptr<Stripable> s)
1128 boost::shared_ptr<SoloControl> sc = s->solo_control();
1131 return Gtkmm2ext::Off;
1134 if (!sc->can_solo()) {
1135 return Gtkmm2ext::Off;
1139 if (sc->self_soloed()) {
1140 return Gtkmm2ext::ExplicitActive;
1141 } else if (sc->soloed_by_others()) {
1142 return Gtkmm2ext::ImplicitActive;
1144 return Gtkmm2ext::Off;
1148 Gtkmm2ext::ActiveState
1149 RouteUI::solo_isolate_active_state (boost::shared_ptr<Stripable> s)
1151 boost::shared_ptr<SoloIsolateControl> sc = s->solo_isolate_control();
1154 return Gtkmm2ext::Off;
1157 if (s->is_master() || s->is_monitor()) {
1158 return Gtkmm2ext::Off;
1161 if (sc->solo_isolated()) {
1162 return Gtkmm2ext::ExplicitActive;
1164 return Gtkmm2ext::Off;
1168 Gtkmm2ext::ActiveState
1169 RouteUI::solo_safe_active_state (boost::shared_ptr<Stripable> s)
1171 boost::shared_ptr<SoloSafeControl> sc = s->solo_safe_control();
1174 return Gtkmm2ext::Off;
1177 if (s->is_master() || s->is_monitor()) {
1178 return Gtkmm2ext::Off;
1181 if (sc->solo_safe()) {
1182 return Gtkmm2ext::ExplicitActive;
1184 return Gtkmm2ext::Off;
1189 RouteUI::update_solo_display ()
1191 bool yn = _route->solo_safe_control()->solo_safe ();
1193 if (solo_safe_check && solo_safe_check->get_active() != yn) {
1194 solo_safe_check->set_active (yn);
1197 yn = _route->solo_isolate_control()->solo_isolated ();
1199 if (solo_isolated_check && solo_isolated_check->get_active() != yn) {
1200 solo_isolated_check->set_active (yn);
1203 set_button_names ();
1205 if (solo_isolated_led) {
1206 if (_route->solo_isolate_control()->solo_isolated()) {
1207 solo_isolated_led->set_active_state (Gtkmm2ext::ExplicitActive);
1209 solo_isolated_led->unset_active_state ();
1213 if (solo_safe_led) {
1214 if (_route->solo_safe_control()->solo_safe()) {
1215 solo_safe_led->set_active_state (Gtkmm2ext::ExplicitActive);
1217 solo_safe_led->unset_active_state ();
1221 solo_button->set_active_state (solo_active_state (_route));
1223 /* some changes to solo status can affect mute display, so catch up
1226 update_mute_display ();
1230 RouteUI::solo_changed_so_update_mute ()
1232 update_mute_display ();
1236 RouteUI::mute_active_state (Session*, boost::shared_ptr<Stripable> s)
1238 boost::shared_ptr<MuteControl> mc = s->mute_control();
1240 if (s->is_monitor()) {
1241 return Gtkmm2ext::Off;
1245 return Gtkmm2ext::Off;
1248 if (Config->get_show_solo_mutes() && !Config->get_solo_control_is_listen_control ()) {
1250 if (mc->muted_by_self ()) {
1252 return Gtkmm2ext::ExplicitActive;
1253 } else if (mc->muted_by_others_soloing () || mc->muted_by_masters ()) {
1254 /* this will reflect both solo mutes AND master mutes */
1255 return Gtkmm2ext::ImplicitActive;
1257 /* no mute at all */
1258 return Gtkmm2ext::Off;
1263 if (mc->muted_by_self()) {
1265 return Gtkmm2ext::ExplicitActive;
1266 } else if (mc->muted_by_masters ()) {
1267 /* this shows only master mutes, not mute-by-others-soloing */
1268 return Gtkmm2ext::ImplicitActive;
1270 /* no mute at all */
1271 return Gtkmm2ext::Off;
1275 return ActiveState(0);
1279 RouteUI::update_mute_display ()
1285 mute_button->set_active_state (mute_active_state (_session, _route));
1290 RouteUI::route_rec_enable_changed ()
1292 blink_rec_display(true); //this lets the button change "immediately" rather than wait for the next blink
1293 update_monitoring_display ();
1297 RouteUI::session_rec_enable_changed ()
1299 blink_rec_display(true); //this lets the button change "immediately" rather than wait for the next blink
1300 update_monitoring_display ();
1304 RouteUI::blink_rec_display (bool blinkOn)
1306 if (!rec_enable_button || !_route) {
1310 if (boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1318 if (track()->rec_enable_control()->get_value()) {
1319 switch (_session->record_status ()) {
1320 case Session::Recording:
1321 rec_enable_button->set_active_state (Gtkmm2ext::ExplicitActive);
1324 case Session::Disabled:
1325 case Session::Enabled:
1326 if (UIConfiguration::instance().get_blink_rec_arm()) {
1327 rec_enable_button->set_active_state ( blinkOn ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off );
1329 rec_enable_button->set_active_state ( ImplicitActive );
1334 if (step_edit_item) {
1335 step_edit_item->set_sensitive (false);
1339 rec_enable_button->unset_active_state ();
1341 if (step_edit_item) {
1342 step_edit_item->set_sensitive (true);
1346 check_rec_enable_sensitivity ();
1350 RouteUI::build_solo_menu (void)
1352 using namespace Menu_Helpers;
1354 solo_menu = new Menu;
1355 solo_menu->set_name ("ArdourContextMenu");
1356 MenuList& items = solo_menu->items();
1357 Gtk::CheckMenuItem* check;
1359 check = new Gtk::CheckMenuItem(_("Solo Isolate"));
1360 check->set_active (_route->solo_isolate_control()->solo_isolated());
1361 check->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_solo_isolated), check));
1362 items.push_back (CheckMenuElem(*check));
1363 solo_isolated_check = dynamic_cast<Gtk::CheckMenuItem*>(&items.back());
1366 check = new Gtk::CheckMenuItem(_("Solo Safe"));
1367 check->set_active (_route->solo_safe_control()->solo_safe());
1368 check->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_solo_safe), check));
1369 items.push_back (CheckMenuElem(*check));
1370 solo_safe_check = dynamic_cast<Gtk::CheckMenuItem*>(&items.back());
1373 //items.push_back (SeparatorElem());
1374 // items.push_back (MenuElem (_("MIDI Bind"), sigc::mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
1379 RouteUI::build_mute_menu(void)
1381 using namespace Menu_Helpers;
1383 mute_menu = new Menu;
1384 mute_menu->set_name ("ArdourContextMenu");
1386 MenuList& items = mute_menu->items();
1388 pre_fader_mute_check = manage (new Gtk::CheckMenuItem(_("Pre Fader Sends")));
1389 init_mute_menu(MuteMaster::PreFader, pre_fader_mute_check);
1390 pre_fader_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::PreFader, pre_fader_mute_check));
1391 items.push_back (CheckMenuElem(*pre_fader_mute_check));
1392 pre_fader_mute_check->show_all();
1394 post_fader_mute_check = manage (new Gtk::CheckMenuItem(_("Post Fader Sends")));
1395 init_mute_menu(MuteMaster::PostFader, post_fader_mute_check);
1396 post_fader_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::PostFader, post_fader_mute_check));
1397 items.push_back (CheckMenuElem(*post_fader_mute_check));
1398 post_fader_mute_check->show_all();
1400 listen_mute_check = manage (new Gtk::CheckMenuItem(_("Control Outs")));
1401 init_mute_menu(MuteMaster::Listen, listen_mute_check);
1402 listen_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::Listen, listen_mute_check));
1403 items.push_back (CheckMenuElem(*listen_mute_check));
1404 listen_mute_check->show_all();
1406 main_mute_check = manage (new Gtk::CheckMenuItem(_("Main Outs")));
1407 init_mute_menu(MuteMaster::Main, main_mute_check);
1408 main_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::Main, main_mute_check));
1409 items.push_back (CheckMenuElem(*main_mute_check));
1410 main_mute_check->show_all();
1412 //items.push_back (SeparatorElem());
1413 // items.push_back (MenuElem (_("MIDI Bind"), sigc::mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
1415 _route->mute_points_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::muting_change, this), gui_context());
1419 RouteUI::init_mute_menu(MuteMaster::MutePoint mp, Gtk::CheckMenuItem* check)
1421 check->set_active (_route->mute_control()->mute_points() & mp);
1425 RouteUI::toggle_mute_menu(MuteMaster::MutePoint mp, Gtk::CheckMenuItem* check)
1427 if (check->get_active()) {
1428 _route->mute_control()->set_mute_points (MuteMaster::MutePoint (_route->mute_control()->mute_points() | mp));
1430 _route->mute_control()->set_mute_points (MuteMaster::MutePoint (_route->mute_control()->mute_points() & ~mp));
1435 RouteUI::muting_change ()
1437 ENSURE_GUI_THREAD (*this, &RouteUI::muting_change)
1440 MuteMaster::MutePoint current = _route->mute_control()->mute_points ();
1442 yn = (current & MuteMaster::PreFader);
1444 if (pre_fader_mute_check->get_active() != yn) {
1445 pre_fader_mute_check->set_active (yn);
1448 yn = (current & MuteMaster::PostFader);
1450 if (post_fader_mute_check->get_active() != yn) {
1451 post_fader_mute_check->set_active (yn);
1454 yn = (current & MuteMaster::Listen);
1456 if (listen_mute_check->get_active() != yn) {
1457 listen_mute_check->set_active (yn);
1460 yn = (current & MuteMaster::Main);
1462 if (main_mute_check->get_active() != yn) {
1463 main_mute_check->set_active (yn);
1468 RouteUI::solo_isolate_button_release (GdkEventButton* ev)
1470 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) {
1474 bool view = solo_isolated_led->active_state();
1475 bool model = _route->solo_isolate_control()->solo_isolated();
1477 /* called BEFORE the view has changed */
1479 if (ev->button == 1) {
1480 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1483 /* disable isolate for all routes */
1484 DisplaySuspender ds;
1485 _session->set_controls (route_list_to_control_list (_session->get_routes(), &Stripable::solo_isolate_control), 0.0, Controllable::NoGroup);
1487 /* enable isolate for all routes */
1488 DisplaySuspender ds;
1489 _session->set_controls (route_list_to_control_list (_session->get_routes(), &Stripable::solo_isolate_control), 1.0, Controllable::NoGroup);
1494 if (model == view) {
1496 /* flip just this route */
1498 boost::shared_ptr<RouteList> rl (new RouteList);
1499 rl->push_back (_route);
1500 DisplaySuspender ds;
1501 _session->set_controls (route_list_to_control_list (rl, &Stripable::solo_isolate_control), view ? 0.0 : 1.0, Controllable::NoGroup);
1510 RouteUI::solo_safe_button_release (GdkEventButton* ev)
1512 if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) {
1516 bool view = solo_safe_led->active_state();
1517 bool model = _route->solo_safe_control()->solo_safe();
1519 if (ev->button == 1) {
1520 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1521 boost::shared_ptr<RouteList> rl (_session->get_routes());
1523 /* disable solo safe for all routes */
1524 DisplaySuspender ds;
1525 for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
1526 (*i)->solo_safe_control()->set_value (0.0, Controllable::NoGroup);
1529 /* enable solo safe for all routes */
1530 DisplaySuspender ds;
1531 for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
1532 (*i)->solo_safe_control()->set_value (1.0, Controllable::NoGroup);
1537 if (model == view) {
1538 /* flip just this route */
1539 _route->solo_safe_control()->set_value (view ? 0.0 : 1.0, Controllable::NoGroup);
1548 RouteUI::toggle_solo_isolated (Gtk::CheckMenuItem* check)
1550 bool view = check->get_active();
1551 bool model = _route->solo_isolate_control()->solo_isolated();
1553 /* called AFTER the view has changed */
1555 if (model != view) {
1556 _route->solo_isolate_control()->set_value (view ? 1.0 : 0.0, Controllable::UseGroup);
1561 RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
1563 _route->solo_safe_control()->set_value (check->get_active() ? 1.0 : 0.0, Controllable::UseGroup);
1566 /** Ask the user to choose a colour, and then apply that color to my route
1569 RouteUI::choose_color ()
1572 Gdk::Color c (gdk_color_from_rgb (_route->presentation_info().color()));
1573 Gdk::Color const color = Gtkmm2ext::UI::instance()->get_color (_("Color Selection"), picked, &c);
1576 set_color (gdk_color_to_rgba (color));
1580 /** Set the route's own color. This may not be used for display if
1581 * the route is in a group which shares its color with its routes.
1584 RouteUI::set_color (uint32_t c)
1586 _route->presentation_info().set_color (c);
1589 /** @return GUI state ID for things that are common to the route in all its representations */
1591 RouteUI::route_state_id () const
1593 return string_compose (X_("route %1"), _route->id().to_s());
1597 RouteUI::set_color_from_route ()
1599 if (_route->presentation_info().color_set()) {
1600 return 0; /* nothing to do */
1603 return 1; /* pick a color */
1606 /** @return true if this name should be used for the route, otherwise false */
1608 RouteUI::verify_new_route_name (const std::string& name)
1610 if (name.find (':') == string::npos) {
1614 MessageDialog colon_msg (
1615 _("The use of colons (':') is discouraged in track and bus names.\nDo you want to use this new name?"),
1616 false, MESSAGE_QUESTION, BUTTONS_NONE
1619 colon_msg.add_button (_("Use the new name"), Gtk::RESPONSE_ACCEPT);
1620 colon_msg.add_button (_("Re-edit the name"), Gtk::RESPONSE_CANCEL);
1622 return (colon_msg.run () == Gtk::RESPONSE_ACCEPT);
1626 RouteUI::route_rename ()
1628 ArdourPrompter name_prompter (true);
1633 name_prompter.set_title (_("Rename Track"));
1635 name_prompter.set_title (_("Rename Bus"));
1637 name_prompter.set_prompt (_("New name:"));
1638 name_prompter.set_initial_text (_route->name());
1639 name_prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1640 name_prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1641 name_prompter.show_all ();
1644 switch (name_prompter.run ()) {
1645 case Gtk::RESPONSE_ACCEPT:
1646 name_prompter.get_result (result);
1647 name_prompter.hide ();
1648 if (result.length()) {
1649 if (verify_new_route_name (result)) {
1650 _route->set_name (result);
1653 /* back to name prompter */
1657 /* nothing entered, just get out of here */
1672 RouteUI::toggle_comment_editor ()
1674 // if (ignore_toggle) {
1678 if (comment_window && comment_window->is_visible ()) {
1679 comment_window->hide ();
1681 open_comment_editor ();
1687 RouteUI::open_comment_editor ()
1689 if (comment_window == 0) {
1690 setup_comment_editor ();
1694 title = _route->name();
1695 title += _(": comment editor");
1697 comment_window->set_title (title);
1698 comment_window->present();
1702 RouteUI::setup_comment_editor ()
1704 comment_window = new ArdourWindow (""); // title will be reset to show route
1705 comment_window->set_skip_taskbar_hint (true);
1706 comment_window->signal_hide().connect (sigc::mem_fun(*this, &MixerStrip::comment_editor_done_editing));
1707 comment_window->set_default_size (400, 200);
1709 comment_area = manage (new TextView());
1710 comment_area->set_name ("MixerTrackCommentArea");
1711 comment_area->set_wrap_mode (WRAP_WORD);
1712 comment_area->set_editable (true);
1713 comment_area->get_buffer()->set_text (_route->comment());
1714 comment_area->show ();
1716 comment_window->add (*comment_area);
1720 RouteUI::comment_changed ()
1722 ignore_comment_edit = true;
1724 comment_area->get_buffer()->set_text (_route->comment());
1726 ignore_comment_edit = false;
1730 RouteUI::comment_editor_done_editing ()
1732 ENSURE_GUI_THREAD (*this, &MixerStrip::comment_editor_done_editing, src)
1734 string const str = comment_area->get_buffer()->get_text();
1735 if (str == _route->comment ()) {
1739 _route->set_comment (str, this);
1743 RouteUI::set_route_active (bool a, bool apply_to_selection)
1745 if (apply_to_selection) {
1746 ARDOUR_UI::instance()->the_editor().get_selection().tracks.foreach_route_ui (boost::bind (&RouteTimeAxisView::set_route_active, _1, a, false));
1748 _route->set_active (a, this);
1753 RouteUI::duplicate_selected_routes ()
1755 ARDOUR_UI::instance()->start_duplicate_routes();
1759 RouteUI::toggle_denormal_protection ()
1761 if (denormal_menu_item) {
1765 ENSURE_GUI_THREAD (*this, &RouteUI::toggle_denormal_protection)
1767 if ((x = denormal_menu_item->get_active()) != _route->denormal_protection()) {
1768 _route->set_denormal_protection (x);
1774 RouteUI::denormal_protection_changed ()
1776 if (denormal_menu_item) {
1777 denormal_menu_item->set_active (_route->denormal_protection());
1782 RouteUI::disconnect_input ()
1784 _route->input()->disconnect (this);
1788 RouteUI::disconnect_output ()
1790 _route->output()->disconnect (this);
1794 RouteUI::is_track () const
1796 return boost::dynamic_pointer_cast<Track>(_route) != 0;
1799 boost::shared_ptr<Track>
1800 RouteUI::track() const
1802 return boost::dynamic_pointer_cast<Track>(_route);
1806 RouteUI::is_audio_track () const
1808 return boost::dynamic_pointer_cast<AudioTrack>(_route) != 0;
1811 boost::shared_ptr<AudioTrack>
1812 RouteUI::audio_track() const
1814 return boost::dynamic_pointer_cast<AudioTrack>(_route);
1818 RouteUI::is_midi_track () const
1820 return boost::dynamic_pointer_cast<MidiTrack>(_route) != 0;
1823 boost::shared_ptr<MidiTrack>
1824 RouteUI::midi_track() const
1826 return boost::dynamic_pointer_cast<MidiTrack>(_route);
1830 RouteUI::has_audio_outputs () const
1832 return (_route->n_outputs().n_audio() > 0);
1836 RouteUI::map_frozen ()
1838 ENSURE_GUI_THREAD (*this, &RouteUI::map_frozen)
1840 AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
1843 check_rec_enable_sensitivity ();
1848 RouteUI::adjust_latency ()
1850 LatencyDialog dialog (_route->name() + _(" latency"), *(_route->output()), _session->frame_rate(), AudioEngine::instance()->samples_per_cycle());
1854 RouteUI::process_save_template_prompter (ArdourPrompter& prompter, const std::string& dir)
1857 std::string safe_name;
1860 prompter.get_result (name, true);
1862 safe_name = legalize_for_path (name);
1863 safe_name += template_suffix;
1865 path = Glib::build_filename (dir, safe_name);
1867 if (Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
1868 bool overwrite = overwrite_file_dialog (prompter,
1869 _("Confirm Template Overwrite"),
1870 _("A template already exists with that name. Do you want to overwrite it?"));
1877 _route->save_as_template (path, name);
1883 RouteUI::save_as_template ()
1887 dir = ARDOUR::user_route_template_directory ();
1889 if (g_mkdir_with_parents (dir.c_str(), 0755)) {
1890 error << string_compose (_("Cannot create route template directory %1"), dir) << endmsg;
1894 ArdourPrompter prompter (true); // modal
1896 prompter.set_title (_("Save As Template"));
1897 prompter.set_prompt (_("Template name:"));
1898 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
1900 bool finished = false;
1902 switch (prompter.run()) {
1903 case RESPONSE_ACCEPT:
1904 finished = process_save_template_prompter (prompter, dir);
1914 RouteUI::check_rec_enable_sensitivity ()
1916 if (!rec_enable_button) {
1917 assert (0); // This should not happen
1920 if (!_session->writable()) {
1921 rec_enable_button->set_sensitive (false);
1925 if (_session->transport_rolling() && rec_enable_button->active_state() && Config->get_disable_disarm_during_roll()) {
1926 rec_enable_button->set_sensitive (false);
1927 } else if (is_audio_track () && track()->freeze_state() == AudioTrack::Frozen) {
1928 rec_enable_button->set_sensitive (false);
1930 rec_enable_button->set_sensitive (true);
1932 if (_route && _route->rec_safe_control () && _route->rec_safe_control()->get_value()) {
1933 rec_enable_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
1935 rec_enable_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
1937 update_monitoring_display ();
1941 RouteUI::parameter_changed (string const & p)
1943 /* this handles RC and per-session parameter changes */
1945 if (p == "disable-disarm-during-roll") {
1946 check_rec_enable_sensitivity ();
1947 } else if (p == "use-monitor-bus" || p == "solo-control-is-listen-control" || p == "listen-position") {
1948 set_button_names ();
1949 } else if (p == "auto-input") {
1950 update_monitoring_display ();
1951 } else if (p == "blink-rec-arm") {
1952 if (UIConfiguration::instance().get_blink_rec_arm()) {
1953 rec_blink_connection.disconnect ();
1954 rec_blink_connection = Timers::blink_connect (sigc::mem_fun (*this, &RouteUI::blink_rec_display));
1956 rec_blink_connection.disconnect ();
1957 RouteUI::blink_rec_display(false);
1963 RouteUI::step_gain_up ()
1965 _route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.1), Controllable::UseGroup);
1969 RouteUI::page_gain_up ()
1971 _route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.5), Controllable::UseGroup);
1975 RouteUI::step_gain_down ()
1977 _route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.1), Controllable::UseGroup);
1981 RouteUI::page_gain_down ()
1983 _route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.5), Controllable::UseGroup);
1987 RouteUI::setup_invert_buttons ()
1989 /* remove old invert buttons */
1990 for (vector<ArdourButton*>::iterator i = _invert_buttons.begin(); i != _invert_buttons.end(); ++i) {
1991 _invert_button_box.remove (**i);
1994 _invert_buttons.clear ();
1996 if (!_route || !_route->input()) {
2000 uint32_t const N = _route->input()->n_ports().n_audio ();
2002 uint32_t const to_add = (N <= _max_invert_buttons) ? N : 1;
2004 for (uint32_t i = 0; i < to_add; ++i) {
2005 ArdourButton* b = manage (new ArdourButton);
2006 b->signal_button_press_event().connect (sigc::mem_fun (*this, &RouteUI::invert_press), false);
2007 b->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::invert_release), i), false);
2009 b->set_name (X_("invert button"));
2012 b->set_text (string_compose (X_("Ø (%1)"), N));
2014 b->set_text (X_("Ø"));
2017 b->set_text (string_compose (X_("Ø%1"), i + 1));
2020 if (N <= _max_invert_buttons) {
2021 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));
2023 UI::instance()->set_tip (*b, _("Click to show a menu of channels to invert polarity"));
2026 _invert_buttons.push_back (b);
2027 _invert_button_box.pack_start (*b);
2030 _invert_button_box.set_spacing (1);
2031 _invert_button_box.show_all ();
2035 RouteUI::set_invert_button_state ()
2037 uint32_t const N = _route->input()->n_ports().n_audio();
2038 if (N > _max_invert_buttons) {
2040 /* One button for many channels; explicit active if all channels are inverted,
2041 implicit active if some are, off if none are.
2044 ArdourButton* b = _invert_buttons.front ();
2046 if (_route->phase_control()->count() == _route->phase_control()->size()) {
2047 b->set_active_state (Gtkmm2ext::ExplicitActive);
2048 } else if (_route->phase_control()->any()) {
2049 b->set_active_state (Gtkmm2ext::ImplicitActive);
2051 b->set_active_state (Gtkmm2ext::Off);
2056 /* One button per channel; just set active */
2059 for (vector<ArdourButton*>::iterator i = _invert_buttons.begin(); i != _invert_buttons.end(); ++i, ++j) {
2060 (*i)->set_active (_route->phase_control()->inverted (j));
2067 RouteUI::invert_release (GdkEventButton* ev, uint32_t i)
2069 if (ev->button == 1 && i < _invert_buttons.size()) {
2070 uint32_t const N = _route->input()->n_ports().n_audio ();
2071 if (N <= _max_invert_buttons) {
2072 /* left-click inverts phase so long as we have a button per channel */
2073 _route->phase_control()->set_phase_invert (i, !_invert_buttons[i]->get_active());
2082 RouteUI::invert_press (GdkEventButton* ev)
2084 using namespace Menu_Helpers;
2086 uint32_t const N = _route->input()->n_ports().n_audio();
2087 if (N <= _max_invert_buttons && ev->button != 3) {
2088 /* If we have an invert button per channel, we only pop
2089 up a menu on right-click; left click is handled
2095 delete _invert_menu;
2096 _invert_menu = new Menu;
2097 _invert_menu->set_name ("ArdourContextMenu");
2098 MenuList& items = _invert_menu->items ();
2100 for (uint32_t i = 0; i < N; ++i) {
2101 items.push_back (CheckMenuElem (string_compose (X_("Ø%1"), i + 1), sigc::bind (sigc::mem_fun (*this, &RouteUI::invert_menu_toggled), i)));
2102 Gtk::CheckMenuItem* e = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
2103 ++_i_am_the_modifier;
2104 e->set_active (_route->phase_control()->inverted (i));
2105 --_i_am_the_modifier;
2108 _invert_menu->popup (0, ev->time);
2114 RouteUI::invert_menu_toggled (uint32_t c)
2116 if (_i_am_the_modifier) {
2121 _route->phase_control()->set_phase_invert (c, !_route->phase_control()->inverted (c));
2125 RouteUI::set_invert_sensitive (bool yn)
2127 for (vector<ArdourButton*>::iterator b = _invert_buttons.begin(); b != _invert_buttons.end(); ++b) {
2128 (*b)->set_sensitive (yn);
2133 RouteUI::request_redraw ()
2136 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2140 /** The Route's gui_changed signal has been emitted */
2142 RouteUI::route_gui_changed (PropertyChange const& what_changed)
2144 if (what_changed.contains (Properties::color)) {
2145 if (set_color_from_route () == 0) {
2146 route_color_changed ();
2150 if (what_changed.contains (Properties::selected)) {
2156 RouteUI::set_selected (bool yn)
2158 Selectable::set_selected (yn);
2160 _route->presentation_info().set_selected (yn);
2165 RouteUI::selected () const
2167 /* XXX not sure if this is a wise design. Probably don't really want
2168 * the cached _selected value from Selectable.
2175 return _route->presentation_info().selected();
2179 RouteUI::track_mode_changed (void)
2182 switch (track()->mode()) {
2183 case ARDOUR::NonLayered:
2184 case ARDOUR::Normal:
2185 rec_enable_button->set_icon (ArdourIcon::RecButton);
2187 case ARDOUR::Destructive:
2188 rec_enable_button->set_icon (ArdourIcon::RecTapeMode);
2191 rec_enable_button->queue_draw();
2194 /** @return the color that this route should use; it maybe its own,
2195 or it maybe that of its route group.
2199 RouteUI::route_color () const
2202 RouteGroup* g = _route->route_group ();
2205 if (g && g->is_color()) {
2206 set_color_from_rgba (c, GroupTabs::group_color (g));
2209 /* deal with older 4.x color, which was stored in the GUI object state */
2211 string p = ARDOUR_UI::instance()->gui_object_state->get_string (route_state_id(), X_("color"));
2215 /* old v4.x or earlier session. Use this information */
2217 int red, green, blue;
2220 stringstream ss (p);
2222 /* old color format version was:
2224 16bit value for red:16 bit value for green:16 bit value for blue
2239 _route->presentation_info().set_color (RGBA_TO_UINT (red, green, blue, 255));
2242 set_color_from_rgba (c, _route->presentation_info().color());
2249 RouteUI::set_showing_sends_to (boost::shared_ptr<Route> send_to)
2251 _showing_sends_to = send_to;
2252 BusSendDisplayChanged (send_to); /* EMIT SIGNAL */
2256 RouteUI::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2258 if (_route == send_to) {
2259 show_sends_button->set_active (true);
2260 send_blink_connection = Timers::blink_connect (sigc::mem_fun (*this, &RouteUI::send_blink));
2262 show_sends_button->set_active (false);
2263 send_blink_connection.disconnect ();
2268 RouteUI::route_group() const
2270 return _route->route_group();
2274 RoutePinWindowProxy::RoutePinWindowProxy(std::string const &name, boost::shared_ptr<ARDOUR::Route> route)
2275 : WM::ProxyBase (name, string())
2276 , _route (boost::weak_ptr<Route> (route))
2278 route->DropReferences.connect (going_away_connection, MISSING_INVALIDATOR, boost::bind (&RoutePinWindowProxy::route_going_away, this), gui_context());
2281 RoutePinWindowProxy::~RoutePinWindowProxy()
2286 ARDOUR::SessionHandlePtr*
2287 RoutePinWindowProxy::session_handle ()
2289 ArdourWindow* aw = dynamic_cast<ArdourWindow*> (_window);
2290 if (aw) { return aw; }
2295 RoutePinWindowProxy::get (bool create)
2297 boost::shared_ptr<Route> r = _route.lock ();
2306 _window = new PluginPinDialog (r);
2307 ArdourWindow* aw = dynamic_cast<ArdourWindow*> (_window);
2309 aw->set_session (_session);
2311 _window->show_all ();
2317 RoutePinWindowProxy::route_going_away ()
2321 WM::Manager::instance().remove (this);
2322 going_away_connection.disconnect();
2326 RouteUI::maybe_add_route_print_mgr ()
2328 if (_route->pinmgr_proxy ()) {
2331 RoutePinWindowProxy* wp = new RoutePinWindowProxy (
2332 string_compose ("RPM-%1", _route->id()), _route);
2333 wp->set_session (_session);
2335 const XMLNode* ui_xml = _session->extra_xml (X_("UI"));
2337 wp->set_state (*ui_xml, 0);
2341 void* existing_ui = _route->pinmgr_proxy ();
2343 wp->use_window (*(reinterpret_cast<Gtk::Window*>(existing_ui)));
2346 _route->set_pingmgr_proxy (wp);
2348 WM::Manager::instance().register_window (wp);
2352 RouteUI::manage_pins ()
2354 RoutePinWindowProxy* proxy = _route->pinmgr_proxy ();
2362 RouteUI::mark_hidden (bool yn)
2364 if (yn != _route->presentation_info().hidden()) {
2365 _route->presentation_info().set_hidden (yn);
2366 return true; // things changed