*/
+#include <boost/algorithm/string.hpp>
+
#include <gtkmm2ext/gtk_ui.h>
#include <gtkmm2ext/choice.h>
#include <gtkmm2ext/doi.h>
#include "ardour/filename_extensions.h"
#include "ardour/midi_track.h"
#include "ardour/internal_send.h"
+#include "ardour/profile.h"
#include "ardour/send.h"
#include "ardour/route.h"
#include "ardour/session.h"
uint32_t RouteUI::_max_invert_buttons = 3;
PBD::Signal1<void, boost::shared_ptr<Route> > RouteUI::BusSendDisplayChanged;
boost::weak_ptr<Route> RouteUI::_showing_sends_to;
+std::string RouteUI::program_port_prefix;
RouteUI::RouteUI (ARDOUR::Session* sess)
: AxisView(sess)
, output_selector (0)
, _invert_menu(0)
{
+ if (program_port_prefix.empty()) {
+ // compare to gtk2_ardour/port_group.cc
+ string lpn (PROGRAM_NAME);
+ boost::to_lower (lpn);
+ program_port_prefix = lpn + ":"; // e.g. "ardour:"
+ }
if (sess) init ();
}
_route->active_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_active_changed, this), gui_context());
_route->mute_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_mute_display, this), gui_context());
- _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::comment_changed, this, _1), gui_context());
+ _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::comment_changed, this), gui_context());
_route->solo_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
_route->solo_safe_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
t->RecordEnableChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context());
rec_enable_button->show();
- rec_enable_button->set_controllable (t->rec_enable_control());
+ rec_enable_button->set_controllable (t->rec_enable_control());
if (is_midi_track()) {
midi_track()->StepEditStatusChange.connect (route_connections, invalidator (*this),
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
- /* Primary-button1 applies change to the mix group even if it is not active
+ /* Primary-button1 inverts the implication of
+ the group being active. If the group is
+ active (for mute), then this modifier means
+ "do not apply to mute". If the group is
+ inactive (for mute), then this modifier
+ means "apply to route". This is all
+ accomplished by passing just the actual
+ route, along with the InverseGroup group
+ control disposition.
+
NOTE: Primary-button2 is MIDI learn.
*/
if (ev->button == 1) {
- if (_route->route_group()) {
-
- rl = _route->route_group()->route_list();
+ rl.reset (new RouteList);
+ rl->push_back (_route);
- if (_mute_release) {
- _mute_release->routes = rl;
- }
- } else {
- rl.reset (new RouteList);
- rl->push_back (_route);
+ if (_mute_release) {
+ _mute_release->routes = rl;
}
DisplaySuspender ds;
- _session->set_mute (rl, !_route->muted(), Session::rt_cleanup, true);
+ _session->set_mute (rl, !_route->muted(), Session::rt_cleanup, Controllable::InverseGroup);
}
} else {
{
if (_mute_release){
DisplaySuspender ds;
- _session->set_mute (_mute_release->routes, _mute_release->active, Session::rt_cleanup, true);
+ _session->set_mute (_mute_release->routes, _mute_release->active, Session::rt_cleanup, Controllable::UseGroup);
delete _mute_release;
_mute_release = 0;
}
DisplaySuspender ds;
if (Config->get_solo_control_is_listen_control()) {
- _session->set_listen (_session->get_routes(), !_route->listening_via_monitor(), Session::rt_cleanup, true);
+ _session->set_listen (_session->get_routes(), !_route->listening_via_monitor(), Session::rt_cleanup, Controllable::UseGroup);
} else {
- _session->set_solo (_session->get_routes(), !_route->self_soloed(), Session::rt_cleanup, true);
+ _session->set_solo (_session->get_routes(), !_route->self_soloed(), Session::rt_cleanup, Controllable::UseGroup);
}
} else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
// shift-click: toggle solo isolated status
- _route->set_solo_isolated (!_route->solo_isolated(), this);
+ _route->set_solo_isolated (!_route->solo_isolated(), Controllable::UseGroup);
delete _solo_release;
_solo_release = 0;
if (ev->button == 1) {
- if (_route->route_group()) {
+ /* Primary-button1 inverts the implication of
+ the group being active. If the group is
+ active (for solo), then this modifier means
+ "do not apply to solo". If the group is
+ inactive (for mute), then this modifier
+ means "apply to route". This is all
+ accomplished by passing just the actual
+ route, along with the InverseGroup group
+ control disposition.
- rl = _route->route_group()->route_list();
+ NOTE: Primary-button2 is MIDI learn.
+ */
- if (_solo_release) {
- _solo_release->routes = rl;
- }
- } else {
- rl.reset (new RouteList);
- rl->push_back (_route);
+ rl.reset (new RouteList);
+ rl->push_back (_route);
+
+ if (_solo_release) {
+ _solo_release->routes = rl;
}
DisplaySuspender ds;
+
if (Config->get_solo_control_is_listen_control()) {
- _session->set_listen (rl, !_route->listening_via_monitor(), Session::rt_cleanup, true);
+ _session->set_listen (rl, !_route->listening_via_monitor(), Session::rt_cleanup, Controllable::InverseGroup);
} else {
- _session->set_solo (rl, !_route->self_soloed(), Session::rt_cleanup, true);
+ _session->set_solo (rl, !_route->self_soloed(), Session::rt_cleanup, Controllable::InverseGroup);
}
}
+ delete _solo_release;
+ _solo_release = 0;
+
} else {
/* click: solo this route */
} else {
DisplaySuspender ds;
if (Config->get_solo_control_is_listen_control()) {
- _session->set_listen (_solo_release->routes, _solo_release->active, Session::rt_cleanup, true);
+ _session->set_listen (_solo_release->routes, _solo_release->active, Session::rt_cleanup, Controllable::UseGroup);
} else {
- _session->set_solo (_solo_release->routes, _solo_release->active, Session::rt_cleanup, true);
+ _session->set_solo (_solo_release->routes, _solo_release->active, Session::rt_cleanup, Controllable::UseGroup);
}
}
boost::shared_ptr<RouteList> rl;
- if (_route->route_group()) {
-
- rl = _route->route_group()->route_list();
-
- } else {
- rl.reset (new RouteList);
- rl->push_back (_route);
- }
+ rl.reset (new RouteList);
+ rl->push_back (_route);
DisplaySuspender ds;
- _session->set_record_enabled (rl, !_route->record_enabled(), Session::rt_cleanup, true);
+ _session->set_record_enabled (rl, !_route->record_enabled(), Session::rt_cleanup, Controllable::InverseGroup);
}
} else if (Keyboard::is_context_menu_event (ev)) {
}
DisplaySuspender ds;
- _session->set_monitoring (rl, mc, Session::rt_cleanup, true);
+ _session->set_monitoring (rl, mc, Session::rt_cleanup, Controllable::UseGroup);
return false;
}
if (model) {
/* disable isolate for all routes */
DisplaySuspender ds;
- _session->set_solo_isolated (_session->get_routes(), false, Session::rt_cleanup, true);
+ _session->set_solo_isolated (_session->get_routes(), false, Session::rt_cleanup, Controllable::NoGroup);
} else {
/* enable isolate for all routes */
DisplaySuspender ds;
- _session->set_solo_isolated (_session->get_routes(), true, Session::rt_cleanup, true);
+ _session->set_solo_isolated (_session->get_routes(), true, Session::rt_cleanup, Controllable::NoGroup);
}
} else {
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (_route);
DisplaySuspender ds;
- _session->set_solo_isolated (rl, !view, Session::rt_cleanup, true);
+ _session->set_solo_isolated (rl, !view, Session::rt_cleanup, Controllable::NoGroup);
}
}
}
/* disable solo safe for all routes */
DisplaySuspender ds;
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- (*i)->set_solo_safe (false, this);
+ (*i)->set_solo_safe (false, Controllable::NoGroup);
}
} else {
/* enable solo safe for all routes */
DisplaySuspender ds;
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- (*i)->set_solo_safe (true, this);
+ (*i)->set_solo_safe (true, Controllable::NoGroup);
}
}
}
else {
if (model == view) {
/* flip just this route */
- _route->set_solo_safe (!view, this);
+ _route->set_solo_safe (!view, Controllable::NoGroup);
}
}
}
/* called AFTER the view has changed */
if (model != view) {
- _route->set_solo_isolated (view, this);
+ _route->set_solo_isolated (view, Controllable::UseGroup);
}
}
void
RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check)
{
- _route->set_solo_safe (check->get_active(), this);
+ _route->set_solo_safe (check->get_active(), Controllable::UseGroup);
}
/** Ask the user to choose a colour, and then apply that color to my route
}
void
-RouteUI::comment_changed (void *src)
+RouteUI::comment_changed ()
{
- ENSURE_GUI_THREAD (*this, &MixerStrip::comment_changed, src)
-
- if (src != this) {
- ignore_comment_edit = true;
- if (comment_area) {
- comment_area->get_buffer()->set_text (_route->comment());
- }
- ignore_comment_edit = false;
+ ignore_comment_edit = true;
+ if (comment_area) {
+ comment_area->get_buffer()->set_text (_route->comment());
}
+ ignore_comment_edit = false;
}
void
}
}
+void
+RouteUI::duplicate_selected_routes ()
+{
+ ARDOUR_UI::instance()->start_duplicate_routes();
+}
+
void
RouteUI::toggle_denormal_protection ()
{
{
ENSURE_GUI_THREAD (*this, &RouteUI::map_frozen)
- AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
+ AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
if (at) {
switch (at->freeze_state()) {
LatencyDialog dialog (_route->name() + _(" latency"), *(_route->output()), _session->frame_rate(), AudioEngine::instance()->samples_per_cycle());
}
-void
-RouteUI::save_as_template ()
+bool
+RouteUI::process_save_template_prompter (ArdourPrompter& prompter, const std::string& dir)
{
std::string path;
std::string safe_name;
- string name;
+ std::string name;
- path = ARDOUR::user_route_template_directory ();
+ prompter.get_result (name, true);
- if (g_mkdir_with_parents (path.c_str(), 0755)) {
- error << string_compose (_("Cannot create route template directory %1"), path) << endmsg;
- return;
+ safe_name = legalize_for_path (name);
+ safe_name += template_suffix;
+
+ path = Glib::build_filename (dir, safe_name);
+
+ if (Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
+ bool overwrite = overwrite_file_dialog (prompter,
+ _("Confirm Template Overwrite"),
+ _("A template already exists with that name. Do you want to overwrite it?"));
+
+ if (!overwrite) {
+ return false;
+ }
}
- Prompter p (true); // modal
+ _route->save_as_template (path, name);
+
+ return true;
+}
+
+void
+RouteUI::save_as_template ()
+{
+ std::string dir;
+
+ dir = ARDOUR::user_route_template_directory ();
- p.set_title (_("Save As Template"));
- p.set_prompt (_("Template name:"));
- p.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
- switch (p.run()) {
- case RESPONSE_ACCEPT:
- break;
- default:
+ if (g_mkdir_with_parents (dir.c_str(), 0755)) {
+ error << string_compose (_("Cannot create route template directory %1"), dir) << endmsg;
return;
}
- p.hide ();
- p.get_result (name, true);
+ ArdourPrompter prompter (true); // modal
- safe_name = legalize_for_path (name);
- safe_name += template_suffix;
+ prompter.set_title (_("Save As Template"));
+ prompter.set_prompt (_("Template name:"));
+ prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
- path = Glib::build_filename (path, safe_name);
-
- _route->save_as_template (path, name);
+ bool finished = false;
+ while (!finished) {
+ switch (prompter.run()) {
+ case RESPONSE_ACCEPT:
+ finished = process_save_template_prompter (prompter, dir);
+ break;
+ default:
+ finished = true;
+ break;
+ }
+ }
}
void
void
RouteUI::step_gain_up ()
{
- _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.1), this);
+ _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.1), Controllable::UseGroup);
}
void
RouteUI::page_gain_up ()
{
- _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.5), this);
+ _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.5), Controllable::UseGroup);
}
void
RouteUI::step_gain_down ()
{
- _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.1), this);
+ _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.1), Controllable::UseGroup);
}
void
RouteUI::page_gain_down ()
{
- _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.5), this);
+ _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.5), Controllable::UseGroup);
}
void
if (_route->is_master() || _route->is_monitor()) {
l->set_markup (string_compose (_("The remote control ID of %1 is: %2\n\n\n"
"The remote control ID of %3 cannot be changed."),
- Glib::Markup::escape_text (_route->name()),
+ Gtkmm2ext::markup_escape_text (_route->name()),
_route->remote_control_id(),
(_route->is_master() ? _("the master bus") : _("the monitor bus"))));
} else {
_route->remote_control_id(),
"<span size=\"small\" style=\"italic\">",
"</span>",
- Glib::Markup::escape_text (_route->name()),
+ Gtkmm2ext::markup_escape_text (_route->name()),
PROGRAM_NAME));
}
dialog.get_vbox()->pack_start (*l);