Move ARDOUR::touch_file to pbd/file_utils.h
[ardour.git] / gtk2_ardour / route_time_axis.cc
index 54b8240b0fc0ae4d8f10bf5b0fb5958b8949fbf1..94ed2a86f044abc8909cf70b6496e1293a8bc77f 100644 (file)
@@ -45,6 +45,8 @@
 #include "ardour/amp.h"
 #include "ardour/meter.h"
 #include "ardour/event_type_map.h"
+#include "ardour/pannable.h"
+#include "ardour/panner.h"
 #include "ardour/processor.h"
 #include "ardour/profile.h"
 #include "ardour/route_group.h"
 
 #include "ardour_ui.h"
 #include "ardour_button.h"
+#include "audio_streamview.h"
 #include "debug.h"
 #include "global_signals.h"
 #include "route_time_axis.h"
 #include "automation_time_axis.h"
 #include "enums.h"
 #include "gui_thread.h"
+#include "item_counts.h"
 #include "keyboard.h"
+#include "paste_context.h"
 #include "playlist_selector.h"
 #include "point_selection.h"
 #include "prompter.h"
@@ -96,9 +101,9 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCan
        , parent_canvas (canvas)
        , no_redraw (false)
        , button_table (3, 3)
-       , route_group_button (_("G"))
-       , playlist_button (_("P"))
-       , automation_button (_("A"))
+       , route_group_button (S_("RTAV|G"))
+       , playlist_button (S_("RTAV|P"))
+       , automation_button (S_("RTAV|A"))
        , automation_action_menu (0)
        , plugins_submenu_item (0)
        , route_group_menu (0)
@@ -107,12 +112,15 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCan
        , color_mode_menu (0)
        , gm (sess, true, 75, 14)
        , _ignore_set_layer_display (false)
+       , gain_automation_item(NULL)
+       , trim_automation_item(NULL)
+       , mute_automation_item(NULL)
+       , pan_automation_item(NULL)
 {
        number_label.set_name("tracknumber label");
        number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
        number_label.set_alignment(.5, .5);
        number_label.set_fallthrough_to_parent (true);
-       track_number_v_size_group->add_widget(number_label);
 
        sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
 }
@@ -150,9 +158,6 @@ RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
                set_gui_property ("visible", false);
        }
 
-       mute_changed (0);
-       update_solo_display ();
-
        timestretch_rect = 0;
        no_redraw = false;
 
@@ -173,7 +178,6 @@ RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
                } else {
                        controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
                }
-               controls_button_size_group->add_widget(*rec_enable_button);
 
                 if (is_midi_track()) {
                         ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
@@ -201,7 +205,7 @@ RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
                blank->show();
        }
 
-       top_hbox.pack_end(gm.get_level_meter(), false, false, 4);
+       top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
 
        if (!ARDOUR::Profile->get_mixbus()) {
                controls_meters_size_group->add_widget (gm.get_level_meter());
@@ -217,8 +221,9 @@ RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
        } else {
                controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
        }
+       // mute button is always present, it is used to
+       // force the 'blank' placeholders to the proper size
        controls_button_size_group->add_widget(*mute_button);
-       track_number_v_size_group->add_widget(*mute_button);
 
        if (!_route->is_master()) {
                if (ARDOUR::Profile->get_mixbus()) {
@@ -226,7 +231,6 @@ RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
                } else {
                        controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
                }
-               controls_button_size_group->add_widget(*solo_button);
        } else {
                Gtk::Fixed *blank = manage(new Gtk::Fixed());
                controls_button_size_group->add_widget(*blank);
@@ -239,12 +243,10 @@ RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
        }
 
        if (ARDOUR::Profile->get_mixbus()) {
-               controls_button_size_group->add_widget(route_group_button);
                controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
                controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
        }
        else if (!ARDOUR::Profile->get_trx()) {
-               controls_button_size_group->add_widget(route_group_button);
                controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
                controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
        }
@@ -253,12 +255,12 @@ RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
        ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
        ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
 
-       mute_button->set_tweaks(ArdourButton::Square);
-       solo_button->set_tweaks(ArdourButton::Square);
-       rec_enable_button->set_tweaks(ArdourButton::Square);
-       playlist_button.set_tweaks(ArdourButton::Square);
-       automation_button.set_tweaks(ArdourButton::Square);
-       route_group_button.set_tweaks(ArdourButton::Square);
+       mute_button->set_tweaks(ArdourButton::TrackHeader);
+       solo_button->set_tweaks(ArdourButton::TrackHeader);
+       rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
+       playlist_button.set_tweaks(ArdourButton::TrackHeader);
+       automation_button.set_tweaks(ArdourButton::TrackHeader);
+       route_group_button.set_tweaks(ArdourButton::TrackHeader);
 
        if (is_midi_track()) {
                ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
@@ -271,21 +273,17 @@ RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
 
        if (ARDOUR::Profile->get_mixbus()) {
                controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
-               controls_button_size_group->add_widget(automation_button);
        }
        else if (!ARDOUR::Profile->get_trx()) {
                controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
-               controls_button_size_group->add_widget(automation_button);
        }
 
        if (is_track() && track()->mode() == ARDOUR::Normal) {
                if (ARDOUR::Profile->get_mixbus()) {
                        controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
-                       controls_button_size_group->add_widget(playlist_button);
                }
                else if (!ARDOUR::Profile->get_trx()) {
                        controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
-                       controls_button_size_group->add_widget(playlist_button);
                }
        }
 
@@ -319,15 +317,13 @@ RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
 
        route_group_menu = new RouteGroupMenu (_session, plist);
 
-       gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
-
        gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
 }
 
 RouteTimeAxisView::~RouteTimeAxisView ()
 {
-       CatchDeletion (this);
-
+       cleanup_gui_properties ();
+       
        for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
                delete *i;
        }
@@ -341,6 +337,7 @@ RouteTimeAxisView::~RouteTimeAxisView ()
        _automation_tracks.clear ();
 
        delete route_group_menu;
+       CatchDeletion (this);
 }
 
 void
@@ -420,9 +417,9 @@ RouteTimeAxisView::update_track_number_visibility ()
        }
        if (show_label) {
                if (ARDOUR::Profile->get_mixbus()) {
-                       controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
+                       controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
                } else {
-                       controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 1, 0);
+                       controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
                }
                // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
                // except the width of the number label is subtracted from the name-hbox, so we
@@ -515,6 +512,47 @@ RouteTimeAxisView::build_automation_action_menu (bool for_selection)
                items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
                items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
        }
+
+       /* Add any route automation */
+
+       if (gain_track) {
+               items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
+               gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
+               gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) && 
+                                                 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
+
+               _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
+       }
+
+       if (trim_track) {
+               items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
+               trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
+               trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
+                                                 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
+
+               _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
+       }
+
+       if (mute_track) {
+               items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
+               mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
+               mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) && 
+                                                 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
+
+               _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
+       }
+
+       if (!pan_tracks.empty()) {
+               items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
+               pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
+               pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
+                                                (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
+
+               set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
+               for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
+                       _main_automation_menu_map[*p] = pan_automation_item;
+               }
+       }
 }
 
 void
@@ -762,7 +800,7 @@ RouteTimeAxisView::build_display_menu ()
                        i->set_active (normal == 0 && tape == 0 && non_layered != 0);
                        i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
 
-                       items.push_back (MenuElem (_("Mode"), *mode_menu));
+                       items.push_back (MenuElem (_("Record Mode"), *mode_menu));
                }
 
 
@@ -825,12 +863,8 @@ RouteTimeAxisView::build_display_menu ()
 
        items.push_back (SeparatorElem());
        items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
-       if (!Profile->get_sae()) {
-               items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
-       } else {
-               items.push_front (SeparatorElem());
-               items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
-       }
+       items.push_front (SeparatorElem());
+       items.push_front (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
 }
 
 void
@@ -888,8 +922,8 @@ RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layer
 
        if (timestretch_rect == 0) {
                timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
-               timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
-               timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
+               timestretch_rect->set_fill_color (ArdourCanvas::HSV (ARDOUR_UI::config()->color ("time stretch fill")).mod (ARDOUR_UI::config()->modifier ("time stretch fill")).color());
+               timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
        }
 
        timestretch_rect->show ();
@@ -933,7 +967,7 @@ RouteTimeAxisView::show_selection (TimeSelection& ts)
 }
 
 void
-RouteTimeAxisView::set_height (uint32_t h)
+RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
 {
        int gmlen = h - 9;
        bool height_changed = (height == 0) || (h != height);
@@ -944,7 +978,7 @@ RouteTimeAxisView::set_height (uint32_t h)
        }
        gm.get_level_meter().setup_meters (gmlen, meter_width);
 
-       TimeAxisView::set_height (h);
+       TimeAxisView::set_height (h, m);
 
        if (_view) {
                _view->set_height ((double) current_height());
@@ -1266,15 +1300,22 @@ RouteTimeAxisView::selection_click (GdkEventButton* ev)
        if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
 
                /* special case: select/deselect all tracks */
+
+               _editor.begin_reversible_selection_op (X_("Selection Click"));
+
                if (_editor.get_selection().selected (this)) {
                        _editor.get_selection().clear_tracks ();
                } else {
                        _editor.select_all_tracks ();
                }
 
+               _editor.commit_reversible_selection_op ();
+
                return;
        }
 
+       _editor.begin_reversible_selection_op (X_("Selection Click"));
+
        switch (ArdourKeyboard::selection_type (ev->state)) {
        case Selection::Toggle:
                _editor.get_selection().toggle (this);
@@ -1292,6 +1333,8 @@ RouteTimeAxisView::selection_click (GdkEventButton* ev)
                _editor.get_selection().add (this);
                break;
        }
+
+       _editor.commit_reversible_selection_op ();
 }
 
 void
@@ -1300,6 +1343,10 @@ RouteTimeAxisView::set_selected_points (PointSelection& points)
        for (Children::iterator i = children.begin(); i != children.end(); ++i) {
                (*i)->set_selected_points (points);
        }
+       AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
+       if (asv) {
+               asv->set_selected_points (points);
+       }
 }
 
 void
@@ -1314,7 +1361,7 @@ RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
  * @param results List to add things to.
  */
 void
-RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
+RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
 {
        double speed = 1.0;
 
@@ -1326,14 +1373,14 @@ RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top
        framepos_t const end_adjusted   = session_frame_to_track_frame(end, speed);
 
        if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
-               _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
+               _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
        }
 
        /* pick up visible automation tracks */
 
        for (Children::iterator i = children.begin(); i != children.end(); ++i) {
                if (!(*i)->hidden()) {
-                       (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
+                       (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
                }
        }
 }
@@ -1544,20 +1591,20 @@ RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
 }
 
 bool
-RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
+RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
 {
        if (!is_track()) {
                return false;
        }
 
-       boost::shared_ptr<Playlist> pl = playlist ();
-       PlaylistSelection::iterator p;
-
-       for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
+       boost::shared_ptr<Playlist>       pl   = playlist ();
+       const ARDOUR::DataType            type = pl->data_type();
+       PlaylistSelection::const_iterator p    = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
 
        if (p == selection.playlists.end()) {
                return false;
        }
+       ctx.counts.increase_n_playlists(type);
 
         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
 
@@ -1566,13 +1613,19 @@ RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, siz
                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
        }
 
+       /* add multi-paste offset if applicable */
+       std::pair<framepos_t, framepos_t> extent   = (*p)->get_extent();
+       const framecnt_t                  duration = extent.second - extent.first;
+       pos += _editor.get_paste_offset(pos, ctx.count, duration);
+
        pl->clear_changes ();
+       pl->clear_owned_changes ();
        if (Config->get_edit_mode() == Ripple) {
                std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
                framecnt_t amount = extent.second - extent.first;
-               pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
+               pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
        }
-       pl->paste (*p, pos, times);
+       pl->paste (*p, pos, ctx.times);
 
        vector<Command*> cmds;
        pl->rdiff (cmds);
@@ -1781,11 +1834,11 @@ RouteTimeAxisView::color_handler ()
 {
        //case cTimeStretchOutline:
        if (timestretch_rect) {
-               timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
+               timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
        }
        //case cTimeStretchFill:
        if (timestretch_rect) {
-               timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
+               timestretch_rect->set_fill_color (ARDOUR_UI::config()->color ("time stretch fill"));
        }
 
        reset_meter();
@@ -1843,6 +1896,126 @@ RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
        }
 }
 
+void
+RouteTimeAxisView::update_gain_track_visibility ()
+{
+       bool const showit = gain_automation_item->get_active();
+
+       if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
+               gain_track->set_marked_for_display (showit);
+
+               /* now trigger a redisplay */
+
+               if (!no_redraw) {
+                        _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
+               }
+       }
+}
+
+void
+RouteTimeAxisView::update_trim_track_visibility ()
+{
+       bool const showit = trim_automation_item->get_active();
+
+       if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
+               trim_track->set_marked_for_display (showit);
+
+               /* now trigger a redisplay */
+
+               if (!no_redraw) {
+                        _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
+               }
+       }
+}
+
+void
+RouteTimeAxisView::update_mute_track_visibility ()
+{
+       bool const showit = mute_automation_item->get_active();
+
+       if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
+               mute_track->set_marked_for_display (showit);
+
+               /* now trigger a redisplay */
+
+               if (!no_redraw) {
+                        _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
+               }
+       }
+}
+
+void
+RouteTimeAxisView::update_pan_track_visibility ()
+{
+       bool const showit = pan_automation_item->get_active();
+       bool changed = false;
+
+       for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
+               if ((*i)->set_marked_for_display (showit)) {
+                       changed = true;
+               }
+       }
+
+       if (changed) {
+               _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
+       }
+}
+
+void
+RouteTimeAxisView::ensure_pan_views (bool show)
+{
+       bool changed = false;
+       for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
+               changed = true;
+               (*i)->set_marked_for_display (false);
+       }
+       if (changed) {
+               _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
+       }
+       pan_tracks.clear();
+
+       if (!_route->panner()) {
+               return;
+       }
+
+       set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
+       set<Evoral::Parameter>::iterator p;
+
+       for (p = params.begin(); p != params.end(); ++p) {
+               boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
+
+               if (pan_control->parameter().type() == NullAutomation) {
+                       error << "Pan control has NULL automation type!" << endmsg;
+                       continue;
+               }
+
+               if (automation_child (pan_control->parameter ()).get () == 0) {
+
+                       /* we don't already have an AutomationTimeAxisView for this parameter */
+
+                       std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
+
+                       boost::shared_ptr<AutomationTimeAxisView> t (
+                                       new AutomationTimeAxisView (_session,
+                                               _route,
+                                               _route->pannable(),
+                                               pan_control,
+                                               pan_control->parameter (),
+                                               _editor,
+                                               *this,
+                                               false,
+                                               parent_canvas,
+                                               name)
+                                       );
+
+                       pan_tracks.push_back (t);
+                       add_automation_child (*p, t, show);
+               } else {
+                       pan_tracks.push_back (automation_child (pan_control->parameter ()));
+               }
+       }
+}
+
 
 void
 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
@@ -2025,7 +2198,7 @@ RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor>
                      << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
                                          processor->name(), what.type(), (int) what.channel(), what.id() )
                      << endmsg;
-               /*NOTREACHED*/
+               abort(); /*NOTREACHED*/
                return;
        }
 
@@ -2113,7 +2286,7 @@ RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_
                request_redraw ();
        }
 
-       if (!EventTypeMap::instance().is_midi_parameter(param)) {
+       if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
                /* MIDI-related parameters are always in the menu, there's no
                   reason to rebuild the menu just because we added a automation
                   lane for one of them. But if we add a non-MIDI automation
@@ -2388,7 +2561,7 @@ RouteTimeAxisView::show_meter ()
 void
 RouteTimeAxisView::reset_meter ()
 {
-       if (Config->get_show_track_meters()) {
+       if (ARDOUR_UI::config()->get_show_track_meters()) {
                int meter_width = 3;
                if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
                        meter_width = 6;
@@ -2410,7 +2583,7 @@ RouteTimeAxisView::meter_changed ()
 {
        ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
        reset_meter();
-       if (_route && !no_redraw) {
+       if (_route && !no_redraw && ARDOUR_UI::config()->get_show_track_meters()) {
                request_redraw ();
        }
        // reset peak when meter point changes
@@ -2491,7 +2664,7 @@ RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
        if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
                if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
                        fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
-                       /*NOTREACHED*/
+                       abort(); /*NOTREACHED*/
                }
 
                _underlay_streams.push_back(v);
@@ -2528,7 +2701,7 @@ RouteTimeAxisView::remove_underlay (StreamView* v)
 
                if (gm == other._underlay_mirrors.end()) {
                        fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
-                       /*NOTREACHED*/
+                       abort(); /*NOTREACHED*/
                }
 
                v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
@@ -2553,19 +2726,19 @@ RouteTimeAxisView::set_button_names ()
        if (Config->get_solo_control_is_listen_control()) {
                switch (Config->get_listen_position()) {
                        case AfterFaderListen:
-                               solo_button->set_text (_("A"));
+                               solo_button->set_text (S_("AfterFader|A"));
                                ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
                                break;
                        case PreFaderListen:
-                               solo_button->set_text (_("P"));
+                               solo_button->set_text (S_("PreFader|P"));
                                ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
                        break;
                }
        } else {
-               solo_button->set_text (_("S"));
+               solo_button->set_text (S_("Solo|S"));
                ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
        }
-       mute_button->set_text (_("M"));
+       mute_button->set_text (S_("Mute|M"));
 }
 
 Gtk::CheckMenuItem*
@@ -2608,6 +2781,29 @@ RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param,
        add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
 }
 
+void
+RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
+{
+       boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
+       if (!c || ! _route->trim()->active()) {
+               return;
+       }
+
+       trim_track.reset (new AutomationTimeAxisView (_session,
+                                                     _route, _route->trim(), c, param,
+                                                     _editor,
+                                                     *this,
+                                                     false,
+                                                     parent_canvas,
+                                                     _route->trim()->describe_parameter(param)));
+
+       if (_view) {
+               _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
+       }
+
+       add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
+}
+
 void
 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
 {