/*
- Copyright (C) 2000-2006 Paul Davis
+ Copyright (C) 2018-2019 Len Ovens
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
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <cmath>
-#include <list>
-#include <algorithm>
-
-#include <sigc++/bind.h>
-
-#include <gtkmm/messagedialog.h>
-
-#include "pbd/convert.h"
-#include "pbd/enumwriter.h"
-#include "pbd/replace_all.h"
-#include "pbd/stacktrace.h"
-
-#include "ardour/amp.h"
#include "ardour/audioengine.h"
-#include "ardour/internal_send.h"
-#include "ardour/io.h"
#include "ardour/pannable.h"
-#include "ardour/panner.h"
#include "ardour/panner_shell.h"
#include "ardour/panner_manager.h"
-#include "ardour/port.h"
#include "ardour/profile.h"
#include "ardour/route.h"
#include "ardour/send.h"
#include "ardour/session.h"
-#include "ardour/types.h"
#include "ardour/user_bundle.h"
+#include "ardour/value_as_string.h"
#include "gtkmm2ext/gtk_ui.h"
#include "gtkmm2ext/menu_elems.h"
#include "keyboard.h"
#include "public_editor.h"
#include "send_ui.h"
+#include "timers.h"
#include "io_selector.h"
#include "utils.h"
#include "gui_thread.h"
using namespace Gtkmm2ext;
using namespace std;
+#define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
+
+FoldbackSend::FoldbackSend (boost::shared_ptr<Send> snd, \
+ boost::shared_ptr<ARDOUR::Route> sr, boost::shared_ptr<ARDOUR::Route> fr, uint32_t wd)
+ : _button (ArdourButton::led_default_elements)
+ , _send (snd)
+ , _send_route (sr)
+ , _foldback_route (fr)
+ , _send_proc (snd)
+ , _send_del (snd)
+ , _width (wd)
+ , pan_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
+ , _adjustment (gain_to_slider_position_with_max (1.0, Config->get_max_gain()), 0, 1, 0.01, 0.1)
+ , _slider (&_adjustment, boost::shared_ptr<PBD::Controllable>(), 0, max(13.f, rintf(13.f * UIConfiguration::instance().get_ui_scale())))
+ , _ignore_ui_adjustment (true)
+ , _slider_persistant_tooltip (&_slider)
+
+{
+
+ HBox * snd_but_pan = new HBox ();
+
+ _button.set_distinct_led_click (true);
+ _button.set_fallthrough_to_parent(true);
+ _button.set_led_left (true);
+ _button.signal_led_clicked.connect (sigc::mem_fun (*this, &FoldbackSend::led_clicked));
+ _button.set_name ("processor prefader");
+ _button.set_layout_ellipsize_width (PX_SCALE(_width) * PANGO_SCALE);
+ _button.set_text_ellipsize (Pango::ELLIPSIZE_END);
+ name_changed ();
+ snd_but_pan->pack_start (_button, true, true);
+ _button.set_active (_send_proc->enabled ());
+ _button.show ();
+
+ if (_foldback_route->input()->n_ports().n_audio() == 2) {
+ _button.set_layout_ellipsize_width (PX_SCALE(_width - 19) * PANGO_SCALE);
+ boost::shared_ptr<Pannable> pannable = _send_del->panner()->pannable();
+ boost::shared_ptr<AutomationControl> ac;
+ ac = pannable->pan_azimuth_control;
+ pan_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
+ pan_control.set_tooltip_prefix (_("Pan: "));
+ pan_control.set_name ("trim knob");
+ pan_control.set_no_show_all (true);
+ snd_but_pan->pack_start (pan_control, false, false);
+ pan_control.show ();
+ pan_control.set_controllable (ac);
+ }
+ boost::shared_ptr<AutomationControl> lc;
+ lc = _send->gain_control();
+ _slider.set_controllable (lc);
+ _slider.set_name ("ProcessorControlSlider");
+ _slider.set_text (_("Level"));
+
+ pack_start (*snd_but_pan, Gtk::PACK_SHRINK);
+ snd_but_pan->show();
+ pack_start (_slider, true, true);
+ _slider.show ();
+ level_changed ();
+
+ _adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &FoldbackSend::level_adjusted));
+ lc->Changed.connect (_connections, invalidator (*this), boost::bind (&FoldbackSend::level_changed, this), gui_context ());
+ _send_proc->ActiveChanged.connect (_connections, invalidator (*this), boost::bind (&FoldbackSend::send_state_changed, this), gui_context ());
+ _button.signal_button_press_event().connect (sigc::mem_fun (*this, &FoldbackSend::button_press));
+ _send_route->PropertyChanged.connect (_connections, invalidator (*this), boost::bind (&FoldbackSend::route_property_changed, this, _1), gui_context());
+
+ show ();
+
+
+}
+
+FoldbackSend::~FoldbackSend ()
+{
+ _connections.drop_connections();
+ _slider.set_controllable (boost::shared_ptr<AutomationControl> ());
+ pan_control.set_controllable (boost::shared_ptr<AutomationControl> ());
+ _send = boost::shared_ptr<Send> ();
+ _send_route = boost::shared_ptr<Route> ();
+ _foldback_route = boost::shared_ptr<Route> ();
+ _send_proc = boost::shared_ptr<Processor> ();
+ _send_del = boost::shared_ptr<Delivery> ();
+
+}
+
+void
+FoldbackSend::route_property_changed (const PropertyChange& what_changed)
+{
+ if (what_changed.contains (ARDOUR::Properties::name)) {
+ name_changed ();
+ }
+}
+
+void
+FoldbackSend::name_changed ()
+{
+ _button.set_text (_send_route->name ());
+
+ ArdourWidgets::set_tooltip (_button, Gtkmm2ext::markup_escape_text(_send_route->name()));
+}
+
+void
+FoldbackSend::led_clicked(GdkEventButton *ev)
+{
+ if (_send_proc) {
+ if (_button.get_active ()) {
+ _send_proc->enable (false);
+
+ } else {
+ _send_proc->enable (true);
+ }
+ }
+}
+
+gboolean
+FoldbackSend::button_press (GdkEventButton* ev)
+{
+ if (ev->button == 1) {
+ Menu* menu = build_send_menu ();
+
+ Gtkmm2ext::anchored_menu_popup(menu, &_button, "", 1, ev->time);
+ return true;
+ }
+ return false;
+}
+
+void
+FoldbackSend::send_state_changed ()
+{
+ _button.set_active (_send_proc->enabled ());
+
+}
+
+void
+FoldbackSend::level_adjusted ()
+{
+ if (_ignore_ui_adjustment) {
+ return;
+ }
+ boost::shared_ptr<AutomationControl> lc = _send->gain_control();
+
+ if (!lc) {
+ return;
+ }
+
+ lc->set_value ( lc->interface_to_internal(_adjustment.get_value ()) , Controllable::NoGroup);
+ set_tooltip ();
+}
+
+void
+FoldbackSend::level_changed ()
+{
+ boost::shared_ptr<AutomationControl> lc = _send->gain_control();
+ if (!lc) {
+ return;
+ }
+
+ _ignore_ui_adjustment = true;
+
+ const double nval = lc->internal_to_interface (lc->get_value ());
+ if (_adjustment.get_value() != nval) {
+ _adjustment.set_value (nval);
+ set_tooltip ();
+ }
+
+ _ignore_ui_adjustment = false;
+}
+
+void
+FoldbackSend::set_tooltip ()
+{
+ boost::shared_ptr<AutomationControl> lc = _send->gain_control();
+
+ if (!lc) {
+ return;
+ }
+ std::string tt = ARDOUR::value_as_string (lc->desc(), lc->get_value ());
+ string sm = Gtkmm2ext::markup_escape_text (tt);
+ _slider_persistant_tooltip.set_tip (sm);
+}
+
+Menu*
+FoldbackSend::build_send_menu ()
+{
+ using namespace Menu_Helpers;
+
+ if (!_send) {
+ return NULL;
+ }
+
+ Menu* menu = manage (new Menu);
+ MenuList& items = menu->items ();
+ menu->set_name ("ArdourContextMenu");
+
+ items.push_back (
+ MenuElem(_("Copy track/bus gain to send"), sigc::bind (sigc::mem_fun (*this, &FoldbackSend::set_gain), -0.1))
+ );
+ items.push_back (
+ MenuElem(_("Set send gain to -inf"), sigc::bind (sigc::mem_fun (*this, &FoldbackSend::set_gain), 0.0))
+ );
+ items.push_back (
+ MenuElem(_("Set send gain to 0dB"), sigc::bind (sigc::mem_fun (*this, &FoldbackSend::set_gain), 1.0))
+ );
+ items.push_back (MenuElem(_("Remove This Send"), sigc::mem_fun (*this, &FoldbackSend::remove_me)));
+
+ return menu;
+
+}
+
+void
+FoldbackSend::set_gain (float new_gain)
+{
+ if (new_gain < 0) {
+ // get level from sending route
+ new_gain = _send_route->gain_control ()->get_value ();
+ }
+ boost::shared_ptr<AutomationControl> lc = _send->gain_control();
+
+ if (!lc) {
+ return;
+ }
+ lc->set_value (new_gain, Controllable::NoGroup);
+
+}
+
+void
+FoldbackSend::remove_me ()
+{
+ boost::shared_ptr<Processor> send_proc = boost::dynamic_pointer_cast<Processor> (_send);
+ _connections.drop_connections();
+ _send_route->remove_processor (send_proc);
+
+}
+
+
FoldbackStrip* FoldbackStrip::_entered_foldback_strip;
PBD::Signal1<void,FoldbackStrip*> FoldbackStrip::CatchDeletion;
, RouteUI (sess)
, _mixer(mx)
, _mixer_owned (true)
+ , _width (80)
, _pr_selection ()
, panners (sess)
- , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
, mute_solo_table (1, 2)
- , bottom_button_table (1, 1)
, _plugin_insert_cnt (0)
, _comment_button (_("Comments"))
, fb_level_control (0)
-// , _visibility (X_("mixer-element-visibility"))
{
+ _session = sess;
init ();
set_route (rt);
}
FoldbackStrip::init ()
{
_entered_foldback_strip= 0;
- route_ops_menu = 0;
- route_select_menu = 0;
ignore_comment_edit = false;
ignore_toggle = false;
comment_area = 0;
- _width_owner = 0;
- /* the length of this string determines the width of the mixer strip when it is set to `wide' */
- longest_label = "longest label";
+ _previous_button.set_name ("mixer strip button");
+ _previous_button.set_icon (ArdourIcon::ScrollLeft);
+ _previous_button.set_tweaks (ArdourButton::Square);
+ UI::instance()->set_tip (&_previous_button, _("Previous foldback bus"), "");
+ _previous_button.set_sensitive (false);
+ _next_button.set_name ("mixer strip button");
+ _next_button.set_icon (ArdourIcon::ScrollRight);
+ _next_button.set_tweaks (ArdourButton::Square);
+ UI::instance()->set_tip (&_next_button, _("Next foldback bus"), "");
+ _next_button.set_sensitive (false);
- output_button.set_text (_("Output"));
- output_button.set_name ("mixer strip button");
-// send_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
-// send_scroller.add (send_display);
+ _hide_button.set_name ("mixer strip button");
+ _hide_button.set_icon (ArdourIcon::HideEye);
+ _hide_button.set_tweaks (ArdourButton::Square);
+ set_tooltip (&_hide_button, _("Hide Foldback strip"));
- send_display.set_flags (CAN_FOCUS);
- send_display.set_name ("ProcessorList");
- send_display.set_data ("sendbox", this);
- send_display.set_size_request (48, -1);
- send_display.set_spacing (0);
+ prev_next_box.pack_start (_previous_button, false, true);
+ prev_next_box.pack_start (_next_button, false, true);
+ prev_next_box.pack_end (_hide_button, false, true);
- insert_box = new ProcessorBox (0, boost::bind (&FoldbackStrip::plugin_selector, this), _pr_selection, 0);
- insert_box->set_no_show_all ();
- insert_box->show ();
+ name_button.set_name ("mixer strip button");
+ name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
+ name_button.set_layout_ellipsize_width (PX_SCALE(_width) * PANGO_SCALE);
-// send_scroller.show ();
- send_display.show ();
+ // invertbuttons and box in route_ui
- // TODO implement ArdourKnob::on_size_request properly
- fb_level_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
+ _show_sends_button.set_name ("send alert button");
+ _show_sends_button.set_text (_("Show Sends"));
+ UI::instance()->set_tip (&_show_sends_button, _("make mixer strips show sends to this bus"), "");
-#define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
- fb_level_control->set_size_request (PX_SCALE(65), PX_SCALE(65));
-#undef PX_SCALE
- fb_level_control->set_tooltip_prefix (_("Level: "));
- fb_level_control->set_name ("monitor section knob");
- fb_level_control->set_no_show_all (true);
+ send_display.set_flags (CAN_FOCUS);
+ send_display.set_spacing (4);
+
+ send_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
+ send_scroller.add (send_display);
+ send_scroller.get_child()->set_name ("FoldbackBusStripBase");
- bottom_button_table.attach (*fb_level_control, 0, 1, 0, 1,FILL,FILL,20,20); //EXPAND
+ // panners from route_ui
+ panners.set_width (Wide);
+ insert_box = new ProcessorBox (0, boost::bind (&FoldbackStrip::plugin_selector, this), _pr_selection, 0);
+ insert_box->set_no_show_all ();
+ insert_box->show ();
+ insert_box->set_session (_session);
+ insert_box->set_width (Wide);
+ insert_box->set_size_request (PX_SCALE(_width + 34), PX_SCALE(100));
mute_solo_table.set_homogeneous (true);
mute_solo_table.set_spacings (2);
+ solo_button->set_text (_("Listen"));
+ mute_solo_table.attach (*solo_button, 0, 2, 0, 1);
+ mute_solo_table.set_size_request (PX_SCALE(_width + 34), PX_SCALE(20));
- show_sends_button->set_text (_("Show Sends"));
-
- show_sends_box.pack_start (*show_sends_button, true, true);
- show_sends_button->show();
-
- bottom_button_table.set_spacings (20);
- bottom_button_table.set_row_spacings (20);
- bottom_button_table.set_homogeneous (true);
+ fb_level_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
+ fb_level_control->set_size_request (PX_SCALE(50), PX_SCALE(50));
+ fb_level_control->set_tooltip_prefix (_("Level: "));
+ fb_level_control->set_name ("foldback knob");
+ fb_level_control->set_no_show_all (true);
- name_button.set_name ("monitor section button");
- name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
+ VBox* level_box = manage (new VBox);
+ level_box->pack_start (*fb_level_control, true, false);
+ master_box.pack_start (*level_box, true, false);
+ master_box.set_size_request (PX_SCALE(_width + 34), PX_SCALE(80));
+ master_box.set_name ("FoldbackBusStripBase");
+ level_box->show ();
- _select_button.set_name ("monitor section button");
- _select_button.set_text (_("Select Foldback Bus"));
+ output_button.set_text (_("Output"));
+ output_button.set_name ("mixer strip button");
+ output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
+ output_button.set_layout_ellipsize_width (PX_SCALE(_width) * PANGO_SCALE);
_comment_button.set_name (X_("mixer strip button"));
_comment_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
- _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
+ _comment_button.set_layout_ellipsize_width (PX_SCALE(_width) * PANGO_SCALE);
global_vpacker.set_border_width (1);
-
- global_vpacker.set_spacing (4);
- global_vpacker.pack_start (_select_button, Gtk::PACK_SHRINK);
+ global_vpacker.set_spacing (2);
+
+ // Packing is from top down to the send box. Thje send box
+ // needs the most room and takes all left over space
+ // Everything below the send box is packed from the bottom up
+ // the panner is the last thing to pack as it doesn't always show
+ // and packing it below the sendbox means nothing moves when it shows
+ // or hides.
+ global_vpacker.pack_start (prev_next_box, Gtk::PACK_SHRINK);
global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
- global_vpacker.pack_start (show_sends_box, Gtk::PACK_SHRINK);
-
+ global_vpacker.pack_start (_show_sends_button, Gtk::PACK_SHRINK);
+ global_vpacker.pack_start (send_scroller, true, true);
#ifndef MIXBUS
//add a spacer underneath the foldback bus;
//this fills the area that is taken up by the scrollbar on the tracks;
#endif
global_vpacker.pack_end (_comment_button, Gtk::PACK_SHRINK);
global_vpacker.pack_end (output_button, Gtk::PACK_SHRINK);
+ global_vpacker.pack_end (master_box, Gtk::PACK_SHRINK);
global_vpacker.pack_end (mute_solo_table, Gtk::PACK_SHRINK);
- global_vpacker.pack_end (panners, Gtk::PACK_SHRINK);
- global_vpacker.pack_end (bottom_button_table, Gtk::PACK_SHRINK);
-// global_vpacker.pack_end (*insert_box, true, true);
global_vpacker.pack_end (*insert_box, Gtk::PACK_SHRINK);
+ global_vpacker.pack_end (panners, Gtk::PACK_SHRINK);
global_frame.add (global_vpacker);
global_frame.set_shadow_type (Gtk::SHADOW_IN);
_selected = true;
set_selected (false);
-
_packed = false;
_embedded = false;
_session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&FoldbackStrip::engine_stopped, this), gui_context());
_session->engine().Running.connect (*this, invalidator (*this), boost::bind (&FoldbackStrip::engine_running, this), gui_context());
- output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
-
output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &FoldbackStrip::output_press), false);
output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &FoldbackStrip::output_release), false);
-
name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &FoldbackStrip::name_button_button_press), false);
- _select_button.signal_button_press_event().connect (sigc::mem_fun (*this, &FoldbackStrip::select_button_button_press), false);
-
- _width = Wide;
-
-
- /* start off as a passthru strip. we'll correct this, if necessary,
- in update_diskstream_display().
- */
-
-
+ _previous_button.signal_clicked.connect (sigc::mem_fun (*this, &FoldbackStrip::previous_button_clicked));
+ _next_button.signal_clicked.connect (sigc::mem_fun (*this, &FoldbackStrip::next_button_clicked));
+ _hide_button.signal_clicked.connect (sigc::mem_fun(*this, &FoldbackStrip::hide_clicked));
+ _show_sends_button.signal_clicked.connect (sigc::mem_fun(*this, &FoldbackStrip::show_sends_clicked));
+ send_scroller.signal_button_press_event().connect (sigc::mem_fun (*this, &FoldbackStrip::send_button_press_event));
+ _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
add_events (Gdk::BUTTON_RELEASE_MASK|
Gdk::ENTER_NOTIFY_MASK|
*this, invalidator (*this), boost::bind (&FoldbackStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
);
- /* Add the widgets under visibility control to the VisibilityGroup; the names used here
- must be the same as those used in RCOptionEditor so that the configuration changes
- are recognised when they occur.
- */
-/* _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
- _visibility.add (&output_button, X_("Output"), _("Output"), false);
- _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
-
- parameter_changed (X_("mixer-element-visibility"));
- UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &FoldbackStrip::parameter_changed));
- Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&FoldbackStrip::parameter_changed, this, _1), gui_context());
- _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&FoldbackStrip::parameter_changed, this, _1), gui_context());
-*/
//watch for mouse enter/exit so we can do some stuff
signal_enter_notify_event().connect (sigc::mem_fun(*this, &FoldbackStrip::mixer_strip_enter_event ));
signal_leave_notify_event().connect (sigc::mem_fun(*this, &FoldbackStrip::mixer_strip_leave_event ));
CatchDeletion (this);
delete fb_level_control;
fb_level_control = 0;
+ _connections.drop_connections();
+ clear_send_box ();
+ send_blink_connection.disconnect ();
if (this ==_entered_foldback_strip)
_entered_foldback_strip = NULL;
{
/// FIX NO route
if (!rt) {
+ clear_send_box ();
RouteUI::self_delete ();
return;
}
- if (show_sends_button->get_parent()) {
- show_sends_box.remove (*show_sends_button);
+ if (_route) {
+ _route->solo_control()->set_value (0.0, Controllable::NoGroup);
}
RouteUI::set_route (rt);
- /* ProcessorBox needs access to _route so that it can read
- GUI object state.
- */
-// processor_box.set_route (rt);
- insert_box->set_route (rt);
-
+ insert_box->set_route (_route);
revert_to_default_display ();
-
-
-// mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
-// mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
-
- if (solo_button->get_parent()) {
- mute_solo_table.remove (*solo_button);
- }
-
- if (mute_button->get_parent()) {
- mute_solo_table.remove (*mute_button);
- }
-
- mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
- mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
- mute_button->show ();
- solo_button->show ();
- show_sends_box.show ();
-
- spacer.show();
-
update_fb_level_control();
- show_sends_box.pack_start (*show_sends_button, true, true);
- show_sends_button->show();
-
-
- delete route_ops_menu;
- route_ops_menu = 0;
-
- delete route_select_menu;
- route_select_menu = 0;
-
- _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&FoldbackStrip::update_output_display, this), gui_context());
-
- _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&FoldbackStrip::io_changed_proxy, this), gui_context ());
+ BusSendDisplayChanged (boost::shared_ptr<Route> ());
+ _showing_sends = false;
+ _show_sends_button.set_active (false);
+ send_blink_connection.disconnect ();
if (_route->panner_shell()) {
update_panner_choices();
_route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&FoldbackStrip::connect_to_pan, this), gui_context());
}
- _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&FoldbackStrip::setup_comment_button, this), gui_context());
+ _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&FoldbackStrip::update_output_display, this), gui_context());
+ _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&FoldbackStrip::io_changed_proxy, this), gui_context ());
- set_stuff_from_route ();
+ _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&FoldbackStrip::setup_comment_button, this), gui_context());
/* now force an update of all the various elements */
+ name_changed ();
+ update_send_box ();
+ _session->FBSendsChanged.connect (route_connections, invalidator (*this), boost::bind (&FoldbackStrip::update_send_box, this), gui_context());
update_mute_display ();
update_solo_display ();
- name_changed ();
comment_changed ();
-
connect_to_pan ();
panners.setup_pan ();
panners.show_all ();
-
update_output_display ();
add_events (Gdk::BUTTON_RELEASE_MASK);
-
+ prev_next_changed ();
+ _previous_button.show();
+ _next_button.show();
+ _hide_button.show();
+ prev_next_box.show ();
+ name_button.show();
+ send_display.show ();
+ send_scroller.show ();
+ _show_sends_button.show();
insert_box->show ();
-
- Route::FedBy fed_by = _route->fed_by();
- for (Route::FedBy::iterator i = fed_by.begin(); i != fed_by.end(); ++i) {
- if (i->sends_only) {
- boost::shared_ptr<Route> s_rt (i->r.lock());
- boost::shared_ptr<Send> snd = s_rt->internal_send_for (_route);
- //s_rt->DropReferences.connect (*this, MISSING_INVALIDATOR, boost::bind (&FoldbackStrip::fill_sendbox, this), this);
- boost::shared_ptr<Processor> processor (snd);
- //boost::shared_ptr<PluginInsert> plugin_insert = boost::dynamic_pointer_cast<PluginInsert> (processor);
-// Ok we have the info... now we need widgets to put it in... I suspect I need a class or three
-// this doesn't work... and I need to clear it out and rebuild each time.
- ArdourWidgets::ArdourButton _button;
- _button.set_distinct_led_click (true);
- _button.set_fallthrough_to_parent(true);
- _button.set_led_left (true);
-// _button.signal_led_clicked.connect (sigc::mem_fun (*this, &FoldbackStrip::led_clicked));
- _button.set_text (s_rt->name ());
-
- //Gtk::VBox snd_entry = NULL;
- Gtk::VBox snd_entry;
- snd_entry.set_border_width (1);
- snd_entry.set_spacing (4);
- snd_entry.pack_start (_button, true, true);
- send_display.pack_start (snd_entry, true, true);
- _button.show ();
- snd_entry.show ();
-
-
- }
- }
-
-
-
- global_frame.show();
- global_vpacker.show();
+ solo_button->show ();
mute_solo_table.show();
- bottom_button_table.show();
- show_sends_box.show_all();
- //send_scroller.show ();
- send_display.show ();
+ master_box.show();
output_button.show();
- name_button.show();
- _select_button.show();
_comment_button.show();
+ spacer.show();
+ global_frame.show();
+ global_vpacker.show();
map_frozen();
show ();
+ set_button_names ();
}
+// predicate for sort call in get_sorted_stripables
+struct StripableByPresentationOrder
+{
+ bool operator () (const boost::shared_ptr<Stripable> & a, const boost::shared_ptr<Stripable> & b) const
+ {
+ return a->presentation_info().order() < b->presentation_info().order();
+ }
+};
+
void
-FoldbackStrip::set_stuff_from_route ()
+FoldbackStrip::update_send_box ()
{
- /* if width is not set, it will be set by the MixerUI or editor */
+ clear_send_box ();
+ if (!_route) {
+ return;
+ }
+ StripableList stripables;
+ stripables.clear ();
- Width width;
- if (get_gui_property ("strip-width", width)) {
-// set_width_enum (width, this);
+ Route::FedBy fed_by = _route->fed_by();
+ for (Route::FedBy::iterator i = fed_by.begin(); i != fed_by.end(); ++i) {
+ if (i->sends_only) {
+ boost::shared_ptr<Route> rt (i->r.lock());
+ boost::shared_ptr<Stripable> s = boost::dynamic_pointer_cast<Stripable> (rt);
+ stripables.push_back (s);
+ }
+ }
+ stripables.sort (StripableByPresentationOrder());
+ for (StripableList::iterator it = stripables.begin(); it != stripables.end(); ++it) {
+
+ boost::shared_ptr<Stripable> s_sp = *it;
+ boost::shared_ptr<Route> s_rt = boost::dynamic_pointer_cast<Route> (s_sp);
+ boost::shared_ptr<Send> snd = s_rt->internal_send_for (_route);
+ if (snd) {
+ FoldbackSend * fb_s = new FoldbackSend (snd, s_rt, _route, _width);
+ send_display.pack_start (*fb_s, Gtk::PACK_SHRINK);
+ fb_s->show ();
+ s_rt->processors_changed.connect (_connections, invalidator (*this), boost::bind (&FoldbackStrip::processors_changed, this, _1), gui_context ());
+ }
}
}
void
-FoldbackStrip::set_packed (bool yn)
+FoldbackStrip::clear_send_box ()
{
- _packed = yn;
- set_gui_property ("visible", _packed);
+ std::vector< Widget* > snd_list = send_display.get_children ();
+ _connections.drop_connections ();
+ for (uint32_t i = 0; i < snd_list.size(); i++) {
+ send_display.remove (*(snd_list[i]));
+ delete snd_list[i];
+ }
+ snd_list.clear();
}
+void
+FoldbackStrip::processors_changed (RouteProcessorChange)
+{
+ update_send_box ();
+}
-struct RouteCompareByName {
- bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
- return a->name().compare (b->name()) < 0;
- }
-};
+void
+FoldbackStrip::set_packed (bool yn)
+{
+ _packed = yn;
+}
gint
FoldbackStrip::output_release (GdkEventButton *ev)
boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
- /* guess the user-intended main type of the route output */
- DataType intended_type = guess_main_type(false);
-
- /* try adding the master bus first */
- boost::shared_ptr<Route> master = _session->master_out();
- if (master) {
- maybe_add_bundle_to_output_menu (master->input()->bundle(), current, intended_type);
- }
-
- /* then other routes inputs */
- RouteList copy = _session->get_routelist ();
- copy.sort (RouteCompareByName ());
- for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
- maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current, intended_type);
- }
+ DataType intended_type = DataType::AUDIO;
/* then try adding user bundles, often labeled/grouped physical inputs */
for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
citems.pop_back ();
}
- if (!ARDOUR::Profile->get_mixbus()) {
- citems.push_back (SeparatorElem());
-
- for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
- citems.push_back (
- MenuElem (
- string_compose (_("Add %1 port"), (*i).to_i18n_string()),
- sigc::bind (sigc::mem_fun (*this, &FoldbackStrip::add_output_port), *i)
- )
- );
- }
- }
-
citems.push_back (SeparatorElem());
citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
return;
}
- /* Don't add the monitor input unless we are Master */
+ /* Don't add the monitor input */
boost::shared_ptr<Route> monitor = _session->monitor_out();
- if ((!_route->is_master()) && monitor && b->has_same_ports (monitor->input()->bundle()))
+ if (monitor && b->has_same_ports (monitor->input()->bundle()))
return;
- /* It should either match exactly our outputs (if |type| is DataType::NIL)
- * or have the same number of |type| channels than our outputs. */
- if (type == DataType::NIL) {
- if(b->nchannels() != _route->n_outputs())
- return;
- } else {
- if (b->nchannels().n(type) != _route->n_outputs().n(type))
- return;
+ /* It should have the same number of |type| channels as our outputs. */
+ if (b->nchannels().n(type) != _route->n_outputs().n(type)) {
+ return;
}
/* Avoid adding duplicates */
boost::shared_ptr<Pannable> p = _route->pannable ();
- //p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
-
- /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
- * However, that only works a panner was previously set.
- *
- * PannerUI must remain subscribed to _panshell->Changed() in case
- * we switch the panner eg. AUX-Send and back
- * _route->panner_shell()->Changed() vs _panshell->Changed
- */
- /*if (panners._panner == 0) {
- panners.panshell_changed ();
- }*/
update_panner_choices();
}
panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
}
-DataType
-FoldbackStrip::guess_main_type(bool for_input, bool favor_connected) const
-{
- /* The heuristic follows these principles:
- * A) If all ports that the user connected are of the same type, then he
- * very probably intends to use the IO with that type. A common subcase
- * is when the IO has only ports of the same type (connected or not).
- * B) If several types of ports are connected, then we should guess based
- * on the likeliness of the user wanting to use a given type.
- * We assume that the DataTypes are ordered from the most likely to the
- * least likely when iterating or comparing them with "<".
- * C) If no port is connected, the same logic can be applied with all ports
- * instead of connected ones. TODO: Try other ideas, for instance look at
- * the last plugin output when |for_input| is false (note: when StrictIO
- * the outs of the last plugin should be the same as the outs of the route
- * modulo the panner which forwards non-audio anyway).
- * All of these constraints are respected by the following algorithm that
- * just returns the most likely datatype found in connected ports if any, or
- * available ports if any (since if all ports are of the same type, the most
- * likely found will be that one obviously). */
-
- boost::shared_ptr<IO> io = for_input ? _route->input() : _route->output();
-
- /* Find most likely type among connected ports */
- if (favor_connected) {
- DataType type = DataType::NIL; /* NIL is always last so least likely */
- for (PortSet::iterator p = io->ports().begin(); p != io->ports().end(); ++p) {
- if (p->connected() && p->type() < type)
- type = p->type();
- }
- if (type != DataType::NIL) {
- /* There has been a connected port (necessarily non-NIL) */
- return type;
- }
- }
-
- /* Find most likely type among available ports.
- * The iterator stops before NIL. */
- for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
- if (io->n_ports().n(*t) > 0)
- return *t;
- }
-
- /* No port at all, return the most likely datatype by default */
- return DataType::front();
-}
-
/*
* Output port labelling
*
uint32_t typed_connection_count = 0;
bool each_typed_port_has_one_connection = true;
- DataType dt = guess_main_type(false);
+ DataType dt = DataType::AUDIO;
boost::shared_ptr<IO> io = _route->output();
/* Fill in the tooltip. Also count:
}
}
-void
-FoldbackStrip::show_passthru_color ()
-{
- //reset_strip_style ();
-}
-
-
void
FoldbackStrip::help_count_plugins (boost::weak_ptr<Processor> p)
{
++_plugin_insert_cnt;
}
}
-void
+
+Gtk::Menu*
FoldbackStrip::build_route_ops_menu ()
{
using namespace Menu_Helpers;
- route_ops_menu = new Menu;
- route_ops_menu->set_name ("ArdourContextMenu");
- MenuList& items = route_ops_menu->items();
+ Menu* menu = manage (new Menu);
+ MenuList& items = menu->items ();
+ menu->set_name ("ArdourContextMenu");
items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
items.push_back (SeparatorElem());
items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &FoldbackStrip::remove_current_fb)));
+ return menu;
}
-void
+Gtk::Menu*
FoldbackStrip::build_route_select_menu ()
{
using namespace Menu_Helpers;
- route_select_menu = new Menu;
- route_select_menu->set_name ("ArdourContextMenu");
- MenuList& items = route_select_menu->items();
+ Menu* menu = manage (new Menu);
+ MenuList& items = menu->items ();
+ menu->set_name ("ArdourContextMenu");
+
StripableList fb_list;
_session->get_stripables (fb_list, PresentationInfo::FoldbackBus);
for (StripableList::iterator s = fb_list.begin(); s != fb_list.end(); ++s) {
}
items.push_back (MenuElem (route->name (), sigc::bind (sigc::mem_fun (*this, &FoldbackStrip::set_route), route)));
}
-
+ return menu;
}
gboolean
FoldbackStrip::name_button_button_press (GdkEventButton* ev)
{
- if (ev->button == 1 || ev->button == 3) {
- list_route_operations ();
+ if (ev->button == 1) {
+ Menu* menu = build_route_select_menu ();
- if (ev->button == 1) {
- Gtkmm2ext::anchored_menu_popup(route_ops_menu, &name_button, "",
+ Gtkmm2ext::anchored_menu_popup(menu, &name_button, "",
1, ev->time);
- } else {
- route_ops_menu->popup (3, ev->time);
- }
-
+ return true;
+ } else if (ev->button == 3) {
+ Menu* r_menu = build_route_ops_menu ();
+ r_menu->popup (3, ev->time);
return true;
}
-
return false;
}
-gboolean
-FoldbackStrip::select_button_button_press (GdkEventButton* ev)
+void
+FoldbackStrip::previous_button_clicked ()
{
- if (ev->button == 1 || ev->button == 3) {
- list_fb_routes ();
-
- if (ev->button == 1) {
- Gtkmm2ext::anchored_menu_popup(route_select_menu, &_select_button, "",
- 1, ev->time);
- } else {
- route_select_menu->popup (3, ev->time);
+ bool past_current = false;
+ StripableList slist;
+ boost::shared_ptr<Route> previous = boost::shared_ptr<Route> ();
+ _session->get_stripables (slist, PresentationInfo::FoldbackBus);
+ if (slist.size () > 1) {
+ for (StripableList::iterator s = slist.begin(); s != slist.end(); ++s) {
+ if ((*s) == _route) {
+ past_current = true;
+ }
+ if (!past_current) {
+ previous = boost::dynamic_pointer_cast<Route> (*s);
+ }
}
+ } else {
+ // only one route do nothing
+ return;
+ }
+ //use previous to set route
+ if (previous) {
+ set_route (previous);
+ }
+}
- return true;
+void
+FoldbackStrip::next_button_clicked ()
+{
+ bool past_current = false;
+ StripableList slist;
+ boost::shared_ptr<Route> next = boost::shared_ptr<Route> ();
+ _session->get_stripables (slist, PresentationInfo::FoldbackBus);
+ if (slist.size () > 1) {
+ for (StripableList::iterator s = slist.begin(); s != slist.end(); ++s) {
+ if (past_current) {
+ next = boost::dynamic_pointer_cast<Route> (*s);
+ break;
+ }
+ if ((*s) == _route) {
+ past_current = true;
+ }
+ }
+ } else {
+ // only one route do nothing
+ return;
+ }
+ //use next to set route
+ if (next) {
+ set_route (next);
}
+}
- return false;
+void
+FoldbackStrip::prev_next_changed ()
+{
+ StripableList slist;
+ _session->get_stripables (slist, PresentationInfo::FoldbackBus);
+ if ((slist.size() < 2) || (boost::dynamic_pointer_cast<Stripable> (_route) == *(slist.begin()))) {
+ _previous_button.set_sensitive (false);
+ } else {
+ _previous_button.set_sensitive (true);
+ }
+ if ((slist.size () < 2) || boost::dynamic_pointer_cast<Stripable> (_route) == *(--slist.end())) {
+ _next_button.set_sensitive (false);
+ } else {
+ _next_button.set_sensitive (true);
+ }
}
void
-FoldbackStrip::list_route_operations ()
+FoldbackStrip::hide_clicked()
{
- delete route_ops_menu;
- build_route_ops_menu ();
+ _hide_button.set_sensitive(false);
+ ActionManager::get_toggle_action (X_("Mixer"), X_("ToggleFoldbackStrip"))->set_active (false);
+ _hide_button.set_sensitive(true);
}
void
-FoldbackStrip::list_fb_routes ()
+FoldbackStrip::show_sends_clicked ()
{
- delete route_select_menu;
- build_route_select_menu ();
+ if (_showing_sends) {
+ BusSendDisplayChanged (boost::shared_ptr<Route> ()); /* EMIT SIGNAL */
+ _showing_sends = false;
+ _show_sends_button.set_active (false);
+ send_blink_connection.disconnect ();
+ } else {
+ BusSendDisplayChanged (_route); /* EMIT SIGNAL */
+ _showing_sends = true;
+ _show_sends_button.set_active (true);
+ send_blink_connection = Timers::blink_connect (sigc::mem_fun (*this, &FoldbackStrip::send_blink));
+ }
}
void
-FoldbackStrip::set_selected (bool yn)
+FoldbackStrip::send_blink (bool onoff)
{
- AxisView::set_selected (yn);
+ if (!(&_show_sends_button)) {
+ return;
+ }
- if (selected()) {
- global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
- global_frame.set_name ("MixerStripSelectedFrame");
+ if (onoff) {
+ _show_sends_button.set_active_state (Gtkmm2ext::ExplicitActive);
} else {
- global_frame.set_shadow_type (Gtk::SHADOW_IN);
- global_frame.set_name ("MixerStripFrame");
+ _show_sends_button.unset_active_state ();
}
+}
+
+void
+FoldbackStrip::set_selected (bool yn)
+{
+
+ global_frame.set_shadow_type (Gtk::SHADOW_IN);
+ global_frame.set_name ("MixerStripFrame");
global_frame.queue_draw ();
-// if (!yn)
-// processor_box.deselect_all_processors();
}
void
void
FoldbackStrip::name_changed ()
{
-
- name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
name_button.set_text (_route->name());
set_tooltip (name_button, Gtkmm2ext::markup_escape_text(_route->name()));
-
-}
-/*
-void
-FoldbackStrip::output_button_resized (Gtk::Allocation& alloc)
-{
- //output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
-}
-
-void
-FoldbackStrip::name_button_resized (Gtk::Allocation& alloc)
-{
- //name_button.set_layout_ellipsize_width (20);
}
-void
-FoldbackStrip::comment_button_resized (Gtk::Allocation& alloc)
-{
- //_comment_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
-}
-*/
void
FoldbackStrip::set_embedded (bool yn)
{
return;
}
-// Gtk::Window* w = processor_box.get_processor_ui (processor);
Gtk::Window* w = insert_box->get_processor_ui (processor);
if (w) {
FoldbackStrip::reset_strip_style ()
{
if (_route->active()) {
- set_name ("AudioBusStripBase");
+ set_name ("FoldbackBusStripBase");
} else {
set_name ("AudioBusStripBaseInactive");
}
send_gone_connection.disconnect ();
output_button.set_sensitive (true);
set_invert_sensitive (true);
- mute_button->set_sensitive (true);
solo_button->set_sensitive (true);
_comment_button.set_sensitive (true);
fb_level_control->set_sensitive (true);
FoldbackStrip::set_button_names ()
{
- mute_button->set_text (_("Mute"));
-
- if (_route && _route->solo_safe_control()->solo_safe()) {
- solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
- } else {
- solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
- }
if (!Config->get_solo_control_is_listen_control()) {
- solo_button->set_text (_("Solo"));
+ solo_button->hide ();
} else {
+ solo_button->set_sensitive (true);
+ solo_button->show ();
+ UI::instance()->set_tip (solo_button, _("Listen on monitor"), "");
switch (Config->get_listen_position()) {
case AfterFaderListen:
- solo_button->set_text (_("AFL"));
+ solo_button->set_text (_("Listen"));
break;
case PreFaderListen:
- solo_button->set_text (_("PFL"));
+ solo_button->set_text (_("Listen"));
break;
}
}
return _mixer.plugin_selector();
}
-string
-FoldbackStrip::state_id () const
-{
- return string_compose ("strip %1", _route->id().to_s());
-}
-
-void
-FoldbackStrip::add_output_port (DataType t)
-{
- _route->output()->add_port ("", this, t);
-}
-
void
FoldbackStrip::route_active_changed ()
{
void
FoldbackStrip::copy_processors ()
{
-// processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
insert_box->processor_operation (ProcessorBox::ProcessorsCopy);
}
void
FoldbackStrip::cut_processors ()
{
-// processor_box.processor_operation (ProcessorBox::ProcessorsCut);
insert_box->processor_operation (ProcessorBox::ProcessorsCut);
}
void
FoldbackStrip::deselect_all_processors ()
{
-// processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
insert_box->processor_operation (ProcessorBox::ProcessorsSelectNone);
}
}
void
-FoldbackStrip::build_sends_menu ()
+FoldbackStrip::create_selected_sends (bool include_buses)
{
- using namespace Menu_Helpers;
+ boost::shared_ptr<StripableList> slist (new StripableList);
+ PresentationInfo::Flag fl = PresentationInfo::AudioTrack;
+ if (include_buses) {
+ fl = PresentationInfo::MixerRoutes;
+ }
+ _session->get_stripables (*slist, fl);
- sends_menu = new Menu;
- sends_menu->set_name ("ArdourContextMenu");
- MenuList& items = sends_menu->items();
+ for (StripableList::iterator i = (*slist).begin(); i != (*slist).end(); ++i) {
+ if ((*i)->is_selected() && !(*i)->is_master() && !(*i)->is_monitor()) {
+ boost::shared_ptr<Route> rt = boost::dynamic_pointer_cast<Route>(*i);
+ if (rt) {
+ rt->add_foldback_send (_route);
+ }
+ }
+ }
- items.push_back (
- MenuElem(_("Assign all tracks"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PreFader, false))
- );
+}
- items.push_back (
- MenuElem(_("Assign all tracks and buses (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_sends), PreFader, true))
- );
+bool
+FoldbackStrip::send_button_press_event (GdkEventButton *ev)
+{
+ if (ev->button == 3) {
+ Menu* menu = build_sends_menu ();
+ menu->popup (3, ev->time);
+ return true;
+ }
+ return false;
+}
+
+Gtk::Menu*
+FoldbackStrip::build_sends_menu ()
+{
+ using namespace Menu_Helpers;
+
+ Menu* menu = manage (new Menu);
+ MenuList& items = menu->items ();
+ menu->set_name ("ArdourContextMenu");
items.push_back (
- MenuElem(_("Assign selected tracks (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PreFader, false))
+ MenuElem(_("Assign selected tracks (prefader)"), sigc::bind (sigc::mem_fun (*this, &FoldbackStrip::create_selected_sends), false))
);
items.push_back (
- MenuElem(_("Assign selected tracks and buses (prefader)"), sigc::bind (sigc::mem_fun (*this, &RouteUI::create_selected_sends), PreFader, true)));
+ MenuElem(_("Assign selected tracks and buses (prefader)"), sigc::bind (sigc::mem_fun (*this, &FoldbackStrip::create_selected_sends), true)));
items.push_back (MenuElem(_("Copy track/bus gains to sends"), sigc::mem_fun (*this, &RouteUI::set_sends_gain_from_track)));
items.push_back (MenuElem(_("Set sends gain to -inf"), sigc::mem_fun (*this, &RouteUI::set_sends_gain_to_zero)));
items.push_back (MenuElem(_("Set sends gain to 0dB"), sigc::mem_fun (*this, &RouteUI::set_sends_gain_to_unity)));
+ return menu;
}
-Gdk::Color
-FoldbackStrip::color () const
-{
- return route_color ();
-}
-
-/*bool
-FoldbackStrip::marked_for_display () const
-{
- return !_route->presentation_info().hidden();
-}*/
-
void
FoldbackStrip::remove_current_fb ()
{
+ clear_send_box ();
StripableList slist;
boost::shared_ptr<Route> next = boost::shared_ptr<Route> ();
boost::shared_ptr<Route> old_route = _route;
}
if (next) {
set_route (next);
+ _session->remove_route (old_route);
+ prev_next_changed ();
+ } else {
+ clear_send_box ();
+ RouteUI::self_delete ();
+ _session->remove_route (old_route);
}
- _session->remove_route (old_route);
-
-
}