open un-writable sessions without complaining, and desensitize all/most actions that...
[ardour.git] / gtk2_ardour / mixer_strip.cc
index dc7189194e38d7a908759568adc5628d78532c9b..400e865a2ea2c87413060710113100946349c336 100644 (file)
 */
 
 #include <cmath>
+#include <algorithm>
 
 #include <sigc++/bind.h>
 
 #include <pbd/convert.h>
+#include <pbd/enumwriter.h>
 
 #include <gtkmm2ext/gtk_ui.h>
 #include <gtkmm2ext/utils.h>
@@ -40,6 +42,7 @@
 #include <ardour/panner.h>
 #include <ardour/send.h>
 #include <ardour/insert.h>
+#include <ardour/profile.h>
 #include <ardour/ladspa_plugin.h>
 #include <ardour/connection.h>
 #include <ardour/session_connection.h>
@@ -62,6 +65,7 @@ using namespace ARDOUR;
 using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
+using namespace std;
 
 int MixerStrip::scrollbar_height = 0;
 
@@ -79,14 +83,15 @@ speed_printer (char buf[32], Gtk::Adjustment& adj, void* arg)
 }
 #endif 
 
-MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt, bool in_mixer)
+MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, bool in_mixer)
        : AxisView(sess),
-         RouteUI (rt, sess, _("Mute"), _("Solo"), _("Record")),
+         RouteUI (sess, _("Mute"), _("Solo"), _("Record")),
          _mixer(mx),
-         pre_redirect_box (PreFader, sess, rt, mx.plugin_selector(), mx.selection(), in_mixer),
-         post_redirect_box (PostFader, sess, rt, mx.plugin_selector(), mx.selection(), in_mixer),
-         gpm (_route, sess),
-         panners (_route, sess),
+         _mixer_owned (in_mixer),
+         pre_redirect_box (PreFader, sess, mx.plugin_selector(), mx.selection(), in_mixer),
+         post_redirect_box (PostFader, sess, mx.plugin_selector(), mx.selection(), in_mixer),
+         gpm (sess),
+         panners (sess),
          button_table (3, 2),
          middle_button_table (1, 2),
          bottom_button_table (1, 2),
@@ -96,53 +101,75 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
          speed_spinner (&speed_adjustment, "MixerStripSpeedBase", true)
 
 {
-       if (set_color_from_route()) {
-               set_color (unique_random_color());
+       init ();
+
+       if (!_mixer_owned) {
+               /* the editor mixer strip: don't destroy it every time
+                  the underlying route goes away.
+               */
+
+               self_destruct = false;
        }
+}
+
+MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt, bool in_mixer)
+       : AxisView(sess),
+         RouteUI (sess, _("Mute"), _("Solo"), _("Record")),
+         _mixer(mx),
+         _mixer_owned (in_mixer),
+         pre_redirect_box (PreFader, sess, mx.plugin_selector(), mx.selection(), in_mixer),
+         post_redirect_box (PostFader, sess, mx.plugin_selector(), mx.selection(), in_mixer),
+         gpm (sess),
+         panners (sess),
+         button_table (3, 2),
+         middle_button_table (1, 2),
+         bottom_button_table (1, 2),
+         meter_point_label (_("pre")),
+         comment_button (_("Comments")),
+         speed_adjustment (1.0, 0.001, 4.0, 0.001, 0.1),
+         speed_spinner (&speed_adjustment, "MixerStripSpeedBase", true)
 
+{
+       init ();
+       set_route (rt);
+}
+
+void
+MixerStrip::init ()
+{
        input_selector = 0;
        output_selector = 0;
        group_menu = 0;
        _marked_for_display = false;
        route_ops_menu = 0;
+       rename_menu_item = 0;
        ignore_comment_edit = false;
        ignore_toggle = false;
        ignore_speed_adjustment = false;
        comment_window = 0;
        comment_area = 0;
+       _width_owner = 0;
 
        width_button.add (*(manage (new Gtk::Image (::get_icon("strip_width")))));
        hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
 
        input_label.set_text (_("Input"));
+       ARDOUR_UI::instance()->set_tip (&input_button, _("Click to choose inputs"), "");
        input_button.add (input_label);
        input_button.set_name ("MixerIOButton");
        input_label.set_name ("MixerIOButtonLabel");
 
        output_label.set_text (_("Output"));
+       ARDOUR_UI::instance()->set_tip (&output_button, _("Click to choose outputs"), "");
        output_button.add (output_label);
        output_button.set_name ("MixerIOButton");
        output_label.set_name ("MixerIOButtonLabel");
 
-       _route->meter_change.connect (mem_fun(*this, &MixerStrip::meter_changed));
+       ARDOUR_UI::instance()->set_tip (&meter_point_button, _("Select metering point"), "");
        meter_point_button.add (meter_point_label);
        meter_point_button.set_name ("MixerStripMeterPreButton");
        meter_point_label.set_name ("MixerStripMeterPreButton");
        
-       switch (_route->meter_point()) {
-       case MeterInput:
-               meter_point_label.set_text (_("input"));
-               break;
-               
-       case MeterPreFader:
-               meter_point_label.set_text (_("pre"));
-               break;
-               
-       case MeterPostFader:
-               meter_point_label.set_text (_("post"));
-               break;
-       }
-       
        /* TRANSLATORS: this string should be longest of the strings
           used to describe meter points. In english, it's "input".
        */
@@ -173,56 +200,21 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
        bottom_button_table.set_col_spacings (0);
        bottom_button_table.set_homogeneous (true);
        bottom_button_table.attach (group_button, 0, 1, 0, 1);
-
-       if (is_audio_track()) {
-               
-               rec_enable_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press), false);
-               rec_enable_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::rec_enable_release));
-
-               rec_enable_button->set_name ("MixerRecordEnableButton");
-
-               AudioTrack* at = audio_track();
-
-               at->FreezeChange.connect (mem_fun(*this, &MixerStrip::map_frozen));
-
-#ifdef VARISPEED_IN_MIXER_STRIP
-               speed_adjustment.signal_value_changed().connect (mem_fun(*this, &MixerStrip::speed_adjustment_changed));
-               
-               speed_frame.set_name ("BaseFrame");
-               speed_frame.set_shadow_type (Gtk::SHADOW_IN);
-               speed_frame.add (speed_spinner);
-               
-               speed_spinner.set_print_func (speed_printer, 0);
-
-               ARDOUR_UI::instance()->tooltips().set_tip (speed_spinner, _("Varispeed"));
-
-               button_table.attach (speed_frame, 0, 2, 5, 6);
-#endif /* VARISPEED_IN_MIXER_STRIP */
-
-               button_table.attach (*rec_enable_button, 0, 2, 2, 3);
-       }
-
+       
        name_button.add (name_label);
        name_button.set_name ("MixerNameButton");
        Gtkmm2ext::set_size_request_to_display_given_text (name_button, "longest label", 2, 2);
-
        name_label.set_name ("MixerNameButtonLabel");
-       if (_route->phase_invert()) {
-               name_label.set_text (X_("Ø ") + name_label.get_text());
-       } else {
-               name_label.set_text (_route->name());
-       }
 
+       ARDOUR_UI::instance()->set_tip (&group_button, _("Mix group"), "");
        group_button.add (group_label);
        group_button.set_name ("MixerGroupButton");
+       Gtkmm2ext::set_size_request_to_display_given_text (group_button, "Group", 2, 2);
+
        group_label.set_name ("MixerGroupButtonLabel");
 
        comment_button.set_name ("MixerCommentButton");
 
-       ARDOUR_UI::instance()->tooltips().set_tip (comment_button, _route->comment()==""        ?
-                                                       _("Click to Add/Edit Comments"):
-                                                       _route->comment());
-
        comment_button.signal_clicked().connect (mem_fun(*this, &MixerStrip::comment_button_clicked));
        
        global_vpacker.set_border_width (0);
@@ -257,19 +249,6 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
        global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
        global_vpacker.pack_start (comment_button, Gtk::PACK_SHRINK);
 
-       if (route()->master() || route()->control()) {
-               
-               if (scrollbar_height == 0) {
-                       HScrollbar scrollbar;
-                       Gtk::Requisition requisition(scrollbar.size_request ());
-                       scrollbar_height = requisition.height;
-               }
-
-               EventBox* spacer = manage (new EventBox);
-               spacer->set_size_request (-1, scrollbar_height);
-               global_vpacker.pack_start (*spacer, false, false);
-       }
-
        global_frame.add (global_vpacker);
        global_frame.set_shadow_type (Gtk::SHADOW_IN);
        global_frame.set_name ("BaseFrame");
@@ -286,22 +265,6 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
 
        _session.engine().Stopped.connect (mem_fun(*this, &MixerStrip::engine_stopped));
        _session.engine().Running.connect (mem_fun(*this, &MixerStrip::engine_running));
-       _route->input_changed.connect (mem_fun(*this, &MixerStrip::input_changed));
-       _route->output_changed.connect (mem_fun(*this, &MixerStrip::output_changed));
-       _route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed));
-       _route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
-       _route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
-       _route->mix_group_changed.connect (mem_fun(*this, &MixerStrip::mix_group_changed));
-       _route->panner().Changed.connect (mem_fun(*this, &MixerStrip::connect_to_pan));
-
-       if (is_audio_track()) {
-               audio_track()->DiskstreamChanged.connect (mem_fun(*this, &MixerStrip::diskstream_changed));
-               get_diskstream()->SpeedChanged.connect (mem_fun(*this, &MixerStrip::speed_changed));
-       }
-
-       _route->name_changed.connect (mem_fun(*this, &RouteUI::name_changed));
-       _route->comment_changed.connect (mem_fun(*this, &MixerStrip::comment_changed));
-       _route->gui_changed.connect (mem_fun(*this, &MixerStrip::route_gui_changed));
 
        input_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::input_press), false);
        output_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::output_press), false);
@@ -311,11 +274,18 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
        mute_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::mute_press), false);
        mute_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::mute_release), false);
 
+       /* we don't need this if its not an audio track, but we don't know that yet and it doesn't
+          hurt (much).
+       */
+
+       rec_enable_button->set_name ("MixerRecordEnableButton");
+       rec_enable_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press), false);
+       rec_enable_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::rec_enable_release));
+
        name_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::name_button_button_press), false);
        group_button.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::select_mix_group), false);
 
        _width = (Width) -1;
-       set_stuff_from_route ();
 
        /* start off as a passthru strip. we'll correct this, if necessary,
           in update_diskstream_display().
@@ -323,6 +293,141 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
 
        set_name ("AudioTrackStripBase");
 
+       add_events (Gdk::BUTTON_RELEASE_MASK);
+}
+
+MixerStrip::~MixerStrip ()
+{
+       GoingAway(); /* EMIT_SIGNAL */
+
+       if (input_selector) {
+               delete input_selector;
+       }
+
+       if (output_selector) {
+               delete output_selector;
+       }
+}
+
+void
+MixerStrip::set_route (boost::shared_ptr<Route> rt)
+{
+       if (rec_enable_button->get_parent()) {
+               button_table.remove (*rec_enable_button);
+       }
+
+#ifdef VARISPEED_IN_MIXER_STRIP
+       if (speed_frame->get_parent()) {
+               button_table.remove (*speed_frame);
+       }
+#endif
+
+       RouteUI::set_route (rt);
+
+       if (input_selector) {
+               delete input_selector;
+               input_selector = 0;
+       }
+
+       if (output_selector) {
+               delete output_selector;
+               output_selector = 0;
+       }
+
+       panners.set_io (rt);
+       gpm.set_io (rt);
+       pre_redirect_box.set_route (rt);
+       post_redirect_box.set_route (rt);
+
+       if (set_color_from_route()) {
+               set_color (unique_random_color());
+       }
+
+       if (_mixer_owned && (route()->master() || route()->control())) {
+               
+               if (scrollbar_height == 0) {
+                       HScrollbar scrollbar;
+                       Gtk::Requisition requisition(scrollbar.size_request ());
+                       scrollbar_height = requisition.height;
+               }
+
+               EventBox* spacer = manage (new EventBox);
+               spacer->set_size_request (-1, scrollbar_height);
+               global_vpacker.pack_start (*spacer, false, false);
+       }
+
+       if (is_audio_track()) {
+
+               boost::shared_ptr<AudioTrack> at = audio_track();
+
+               connections.push_back (at->FreezeChange.connect (mem_fun(*this, &MixerStrip::map_frozen)));
+
+#ifdef VARISPEED_IN_MIXER_STRIP
+               speed_adjustment.signal_value_changed().connect (mem_fun(*this, &MixerStrip::speed_adjustment_changed));
+               
+               speed_frame.set_name ("BaseFrame");
+               speed_frame.set_shadow_type (Gtk::SHADOW_IN);
+               speed_frame.add (speed_spinner);
+               
+               speed_spinner.set_print_func (speed_printer, 0);
+
+               ARDOUR_UI::instance()->tooltips().set_tip (speed_spinner, _("Varispeed"));
+
+               button_table.attach (speed_frame, 0, 2, 5, 6);
+#endif /* VARISPEED_IN_MIXER_STRIP */
+
+               button_table.attach (*rec_enable_button, 0, 2, 2, 3);
+               rec_enable_button->set_sensitive (_session.writable());
+               rec_enable_button->show();
+       }
+
+       if (_route->phase_invert()) {
+               name_label.set_text (X_("Ø ") + name_label.get_text());
+       } else {
+               name_label.set_text (_route->name());
+       }
+
+       switch (_route->meter_point()) {
+       case MeterInput:
+               meter_point_label.set_text (_("input"));
+               break;
+               
+       case MeterPreFader:
+               meter_point_label.set_text (_("pre"));
+               break;
+               
+       case MeterPostFader:
+               meter_point_label.set_text (_("post"));
+               break;
+       }
+
+       delete route_ops_menu;
+       route_ops_menu = 0;
+       
+       ARDOUR_UI::instance()->tooltips().set_tip (comment_button, _route->comment().empty() ?
+                                                  _("Click to Add/Edit Comments"):
+                                                  _route->comment());
+
+       connections.push_back (_route->meter_change.connect (mem_fun(*this, &MixerStrip::meter_changed)));
+       connections.push_back (_route->input_changed.connect (mem_fun(*this, &MixerStrip::input_changed)));
+       connections.push_back (_route->output_changed.connect (mem_fun(*this, &MixerStrip::output_changed)));
+       connections.push_back (_route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed)));
+       connections.push_back (_route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
+       connections.push_back (_route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed)));
+       connections.push_back (_route->mix_group_changed.connect (mem_fun(*this, &MixerStrip::mix_group_changed)));
+       connections.push_back (_route->panner().Changed.connect (mem_fun(*this, &MixerStrip::connect_to_pan)));
+
+       if (is_audio_track()) {
+               connections.push_back (audio_track()->DiskstreamChanged.connect (mem_fun(*this, &MixerStrip::diskstream_changed)));
+               connections.push_back (get_diskstream()->SpeedChanged.connect (mem_fun(*this, &MixerStrip::speed_changed)));
+       }
+
+       connections.push_back (_route->name_changed.connect (mem_fun(*this, &RouteUI::name_changed)));
+       connections.push_back (_route->comment_changed.connect (mem_fun(*this, &MixerStrip::comment_changed)));
+       connections.push_back (_route->gui_changed.connect (mem_fun(*this, &MixerStrip::route_gui_changed)));
+
+       set_stuff_from_route ();
+
        /* now force an update of all the various elements */
 
        pre_redirect_box.update();
@@ -344,43 +449,19 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, boost::shared_ptr<Route> rt
        update_diskstream_display ();
        update_input_display ();
        update_output_display ();
-
-       add_events (Gdk::BUTTON_RELEASE_MASK);
-}
-
-MixerStrip::~MixerStrip ()
-{
-       GoingAway(); /* EMIT_SIGNAL */
-
-       if (input_selector) {
-               delete input_selector;
-       }
-
-       if (output_selector) {
-               delete output_selector;
-       }
 }
 
 void
 MixerStrip::set_stuff_from_route ()
 {
        XMLProperty *prop;
-       
+
        ensure_xml_node ();
 
+       /* if width is not set, it will be set by the MixerUI or editor */
+
        if ((prop = xml_node->property ("strip_width")) != 0) {
-               if (prop->value() == "wide") {
-                       set_width (Wide);
-               } else if (prop->value() == "narrow") {
-                       set_width (Narrow);
-               }
-               else {
-                       error << string_compose(_("unknown strip width \"%1\" in XML GUI information"), prop->value()) << endmsg;
-                       set_width (Wide);
-               }
-       }
-       else {
-               set_width (Wide);
+               set_width (Width (string_2_enum (prop->value(), _width)), this);
        }
 
        if ((prop = xml_node->property ("shown_mixer")) != 0) {
@@ -389,47 +470,47 @@ MixerStrip::set_stuff_from_route ()
                } else {
                        _marked_for_display = true;
                }
-       }
-       else {
+       } else {
                /* backwards compatibility */
                _marked_for_display = true;
        }
 }
 
 void
-MixerStrip::set_width (Width w)
+MixerStrip::set_width (Width w, void* owner)
 {
        /* always set the gpm width again, things may be hidden */
+
        gpm.set_width (w);
        panners.set_width (w);
        pre_redirect_box.set_width (w);
        post_redirect_box.set_width (w);
-       
-       if (_width == w) {
-               return;
-       }
+
+       _width_owner = owner;
 
        ensure_xml_node ();
        
        _width = w;
-       
+
+       if (_width_owner == this) {
+               xml_node->add_property ("strip_width", enum_2_string (_width));
+       }
+
        switch (w) {
        case Wide:
-               set_size_request (-1, -1);
-               xml_node->add_property ("strip_width", "wide");
-               
+
                if (rec_enable_button)  {
-                       ((Gtk::Label*)rec_enable_button->get_child())->set_text (_("record"));
+                       ((Gtk::Label*)rec_enable_button->get_child())->set_text (_("Record"));
                }
                ((Gtk::Label*)mute_button->get_child())->set_text  (_("Mute"));
                ((Gtk::Label*)solo_button->get_child())->set_text (_("Solo"));
 
                if (_route->comment() == "") {
                       comment_button.unset_bg (STATE_NORMAL);
-                      ((Gtk::Label*)comment_button.get_child())->set_text (_("comments"));
+                      ((Gtk::Label*)comment_button.get_child())->set_text (_("Comments"));
                } else {
                       comment_button.modify_bg (STATE_NORMAL, color());
-                      ((Gtk::Label*)comment_button.get_child())->set_text (_("*comments*"));
+                      ((Gtk::Label*)comment_button.get_child())->set_text (_("*Comments*"));
                }
 
                ((Gtk::Label*)gpm.gain_automation_style_button.get_child())->set_text (gpm.astyle_string(_route->gain_automation_curve().automation_style()));
@@ -437,12 +518,10 @@ MixerStrip::set_width (Width w)
                ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (panners.astyle_string(_route->panner().automation_style()));
                ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (panners.astate_string(_route->panner().automation_state()));
                Gtkmm2ext::set_size_request_to_display_given_text (name_button, "long", 2, 2);
+               set_size_request (-1, -1);
                break;
 
        case Narrow:
-               set_size_request (50, -1);
-               xml_node->add_property ("strip_width", "narrow");
-
                if (rec_enable_button) {
                        ((Gtk::Label*)rec_enable_button->get_child())->set_text (_("Rec"));
                }
@@ -462,14 +541,16 @@ MixerStrip::set_width (Width w)
                ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (panners.short_astyle_string(_route->panner().automation_style()));
                ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (panners.short_astate_string(_route->panner().automation_state()));
                Gtkmm2ext::set_size_request_to_display_given_text (name_button, "longest label", 2, 2);
+               set_size_request (max (50, gpm.get_gm_width()), -1);
                break;
        }
-
        update_input_display ();
        update_output_display ();
        mix_group_changed (0);
        name_changed (0);
-
+#ifdef GTKOSX
+       WidthChanged();
+#endif
 }
 
 void
@@ -562,7 +643,7 @@ MixerStrip::input_press (GdkEventButton *ev)
                msg.run ();
                return true;
        }
-       
+
        switch (ev->button) {
 
        case 1:
@@ -666,11 +747,10 @@ MixerStrip::add_connection_to_output_menu (ARDOUR::Connection* c)
 void
 MixerStrip::update_diskstream_display ()
 {
-       if (is_audio_track()) {
-
-               map_frozen ();
+       map_frozen ();
+       update_input_display ();
 
-               update_input_display ();
+       if (is_audio_track()) {
 
                if (input_selector) {
                        input_selector->hide_all ();
@@ -680,9 +760,6 @@ MixerStrip::update_diskstream_display ()
 
        } else {
 
-               map_frozen ();
-
-               update_input_display ();
                show_passthru_color ();
        }
 }
@@ -691,7 +768,7 @@ void
 MixerStrip::connect_to_pan ()
 {
        ENSURE_GUI_THREAD(mem_fun(*this, &MixerStrip::connect_to_pan));
-       
+
        panstate_connection.disconnect ();
        panstyle_connection.disconnect ();
 
@@ -742,6 +819,7 @@ MixerStrip::update_output_display ()
                        break;
                }
        }
+
        gpm.setup_meters ();
        panners.setup_pan ();
 }
@@ -762,17 +840,20 @@ void
 MixerStrip::input_changed (IOChange change, void *src)
 {
        Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_input_display));
+       set_width(_width, this);
 }
 
 void
 MixerStrip::output_changed (IOChange change, void *src)
 {
        Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_output_display));
+       set_width(_width, this);
 }
 
 
 void 
-MixerStrip::comment_editor_done_editing() {
+MixerStrip::comment_editor_done_editing() 
+{
        string str =  comment_area->get_buffer()->get_text();
        if (_route->comment() != str) {
                _route->set_comment (str, this);
@@ -928,7 +1009,7 @@ MixerStrip::mix_group_changed (void *ignored)
        RouteGroup *rg = _route->mix_group();
        
        if (rg) {
-               group_label.set_text (rg->name());
+               group_label.set_text (PBD::short_version (rg->name(), 5));
        } else {
                switch (_width) {
                case Wide:
@@ -972,13 +1053,14 @@ void
 MixerStrip::build_route_ops_menu ()
 {
        using namespace Menu_Helpers;
-
-       route_ops_menu = manage (new Menu);
+       route_ops_menu = new Menu;
        route_ops_menu->set_name ("ArdourContextMenu");
 
        MenuList& items = route_ops_menu->items();
-       
+
+       items.push_back (MenuElem (_("Save As Template"), mem_fun(*this, &RouteUI::save_as_template)));
        items.push_back (MenuElem (_("Rename"), mem_fun(*this, &RouteUI::route_rename)));
+       rename_menu_item = &items.back();
        items.push_back (SeparatorElem());
        items.push_back (CheckMenuElem (_("Active"), mem_fun (*this, &RouteUI::toggle_route_active)));
        route_active_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
@@ -987,11 +1069,15 @@ MixerStrip::build_route_ops_menu ()
        items.push_back (CheckMenuElem (_("Invert Polarity"), mem_fun (*this, &RouteUI::toggle_polarity)));
        polarity_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
        polarity_menu_item->set_active (_route->phase_invert());
+       items.push_back (CheckMenuElem (_("Protect against denormals"), mem_fun (*this, &RouteUI::toggle_denormal_protection)));
+       denormal_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
+       denormal_menu_item->set_active (_route->denormal_protection());
 
-       build_remote_control_menu ();
-       
-       items.push_back (SeparatorElem());
-       items.push_back (MenuElem (_("Remote Control ID"), *remote_control_menu));
+       if (!Profile->get_sae()) {
+               build_remote_control_menu ();
+               items.push_back (SeparatorElem());
+               items.push_back (MenuElem (_("Remote Control ID"), *remote_control_menu));
+        }
 
        items.push_back (SeparatorElem());
        items.push_back (MenuElem (_("Remove"), mem_fun(*this, &RouteUI::remove_this_route)));
@@ -1000,8 +1086,10 @@ MixerStrip::build_route_ops_menu ()
 gint
 MixerStrip::name_button_button_press (GdkEventButton* ev)
 {
-       if (ev->button == 1) {
+       if (ev->button == 1 || ev->button == 3) {
                list_route_operations ();
+               /* do not allow rename if the track is record-enabled */
+               rename_menu_item->set_sensitive (!_route->record_enabled());
                route_ops_menu->popup (1, ev->time);
        }
        return FALSE;
@@ -1089,10 +1177,10 @@ MixerStrip::width_clicked ()
 {
        switch (_width) {
        case Wide:
-               set_width (Narrow);
+               set_width (Narrow, this);
                break;
        case Narrow:
-               set_width (Wide);
+               set_width (Wide, this);
                break;
        }
 }
@@ -1100,16 +1188,16 @@ MixerStrip::width_clicked ()
 void
 MixerStrip::hide_clicked ()
 {
-    // LAME fix to reset the button status for when it is redisplayed (part 1)
-    hide_button.set_sensitive(false);
-    
+       // LAME fix to reset the button status for when it is redisplayed (part 1)
+       hide_button.set_sensitive(false);
+       
        if (_embedded) {
-                Hiding(); /* EMIT_SIGNAL */
+               Hiding(); /* EMIT_SIGNAL */
        } else {
                _mixer.hide_strip (this);
        }
        
-    // (part 2)
+       // (part 2)
        hide_button.set_sensitive(true);
 }
 
@@ -1124,7 +1212,7 @@ MixerStrip::map_frozen ()
 {
        ENSURE_GUI_THREAD (mem_fun(*this, &MixerStrip::map_frozen));
 
-       AudioTrack* at = audio_track();
+        boost::shared_ptr<AudioTrack> at = audio_track();
 
        if (at) {
                switch (at->freeze_state()) {
@@ -1137,9 +1225,17 @@ MixerStrip::map_frozen ()
                        pre_redirect_box.set_sensitive (true);
                        post_redirect_box.set_sensitive (true);
                        speed_spinner.set_sensitive (true);
+                       // XXX need some way, maybe, to retoggle redirect editors
                        break;
                }
        }
+       
+       hide_redirect_editors ();
+}
+
+void
+MixerStrip::hide_redirect_editors ()
+{
        _route->foreach_redirect (this, &MixerStrip::hide_redirect_editor);
 }
 
@@ -1161,19 +1257,19 @@ MixerStrip::route_active_changed ()
        if (is_audio_track()) {
                if (_route->active()) {
                        set_name ("AudioTrackStripBase");
-                       gpm.set_meter_strip_name ("AudioTrackStripBase");
+                       gpm.set_meter_strip_name ("AudioTrackMetrics");
                } else {
                        set_name ("AudioTrackStripBaseInactive");
-                       gpm.set_meter_strip_name ("AudioTrackStripBaseInactive");
+                       gpm.set_meter_strip_name ("AudioTrackMetricsInactive");
                }
                gpm.set_fader_name ("AudioTrackFader");
        } else { // FIXME: assumed audio bus
                if (_route->active()) {
                        set_name ("AudioBusStripBase");
-                       gpm.set_meter_strip_name ("AudioBusStripBase");
+                       gpm.set_meter_strip_name ("AudioBusMetrics");
                } else {
                        set_name ("AudioBusStripBaseInactive");
-                       gpm.set_meter_strip_name ("AudioBusStripBaseInactive");
+                       gpm.set_meter_strip_name ("AudioBusMetricsInactive");
                }
                gpm.set_fader_name ("AudioBusFader");
        }
@@ -1201,20 +1297,23 @@ MixerStrip::meter_changed (void *src)
 
        ENSURE_GUI_THREAD (bind (mem_fun(*this, &MixerStrip::meter_changed), src));
 
-               switch (_route->meter_point()) {
-               case MeterInput:
-                       meter_point_label.set_text (_("input"));
-                       break;
-                       
-               case MeterPreFader:
-                       meter_point_label.set_text (_("pre"));
-                       break;
-                       
-               case MeterPostFader:
-                       meter_point_label.set_text (_("post"));
-                       break;
-               }
-
-               gpm.setup_meters ();
+       switch (_route->meter_point()) {
+       case MeterInput:
+               meter_point_label.set_text (_("input"));
+               break;
+               
+       case MeterPreFader:
+               meter_point_label.set_text (_("pre"));
+               break;
+               
+       case MeterPostFader:
+               meter_point_label.set_text (_("post"));
+               break;
+       }
+       
+       gpm.setup_meters ();
+       // reset peak when meter point changes
+       gpm.reset_peak_display();
+       set_width (_width, this);
 }