Editor zoom: add zoom_to_extents()
[ardour.git] / gtk2_ardour / editor_ops.cc
index 5419ce937484ecc353bf1108391c25bc101f68f2..41fe50d6ef59b79db0853c9807733588500bc128 100644 (file)
@@ -28,6 +28,8 @@
 #include <map>
 #include <set>
 
+#include <gtkmm/messagedialog.h>
+
 #include "pbd/error.h"
 #include "pbd/basename.h"
 #include "pbd/pthread_utils.h"
 #include "pbd/whitespace.h"
 #include "pbd/stateful_diff_command.h"
 
-#include <gtkmm2ext/utils.h>
-#include <gtkmm2ext/choice.h>
-#include <gtkmm2ext/popup.h>
+#include "gtkmm2ext/utils.h"
+
+#include "widgets/choice.h"
+#include "widgets/popup.h"
+#include "widgets/prompter.h"
 
 #include "ardour/audio_track.h"
 #include "ardour/audioregion.h"
@@ -59,6 +63,7 @@
 #include "ardour/strip_silence.h"
 #include "ardour/transient_detector.h"
 #include "ardour/transpose.h"
+#include "ardour/vca_manager.h"
 
 #include "canvas/canvas.h"
 
@@ -82,6 +87,7 @@
 #include "item_counts.h"
 #include "keyboard.h"
 #include "midi_region_view.h"
+#include "mixer_ui.h"
 #include "mixer_strip.h"
 #include "mouse_cursors.h"
 #include "normalize_dialog.h"
 #include "transpose_dialog.h"
 #include "transform_dialog.h"
 #include "ui_config.h"
+#include "vca_time_axis.h"
 
 #include "pbd/i18n.h"
 
@@ -109,6 +116,7 @@ using namespace ARDOUR;
 using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
+using namespace ArdourWidgets;
 using namespace Editing;
 using Gtkmm2ext::Keyboard;
 
@@ -119,6 +127,13 @@ using Gtkmm2ext::Keyboard;
 void
 Editor::undo (uint32_t n)
 {
+       if (_session && _session->actively_recording()) {
+               /* no undo allowed while recording. Session will check also,
+                  but we don't even want to get to that.
+               */
+               return;
+       }
+
        if (_drags->active ()) {
                _drags->abort ();
        }
@@ -136,12 +151,19 @@ Editor::undo (uint32_t n)
 void
 Editor::redo (uint32_t n)
 {
+       if (_session && _session->actively_recording()) {
+               /* no redo allowed while recording. Session will check also,
+                  but we don't even want to get to that.
+               */
+               return;
+       }
+
        if (_drags->active ()) {
                _drags->abort ();
        }
 
        if (_session) {
-               _session->redo (n);
+       _session->redo (n);
                if (_session->redo_depth() == 0) {
                        redo_action->set_sensitive(false);
                }
@@ -151,7 +173,7 @@ Editor::redo (uint32_t n)
 }
 
 void
-Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int32_t sub_num)
+Editor::split_regions_at (MusicFrame where, RegionSelection& regions, bool snap_frame)
 {
        bool frozen = false;
 
@@ -177,10 +199,14 @@ Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int3
                case SnapToRegionEnd:
                        break;
                default:
-                       snap_to (where);
+                       if (snap_frame) {
+                               snap_to (where);
+                       }
                }
        } else {
-               snap_to (where);
+               if (snap_frame) {
+                       snap_to (where);
+               }
 
                frozen = true;
                EditorFreeze(); /* Emit Signal */
@@ -194,7 +220,7 @@ Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int3
                   have something to split.
                */
 
-               if (!(*a)->region()->covers (where)) {
+               if (!(*a)->region()->covers (where.frame)) {
                        ++a;
                        continue;
                }
@@ -226,7 +252,7 @@ Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int3
 
                if (pl) {
                        pl->clear_changes ();
-                       pl->split_region ((*a)->region(), where, sub_num);
+                       pl->split_region ((*a)->region(), where);
                        _session->add_command (new StatefulDiffCommand (pl));
                }
 
@@ -258,7 +284,6 @@ Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int3
        if (working_on_selection) {
                // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
 
-               _ignore_follow_edits = true;  // a split will change the region selection in mysterious ways;  it's not practical or wanted to follow this edit
                RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
                /* There are three classes of regions that we might want selected after
                   splitting selected regions:
@@ -273,7 +298,7 @@ Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int3
                }
 
                for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
-                       if ((*ri)->region()->position() < where) {
+                       if ((*ri)->region()->position() < where.frame) {
                                // new regions created before the split
                                if (rsas & NewlyCreatedLeft) {
                                        selection->add (*ri);
@@ -285,13 +310,10 @@ Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int3
                                }
                        }
                }
-               _ignore_follow_edits = false;
        } else {
-               _ignore_follow_edits = true;
                if( working_on_selection ) {
                        selection->add (latest_regionviews);  //these are the new regions created after the split
                }
-               _ignore_follow_edits = false;
        }
 
        commit_reversible_command ();
@@ -404,6 +426,7 @@ Editor::nudge_forward (bool next, bool force_playhead)
 
                bool is_start;
                bool in_command = false;
+               const int32_t divisions = get_grid_music_divisions (0);
 
                for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
 
@@ -419,9 +442,9 @@ Editor::nudge_forward (bool next, bool force_playhead)
                                                distance = next_distance;
                                        }
                                        if (max_framepos - distance > loc->start() + loc->length()) {
-                                               loc->set_start (loc->start() + distance);
+                                               loc->set_start (loc->start() + distance, false, true, divisions);
                                        } else {
-                                               loc->set_start (max_framepos - loc->length());
+                                               loc->set_start (max_framepos - loc->length(), false, true, divisions);
                                        }
                                } else {
                                        distance = get_nudge_distance (loc->end(), next_distance);
@@ -429,9 +452,9 @@ Editor::nudge_forward (bool next, bool force_playhead)
                                                distance = next_distance;
                                        }
                                        if (max_framepos - distance > loc->end()) {
-                                               loc->set_end (loc->end() + distance);
+                                               loc->set_end (loc->end() + distance, false, true, divisions);
                                        } else {
-                                               loc->set_end (max_framepos);
+                                               loc->set_end (max_framepos, false, true, divisions);
                                        }
                                        if (loc->is_session_range()) {
                                                _session->set_end_is_free (false);
@@ -511,9 +534,9 @@ Editor::nudge_backward (bool next, bool force_playhead)
                                                distance = next_distance;
                                        }
                                        if (distance < loc->start()) {
-                                               loc->set_start (loc->start() - distance);
+                                               loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
                                        } else {
-                                               loc->set_start (0);
+                                               loc->set_start (0, false, true, get_grid_music_divisions(0));
                                        }
                                } else {
                                        distance = get_nudge_distance (loc->end(), next_distance);
@@ -523,9 +546,9 @@ Editor::nudge_backward (bool next, bool force_playhead)
                                        }
 
                                        if (distance < loc->end() - loc->length()) {
-                                               loc->set_end (loc->end() - distance);
+                                               loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
                                        } else {
-                                               loc->set_end (loc->length());
+                                               loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
                                        }
                                        if (loc->is_session_range()) {
                                                _session->set_end_is_free (false);
@@ -609,9 +632,9 @@ Editor::nudge_backward_capture_offset ()
 }
 
 struct RegionSelectionPositionSorter {
-        bool operator() (RegionView* a, RegionView* b) {
-                return a->region()->position() < b->region()->position();
-        }
+       bool operator() (RegionView* a, RegionView* b) {
+               return a->region()->position() < b->region()->position();
+       }
 };
 
 void
@@ -700,9 +723,12 @@ Editor::build_region_boundary_cache ()
                return;
        }
 
+       bool maybe_first_frame = false;
+
        switch (_snap_type) {
        case SnapToRegionStart:
                interesting_points.push_back (Start);
+               maybe_first_frame = true;
                break;
        case SnapToRegionEnd:
                interesting_points.push_back (End);
@@ -713,6 +739,7 @@ Editor::build_region_boundary_cache ()
        case SnapToRegionBoundary:
                interesting_points.push_back (Start);
                interesting_points.push_back (End);
+               maybe_first_frame = true;
                break;
        default:
                fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
@@ -729,6 +756,17 @@ Editor::build_region_boundary_cache ()
                tlist = track_views.filter_to_unique_playlists ();
        }
 
+       if (maybe_first_frame) {
+               TrackViewList::const_iterator i;
+               for (i = tlist.begin(); i != tlist.end(); ++i) {
+                       boost::shared_ptr<Playlist> pl = (*i)->playlist();
+                       if (pl && pl->count_regions_at (0)) {
+                               region_boundary_cache.push_back (0);
+                               break;
+                       }
+               }
+       }
+
        while (pos < _session->current_end_frame() && !at_end) {
 
                framepos_t rpos;
@@ -1139,7 +1177,7 @@ Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
                return;
        }
 
-       loc->move_to (target);
+       loc->move_to (target, 0);
 }
 
 void
@@ -1216,7 +1254,7 @@ Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
 
        pos = track_frame_to_session_frame(pos, speed);
 
-       loc->move_to (pos);
+       loc->move_to (pos, 0);
 }
 
 void
@@ -1263,7 +1301,7 @@ Editor::selected_marker_to_selection_start ()
                return;
        }
 
-       loc->move_to (pos);
+       loc->move_to (pos, 0);
 }
 
 void
@@ -1298,7 +1336,7 @@ Editor::selected_marker_to_selection_end ()
                return;
        }
 
-       loc->move_to (pos);
+       loc->move_to (pos, 0);
 }
 
 void
@@ -1350,6 +1388,7 @@ Editor::cursor_align (bool playhead_to_edit)
                _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
 
        } else {
+               const int32_t divisions = get_grid_music_divisions (0);
                /* move selected markers to playhead */
 
                for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
@@ -1358,10 +1397,10 @@ Editor::cursor_align (bool playhead_to_edit)
                        Location* loc = find_location_from_marker (*i, ignored);
 
                        if (loc->is_mark()) {
-                               loc->set_start (playhead_cursor->current_frame ());
+                               loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
                        } else {
                                loc->set (playhead_cursor->current_frame (),
-                                         playhead_cursor->current_frame () + loc->length());
+                                         playhead_cursor->current_frame () + loc->length(), true, divisions);
                        }
                }
        }
@@ -1434,6 +1473,22 @@ Editor::scroll_tracks_up_line ()
        reset_y_origin (vertical_adjustment.get_value() - 60);
 }
 
+void
+Editor::select_topmost_track ()
+{
+       const double top_of_trackviews = vertical_adjustment.get_value();
+       for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
+               if ((*t)->hidden()) {
+                       continue;
+               }
+               std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
+               if (res.first) {
+                       selection->set (*t);
+                       break;
+               }
+       }
+}
+
 bool
 Editor::scroll_down_one_track (bool skip_child_views)
 {
@@ -1695,27 +1750,52 @@ Editor::tav_zoom_smooth (bool coarser, bool force_all)
 }
 
 void
-Editor::temporal_zoom_step_mouse_focus (bool coarser)
+Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
 {
        Editing::ZoomFocus temp_focus = zoom_focus;
        zoom_focus = Editing::ZoomFocusMouse;
-       temporal_zoom_step (coarser);
+       temporal_zoom_step_scale (zoom_out, scale);
        zoom_focus = temp_focus;
 }
 
 void
-Editor::temporal_zoom_step (bool coarser)
+Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
 {
-       ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser)
+       temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
+}
+
+void
+Editor::temporal_zoom_step (bool zoom_out)
+{
+       temporal_zoom_step_scale (zoom_out, 2.0);
+}
+
+void
+Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
+{
+       ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
 
        framecnt_t nspp = samples_per_pixel;
 
-       if (coarser) {
-               nspp *= 2;
+       if (zoom_out) {
+               nspp *= scale;
+               if (nspp == samples_per_pixel) {
+                       nspp *= 2.0;
+               }
        } else {
-               nspp /= 2;
+               nspp /= scale;
+               if (nspp == samples_per_pixel) {
+                       nspp /= 2.0;
+               }
        }
 
+       //zoom-behavior-tweaks
+       //limit our maximum zoom to the session gui extents value
+       std::pair<framepos_t, framepos_t> ext = session_gui_extents();
+       framecnt_t session_extents_pp = ( ext.second - ext.first )  / _visible_canvas_width;
+       if (nspp > session_extents_pp)
+               nspp = session_extents_pp;
+
        temporal_zoom (nspp);
 }
 
@@ -1735,6 +1815,7 @@ Editor::temporal_zoom (framecnt_t fpp)
        framepos_t leftmost_after_zoom = 0;
        framepos_t where;
        bool in_track_canvas;
+       bool use_mouse_frame = true;
        framecnt_t nfpp;
        double l;
 
@@ -1795,18 +1876,13 @@ Editor::temporal_zoom (framecnt_t fpp)
        case ZoomFocusMouse:
                /* try to keep the mouse over the same point in the display */
 
-               if (!mouse_frame (where, in_track_canvas)) {
-                       /* use playhead instead */
-                       where = playhead_cursor->current_frame ();
-
-                       if (where < half_page_size) {
-                               leftmost_after_zoom = 0;
-                       } else {
-                               leftmost_after_zoom = where - half_page_size;
-                       }
-
-               } else {
+               if (_drags->active()) {
+                       where = _drags->current_pointer_frame ();
+               } else if (!mouse_frame (where, in_track_canvas)) {
+                       use_mouse_frame = false;
+               }
 
+               if (use_mouse_frame) {
                        l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
 
                        if (l < 0) {
@@ -1816,8 +1892,16 @@ Editor::temporal_zoom (framecnt_t fpp)
                        } else {
                                leftmost_after_zoom = (framepos_t) l;
                        }
-               }
+               } else {
+                       /* use playhead instead */
+                       where = playhead_cursor->current_frame ();
 
+                       if (where < half_page_size) {
+                               leftmost_after_zoom = 0;
+                       } else {
+                               leftmost_after_zoom = where - half_page_size;
+                       }
+               }
                break;
 
        case ZoomFocusEdit:
@@ -1880,53 +1964,6 @@ Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
        }
 }
 
-void
-Editor::temporal_zoom_region (bool both_axes)
-{
-       framepos_t start = max_framepos;
-       framepos_t end = 0;
-       set<TimeAxisView*> tracks;
-
-       if ( !get_selection_extents(start, end) )
-               return;
-
-       calc_extra_zoom_edges (start, end);
-
-       /* if we're zooming on both axes we need to save track heights etc.
-        */
-
-       undo_visual_stack.push_back (current_visual_state (both_axes));
-
-       PBD::Unwinder<bool> nsv (no_save_visual, true);
-
-       temporal_zoom_by_frame (start, end);
-
-       if (both_axes) {
-               uint32_t per_track_height = (uint32_t) floor ((_visible_canvas_height - 10.0) / tracks.size());
-
-               /* set visible track heights appropriately */
-
-               for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
-                       (*t)->set_height (per_track_height);
-               }
-
-               /* hide irrelevant tracks */
-
-               DisplaySuspender ds;
-
-               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
-                       if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
-                               hide_track_in_display (*i);
-                       }
-               }
-
-               vertical_adjustment.set_value (0.0);
-       }
-
-       redo_visual_stack.push_back (current_visual_state (both_axes));
-}
-
-
 bool
 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
 {
@@ -1968,7 +2005,7 @@ Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
 
 
 void
-Editor::temporal_zoom_selection (bool both_axes)
+Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
 {
        if (!selection) return;
 
@@ -1976,23 +2013,18 @@ Editor::temporal_zoom_selection (bool both_axes)
 
        //ToDo:  if control points are selected, zoom to that
 
-       //if region(s) are selected, zoom to that
-       if ( !selection->regions.empty() )
-               temporal_zoom_region (both_axes);
+       if (axes == Horizontal || axes == Both) {
 
-       //if a range is selected, zoom to that
-       if (!selection->time.empty()) {
-
-               framepos_t start,  end;
+               framepos_t start, end;
                if (get_selection_extents (start, end)) {
-                       calc_extra_zoom_edges(start, end);
+                       calc_extra_zoom_edges (start, end);
                        temporal_zoom_by_frame (start, end);
                }
-
-               if (both_axes)
-                       fit_selection();
        }
 
+       if (axes == Vertical || axes == Both) {
+               fit_selection ();
+       }
 }
 
 void
@@ -2026,6 +2058,39 @@ Editor::temporal_zoom_session ()
        }
 }
 
+void
+Editor::temporal_zoom_extents ()
+{
+       ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
+
+       if (_session) {
+               std::pair<framepos_t, framepos_t> ext = session_gui_extents( false );  //in this case we want to zoom to the extents explicitly; ignore the users prefs for extra padding
+
+               framecnt_t start = ext.first;
+               framecnt_t end = ext.second;
+
+               if (_session->actively_recording () ) {
+                       framepos_t cur = playhead_cursor->current_frame ();
+                       if (cur > end) {
+                               /* recording beyond the end marker; zoom out
+                                * by 5 seconds more so that if 'follow
+                                * playhead' is active we don't immediately
+                                * scroll.
+                                */
+                               end = cur + _session->frame_rate() * 5;
+                       }
+               }
+
+               if ((start == 0 && end == 0) || end < start) {
+                       return;
+               }
+
+               calc_extra_zoom_edges(start, end);
+
+               temporal_zoom_by_frame (start, end);
+       }
+}
+
 void
 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
 {
@@ -2114,7 +2179,7 @@ Editor::choose_new_marker_name(string &name) {
                return true;
        }
 
-       ArdourPrompter dialog (true);
+       Prompter dialog (true);
 
        dialog.set_prompt (_("New Name:"));
 
@@ -2159,7 +2224,7 @@ Editor::add_location_from_selection ()
        framepos_t end = selection->time[clicked_selection].end;
 
        _session->locations()->next_available_name(rangename,"selection");
-       Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
+       Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
 
        begin_reversible_command (_("add marker"));
 
@@ -2182,7 +2247,7 @@ Editor::add_location_mark (framepos_t where)
        if (!choose_new_marker_name(markername)) {
                return;
        }
-       Location *location = new Location (*_session, where, where, markername, Location::IsMark);
+       Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
        begin_reversible_command (_("add marker"));
 
        XMLNode &before = _session->locations()->get_state();
@@ -2243,19 +2308,29 @@ Editor::set_session_end_from_playhead ()
        _session->set_end_is_free (false);
 }
 
+
+void
+Editor::toggle_location_at_playhead_cursor ()
+{
+       if (!do_remove_location_at_playhead_cursor())
+       {
+               add_location_from_playhead_cursor();
+       }
+}
+
 void
 Editor::add_location_from_playhead_cursor ()
 {
        add_location_mark (_session->audible_frame());
 }
 
-void
-Editor::remove_location_at_playhead_cursor ()
+bool
+Editor::do_remove_location_at_playhead_cursor ()
 {
+       bool removed = false;
        if (_session) {
                //set up for undo
                XMLNode &before = _session->locations()->get_state();
-               bool removed = false;
 
                //find location(s) at this time
                Locations::LocationList locs;
@@ -2275,6 +2350,13 @@ Editor::remove_location_at_playhead_cursor ()
                        commit_reversible_command ();
                }
        }
+       return removed;
+}
+
+void
+Editor::remove_location_at_playhead_cursor ()
+{
+       do_remove_location_at_playhead_cursor ();
 }
 
 /** Add a range marker around each selected region */
@@ -2294,7 +2376,7 @@ Editor::add_locations_from_region ()
 
                boost::shared_ptr<Region> region = (*i)->region ();
 
-               Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
+               Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
 
                _session->locations()->add (location, true);
                commit = true;
@@ -2335,7 +2417,7 @@ Editor::add_location_from_region ()
        }
 
        // single range spanning all selected
-       Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
+       Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
        _session->locations()->add (location, true);
 
        begin_reversible_command (_("add marker"));
@@ -2371,6 +2453,14 @@ Editor::jump_backward_to_mark ()
 
        framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
 
+       //handle the case where we are rolling, and we're less than one-half second past the mark, we want to go to the prior mark...
+       if ( _session->transport_rolling() ) {
+               if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
+                       framepos_t prior = _session->locations()->first_mark_before ( pos );
+                       pos = prior;
+               }
+       }
+
        if (pos < 0) {
                return;
        }
@@ -2390,7 +2480,7 @@ Editor::set_mark ()
                return;
        }
 
-       _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
+       _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
 }
 
 void
@@ -2593,24 +2683,19 @@ Editor::play_selection ()
        _session->request_play_range (&lar, true);
 }
 
-framepos_t
-Editor::get_preroll ()
-{
-       return Config->get_preroll_seconds() * _session->frame_rate();
-}
-
 
 void
-Editor::maybe_locate_with_edit_preroll ( framepos_t location )
+Editor::maybe_locate_with_edit_preroll (framepos_t location)
 {
-       if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _ignore_follow_edits || _session->config.get_external_sync() )
+       if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
                return;
 
-       location -= get_preroll();
+       location -= _session->preroll_samples (location);
 
        //don't try to locate before the beginning of time
-       if ( location < 0 )
+       if (location < 0) {
                location = 0;
+       }
 
        //if follow_playhead is on, keep the playhead on the screen
        if ( _follow_playhead )
@@ -2623,15 +2708,15 @@ Editor::maybe_locate_with_edit_preroll ( framepos_t location )
 void
 Editor::play_with_preroll ()
 {
-       {
-               framepos_t preroll = get_preroll();
+       framepos_t start, end;
+       if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
+               const framepos_t preroll = _session->preroll_samples (start);
 
-               framepos_t start, end;
-               if (!get_selection_extents ( start, end))
-                       return;
+               framepos_t ret = start;
 
-               if (start > preroll)
+               if (start > preroll) {
                        start = start - preroll;
+               }
 
                end = end + preroll;  //"post-roll"
 
@@ -2640,9 +2725,35 @@ Editor::play_with_preroll ()
                lar.push_back (ar);
 
                _session->request_play_range (&lar, true);
+               _session->set_requested_return_frame (ret);  //force auto-return to return to range start, without the preroll
+       } else {
+               framepos_t ph = playhead_cursor->current_frame ();
+               const framepos_t preroll = _session->preroll_samples (ph);
+               framepos_t start;
+               if (ph > preroll) {
+                       start = ph - preroll;
+               } else {
+                       start = 0;
+               }
+               _session->request_locate (start, true);
+               _session->set_requested_return_frame (ph);  //force auto-return to return to playhead location, without the preroll
        }
 }
 
+void
+Editor::rec_with_preroll ()
+{
+       framepos_t ph = playhead_cursor->current_frame ();
+       framepos_t preroll = _session->preroll_samples (ph);
+       _session->request_preroll_record_trim (ph, preroll);
+}
+
+void
+Editor::rec_with_count_in ()
+{
+       _session->request_count_in_record ();
+}
+
 void
 Editor::play_location (Location& location)
 {
@@ -2978,7 +3089,7 @@ Editor::split_multichannel_region ()
        vector< boost::shared_ptr<Region> > v;
 
        for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
-               (*x)->region()->separate_by_channel (*_session, v);
+               (*x)->region()->separate_by_channel (v);
        }
 }
 
@@ -3090,8 +3201,8 @@ Editor::separate_regions_between (const TimeSelection& ts)
                                if (!latest_regionviews.empty()) {
 
                                        rtv->view()->foreach_regionview (sigc::bind (
-                                                                                sigc::ptr_fun (add_if_covered),
-                                                                                &(*t), &new_selection));
+                                                                                    sigc::ptr_fun (add_if_covered),
+                                                                                    &(*t), &new_selection));
 
                                        if (!in_command) {
                                                begin_reversible_command (_("separate"));
@@ -3121,8 +3232,8 @@ Editor::separate_regions_between (const TimeSelection& ts)
 }
 
 struct PlaylistState {
-    boost::shared_ptr<Playlist> playlist;
-    XMLNode*  before;
+       boost::shared_ptr<Playlist> playlist;
+       XMLNode*  before;
 };
 
 /** Take tracks from get_tracks_for_range_action and cut any regions
@@ -3225,10 +3336,10 @@ Editor::separate_under_selected_regions ()
 
                boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
 
-               if (!playlist) {
+               if (!playlist) {
                        // is this check necessary?
                        continue;
-               }
+               }
 
                vector<PlaylistState>::iterator i;
 
@@ -3244,7 +3355,7 @@ Editor::separate_under_selected_regions ()
                        PlaylistState before;
                        before.playlist = playlist;
                        before.before = &playlist->get_state();
-
+                       playlist->clear_changes ();
                        playlist->freeze ();
                        playlists.push_back(before);
                }
@@ -3271,15 +3382,22 @@ Editor::crop_region_to_selection ()
 {
        if (!selection->time.empty()) {
 
-               crop_region_to (selection->time.start(), selection->time.end_frame());
-
+               begin_reversible_command (_("Crop Regions to Time Selection"));
+               for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
+                       crop_region_to ((*i).start, (*i).end);
+               }
+               commit_reversible_command();
        } else {
 
                framepos_t start;
                framepos_t end;
 
                if (get_edit_op_range (start, end)) {
+                       begin_reversible_command (_("Crop Regions to Edit Range"));
+
                        crop_region_to (start, end);
+
+                       commit_reversible_command();
                }
        }
 
@@ -3326,7 +3444,6 @@ Editor::crop_region_to (framepos_t start, framepos_t end)
        framepos_t new_start;
        framepos_t new_end;
        framecnt_t new_length;
-       bool in_command = false;
 
        for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
 
@@ -3356,19 +3473,11 @@ Editor::crop_region_to (framepos_t start, framepos_t end)
                        new_end = min (end, new_end);
                        new_length = new_end - new_start + 1;
 
-                       if(!in_command) {
-                               begin_reversible_command (_("trim to selection"));
-                               in_command = true;
-                       }
                        (*i)->clear_changes ();
                        (*i)->trim_to (new_start, new_length);
                        _session->add_command (new StatefulDiffCommand (*i));
                }
        }
-
-       if (in_command) {
-               commit_reversible_command ();
-       }
 }
 
 void
@@ -3520,9 +3629,9 @@ Editor::align_regions (RegionPoint what)
 }
 
 struct RegionSortByTime {
-    bool operator() (const RegionView* a, const RegionView* b) {
-           return a->region()->position() < b->region()->position();
-    }
+       bool operator() (const RegionView* a, const RegionView* b) {
+               return a->region()->position() < b->region()->position();
+       }
 };
 
 void
@@ -3676,10 +3785,8 @@ Editor::trim_region (bool front)
 
                        if (front) {
                                (*i)->region()->trim_front (where);
-                               maybe_locate_with_edit_preroll ( where );
                        } else {
                                (*i)->region()->trim_end (where);
-                               maybe_locate_with_edit_preroll ( where );
                        }
 
                        _session->add_command (new StatefulDiffCommand ((*i)->region()));
@@ -3806,26 +3913,26 @@ Editor::trim_to_region(bool forward)
 
                if (forward) {
 
-                   next_region = playlist->find_next_region (region->first_frame(), Start, 1);
+                       next_region = playlist->find_next_region (region->first_frame(), Start, 1);
 
-                   if (!next_region) {
-                       continue;
-                   }
+                       if (!next_region) {
+                               continue;
+                       }
 
-                   region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
-                   arv->region_changed (PropertyChange (ARDOUR::Properties::length));
+                       region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
+                       arv->region_changed (PropertyChange (ARDOUR::Properties::length));
                }
                else {
 
-                   next_region = playlist->find_next_region (region->first_frame(), Start, 0);
+                       next_region = playlist->find_next_region (region->first_frame(), Start, 0);
 
-                   if(!next_region){
-                       continue;
-                   }
+                       if(!next_region){
+                               continue;
+                       }
 
-                   region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
+                       region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
 
-                   arv->region_changed (ARDOUR::bounds_change);
+                       arv->region_changed (ARDOUR::bounds_change);
                }
 
                if (!in_command) {
@@ -3898,8 +4005,8 @@ Editor::freeze_route ()
 
        if (clicked_routeview->track()->has_external_redirects()) {
                MessageDialog d (string_compose (_("<b>%1</b>\n\nThis track has at least one send/insert/return as part of its signal flow.\n\n"
-                                                  "Freezing will only process the signal as far as the first send/insert/return."),
-                                                clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
+                                                  "Freezing will only process the signal as far as the first send/insert/return."),
+                                                clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
 
                d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
                d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
@@ -4096,8 +4203,9 @@ Editor::cut_copy (CutCopyOp op)
                }
        }
 
-       if ( op != Delete )  //"Delete" doesn't change copy/paste buf
+       if ( op != Delete ) { //"Delete" doesn't change copy/paste buf
                cut_buffer->clear ();
+       }
 
        if (entered_marker) {
 
@@ -4169,7 +4277,7 @@ Editor::cut_copy (CutCopyOp op)
        if (did_edit) {
                /* reset repeated paste state */
                paste_count    = 0;
-               last_paste_pos = 0;
+               last_paste_pos = -1;
                commit_reversible_command ();
        }
 
@@ -4178,6 +4286,7 @@ Editor::cut_copy (CutCopyOp op)
        }
 }
 
+
 struct AutomationRecord {
        AutomationRecord () : state (0) , line(NULL) {}
        AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
@@ -4187,6 +4296,12 @@ struct AutomationRecord {
        boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
 };
 
+struct PointsSelectionPositionSorter {
+       bool operator() (ControlPoint* a, ControlPoint* b) {
+               return (*(a->model()))->when < (*(b->model()))->when;
+       }
+};
+
 /** Cut, copy or clear selected automation points.
  *  @param op Operation (Cut, Copy or Clear)
  */
@@ -4204,9 +4319,12 @@ Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool mid
        typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
        Lists lists;
 
+       /* user could select points in any order */
+       selection->points.sort(PointsSelectionPositionSorter ());
+
        /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
-       for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
-               const AutomationLine&                   line = (*i)->line();
+       for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
+               const AutomationLine&                   line = (*sel_point)->line();
                const boost::shared_ptr<AutomationList> al   = line.the_list();
                if (lists.find (al) == lists.end ()) {
                        /* We haven't seen this list yet, so make a record for it.  This includes
@@ -4225,18 +4343,18 @@ Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool mid
                }
 
                /* Add all selected points to the relevant copy ControlLists */
-               framepos_t start = std::numeric_limits<framepos_t>::max();
-               for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
-                       boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
-                       AutomationList::const_iterator    j  = (*i)->model();
+               MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
+               for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
+                       boost::shared_ptr<AutomationList>    al = (*sel_point)->line().the_list();
+                       AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
 
-                       lists[al].copy->fast_simple_add ((*j)->when, (*j)->value);
+                       lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
                        if (midi) {
                                /* Update earliest MIDI start time in beats */
-                               earliest = std::min(earliest, Evoral::Beats((*j)->when));
+                               earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
                        } else {
                                /* Update earliest session start time in frames */
-                               start = std::min(start, (*i)->line().session_position(j));
+                               start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
                        }
                }
 
@@ -4247,24 +4365,25 @@ Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool mid
                        }
                        earliest.round_down_to_beat();
                } else {
-                       if (start == std::numeric_limits<double>::max()) {
-                               start = 0;  // Weird... don't offset
+                       if (start.frame == std::numeric_limits<double>::max()) {
+                               start.frame = 0;  // Weird... don't offset
                        }
                        snap_to(start, RoundDownMaybe);
                }
 
-               const double line_offset = midi ? earliest.to_double() : start;
+               const double line_offset = midi ? earliest.to_double() : start.frame;
                for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
                        /* Correct this copy list so that it is relative to the earliest
                           start time, so relative ordering between points is preserved
                           when copying from several lists and the paste starts at the
                           earliest copied piece of data. */
-                       for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
-                               (*j)->when -= line_offset;
+                       boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
+                       for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
+                               (*ctrl_evt)->when -= line_offset;
                        }
 
                        /* And add it to the cut buffer */
-                       cut_buffer->add (i->second.copy);
+                       cut_buffer->add (al_cpy);
                }
        }
 
@@ -4276,9 +4395,22 @@ Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool mid
                }
 
                /* Remove each selected point from its AutomationList */
-               for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
-                       boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
-                       al->erase ((*i)->model ());
+               for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
+                       AutomationLine& line = (*sel_point)->line ();
+                       boost::shared_ptr<AutomationList> al = line.the_list();
+
+                       bool erase = true;
+
+                       if (dynamic_cast<AudioRegionGainLine*> (&line)) {
+                               /* removing of first and last gain point in region gain lines is prohibited*/
+                               if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
+                                       erase = false;
+                               }
+                       }
+
+                       if(erase) {
+                               al->erase ((*sel_point)->model ());
+                       }
                }
 
                /* Thaw the lists and add undo records for them */
@@ -4319,16 +4451,16 @@ Editor::cut_copy_midi (CutCopyOp op)
 }
 
 struct lt_playlist {
-    bool operator () (const PlaylistState& a, const PlaylistState& b) {
-           return a.playlist < b.playlist;
-    }
+       bool operator () (const PlaylistState& a, const PlaylistState& b) {
+               return a.playlist < b.playlist;
+       }
 };
 
 struct PlaylistMapping {
-    TimeAxisView* tv;
-    boost::shared_ptr<Playlist> pl;
+       TimeAxisView* tv;
+       boost::shared_ptr<Playlist> pl;
 
-    PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
+       PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
 };
 
 /** Remove `clicked_regionview' */
@@ -4392,10 +4524,10 @@ Editor::remove_selected_regions ()
 
                boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
 
-               if (!playlist) {
+               if (!playlist) {
                        // is this check necessary?
                        continue;
-               }
+               }
 
                /* get_regions_from_selection_and_entered() guarantees that
                   the playlists involved are unique, so there is no need
@@ -4641,29 +4773,28 @@ Editor::cut_copy_ranges (CutCopyOp op)
 void
 Editor::paste (float times, bool from_context)
 {
-        DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
-
-       paste_internal (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), times, get_grid_music_divisions (0));
+       DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
+       MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
+       paste_internal (where.frame, times, 0);
 }
 
 void
 Editor::mouse_paste ()
 {
-       framepos_t where;
+       MusicFrame where (0, 0);
        bool ignored;
-
-       if (!mouse_frame (where, ignored)) {
+       if (!mouse_frame (where.frame, ignored)) {
                return;
        }
 
        snap_to (where);
-       paste_internal (where, 1, get_grid_music_divisions (0));
+       paste_internal (where.frame, 1, where.division);
 }
 
 void
 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
 {
-        DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
+       DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
 
        if (cut_buffer->empty(internal_editing())) {
                return;
@@ -4671,7 +4802,7 @@ Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
 
        if (position == max_framepos) {
                position = get_preferred_edit_position();
-                DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
+               DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
        }
 
        if (position == last_paste_pos) {
@@ -4688,8 +4819,8 @@ Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
        TrackViewList ts;
        if (!selection->tracks.empty()) {
                /* If there is a track selection, paste into exactly those tracks and
-                  only those tracks.  This allows the user to be explicit and override
-                  the below "do the reasonable thing" logic. */
+                * only those tracks.  This allows the user to be explicit and override
+                * the below "do the reasonable thing" logic. */
                ts = selection->tracks.filter_to_unique_playlists ();
                sort_track_selection (ts);
        } else {
@@ -4753,8 +4884,8 @@ Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
            /* Only one line copied, and one automation track selected.  Do a
               "greedy" paste from one automation type to another. */
 
-           PasteContext ctx(paste_count, times, ItemCounts(), true);
-           ts.front()->paste (position, *cut_buffer, ctx, sub_num);
+               PasteContext ctx(paste_count, times, ItemCounts(), true);
+               ts.front()->paste (position, *cut_buffer, ctx, sub_num);
 
        } else {
 
@@ -4769,6 +4900,13 @@ Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
        commit_reversible_command ();
 }
 
+void
+Editor::duplicate_regions (float times)
+{
+       RegionSelection rs (get_regions_from_selection_and_entered());
+       duplicate_some_regions (rs, times);
+}
+
 void
 Editor::duplicate_some_regions (RegionSelection& regions, float times)
 {
@@ -4849,18 +4987,19 @@ Editor::duplicate_selection (float times)
        }
 
        if (in_command) {
-               // now "move" range selection to after the current range selection
-               framecnt_t distance = 0;
-
-               if (clicked_selection) {
-                       distance = selection->time[clicked_selection].end -
-                                  selection->time[clicked_selection].start;
-               } else {
-                       distance = selection->time.end_frame() - selection->time.start();
-               }
+               if (times == 1.0f) {
+                       // now "move" range selection to after the current range selection
+                       framecnt_t distance = 0;
 
-               selection->move_time (distance);
+                       if (clicked_selection) {
+                               distance =
+                                   selection->time[clicked_selection].end - selection->time[clicked_selection].start;
+                       } else {
+                               distance = selection->time.end_frame () - selection->time.start ();
+                       }
 
+                       selection->move_time (distance);
+               }
                commit_reversible_command ();
        }
 }
@@ -4871,7 +5010,7 @@ Editor::reset_point_selection ()
 {
        for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
                ARDOUR::AutomationList::iterator j = (*i)->model ();
-               (*j)->value = (*i)->line().the_list()->default_value ();
+               (*j)->value = (*i)->line().the_list()->descriptor ().normal;
        }
 }
 
@@ -4963,12 +5102,12 @@ Editor::remove_last_capture ()
 
        if (Config->get_verify_remove_last_capture()) {
                prompt  = _("Do you really want to destroy the last capture?"
-                           "\n(This is destructive and cannot be undone)");
+                           "\n(This is destructive and cannot be undone)");
 
                choices.push_back (_("No, do nothing."));
                choices.push_back (_("Yes, destroy it."));
 
-               Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
+               Choice prompter (_("Destroy last capture"), prompt, choices);
 
                if (prompter.run () == 1) {
                        _session->remove_last_capture ();
@@ -4996,7 +5135,7 @@ Editor::normalize_region ()
 
        NormalizeDialog dialog (rs.size() > 1);
 
-       if (dialog.run () == RESPONSE_CANCEL) {
+       if (dialog.run () != RESPONSE_ACCEPT) {
                return;
        }
 
@@ -5010,25 +5149,36 @@ Editor::normalize_region ()
           obtain the maximum amplitude of them all.
        */
        list<double> max_amps;
+       list<double> rms_vals;
        double max_amp = 0;
+       double max_rms = 0;
+       bool use_rms = dialog.constrain_rms ();
+
        for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
                AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
-               if (arv) {
-                       dialog.descend (1.0 / regions);
-                       double const a = arv->audio_region()->maximum_amplitude (&dialog);
-
-                       if (a == -1) {
-                               /* the user cancelled the operation */
-                               return;
-                       }
+               if (!arv) {
+                       continue;
+               }
+               dialog.descend (1.0 / regions);
+               double const a = arv->audio_region()->maximum_amplitude (&dialog);
+               if (use_rms) {
+                       double r = arv->audio_region()->rms (&dialog);
+                       max_rms = max (max_rms, r);
+                       rms_vals.push_back (r);
+               }
 
-                       max_amps.push_back (a);
-                       max_amp = max (max_amp, a);
-                       dialog.ascend ();
+               if (a == -1) {
+                       /* the user cancelled the operation */
+                       return;
                }
+
+               max_amps.push_back (a);
+               max_amp = max (max_amp, a);
+               dialog.ascend ();
        }
 
        list<double>::const_iterator a = max_amps.begin ();
+       list<double>::const_iterator l = rms_vals.begin ();
        bool in_command = false;
 
        for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
@@ -5039,9 +5189,21 @@ Editor::normalize_region ()
 
                arv->region()->clear_changes ();
 
-               double const amp = dialog.normalize_individually() ? *a : max_amp;
+               double amp = dialog.normalize_individually() ? *a : max_amp;
+               double target = dialog.target_peak (); // dB
+
+               if (use_rms) {
+                       double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
+                       const double t_rms = dialog.target_rms ();
+                       const gain_t c_peak = dB_to_coefficient (target);
+                       const gain_t c_rms  = dB_to_coefficient (t_rms);
+                       if ((amp_rms / c_rms) > (amp / c_peak)) {
+                               amp = amp_rms;
+                               target = t_rms;
+                       }
+               }
 
-               arv->audio_region()->normalize (amp, dialog.target ());
+               arv->audio_region()->normalize (amp, target);
 
                if (!in_command) {
                        begin_reversible_command (_("normalize"));
@@ -5050,6 +5212,7 @@ Editor::normalize_region ()
                _session->add_command (new StatefulDiffCommand (arv->region()));
 
                ++a;
+               ++l;
        }
 
        if (in_command) {
@@ -5133,6 +5296,38 @@ Editor::adjust_region_gain (bool up)
        }
 }
 
+void
+Editor::reset_region_gain ()
+{
+       RegionSelection rs = get_regions_from_selection_and_entered ();
+
+       if (!_session || rs.empty()) {
+               return;
+       }
+
+       bool in_command = false;
+
+       for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
+               AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
+               if (!arv) {
+                       continue;
+               }
+
+               arv->region()->clear_changes ();
+
+               arv->audio_region()->set_scale_amplitude (1.0f);
+
+               if (!in_command) {
+                               begin_reversible_command ("reset region gain");
+                               in_command = true;
+               }
+               _session->add_command (new StatefulDiffCommand (arv->region()));
+       }
+
+       if (in_command) {
+               commit_reversible_command ();
+       }
+}
 
 void
 Editor::reverse_region ()
@@ -5162,7 +5357,7 @@ Editor::strip_region_silence ()
 
        for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
                AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
-               if (arv) {
+               if (arv) {
                        audio_only.push_back (arv);
                }
        }
@@ -5192,8 +5387,7 @@ Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv
        vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
        v.push_back (selected);
 
-       framepos_t    pos_frames = mrv.midi_region()->position() - mrv.midi_region()->start();
-       Evoral::Beats pos_beats  = _session->tempo_map().framewalk_to_beats(0, pos_frames);
+       Evoral::Beats pos_beats  = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
 
        return op (mrv.midi_region()->model(), pos_beats, v);
 }
@@ -5230,6 +5424,7 @@ Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
 
        if (in_command) {
                commit_reversible_command ();
+               _session->set_dirty ();
        }
 }
 
@@ -5298,6 +5493,11 @@ Editor::quantize_regions (const RegionSelection& rs)
                quantize_dialog = new QuantizeDialog (*this);
        }
 
+       if (quantize_dialog->is_mapped()) {
+               /* in progress already */
+               return;
+       }
+
        quantize_dialog->present ();
        const int r = quantize_dialog->run ();
        quantize_dialog->hide ();
@@ -5403,7 +5603,7 @@ Editor::insert_patch_change (bool from_context)
        MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
 
        Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
-        PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
+       PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
 
        if (d.run() == RESPONSE_CANCEL) {
                return;
@@ -5660,11 +5860,17 @@ Editor::toggle_region_lock_style ()
                return;
        }
 
-       begin_reversible_command (_("region lock style"));
+       Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
+       vector<Widget*> proxies = a->get_proxies();
+       Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
+
+       assert (cmi);
+
+       begin_reversible_command (_("toggle region lock style"));
 
        for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
                (*i)->region()->clear_changes ();
-               PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
+               PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
                (*i)->region()->set_position_lock_style (ns);
                _session->add_command (new StatefulDiffCommand ((*i)->region()));
        }
@@ -5725,18 +5931,18 @@ Editor::toggle_solo ()
        boost::shared_ptr<ControlList> cl (new ControlList);
 
        for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
-               RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
+               StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
 
-               if (!rtav) {
+               if (!stav || !stav->stripable()->solo_control()) {
                        continue;
                }
 
                if (first) {
-                       new_state = !rtav->route()->soloed ();
+                       new_state = !stav->stripable()->solo_control()->soloed ();
                        first = false;
                }
 
-               cl->push_back (rtav->route()->solo_control());
+               cl->push_back (stav->stripable()->solo_control());
        }
 
        _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
@@ -5747,24 +5953,24 @@ Editor::toggle_mute ()
 {
        bool new_state = false;
        bool first = true;
-       boost::shared_ptr<RouteList> rl (new RouteList);
+       boost::shared_ptr<ControlList> cl (new ControlList);
 
        for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
-               RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
+               StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
 
-               if (!rtav) {
+               if (!stav || !stav->stripable()->mute_control()) {
                        continue;
                }
 
                if (first) {
-                       new_state = !rtav->route()->muted();
+                       new_state = !stav->stripable()->mute_control()->muted();
                        first = false;
                }
 
-               rl->push_back (rtav->route());
+               cl->push_back (stav->stripable()->mute_control());
        }
 
-       _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
+       _session->set_controls (cl, new_state, Controllable::UseGroup);
 }
 
 void
@@ -6086,10 +6292,10 @@ Editor::update_region_fade_visibility ()
 void
 Editor::set_edit_point ()
 {
-       framepos_t where;
        bool ignored;
+       MusicFrame where (0, 0);
 
-       if (!mouse_frame (where, ignored)) {
+       if (!mouse_frame (where.frame, ignored)) {
                return;
        }
 
@@ -6097,7 +6303,7 @@ Editor::set_edit_point ()
 
        if (selection->markers.empty()) {
 
-               mouse_add_new_marker (where);
+               mouse_add_new_marker (where.frame);
 
        } else {
                bool ignored;
@@ -6105,7 +6311,7 @@ Editor::set_edit_point ()
                Location* loc = find_location_from_marker (selection->markers.front(), ignored);
 
                if (loc) {
-                       loc->move_to (where);
+                       loc->move_to (where.frame, where.division);
                }
        }
 }
@@ -6116,23 +6322,25 @@ Editor::set_playhead_cursor ()
        if (entered_marker) {
                _session->request_locate (entered_marker->position(), _session->transport_rolling());
        } else {
-               framepos_t where;
+               MusicFrame where (0, 0);
                bool ignored;
 
-               if (!mouse_frame (where, ignored)) {
+               if (!mouse_frame (where.frame, ignored)) {
                        return;
                }
 
                snap_to (where);
 
                if (_session) {
-                       _session->request_locate (where, _session->transport_rolling());
+                       _session->request_locate (where.frame, _session->transport_rolling());
                }
        }
 
-       if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
-               cancel_time_selection();
-       }
+//not sure what this was for;  remove it for now.
+//     if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
+//             cancel_time_selection();
+//     }
+
 }
 
 void
@@ -6152,23 +6360,21 @@ Editor::split_region ()
        if (current_mouse_mode() == MouseObject) {  //don't try this for Internal Edit, Stretch, Draw, etc.
 
                RegionSelection rs = get_regions_from_selection_and_edit_point ();
-
-               framepos_t where = get_preferred_edit_position ();
+               const framepos_t pos = get_preferred_edit_position();
+               const int32_t division = get_grid_music_divisions (0);
+               MusicFrame where (pos, division);
 
                if (rs.empty()) {
                        return;
                }
 
-               if (snap_musical()) {
-                       split_regions_at (where, rs, get_grid_music_divisions (0));
-               } else {
-                       split_regions_at (where, rs, 0);
-               }
+               split_regions_at (where, rs);
+
        }
 }
 
 void
-Editor::select_next_route()
+Editor::select_next_stripable (bool routes_only)
 {
        if (selection->tracks.empty()) {
                selection->set (track_views.front());
@@ -6177,7 +6383,7 @@ Editor::select_next_route()
 
        TimeAxisView* current = selection->tracks.front();
 
-       RouteUI *rui;
+       bool valid;
        do {
                for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
 
@@ -6193,9 +6399,14 @@ Editor::select_next_route()
                        }
                }
 
-               rui = dynamic_cast<RouteUI *>(current);
+               if (routes_only) {
+                       RouteUI* rui = dynamic_cast<RouteUI *>(current);
+                       valid = rui && rui->route()->active();
+               } else {
+                       valid = 0 != current->stripable ().get();
+               }
 
-       } while (current->hidden() || (rui == NULL) || !rui->route()->active());
+       } while (current->hidden() || !valid);
 
        selection->set (current);
 
@@ -6203,7 +6414,7 @@ Editor::select_next_route()
 }
 
 void
-Editor::select_prev_route()
+Editor::select_prev_stripable (bool routes_only)
 {
        if (selection->tracks.empty()) {
                selection->set (track_views.front());
@@ -6212,7 +6423,7 @@ Editor::select_prev_route()
 
        TimeAxisView* current = selection->tracks.front();
 
-       RouteUI *rui;
+       bool valid;
        do {
                for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
 
@@ -6226,9 +6437,14 @@ Editor::select_prev_route()
                                break;
                        }
                }
-               rui = dynamic_cast<RouteUI *>(current);
+               if (routes_only) {
+                       RouteUI* rui = dynamic_cast<RouteUI *>(current);
+                       valid = rui && rui->route()->active();
+               } else {
+                       valid = 0 != current->stripable ().get();
+               }
 
-       } while (current->hidden() || (rui == NULL) || !rui->route()->active());
+       } while (current->hidden() || !valid);
 
        selection->set (current);
 
@@ -6282,6 +6498,59 @@ Editor::set_punch_from_selection ()
        set_punch_range (start, end,  _("set punch range from selection"));
 }
 
+void
+Editor::set_auto_punch_range ()
+{
+       // auto punch in/out button from a single button
+       // If Punch In is unset, set punch range from playhead to end, enable punch in
+       // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
+       //   rewound beyond the Punch In marker, in which case that marker will be moved back
+       //   to the current playhead position.
+       // If punch out is set, it clears the punch range and Punch In/Out buttons
+
+       if (_session == 0) {
+               return;
+       }
+
+       Location* tpl = transport_punch_location();
+       framepos_t now = playhead_cursor->current_frame();
+       framepos_t begin = now;
+       framepos_t end = _session->current_end_frame();
+
+       if (!_session->config.get_punch_in()) {
+               // First Press - set punch in and create range from here to eternity
+               set_punch_range (begin, end, _("Auto Punch In"));
+               _session->config.set_punch_in(true);
+       } else if (tpl && !_session->config.get_punch_out()) {
+               // Second press - update end range marker and set punch_out
+               if (now < tpl->start()) {
+                       // playhead has been rewound - move start back  and pretend nothing happened
+                       begin = now;
+                       set_punch_range (begin, end, _("Auto Punch In/Out"));
+               } else {
+                       // normal case for 2nd press - set the punch out
+                       end = playhead_cursor->current_frame ();
+                       set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
+                       _session->config.set_punch_out(true);
+               }
+       } else  {
+               if (_session->config.get_punch_out()) {
+                       _session->config.set_punch_out(false);
+               }
+
+               if (_session->config.get_punch_in()) {
+                       _session->config.set_punch_in(false);
+               }
+
+               if (tpl)
+               {
+                       // third press - unset punch in/out and remove range
+                       _session->locations()->remove(tpl);
+               }
+       }
+
+}
+
 void
 Editor::set_session_extents_from_selection ()
 {
@@ -6318,7 +6587,7 @@ Editor::set_punch_start_from_edit_point ()
 {
        if (_session) {
 
-               framepos_t start = 0;
+               MusicFrame start (0, 0);
                framepos_t end = max_framepos;
 
                //use the existing punch end, if any
@@ -6328,20 +6597,20 @@ Editor::set_punch_start_from_edit_point ()
                }
 
                if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
-                       start = _session->audible_frame();
+                       start.frame = _session->audible_frame();
                } else {
-                       start = get_preferred_edit_position();
+                       start.frame = get_preferred_edit_position();
                }
 
                //snap the selection start/end
                snap_to(start);
 
                //if there's not already a sensible selection endpoint, go "forever"
-               if ( start > end ) {
+               if (start.frame > end ) {
                        end = max_framepos;
                }
 
-               set_punch_range (start, end, _("set punch start from EP"));
+               set_punch_range (start.frame, end, _("set punch start from EP"));
        }
 
 }
@@ -6352,7 +6621,7 @@ Editor::set_punch_end_from_edit_point ()
        if (_session) {
 
                framepos_t start = 0;
-               framepos_t end = max_framepos;
+               MusicFrame end (max_framepos, 0);
 
                //use the existing punch start, if any
                Location* tpl = transport_punch_location();
@@ -6361,15 +6630,15 @@ Editor::set_punch_end_from_edit_point ()
                }
 
                if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
-                       end = _session->audible_frame();
+                       end.frame = _session->audible_frame();
                } else {
-                       end = get_preferred_edit_position();
+                       end.frame = get_preferred_edit_position();
                }
 
                //snap the selection start/end
-               snap_to(end);
+               snap_to (end);
 
-               set_punch_range (start, end, _("set punch end from EP"));
+               set_punch_range (start, end.frame, _("set punch end from EP"));
 
        }
 }
@@ -6379,7 +6648,7 @@ Editor::set_loop_start_from_edit_point ()
 {
        if (_session) {
 
-               framepos_t start = 0;
+               MusicFrame start (0, 0);
                framepos_t end = max_framepos;
 
                //use the existing loop end, if any
@@ -6389,20 +6658,20 @@ Editor::set_loop_start_from_edit_point ()
                }
 
                if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
-                       start = _session->audible_frame();
+                       start.frame = _session->audible_frame();
                } else {
-                       start = get_preferred_edit_position();
+                       start.frame = get_preferred_edit_position();
                }
 
                //snap the selection start/end
-               snap_to(start);
+               snap_to (start);
 
                //if there's not already a sensible selection endpoint, go "forever"
-               if ( start > end ) {
+               if (start.frame > end ) {
                        end = max_framepos;
                }
 
-               set_loop_range (start, end, _("set loop start from EP"));
+               set_loop_range (start.frame, end, _("set loop start from EP"));
        }
 
 }
@@ -6413,7 +6682,7 @@ Editor::set_loop_end_from_edit_point ()
        if (_session) {
 
                framepos_t start = 0;
-               framepos_t end = max_framepos;
+               MusicFrame end (max_framepos, 0);
 
                //use the existing loop start, if any
                Location* tpl = transport_loop_location();
@@ -6422,15 +6691,15 @@ Editor::set_loop_end_from_edit_point ()
                }
 
                if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
-                       end = _session->audible_frame();
+                       end.frame = _session->audible_frame();
                } else {
-                       end = get_preferred_edit_position();
+                       end.frame = get_preferred_edit_position();
                }
 
                //snap the selection start/end
                snap_to(end);
 
-               set_loop_range (start, end, _("set loop end from EP"));
+               set_loop_range (start, end.frame, _("set loop end from EP"));
        }
 }
 
@@ -6563,12 +6832,13 @@ Editor::define_one_bar (framepos_t start, framepos_t end)
        XMLNode& before (_session->tempo_map().get_state());
 
        if (do_global) {
-               _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
+               _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
        } else if (t.frame() == start) {
-               _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
+               _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
        } else {
+               /* constant tempo */
                const Tempo tempo (beats_per_minute, t.note_type());
-               _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
+               _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
        }
 
        XMLNode& after (_session->tempo_map().get_state());
@@ -6844,10 +7114,12 @@ Editor::snap_regions_to_grid ()
                        used_playlists.push_back(pl);
                        pl->freeze();
                }
+               (*r)->region()->clear_changes ();
 
-               framepos_t start_frame = (*r)->region()->first_frame ();
-               snap_to (start_frame);
-               (*r)->region()->set_position (start_frame);
+               MusicFrame start ((*r)->region()->first_frame (), 0);
+               snap_to (start);
+               (*r)->region()->set_position (start.frame, start.division);
+               _session->add_command(new StatefulDiffCommand ((*r)->region()));
        }
 
        while (used_playlists.size() > 0) {
@@ -6941,11 +7213,16 @@ Editor::close_region_gaps ()
                        continue;
                }
 
+               (*r)->region()->clear_changes ();
                (*r)->region()->trim_front( (position - pull_back_frames));
+
+               last_region->clear_changes ();
                last_region->trim_end( (position - pull_back_frames + crossfade_len));
 
-               last_region = (*r)->region();
+               _session->add_command (new StatefulDiffCommand ((*r)->region()));
+               _session->add_command (new StatefulDiffCommand (last_region));
 
+               last_region = (*r)->region();
                idx++;
        }
 
@@ -7045,11 +7322,12 @@ Editor::playhead_forward_to_grid ()
                return;
        }
 
-       framepos_t pos = playhead_cursor->current_frame ();
-       if (pos < max_framepos - 1) {
-               pos += 2;
-               snap_to_internal (pos, RoundUpAlways, false);
-               _session->request_locate (pos);
+       MusicFrame pos (playhead_cursor->current_frame (), 0);
+
+       if (pos.frame < max_framepos - 1) {
+               pos.frame += 2;
+               snap_to_internal (pos, RoundUpAlways, false, true);
+               _session->request_locate (pos.frame);
        }
 }
 
@@ -7061,11 +7339,12 @@ Editor::playhead_backward_to_grid ()
                return;
        }
 
-       framepos_t pos = playhead_cursor->current_frame ();
-       if (pos > 2) {
-               pos -= 2;
-               snap_to_internal (pos, RoundDownAlways, false);
-               _session->request_locate (pos);
+       MusicFrame pos  (playhead_cursor->current_frame (), 0);
+
+       if (pos.frame > 2) {
+               pos.frame -= 2;
+               snap_to_internal (pos, RoundDownAlways, false, true);
+               _session->request_locate (pos.frame);
        }
 }
 
@@ -7134,20 +7413,29 @@ Editor::_remove_tracks ()
        string prompt;
        int ntracks = 0;
        int nbusses = 0;
+       int nvcas = 0;
        const char* trackstr;
        const char* busstr;
+       const char* vcastr;
        vector<boost::shared_ptr<Route> > routes;
+       vector<boost::shared_ptr<VCA> > vcas;
        bool special_bus = false;
 
        for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
+               VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
+               if (vtv) {
+                       vcas.push_back (vtv->vca());
+                       ++nvcas;
+                       continue;
+               }
                RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
                if (!rtv) {
                        continue;
                }
                if (rtv->is_track()) {
-                       ntracks++;
+                       ++ntracks;
                } else {
-                       nbusses++;
+                       ++nbusses;
                }
                routes.push_back (rtv->_route);
 
@@ -7174,58 +7462,106 @@ edit your ardour.rc file to set the\n\
                return;
        }
 
-       if (ntracks + nbusses == 0) {
+       if (ntracks + nbusses + nvcas == 0) {
                return;
        }
 
+       string title;
+
        trackstr = P_("track", "tracks", ntracks);
        busstr = P_("bus", "busses", nbusses);
+       vcastr = P_("VCA", "VCAs", nvcas);
 
-       if (ntracks) {
-               if (nbusses) {
-                       prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
-                                                   "(You may also lose the playlists associated with the %2)\n\n"
-                                                   "This action cannot be undone, and the session file will be overwritten!"),
-                                                 ntracks, trackstr, nbusses, busstr);
-               } else {
-                       prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
-                                                   "(You may also lose the playlists associated with the %2)\n\n"
-                                                   "This action cannot be undone, and the session file will be overwritten!"),
-                                                 ntracks, trackstr);
-               }
-       } else if (nbusses) {
-               prompt  = string_compose (_("Do you really want to remove %1 %2?\n\n"
-                                           "This action cannot be undone, and the session file will be overwritten"),
-                                         nbusses, busstr);
+       if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
+               title = _("Remove various strips");
+               prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
+                                                 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
+       }
+       else if (ntracks > 0 && nbusses > 0) {
+               title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
+               prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
+                               ntracks, trackstr, nbusses, busstr);
+       }
+       else if (ntracks > 0 && nvcas > 0) {
+               title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
+               prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
+                               ntracks, trackstr, nvcas, vcastr);
+       }
+       else if (nbusses > 0 && nvcas > 0) {
+               title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
+               prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
+                               nbusses, busstr, nvcas, vcastr);
+       }
+       else if (ntracks > 0) {
+               title = string_compose (_("Remove %1"), trackstr);
+               prompt  = string_compose (_("Do you really want to remove %1 %2?"),
+                               ntracks, trackstr);
+       }
+       else if (nbusses > 0) {
+               title = string_compose (_("Remove %1"), busstr);
+               prompt  = string_compose (_("Do you really want to remove %1 %2?"),
+                               nbusses, busstr);
+       }
+       else if (nvcas > 0) {
+               title = string_compose (_("Remove %1"), vcastr);
+               prompt  = string_compose (_("Do you really want to remove %1 %2?"),
+                               nvcas, vcastr);
+       }
+       else {
+               assert (0);
+       }
+
+       if (ntracks > 0) {
+                       prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
        }
 
+       prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
+
        choices.push_back (_("No, do nothing."));
-       if (ntracks + nbusses > 1) {
+       if (ntracks + nbusses + nvcas > 1) {
                choices.push_back (_("Yes, remove them."));
        } else {
                choices.push_back (_("Yes, remove it."));
        }
 
-       string title;
-       if (ntracks) {
-               title = string_compose (_("Remove %1"), trackstr);
-       } else {
-               title = string_compose (_("Remove %1"), busstr);
-       }
-
        Choice prompter (title, prompt, choices);
 
        if (prompter.run () != 1) {
                return;
        }
 
+       if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
+               /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
+                * route). If the deleted route is currently displayed in the Editor-Mixer (highly
+                * likely because deletion requires selection) this will call
+                * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
+                * It's likewise likely that the route that has just been displayed in the
+                * Editor-Mixer will be next in line for deletion.
+                *
+                * So simply switch to the master-bus (if present)
+                */
+               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+                       if ((*i)->stripable ()->is_master ()) {
+                               set_selected_mixer_strip (*(*i));
+                               break;
+                       }
+               }
+       }
+
        {
+               PresentationInfo::ChangeSuspender cs;
                DisplaySuspender ds;
+
                boost::shared_ptr<RouteList> rl (new RouteList);
                for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
                        rl->push_back (*x);
                }
                _session->remove_routes (rl);
+
+               for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
+                       _session->vca_manager().remove_vca (*x);
+               }
+
        }
        /* TrackSelection and RouteList leave scope,
         * destructors are called,
@@ -7252,7 +7588,7 @@ Editor::do_insert_time ()
        }
 
        insert_time (
-               get_preferred_edit_position (EDIT_IGNORE_MOUSE),
+               d.position(),
                d.distance(),
                d.intersected_region_action (),
                d.all_playlists(),
@@ -7308,17 +7644,18 @@ Editor::insert_time (
                        (*i)->clear_changes ();
                        (*i)->clear_owned_changes ();
 
+                       if (!in_command) {
+                               begin_reversible_command (_("insert time"));
+                               in_command = true;
+                       }
+
                        if (opt == SplitIntersected) {
                                /* non musical split */
-                               (*i)->split (pos, 0);
+                               (*i)->split (MusicFrame (pos, 0));
                        }
 
                        (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
 
-                       if (!in_command) {
-                               begin_reversible_command (_("insert time"));
-                               in_command = true;
-                       }
                        vector<Command*> cmds;
                        (*i)->rdiff (cmds);
                        _session->add_commands (cmds);
@@ -7340,6 +7677,7 @@ Editor::insert_time (
        /* markers */
        if (markers_too) {
                bool moved = false;
+               const int32_t divisions = get_grid_music_divisions (0);
                XMLNode& before (_session->locations()->get_state());
                Locations::LocationList copy (_session->locations()->list());
 
@@ -7356,9 +7694,9 @@ Editor::insert_time (
                                if ((*i)->start() >= pos) {
                                        // move end first, in case we're moving by more than the length of the range
                                        if (!(*i)->is_mark()) {
-                                               (*i)->set_end ((*i)->end() + frames);
+                                               (*i)->set_end ((*i)->end() + frames, false, true, divisions);
                                        }
-                                       (*i)->set_start ((*i)->start() + frames);
+                                       (*i)->set_start ((*i)->start() + frames, false, true, divisions);
                                        moved = true;
                                }
 
@@ -7401,7 +7739,6 @@ Editor::do_remove_time ()
                return;
        }
 
-       framepos_t pos = get_preferred_edit_position (EDIT_IGNORE_MOUSE);
        InsertRemoveTimeDialog d (*this, true);
 
        int response = d.run ();
@@ -7417,7 +7754,7 @@ Editor::do_remove_time ()
        }
 
        remove_time (
-               pos,
+               d.position(),
                distance,
                SplitIntersected,
                d.move_glued(),
@@ -7430,7 +7767,7 @@ Editor::do_remove_time ()
 
 void
 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
-                    bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
+                     bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
 {
        if (Config->get_edit_mode() == Lock) {
                error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
@@ -7446,16 +7783,17 @@ Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
 
                        XMLNode &before = pl->get_state();
 
+                       if (!in_command) {
+                               begin_reversible_command (_("remove time"));
+                               in_command = true;
+                       }
+
                        std::list<AudioRange> rl;
                        AudioRange ar(pos, pos+frames, 0);
                        rl.push_back(ar);
                        pl->cut (rl);
                        pl->shift (pos, -frames, true, ignore_music_glue);
 
-                       if (!in_command) {
-                               begin_reversible_command (_("remove time"));
-                               in_command = true;
-                       }
                        XMLNode &after = pl->get_state();
 
                        _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
@@ -7472,6 +7810,7 @@ Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
                }
        }
 
+       const int32_t divisions = get_grid_music_divisions (0);
        std::list<Location*> loc_kill_list;
 
        /* markers */
@@ -7500,20 +7839,20 @@ Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
                                                // if we're removing more time than the length of the range
                                                if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
                                                        // start is within cut
-                                                       (*i)->set_start (pos);  // bring the start marker to the beginning of the cut
+                                                       (*i)->set_start (pos, false, true,divisions);  // bring the start marker to the beginning of the cut
                                                        moved = true;
                                                } else if ((*i)->start() >= pos+frames) {
                                                        // start (and thus entire range) lies beyond end of cut
-                                                       (*i)->set_start ((*i)->start() - frames); // slip the start marker back
+                                                       (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
                                                        moved = true;
                                                }
                                                if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
                                                        // end is inside cut
-                                                       (*i)->set_end (pos);  // bring the end to the cut
+                                                       (*i)->set_end (pos, false, true, divisions);  // bring the end to the cut
                                                        moved = true;
                                                } else if ((*i)->end() >= pos+frames) {
                                                        // end is beyond end of cut
-                                                       (*i)->set_end ((*i)->end() - frames); // slip the end marker back
+                                                       (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
                                                        moved = true;
                                                }
 
@@ -7522,7 +7861,7 @@ Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
                                        loc_kill_list.push_back(*i);
                                        moved = true;
                                } else if ((*i)->start() >= pos) {
-                                       (*i)->set_start ((*i)->start() -frames);
+                                       (*i)->set_start ((*i)->start() -frames, false, true, divisions);
                                        moved = true;
                                }
 
@@ -7567,32 +7906,31 @@ Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
 void
 Editor::fit_selection ()
 {
-        if (!selection->tracks.empty()) {
-                fit_tracks (selection->tracks);
-        } else {
-                TrackViewList tvl;
-
-                /* no selected tracks - use tracks with selected regions */
+       if (!selection->tracks.empty()) {
+               fit_tracks (selection->tracks);
+       } else {
+               TrackViewList tvl;
 
-                if (!selection->regions.empty()) {
-                        for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
-                                tvl.push_back (&(*r)->get_time_axis_view ());
-                        }
+               /* no selected tracks - use tracks with selected regions */
 
-                        if (!tvl.empty()) {
-                                fit_tracks (tvl);
-                        }
-                } else if (internal_editing()) {
-                        /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
-                           the entered track
-                        */
-                        if (entered_track) {
-                                tvl.push_back (entered_track);
-                                fit_tracks (tvl);
-                        }
-                }
-        }
+               if (!selection->regions.empty()) {
+                       for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
+                               tvl.push_back (&(*r)->get_time_axis_view ());
+                       }
 
+                       if (!tvl.empty()) {
+                               fit_tracks (tvl);
+                       }
+               } else if (internal_editing()) {
+                       /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
+                        * the entered track
+                        */
+                       if (entered_track) {
+                               tvl.push_back (entered_track);
+                               fit_tracks (tvl);
+                       }
+               }
+       }
 }
 
 void
@@ -7616,11 +7954,11 @@ Editor::fit_tracks (TrackViewList & tracks)
        }
 
        /* compute the per-track height from:
-
-          total canvas visible height -
-                 height that will be taken by visible children of selected
-                 tracks - height of the ruler/hscroll area
-       */
+        *
+        * total canvas visible height
+        *  - height that will be taken by visible children of selected tracks
+         - height of the ruler/hscroll area
+        */
        uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
        double first_y_pos = DBL_MAX;
 
@@ -7733,7 +8071,7 @@ Editor::start_visual_state_op (uint32_t n)
 void
 Editor::cancel_visual_state_op (uint32_t n)
 {
-        goto_visual_state (n);
+       goto_visual_state (n);
 }
 
 void
@@ -7860,6 +8198,8 @@ Editor::toggle_midi_input_active (bool flip_others)
        _session->set_exclusive_input_active (rl, onoff, flip_others);
 }
 
+static bool ok_fine (GdkEventAny*) { return true; }
+
 void
 Editor::lock ()
 {
@@ -7868,6 +8208,7 @@ Editor::lock ()
 
                Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
                lock_dialog->get_vbox()->pack_start (*padlock);
+               lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
 
                ArdourButton* b = manage (new ArdourButton);
                b->set_name ("lock button");
@@ -7883,6 +8224,8 @@ Editor::lock ()
        _main_menu_disabler = new MainMenuDisabler;
 
        lock_dialog->present ();
+
+       lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
 }
 
 void