/*
- Copyright (C) 2002-2006 Paul Davis
+ Copyright (C) 2002-2006 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <gtkmm2ext/doi.h>
#include <gtkmm2ext/bindable_button.h>
#include <gtkmm2ext/barcontroller.h>
+#include <gtkmm2ext/gtk_ui.h>
-#include <ardour/route_group.h>
-#include <pbd/memento_command.h>
-#include <pbd/stacktrace.h>
-#include <pbd/shiva.h>
-#include <pbd/controllable.h>
+#include "ardour/route_group.h"
+#include "ardour/dB.h"
+#include "pbd/memento_command.h"
+#include "pbd/stacktrace.h"
+#include "pbd/shiva.h"
+#include "pbd/controllable.h"
+#include "pbd/enumwriter.h"
+#include "ardour_ui.h"
+#include "editor.h"
#include "route_ui.h"
#include "keyboard.h"
#include "utils.h"
#include "gui_thread.h"
#include "ardour_dialog.h"
#include "latency_gui.h"
-
-#include <ardour/route.h>
-#include <ardour/session.h>
-#include <ardour/audioengine.h>
-#include <ardour/audio_track.h>
-#include <ardour/audio_diskstream.h>
-#include <ardour/midi_track.h>
-#include <ardour/midi_diskstream.h>
+#include "mixer_strip.h"
+#include "automation_time_axis.h"
+#include "route_time_axis.h"
+
+#include "ardour/route.h"
+#include "ardour/event_type_map.h"
+#include "ardour/session.h"
+#include "ardour/audioengine.h"
+#include "ardour/audio_track.h"
+#include "ardour/audio_diskstream.h"
+#include "ardour/midi_track.h"
+#include "ardour/midi_diskstream.h"
+#include "ardour/template_utils.h"
+#include "ardour/filename_extensions.h"
+#include "ardour/directory_names.h"
+#include "ardour/profile.h"
#include "i18n.h"
using namespace sigc;
using namespace ARDOUR;
using namespace PBD;
-RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt, ARDOUR::Session& sess, const char* m_name,
- const char* s_name, const char* r_name)
- : AxisView(sess),
- _route(rt),
- mute_button(0),
- solo_button(0),
- rec_enable_button(0)
+RouteUI::RouteUI (ARDOUR::Session& sess)
+ : AxisView(sess)
+{
+ init ();
+}
+
+RouteUI::RouteUI (boost::shared_ptr<ARDOUR::Route> rt, ARDOUR::Session& sess)
+ : AxisView(sess)
+{
+ init ();
+ set_route (rt);
+}
+
+RouteUI::~RouteUI()
+{
+ /* derived classes should emit GoingAway so that they receive the signal
+ when the object is still a legal derived instance.
+ */
+
+ delete solo_menu;
+ delete mute_menu;
+ delete sends_menu;
+}
+
+void
+RouteUI::init ()
{
+ self_destruct = true;
xml_node = 0;
mute_menu = 0;
solo_menu = 0;
- remote_control_menu = 0;
+ sends_menu = 0;
+ pre_fader_mute_check = 0;
+ post_fader_mute_check = 0;
+ listen_mute_check = 0;
+ main_mute_check = 0;
ignore_toggle = false;
wait_for_release = false;
route_active_menu_item = 0;
- was_solo_safe = false;
polarity_menu_item = 0;
denormal_menu_item = 0;
+ multiple_mute_change = false;
+ multiple_solo_change = false;
- if (set_color_from_route()) {
- set_color (unique_random_color());
+ mute_button = manage (new BindableToggleButton ());
+ mute_button->set_self_managed (true);
+ mute_button->set_name ("MuteButton");
+ mute_button->add (mute_button_label);
+ mute_button_label.show ();
+ UI::instance()->set_tip (mute_button, _("Mute this track"), "");
+
+ solo_button = manage (new BindableToggleButton ());
+ solo_button->set_self_managed (true);
+ solo_button->set_name ("SoloButton");
+ solo_button->add (solo_button_label);
+ solo_button_label.show ();
+ UI::instance()->set_tip (solo_button, _("Mute other (non-soloed) tracks"), "");
+ solo_button->set_no_show_all (true);
+
+ rec_enable_button = manage (new BindableToggleButton ());
+ rec_enable_button->set_name ("RecordEnableButton");
+ rec_enable_button->set_self_managed (true);
+ rec_enable_button->add (rec_enable_button_label);
+ rec_enable_button_label.show ();
+ UI::instance()->set_tip (rec_enable_button, _("Enable recording on this track"), "");
+
+ show_sends_button = manage (new BindableToggleButton (""));
+ show_sends_button->set_name ("SendAlert");
+ show_sends_button->set_self_managed (true);
+ UI::instance()->set_tip (show_sends_button, _("make mixer strips show sends to this bus"), "");
+
+ _session.SoloChanged.connect (mem_fun(*this, &RouteUI::solo_changed_so_update_mute));
+ _session.TransportStateChange.connect (mem_fun (*this, &RouteUI::check_rec_enable_sensitivity));
+
+ Config->ParameterChanged.connect (mem_fun (*this, &RouteUI::parameter_changed));
+}
+
+void
+RouteUI::reset ()
+{
+ //Remove route connections associated with us.
+ for (vector<sigc::connection>::iterator it = connections.begin(); it!=connections.end(); ++it) {
+ (*it).disconnect();
}
- new PairedShiva<Route,RouteUI> (*_route, *this);
+ connections.clear ();
- _route->active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed));
+ delete solo_menu;
+ solo_menu = 0;
- mute_button = manage (new BindableToggleButton (*_route->mute_control().get(), m_name ));
- mute_button->set_self_managed (true);
+ delete mute_menu;
+ mute_menu = 0;
- solo_button = manage (new BindableToggleButton (*_route->solo_control().get(), s_name ));
- solo_button->set_self_managed (true);
+ if (xml_node) {
+ /* do not delete the node - its owned by the route */
+ xml_node = 0;
+ }
- mute_button->set_name ("MuteButton");
- solo_button->set_name ("SoloButton");
+ route_active_menu_item = 0;
+ polarity_menu_item = 0;
+ denormal_menu_item = 0;
+}
- _route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed));
- _route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
- _route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
+void
+RouteUI::set_route (boost::shared_ptr<Route> rp)
+{
+ reset ();
- /* when solo changes, update mute state too, in case the user wants us to display it */
+ _route = rp;
- _session.SoloChanged.connect (mem_fun(*this, &RouteUI::solo_changed_so_update_mute));
-
- if (is_track()) {
- boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
+ if (set_color_from_route()) {
+ set_color (unique_random_color());
+ }
+
+ /* no, there is no memory leak here. This object cleans itself (and other stuff)
+ up when the route is destroyed.
+ */
+
+ if (self_destruct) {
+ new PairedShiva<Route,RouteUI> (*_route, *this);
+ }
- t->diskstream()->RecordEnableChanged.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed));
+ mute_button->set_controllable (_route->mute_control());
+ solo_button->set_controllable (_route->solo_control());
- _session.RecordStateChanged.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed));
+ connections.push_back (_route->active_changed.connect (mem_fun (*this, &RouteUI::route_active_changed)));
+ connections.push_back (_route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed)));
+ connections.push_back (_route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
+ connections.push_back (_route->listen_changed.connect (mem_fun(*this, &RouteUI::listen_changed)));
+ connections.push_back (_route->solo_isolated_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
- rec_enable_button = manage (new BindableToggleButton (*t->rec_enable_control().get(), r_name ));
- rec_enable_button->set_name ("RecordEnableButton");
- rec_enable_button->set_self_managed (true);
+ if (_session.writable() && is_track()) {
+ boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
+
+ connections.push_back (t->diskstream()->RecordEnableChanged.connect (mem_fun (*this, &RouteUI::route_rec_enable_changed)));
+ connections.push_back (_session.RecordStateChanged.connect (mem_fun (*this, &RouteUI::session_rec_enable_changed)));
+
+ rec_enable_button->show();
+ rec_enable_button->set_controllable (t->rec_enable_control());
update_rec_display ();
- }
+ }
mute_button->unset_flags (Gtk::CAN_FOCUS);
solo_button->unset_flags (Gtk::CAN_FOCUS);
-
- _route->RemoteControlIDChanged.connect (mem_fun(*this, &RouteUI::refresh_remote_control_menu));
+
+ mute_button->show();
+
+ if (_route->is_control()) {
+ solo_button->hide ();
+ } else {
+ solo_button->show();
+ }
/* map the current state */
- map_frozen ();
-}
+ mute_changed (0);
+ solo_changed (0);
-RouteUI::~RouteUI()
-{
- GoingAway (); /* EMIT SIGNAL */
- delete mute_menu;
+ map_frozen ();
}
bool
RouteUI::mute_press(GdkEventButton* ev)
{
+ if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
+ return true;
+ }
+ multiple_mute_change = false;
if (!ignore_toggle) {
if (Keyboard::is_context_menu_event (ev)) {
} else {
- if (ev->button == 2) {
- // ctrl-button2 click is the midi binding click
+ if (Keyboard::is_button2_event (ev)) {
+ // Primary-button2 click is the midi binding click
// button2-click is "momentary"
-
- if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
+
+ if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier))) {
wait_for_release = true;
} else {
return false;
}
}
- if (ev->button == 1 || ev->button == 2) {
+ if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
- if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
+ if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
- /* ctrl-shift-click applies change to all routes */
+#if 0
+ /* Primary-Tertiary-click applies change to all routes */
_session.begin_reversible_command (_("mute change"));
Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(_session, this);
- _session.set_all_mute (!_route->muted());
+ _session.set_mute (!_route->muted());
cmd->mark();
_session.add_command(cmd);
_session.commit_reversible_command ();
+ multiple_mute_change = true;
+#endif
- } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
- /* ctrl-click applies change to the mix group.
- ctrl-button2 is MIDI learn.
+#if 0
+ /* Primary-button1 applies change to the mix group.
+ NOTE: Primary-button2 is MIDI learn.
*/
if (ev->button == 1) {
- set_mix_group_mute (_route, !_route->muted());
+ set_route_group_mute (_route, !_route->muted());
}
-
+#endif
+
} else {
+#if 0
/* plain click applies change to this route */
-
- reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this);
+ if (wait_for_release) {
+ _route->set_mute (!_route->muted(), this);
+ } else {
+ reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this);
+ }
+#endif
}
}
}
}
bool
-RouteUI::mute_release(GdkEventButton* ev)
+RouteUI::mute_release(GdkEventButton*)
{
if (!ignore_toggle) {
if (wait_for_release){
wait_for_release = false;
- // undo the last op
- // because the press was the last undoable thing we did
- _session.undo (1U);
+ if (multiple_mute_change) {
+ multiple_mute_change = false;
+ // undo the last op
+ // because the press was the last undoable thing we did
+ _session.undo (1U);
+ } else {
+ _route->set_mute (!_route->muted(), this);
+ }
}
}
return true;
}
+void
+RouteUI::post_solo_cleanup (SessionEvent* ev, bool was_not_latched)
+{
+ ENSURE_GUI_THREAD (bind (mem_fun (*this, &RouteUI::post_solo_cleanup), ev, was_not_latched));
+
+ delete ev;
+
+ if (was_not_latched) {
+ Config->set_solo_latched (false);
+ }
+}
+
bool
RouteUI::solo_press(GdkEventButton* ev)
{
- if (!ignore_toggle) {
+ /* ignore double/triple clicks */
- if (Keyboard::is_context_menu_event (ev)) {
-
- if (solo_menu == 0) {
- build_solo_menu ();
- }
+ if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
+ return true;
+ }
- solo_menu->popup (1, ev->time);
+ if (Config->get_solo_control_is_listen_control()) {
- } else {
+ _route->set_listen (!_route->listening(), this);
- if (ev->button == 2) {
+ } else {
- // ctrl-button2 click is the midi binding click
- // button2-click is "momentary"
-
- if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
- wait_for_release = true;
- } else {
- return false;
+ multiple_solo_change = false;
+ if (!ignore_toggle) {
+
+ if (Keyboard::is_context_menu_event (ev)) {
+
+ if (solo_menu == 0) {
+ build_solo_menu ();
}
- }
- if (ev->button == 1 || ev->button == 2) {
+ solo_menu->popup (1, ev->time);
- if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
+ } else {
- /* ctrl-shift-click applies change to all routes */
+ if (Keyboard::is_button2_event (ev)) {
- _session.begin_reversible_command (_("solo change"));
- Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
- _session.set_all_solo (!_route->soloed());
- cmd->mark();
- _session.add_command (cmd);
- _session.commit_reversible_command ();
-
- } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
+ // Primary-button2 click is the midi binding click
+ // button2-click is "momentary"
+
+ if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier))) {
+ wait_for_release = true;
+ } else {
+ return false;
+ }
+ }
- // ctrl-alt-click: exclusively solo this track, not a toggle */
+ if (ev->button == 1 || Keyboard::is_button2_event (ev)) {
- _session.begin_reversible_command (_("solo change"));
- Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (_session, this);
- _session.set_all_solo (false);
- _route->set_solo (true, this);
- cmd->mark();
- _session.add_command(cmd);
- _session.commit_reversible_command ();
+ if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
- } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
+ /* Primary-Tertiary-click applies change to all routes */
+ bool was_not_latched = false;
- // shift-click: set this route to solo safe
+ if (!Config->get_solo_latched ()) {
+ was_not_latched = true;
+ /*
+ XXX it makes no sense to solo all tracks if we're
+ not in latched mode, but doing nothing feels like a bug,
+ so do it anyway
+ */
+ Config->set_solo_latched (true);
+ }
- _route->set_solo_safe (!_route->solo_safe(), this);
- wait_for_release = false;
+ SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
+ ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_solo), _session.get_routes(), !_route->soloed());
+ ev->rt_return = bind (sigc::mem_fun (*this, &RouteUI::post_solo_cleanup), was_not_latched);
+
+ _session.queue_event (ev);
- } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
+ } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
- /* ctrl-click: solo mix group.
- ctrl-button2 is MIDI learn.
- */
+ // Primary-Secondary-click: exclusively solo this track, not a toggle */
+
+ //boost::shared_ptr<RouteList> rl (new RouteList);
+ //rl->push_back (route());
- if (ev->button == 1) {
- set_mix_group_solo (_route, !_route->soloed());
- }
+ //SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
+ // ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_just_one_solo), rl, true);
+ //ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
+
+ //_session.queue_event (ev);
- } else {
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
+
+ // shift-click: toggle solo isolated status
+
+ _route->set_solo_isolated (!_route->solo_isolated(), this);
+ wait_for_release = false;
+
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
+
+#if 0
+ /* Primary-button1: solo mix group.
+ NOTE: Primary-button2 is MIDI learn.
+ */
+
+ if (ev->button == 1) {
+ queue_route_group_op (RouteGroup::Solo, &Session::set_all_solo, !_route->soloed());
+ }
+#endif
- /* click: solo this route */
- reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
+ } else {
+
+ /* click: solo this route */
+
+ boost::shared_ptr<RouteList> rl (new RouteList);
+ rl->push_back (route());
+
+ SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
+ ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_solo), rl, !rec_enable_button->get_active());
+ ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
+
+ _session.queue_event (ev);
+ }
}
}
}
}
bool
-RouteUI::solo_release(GdkEventButton* ev)
+RouteUI::solo_release(GdkEventButton*)
{
if (!ignore_toggle) {
if (wait_for_release) {
wait_for_release = false;
- // undo the last op
- // because the press was the last undoable thing we did
-
- _session.undo (1U);
+ if (multiple_solo_change) {
+ multiple_solo_change = false;
+ // undo the last op
+ // because the press was the last undoable thing we did
+ _session.undo (1U);
+ } else {
+ // we don't use "undo the last op"
+ // here because its expensive for the GUI
+ _route->set_solo (!_route->soloed(), this);
+ }
}
}
return true;
}
+void
+RouteUI::post_rtop_cleanup (SessionEvent* ev)
+{
+ ENSURE_GUI_THREAD (bind (mem_fun (*this, &RouteUI::post_rtop_cleanup), ev));
+ delete ev;
+}
+
+void
+RouteUI::post_group_rtop_cleanup (SessionEvent* ev, RouteGroup* rg, RouteGroup::Property prop)
+{
+ ENSURE_GUI_THREAD (bind (mem_fun (*this, &RouteUI::post_group_rtop_cleanup), ev, rg, prop));
+ delete ev;
+ rg->set_property (prop, false);
+}
+
+void
+RouteUI::queue_route_group_op (RouteGroup::Property prop, void (Session::*session_method)(boost::shared_ptr<RouteList>, bool), bool yn)
+{
+ RouteGroup* rg = _route->route_group();
+ bool prop_was_active;
+
+ if (rg) {
+ prop_was_active = rg->active_property (prop);
+ rg->set_property (prop, true);
+ } else {
+ prop_was_active = false;
+ }
+
+ /* we will queue the op for just this route, but because its route group now has the relevant property marked active,
+ the operation will apply to the whole group (if there is a group)
+ */
+
+ boost::shared_ptr<RouteList> rl (new RouteList);
+ rl->push_back (route());
+
+ SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
+ ev->rt_slot = bind (sigc::mem_fun (_session, session_method), rl, yn);
+ if (rg && !prop_was_active) {
+ ev->rt_return = bind (sigc::mem_fun (*this, &RouteUI::post_group_rtop_cleanup), rg, prop);
+ } else {
+ ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
+ }
+
+ _session.queue_event (ev);
+}
+
bool
RouteUI::rec_enable_press(GdkEventButton* ev)
{
+ if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
+ return true;
+ }
+
if (!_session.engine().connected()) {
MessageDialog msg (_("Not connected to JACK - cannot engage record"));
msg.run ();
if (!ignore_toggle && is_track() && rec_enable_button) {
- if (ev->button == 2 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
+ if (Keyboard::is_button2_event (ev) && Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
// do nothing on midi bind event
+ return false;
- } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
- _session.begin_reversible_command (_("rec-enable change"));
- Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
+#if 0
+ _session.set_record_enable (_session.get_route(), !rec_enable_button->get_active(), sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup));
+#endif
- if (rec_enable_button->get_active()) {
- _session.record_disenable_all ();
- } else {
- _session.record_enable_all ();
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
+
+ /* Primary-button1 applies change to the route group (even if it is not active)
+ NOTE: Primary-button2 is MIDI learn.
+ */
+#if 0
+ if (ev->button == 1 && _route->route_group()) {
+ _session.set_record_enable (_route->route_group(), !rec_enable_button->get_active(),
+ queue_route_group_op (RouteGroup::RecEnable, &Session::set_record_enable,
+ }
+#endif
+
+ } else if (Keyboard::is_context_menu_event (ev)) {
+
+ /* do this on release */
+
+ } else {
+
+#if 0
+ boost::shared_ptr<RouteList> rl (new RouteList);
+ rl->push_back (route());
+ _session.set_record_enable (rl, !rec_enable_button->get_active(), sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup));
+#endif
+ }
+ }
+
+ return true;
+}
+
+bool
+RouteUI::rec_enable_release (GdkEventButton*)
+{
+ return true;
+}
+
+void
+RouteUI::build_sends_menu ()
+{
+ using namespace Menu_Helpers;
+
+ sends_menu = new Menu;
+ sends_menu->set_name ("ArdourContextMenu");
+ MenuList& items = sends_menu->items();
+
+ items.push_back (MenuElem(_("Assign all tracks (prefader)"), bind (mem_fun (*this, &RouteUI::create_sends), PreFader)));
+ items.push_back (MenuElem(_("Assign all tracks (postfader)"), bind (mem_fun (*this, &RouteUI::create_sends), PostFader)));
+ items.push_back (MenuElem(_("Assign selected tracks (prefader)"), bind (mem_fun (*this, &RouteUI::create_selected_sends), PreFader)));
+ items.push_back (MenuElem(_("Assign selected tracks (postfader)"), bind (mem_fun (*this, &RouteUI::create_selected_sends), PostFader)));
+ items.push_back (MenuElem(_("Copy track gains to sends"), mem_fun (*this, &RouteUI::set_sends_gain_from_track)));
+ items.push_back (MenuElem(_("Set sends gain to -inf"), mem_fun (*this, &RouteUI::set_sends_gain_to_zero)));
+ items.push_back (MenuElem(_("Set sends gain to 0dB"), mem_fun (*this, &RouteUI::set_sends_gain_to_unity)));
+
+}
+
+void
+RouteUI::create_sends (Placement p)
+{
+ _session.globally_add_internal_sends (_route, p);
+}
+
+void
+RouteUI::create_selected_sends (Placement p)
+{
+ boost::shared_ptr<RouteList> rlist (new RouteList);
+ TrackSelection& selected_tracks (ARDOUR_UI::instance()->the_editor().get_selection().tracks);
+
+ for (TrackSelection::iterator i = selected_tracks.begin(); i != selected_tracks.end(); ++i) {
+ RouteTimeAxisView* rtv;
+ RouteUI* rui;
+ if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
+ if ((rui = dynamic_cast<RouteUI*>(rtv)) != 0) {
+ if (boost::dynamic_pointer_cast<AudioTrack>(rui->route())) {
+ rlist->push_back (rui->route());
+ }
}
+ }
+ }
+
+ _session.add_internal_sends (_route, p, rlist);
+}
- cmd->mark();
- _session.add_command(cmd);
- _session.commit_reversible_command ();
+void
+RouteUI::set_sends_gain_from_track ()
+{
+ _session.globally_set_send_gains_from_track (_route);
+}
- } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
+void
+RouteUI::set_sends_gain_to_zero ()
+{
+ _session.globally_set_send_gains_to_zero (_route);
+}
+
+void
+RouteUI::set_sends_gain_to_unity ()
+{
+ _session.globally_set_send_gains_to_unity (_route);
+}
- set_mix_group_rec_enable (_route, !_route->record_enabled());
+bool
+RouteUI::show_sends_press(GdkEventButton* ev)
+{
+ if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
+ return true;
+ }
+
+ if (!ignore_toggle && !is_track() && show_sends_button) {
+
+ if (Keyboard::is_button2_event (ev) && Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
+
+ // do nothing on midi bind event
+ return false;
+
+ } else if (Keyboard::is_context_menu_event (ev)) {
+
+ if (sends_menu == 0) {
+ build_sends_menu ();
+ }
+
+ sends_menu->popup (0, ev->time);
} else {
- reversibly_apply_track_boolean ("rec-enable change", &Track::set_record_enable, !track()->record_enabled(), this);
+ /* change button state */
+
+ show_sends_button->set_active (!show_sends_button->get_active());
+
+ /* start blinking */
+
+ if (show_sends_button->get_active()) {
+ /* show sends to this bus */
+ MixerStrip::SwitchIO (_route);
+ send_blink_connection = ARDOUR_UI::instance()->Blink.connect (mem_fun(*this, &RouteUI::send_blink));
+ } else {
+ /* everybody back to normal */
+ send_blink_connection.disconnect ();
+ MixerStrip::SwitchIO (boost::shared_ptr<Route>());
+ }
+
}
}
}
bool
-RouteUI::rec_enable_release (GdkEventButton* ev)
+RouteUI::show_sends_release (GdkEventButton*)
{
return true;
}
void
-RouteUI::solo_changed(void* src)
+RouteUI::send_blink (bool onoff)
+{
+ if (!show_sends_button) {
+ return;
+ }
+
+ if (onoff) {
+ show_sends_button->set_state (STATE_ACTIVE);
+ } else {
+ show_sends_button->set_state (STATE_NORMAL);
+ }
+}
+
+void
+RouteUI::solo_changed(void* /*src*/)
+{
+ Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
+}
+
+
+void
+RouteUI::listen_changed(void* /*src*/)
{
Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_solo_display));
}
+int
+RouteUI::solo_visual_state (boost::shared_ptr<Route> r)
+{
+ if (Config->get_solo_control_is_listen_control()) {
+
+ if (r->listening()) {
+ return 1;
+ } else {
+ return 0;
+ }
+
+ } else {
+
+ if (r->solo_isolated()) {
+ return 2;
+ } else if (r->soloed()) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
void
RouteUI::update_solo_display ()
{
bool x;
- vector<Gdk::Color> fg_colors;
- Gdk::Color c;
- if (solo_button->get_active() != (x = _route->soloed())){
- ignore_toggle = true;
- solo_button->set_active(x);
- ignore_toggle = false;
- }
-
- if (_route->solo_safe()) {
- solo_button->set_visual_state (2);
- } else if (_route->soloed()) {
- solo_button->set_visual_state (1);
+ if (Config->get_solo_control_is_listen_control()) {
+
+ if (solo_button->get_active() != (x = _route->listening())) {
+ ignore_toggle = true;
+ solo_button->set_active(x);
+ ignore_toggle = false;
+ }
+
} else {
- solo_button->set_visual_state (0);
+
+ if (solo_button->get_active() != (x = _route->soloed())) {
+ ignore_toggle = true;
+ solo_button->set_active (x);
+ ignore_toggle = false;
+ }
+
}
+
+ solo_button->set_visual_state (solo_visual_state (_route));
}
void
}
void
-RouteUI::mute_changed(void* src)
+RouteUI::mute_changed(void* /*src*/)
{
Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &RouteUI::update_mute_display));
}
+int
+RouteUI::mute_visual_state (Session& s, boost::shared_ptr<Route> r)
+{
+ if (Config->get_show_solo_mutes()) {
+
+ if (r->muted ()) {
+ /* full mute */
+ return 2;
+ } else if (s.soloing() && !r->soloed() && !r->solo_isolated()) {
+ /* mute-because-not-soloed */
+ return 1;
+ } else {
+ /* no mute at all */
+ return 0;
+ }
+
+ } else {
+
+ if (r->muted()) {
+ /* full mute */
+ return 2;
+ } else {
+ /* no mute at all */
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
void
RouteUI::update_mute_display ()
{
ignore_toggle = false;
}
- /* now attend to visual state */
-
- if (Config->get_show_solo_mutes()) {
- if (_route->muted()) {
- mute_button->set_visual_state (2);
- } else if (!_route->soloed() && _route->solo_muted()) {
-
- mute_button->set_visual_state (1);
- } else {
- mute_button->set_visual_state (0);
- }
- } else {
- if (_route->muted()) {
- mute_button->set_visual_state (2);
- } else {
- mute_button->set_visual_state (0);
- }
- }
-
+ mute_button->set_visual_state (mute_visual_state (_session, _route));
}
void
rec_enable_button->set_active (model);
ignore_toggle = false;
}
-
+
/* now make sure its color state is correct */
if (model) {
} else {
rec_enable_button->set_visual_state (0);
}
-}
-void
-RouteUI::build_remote_control_menu ()
-{
- remote_control_menu = manage (new Menu);
- refresh_remote_control_menu ();
-}
-
-void
-RouteUI::refresh_remote_control_menu ()
-{
- ENSURE_GUI_THREAD (mem_fun (*this, &RouteUI::refresh_remote_control_menu));
-
- // only refresh the menu if it has been instantiated
-
- if (remote_control_menu == 0) {
- return;
- }
-
- using namespace Menu_Helpers;
-
- RadioMenuItem::Group rc_group;
- CheckMenuItem* rc_active;
- uint32_t limit = _session.ntracks() + _session.nbusses();
- char buf[32];
-
- MenuList& rc_items = remote_control_menu->items();
- rc_items.clear ();
-
- /* note that this menu list starts at zero, not 1, because zero
- is a valid, if useless, ID.
- */
-
- limit += 4; /* leave some breathing room */
-
- rc_items.push_back (RadioMenuElem (rc_group, _("None")));
- if (_route->remote_control_id() == 0) {
- rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
- rc_active->set_active ();
- }
-
- for (uint32_t i = 1; i < limit; ++i) {
- snprintf (buf, sizeof (buf), "%u", i);
- rc_items.push_back (RadioMenuElem (rc_group, buf));
- rc_active = dynamic_cast<RadioMenuItem*>(&rc_items.back());
- if (_route->remote_control_id() == i) {
- rc_active = dynamic_cast<CheckMenuItem*> (&rc_items.back());
- rc_active->set_active ();
- }
- rc_active->signal_activate().connect (bind (mem_fun (*this, &RouteUI::set_remote_control_id), i, rc_active));
- }
-}
-
-void
-RouteUI::set_remote_control_id (uint32_t id, CheckMenuItem* item)
-{
- /* this is called when the radio menu item is toggled, and so
- is actually invoked twice per menu selection. we only
- care about the invocation for the item that was being
- marked active.
- */
-
- if (item->get_active()) {
- _route->set_remote_control_id (id);
- }
+ check_rec_enable_sensitivity ();
}
void
RouteUI::build_solo_menu (void)
{
using namespace Menu_Helpers;
-
+
solo_menu = new Menu;
solo_menu->set_name ("ArdourContextMenu");
MenuList& items = solo_menu->items();
CheckMenuItem* check;
- check = new CheckMenuItem(_("Solo-safe"));
+ check = new CheckMenuItem(_("Solo Isolate"));
+ check->set_active (_route->solo_isolated());
+ check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_isolated), check));
+ _route->solo_isolated_changed.connect(bind (mem_fun (*this, &RouteUI::solo_isolated_toggle), check));
+ items.push_back (CheckMenuElem(*check));
+ check->show_all();
+
+ check = new CheckMenuItem(_("Solo Safe"));
check->set_active (_route->solo_safe());
check->signal_toggled().connect (bind (mem_fun (*this, &RouteUI::toggle_solo_safe), check));
_route->solo_safe_changed.connect(bind (mem_fun (*this, &RouteUI::solo_safe_toggle), check));
//items.push_back (SeparatorElem());
// items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
-
+
}
void
RouteUI::build_mute_menu(void)
{
using namespace Menu_Helpers;
-
+
mute_menu = new Menu;
mute_menu->set_name ("ArdourContextMenu");
- MenuList& items = mute_menu->items();
- CheckMenuItem* check;
-
- check = new CheckMenuItem(_("Pre Fader"));
- init_mute_menu(PRE_FADER, check);
- check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), PRE_FADER, check));
- _route->pre_fader_changed.connect(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), check));
- items.push_back (CheckMenuElem(*check));
- check->show_all();
- check = new CheckMenuItem(_("Post Fader"));
- init_mute_menu(POST_FADER, check);
- check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), POST_FADER, check));
- _route->post_fader_changed.connect(bind (mem_fun (*this, &RouteUI::post_fader_toggle), check));
- items.push_back (CheckMenuElem(*check));
- check->show_all();
-
- check = new CheckMenuItem(_("Control Outs"));
- init_mute_menu(CONTROL_OUTS, check);
- check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), CONTROL_OUTS, check));
- _route->control_outs_changed.connect(bind (mem_fun (*this, &RouteUI::control_outs_toggle), check));
- items.push_back (CheckMenuElem(*check));
- check->show_all();
+ MenuList& items = mute_menu->items();
- check = new CheckMenuItem(_("Main Outs"));
- init_mute_menu(MAIN_OUTS, check);
- check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MAIN_OUTS, check));
- _route->main_outs_changed.connect(bind (mem_fun (*this, &RouteUI::main_outs_toggle), check));
- items.push_back (CheckMenuElem(*check));
- check->show_all();
+ pre_fader_mute_check = manage (new CheckMenuItem(_("Pre Fader")));
+ init_mute_menu(MuteMaster::PreFader, pre_fader_mute_check);
+ pre_fader_mute_check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::PreFader, pre_fader_mute_check));
+ items.push_back (CheckMenuElem(*pre_fader_mute_check));
+ pre_fader_mute_check->show_all();
+
+ post_fader_mute_check = manage (new CheckMenuItem(_("Post Fader")));
+ init_mute_menu(MuteMaster::PostFader, post_fader_mute_check);
+ post_fader_mute_check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::PostFader, post_fader_mute_check));
+ items.push_back (CheckMenuElem(*post_fader_mute_check));
+ post_fader_mute_check->show_all();
+
+ listen_mute_check = manage (new CheckMenuItem(_("Control Outs")));
+ init_mute_menu(MuteMaster::Listen, listen_mute_check);
+ listen_mute_check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::Listen, listen_mute_check));
+ items.push_back (CheckMenuElem(*listen_mute_check));
+ listen_mute_check->show_all();
+
+ main_mute_check = manage (new CheckMenuItem(_("Main Outs")));
+ init_mute_menu(MuteMaster::Main, main_mute_check);
+ main_mute_check->signal_toggled().connect(bind (mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::Main, main_mute_check));
+ items.push_back (CheckMenuElem(*main_mute_check));
+ main_mute_check->show_all();
//items.push_back (SeparatorElem());
// items.push_back (MenuElem (_("MIDI Bind"), mem_fun (*mute_button, &BindableToggleButton::midi_learn)));
+
+ _route->mute_points_changed.connect (mem_fun (*this, &RouteUI::muting_change));
}
void
-RouteUI::init_mute_menu(mute_type type, CheckMenuItem* check)
+RouteUI::init_mute_menu(MuteMaster::MutePoint mp, CheckMenuItem* check)
{
- if (_route->get_mute_config (type)) {
- check->set_active (true);
+ check->set_active (_route->mute_points() & mp);
+}
+
+void
+RouteUI::toggle_mute_menu(MuteMaster::MutePoint mp, Gtk::CheckMenuItem* check)
+{
+ if (check->get_active()) {
+ _route->set_mute_points (MuteMaster::MutePoint (_route->mute_points() | mp));
+ } else {
+ _route->set_mute_points (MuteMaster::MutePoint (_route->mute_points() & ~mp));
}
}
void
-RouteUI::toggle_mute_menu(mute_type type, Gtk::CheckMenuItem* check)
+RouteUI::muting_change ()
{
- _route->set_mute_config(type, check->get_active(), this);
+ ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::muting_change));
+
+ bool yn;
+ MuteMaster::MutePoint current = _route->mute_points ();
+
+ yn = (current & MuteMaster::PreFader);
+
+ if (pre_fader_mute_check->get_active() != yn) {
+ pre_fader_mute_check->set_active (yn);
+ }
+
+ yn = (current & MuteMaster::PostFader);
+
+ if (post_fader_mute_check->get_active() != yn) {
+ post_fader_mute_check->set_active (yn);
+ }
+
+ yn = (current & MuteMaster::Listen);
+
+ if (listen_mute_check->get_active() != yn) {
+ listen_mute_check->set_active (yn);
+ }
+
+ yn = (current & MuteMaster::Main);
+
+ if (main_mute_check->get_active() != yn) {
+ main_mute_check->set_active (yn);
+ }
+}
+
+void
+RouteUI::toggle_solo_isolated (Gtk::CheckMenuItem* check)
+{
+ _route->set_solo_isolated (check->get_active(), this);
}
void
}
void
-RouteUI::set_mix_group_solo(boost::shared_ptr<Route> route, bool yn)
+RouteUI::set_route_group_solo(boost::shared_ptr<Route> route, bool yn)
{
- RouteGroup* mix_group;
+ RouteGroup* route_group;
- if((mix_group = route->mix_group()) != 0){
+ if((route_group = route->route_group()) != 0){
_session.begin_reversible_command (_("mix group solo change"));
Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
- mix_group->apply(&Route::set_solo, yn, this);
+ route_group->apply(&Route::set_solo, yn, this);
cmd->mark();
_session.add_command (cmd);
_session.commit_reversible_command ();
RouteUI::reversibly_apply_route_boolean (string name, void (Route::*func)(bool, void *), bool yn, void *arg)
{
_session.begin_reversible_command (name);
- XMLNode &before = _route->get_state();
- bind(mem_fun(*_route, func), yn, arg)();
- XMLNode &after = _route->get_state();
- _session.add_command (new MementoCommand<Route>(*_route, &before, &after));
+ XMLNode &before = _route->get_state();
+ bind(mem_fun(*_route, func), yn, arg)();
+ XMLNode &after = _route->get_state();
+ _session.add_command (new MementoCommand<Route>(*_route, &before, &after));
_session.commit_reversible_command ();
}
RouteUI::reversibly_apply_track_boolean (string name, void (Track::*func)(bool, void *), bool yn, void *arg)
{
_session.begin_reversible_command (name);
- XMLNode &before = track()->get_state();
+ XMLNode &before = track()->get_state();
bind (mem_fun (*track(), func), yn, arg)();
- XMLNode &after = track()->get_state();
+ XMLNode &after = track()->get_state();
_session.add_command (new MementoCommand<Track>(*track(), &before, &after));
_session.commit_reversible_command ();
}
void
-RouteUI::set_mix_group_mute(boost::shared_ptr<Route> route, bool yn)
+RouteUI::set_route_group_mute(boost::shared_ptr<Route> route, bool yn)
{
- RouteGroup* mix_group;
+ RouteGroup* route_group;
- if((mix_group = route->mix_group()) != 0){
+ if((route_group = route->route_group()) != 0){
_session.begin_reversible_command (_("mix group mute change"));
Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand (_session, this);
- mix_group->apply(&Route::set_mute, yn, this);
+ route_group->apply(&Route::set_mute, yn, this);
cmd->mark();
_session.add_command(cmd);
_session.commit_reversible_command ();
}
void
-RouteUI::set_mix_group_rec_enable(boost::shared_ptr<Route> route, bool yn)
+RouteUI::set_route_group_rec_enable(boost::shared_ptr<Route> route, bool yn)
{
- RouteGroup* mix_group;
+ RouteGroup* route_group;
- if((mix_group = route->mix_group()) != 0){
+ if((route_group = route->route_group()) != 0){
_session.begin_reversible_command (_("mix group rec-enable change"));
Session::GlobalRecordEnableStateCommand *cmd = new Session::GlobalRecordEnableStateCommand(_session, this);
- mix_group->apply (&Route::set_record_enable, yn, this);
+ route_group->apply (&Route::set_record_enable, yn, this);
cmd->mark();
_session.add_command(cmd);
_session.commit_reversible_command ();
RouteUI::set_color (const Gdk::Color & c)
{
char buf[64];
-
+
_color = c;
-
+
ensure_xml_node ();
snprintf (buf, sizeof (buf), "%d:%d:%d", c.get_red(), c.get_green(), c.get_blue());
xml_node->add_property ("color", buf);
}
XMLNode*
-RouteUI::get_child_xml_node (const string & childname)
+RouteUI::get_automation_child_xml_node (Evoral::Parameter param)
{
- XMLNode* child;
-
ensure_xml_node ();
-
-
- if ((child = find_named_node (*xml_node, childname)) == 0) {
- child = new XMLNode (childname);
- xml_node->add_child_nocopy (*child);
+
+ XMLNodeList kids = xml_node->children();
+ XMLNodeConstIterator iter;
+
+ const string sym = ARDOUR::EventTypeMap::instance().to_symbol(param);
+
+ for (iter = kids.begin(); iter != kids.end(); ++iter) {
+ if ((*iter)->name() == AutomationTimeAxisView::state_node_name) {
+ XMLProperty* type = (*iter)->property("automation-id");
+ if (type && type->value() == sym)
+ return *iter;
+ }
}
+ // Didn't find it, make a new one
+ XMLNode* child = new XMLNode (AutomationTimeAxisView::state_node_name);
+ child->add_property("automation-id", sym);
+ xml_node->add_child_nocopy (*child);
+
return child;
}
RouteUI::set_color_from_route ()
{
XMLProperty *prop;
-
+
RouteUI::ensure_xml_node ();
if ((prop = xml_node->property ("color")) != 0) {
_color.set_green(g);
_color.set_blue(b);
return 0;
- }
+ }
return 1;
}
RouteUI::idle_remove_this_route (RouteUI *rui)
{
rui->_session.remove_route (rui->_route);
- return FALSE;
+ return false;
}
void
name_prompter.get_result (result);
if (result.length()) {
_route->set_name (result);
- }
+ }
break;
}
return;
-
+
}
void
bool x;
ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_polarity));
-
+
if ((x = polarity_menu_item->get_active()) != _route->phase_invert()) {
- _route->set_phase_invert (x, this);
+ _route->set_phase_invert (x);
if (x) {
name_label.set_text (X_("Ø ") + name_label.get_text());
} else {
void
RouteUI::polarity_changed ()
{
- /* no signal for this yet */
+ if (_route->phase_invert()) {
+ name_label.set_text (X_("Ø ") + name_label.get_text());
+ } else {
+ name_label.set_text (_route->name());
+ }
}
void
bool x;
ENSURE_GUI_THREAD(mem_fun (*this, &RouteUI::toggle_denormal_protection));
-
+
if ((x = denormal_menu_item->get_active()) != _route->denormal_protection()) {
- _route->set_denormal_protection (x, this);
+ _route->set_denormal_protection (x);
}
}
}
void
RouteUI::denormal_protection_changed ()
{
- /* no signal for this yet */
+ if (denormal_menu_item) {
+ denormal_menu_item->set_active (_route->denormal_protection());
+ }
}
-
void
-RouteUI::solo_safe_toggle(void* src, Gtk::CheckMenuItem* check)
+RouteUI::solo_isolated_toggle(void* /*src*/, Gtk::CheckMenuItem* check)
{
- bool yn = _route->solo_safe ();
+ bool yn = _route->solo_isolated ();
if (check->get_active() != yn) {
check->set_active (yn);
}
}
-void
-RouteUI::pre_fader_toggle(void* src, Gtk::CheckMenuItem* check)
-{
- ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::pre_fader_toggle), src, check));
-
- bool yn = _route->get_mute_config(PRE_FADER);
- if (check->get_active() != yn) {
- check->set_active (yn);
- }
-}
-void
-RouteUI::post_fader_toggle(void* src, Gtk::CheckMenuItem* check)
-{
- ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::post_fader_toggle), src, check));
-
- bool yn = _route->get_mute_config(POST_FADER);
- if (check->get_active() != yn) {
- check->set_active (yn);
- }
-}
void
-RouteUI::control_outs_toggle(void* src, Gtk::CheckMenuItem* check)
+RouteUI::solo_safe_toggle(void* /*src*/, Gtk::CheckMenuItem* check)
{
- ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::control_outs_toggle), src, check));
-
- bool yn = _route->get_mute_config(CONTROL_OUTS);
- if (check->get_active() != yn) {
- check->set_active (yn);
- }
-}
+ bool yn = _route->solo_safe ();
-void
-RouteUI::main_outs_toggle(void* src, Gtk::CheckMenuItem* check)
-{
- ENSURE_GUI_THREAD(bind (mem_fun (*this, &RouteUI::main_outs_toggle), src, check));
-
- bool yn = _route->get_mute_config(MAIN_OUTS);
if (check->get_active() != yn) {
check->set_active (yn);
}
void
RouteUI::disconnect_input ()
{
- _route->disconnect_inputs (this);
+ _route->input()->disconnect (this);
}
void
RouteUI::disconnect_output ()
{
- _route->disconnect_outputs (this);
+ _route->output()->disconnect (this);
}
bool
void
RouteUI::adjust_latency ()
{
- LatencyDialog dialog (_route->name() + _("latency"), *(_route.get()), _session.frame_rate(), _session.engine().frames_per_cycle());
+ LatencyDialog dialog (_route->name() + _(" latency"), *(_route->output()), _session.frame_rate(), _session.engine().frames_per_cycle());
+}
+
+void
+RouteUI::save_as_template ()
+{
+ sys::path path;
+ Glib::ustring safe_name;
+ string name;
+
+ path = ARDOUR::user_route_template_directory ();
+
+ if (g_mkdir_with_parents (path.to_string().c_str(), 0755)) {
+ error << string_compose (_("Cannot create route template directory %1"), path.to_string()) << endmsg;
+ return;
+ }
+
+ Prompter p (true); // modal
+
+ p.set_prompt (_("Template name:"));
+ switch (p.run()) {
+ case RESPONSE_ACCEPT:
+ break;
+ default:
+ return;
+ }
+
+ p.hide ();
+ p.get_result (name, true);
+
+ safe_name = legalize_for_path (name);
+ safe_name += template_suffix;
+
+ path /= safe_name;
+
+ _route->save_as_template (path.to_string(), name);
+}
+
+void
+RouteUI::check_rec_enable_sensitivity ()
+{
+ if (_session.transport_rolling() && rec_enable_button->get_active() && Config->get_disable_disarm_during_roll()) {
+ rec_enable_button->set_sensitive (false);
+ } else {
+ rec_enable_button->set_sensitive (true);
+ }
+}
+
+void
+RouteUI::parameter_changed (string const & p)
+{
+ ENSURE_GUI_THREAD (bind (mem_fun (*this, &RouteUI::parameter_changed), p));
+
+ if (p == "disable-disarm-during-roll") {
+ check_rec_enable_sensitivity ();
+ } else if (p == "solo-control-is-listen-control") {
+ set_button_names ();
+ } else if (p == "listen-position") {
+ set_button_names ();
+ }
+}
+
+void
+RouteUI::step_gain_up ()
+{
+ _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.1), this);
+}
+
+void
+RouteUI::page_gain_up ()
+{
+ _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.5), this);
+}
+
+void
+RouteUI::step_gain_down ()
+{
+ _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.1), this);
+}
+
+void
+RouteUI::page_gain_down ()
+{
+ _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.5), this);
+}
+
+void
+RouteUI::open_remote_control_id_dialog ()
+{
+ ArdourDialog dialog (_("Remote Control ID"));
+
+ uint32_t const limit = _session.ntracks() + _session.nbusses () + 4;
+
+ HBox* hbox = manage (new HBox);
+ hbox->set_spacing (6);
+ hbox->pack_start (*manage (new Label (_("Remote control ID:"))));
+ SpinButton* spin = manage (new SpinButton);
+ spin->set_digits (0);
+ spin->set_increments (1, 10);
+ spin->set_range (0, limit);
+ spin->set_value (_route->remote_control_id());
+ hbox->pack_start (*spin);
+ dialog.get_vbox()->pack_start (*hbox);
+
+ dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
+ dialog.add_button (Stock::APPLY, RESPONSE_ACCEPT);
+
+ dialog.show_all ();
+ int const r = dialog.run ();
+
+ if (r == RESPONSE_ACCEPT) {
+ _route->set_remote_control_id (spin->get_value_as_int ());
+ }
}