prevent everything except the name column from changing selection in EditorRoutes
[ardour.git] / gtk2_ardour / foldback_strip.cc
index 898023d5aa8e1ee97f17dad30341de43f3462df5..17fd37c1b2a36eef2bd1f1a304e265e0459dbf64 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    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"
@@ -59,6 +41,7 @@
 #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"
@@ -73,6 +56,238 @@ using namespace Gtk;
 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;
 
@@ -81,16 +296,15 @@ FoldbackStrip::FoldbackStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Rou
        , 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);
 }
@@ -99,78 +313,100 @@ void
 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;
@@ -190,11 +426,10 @@ FoldbackStrip::init ()
 #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);
@@ -206,30 +441,22 @@ FoldbackStrip::init ()
 
        _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|
@@ -243,19 +470,6 @@ FoldbackStrip::init ()
                *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 ));
@@ -267,6 +481,9 @@ FoldbackStrip::~FoldbackStrip ()
        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;
@@ -317,159 +534,140 @@ FoldbackStrip::set_route (boost::shared_ptr<Route> rt)
 {
        /// 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)
@@ -512,21 +710,7 @@ FoldbackStrip::output_press (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) {
@@ -547,19 +731,6 @@ FoldbackStrip::output_press (GdkEventButton *ev)
                        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)));
 
@@ -596,19 +767,14 @@ FoldbackStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARD
                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 */
@@ -641,18 +807,6 @@ FoldbackStrip::connect_to_pan ()
 
        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();
 }
 
@@ -671,53 +825,6 @@ FoldbackStrip::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
  *
@@ -768,7 +875,7 @@ FoldbackStrip::update_io_button ()
        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:
@@ -1004,13 +1111,6 @@ FoldbackStrip::setup_comment_button ()
        }
 }
 
-void
-FoldbackStrip::show_passthru_color ()
-{
-       //reset_strip_style ();
-}
-
-
 void
 FoldbackStrip::help_count_plugins (boost::weak_ptr<Processor> p)
 {
@@ -1028,14 +1128,15 @@ 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)));
 
@@ -1061,16 +1162,18 @@ FoldbackStrip::build_route_ops_menu ()
 
        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) {
@@ -1081,79 +1184,144 @@ FoldbackStrip::build_route_select_menu ()
                }
                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
@@ -1167,32 +1335,11 @@ FoldbackStrip::route_property_changed (const PropertyChange& what_changed)
 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)
 {
@@ -1222,7 +1369,6 @@ FoldbackStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
                return;
        }
 
-//     Gtk::Window* w = processor_box.get_processor_ui (processor);
        Gtk::Window* w = insert_box->get_processor_ui (processor);
 
        if (w) {
@@ -1234,7 +1380,7 @@ void
 FoldbackStrip::reset_strip_style ()
 {
                        if (_route->active()) {
-                               set_name ("AudioBusStripBase");
+                               set_name ("FoldbackBusStripBase");
                        } else {
                                set_name ("AudioBusStripBaseInactive");
                        }
@@ -1263,7 +1409,6 @@ FoldbackStrip::drop_send ()
        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);
@@ -1302,22 +1447,18 @@ void
 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;
                }
        }
@@ -1329,18 +1470,6 @@ FoldbackStrip::plugin_selector()
        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 ()
 {
@@ -1350,14 +1479,12 @@ 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);
 }
 
@@ -1376,7 +1503,6 @@ FoldbackStrip::select_all_processors ()
 void
 FoldbackStrip::deselect_all_processors ()
 {
-//     processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
        insert_box->processor_operation (ProcessorBox::ProcessorsSelectNone);
 }
 
@@ -1399,50 +1525,64 @@ FoldbackStrip::ab_plugins ()
 }
 
 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;
@@ -1457,8 +1597,11 @@ FoldbackStrip::remove_current_fb ()
        }
        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);
-
-
 }