use new method in MidiPatchManager to use MIDNAM data when setting a MidiTimeAxisView
[ardour.git] / gtk2_ardour / editor_ops.cc
index 1f678d31f463720838d3bbf77a1880ac8ffa2068..195d4d16436b6bbb41aa9e926eafbe0dcbd9abfc 100644 (file)
@@ -1,21 +1,32 @@
 /*
-    Copyright (C) 2000-2004 Paul Davis
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
+ * Copyright (C) 2005-2006 Taybin Rutkin <taybin@taybin.com>
+ * Copyright (C) 2005-2009 Sampo Savolainen <v2@iki.fi>
+ * Copyright (C) 2005-2018 Paul Davis <paul@linuxaudiosystems.com>
+ * Copyright (C) 2005 Karsten Wiese <fzuuzf@googlemail.com>
+ * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
+ * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
+ * Copyright (C) 2007-2017 Tim Mayberry <mojofunk@gmail.com>
+ * Copyright (C) 2013-2016 Colin Fletcher <colin.m.fletcher@googlemail.com>
+ * Copyright (C) 2013-2017 John Emmas <john@creativepost.co.uk>
+ * Copyright (C) 2013-2017 Nick Mainsbridge <mainsbridge@gmail.com>
+ * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
+ * Copyright (C) 2015 AndrĂ© Nusser <andre.nusser@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
 
 /* Note: public Editor methods are documented in public_editor.h */
 
 #include "ardour/legatize.h"
 #include "ardour/region_factory.h"
 #include "ardour/reverse.h"
+#include "ardour/selection.h"
 #include "ardour/session.h"
 #include "ardour/session_playlists.h"
+#include "ardour/source.h"
 #include "ardour/strip_silence.h"
 #include "ardour/transient_detector.h"
+#include "ardour/transport_master_manager.h"
 #include "ardour/transpose.h"
 #include "ardour/vca_manager.h"
 
 #include "canvas/canvas.h"
 
 #include "actions.h"
+#include "ardour_message.h"
+#include "ardour_ui.h"
 #include "audio_region_view.h"
 #include "audio_streamview.h"
 #include "audio_time_axis.h"
@@ -80,6 +96,7 @@
 #include "editor_cursors.h"
 #include "editor_drag.h"
 #include "editor_regions.h"
+#include "editor_sources.h"
 #include "editor_routes.h"
 #include "gui_thread.h"
 #include "insert_remove_time_dialog.h"
 #include "transpose_dialog.h"
 #include "transform_dialog.h"
 #include "ui_config.h"
+#include "utils.h"
 #include "vca_time_axis.h"
 
 #include "pbd/i18n.h"
@@ -137,6 +155,7 @@ Editor::undo (uint32_t n)
        if (_drags->active ()) {
                _drags->abort ();
        }
+       paste_count = 0;
 
        if (_session) {
                _session->undo (n);
@@ -161,6 +180,7 @@ Editor::redo (uint32_t n)
        if (_drags->active ()) {
                _drags->abort ();
        }
+       paste_count = 0;
 
        if (_session) {
        _session->redo (n);
@@ -173,13 +193,10 @@ Editor::redo (uint32_t n)
 }
 
 void
-Editor::split_regions_at (MusicSample where, RegionSelection& regions, bool snap_sample)
+Editor::split_regions_at (MusicSample where, RegionSelection& regions)
 {
        bool frozen = false;
 
-       RegionSelection pre_selected_regions = selection->regions;
-       bool working_on_selection = !pre_selected_regions.empty();
-
        list<boost::shared_ptr<Playlist> > used_playlists;
        list<RouteTimeAxisView*> used_trackviews;
 
@@ -189,25 +206,11 @@ Editor::split_regions_at (MusicSample where, RegionSelection& regions, bool snap
 
        begin_reversible_command (_("split"));
 
-       // if splitting a single region, and snap-to is using
-       // region boundaries, don't pay attention to them
 
        if (regions.size() == 1) {
-               switch (_snap_type) {
-               case SnapToRegionStart:
-               case SnapToRegionSync:
-               case SnapToRegionEnd:
-                       break;
-               default:
-                       if (snap_sample) {
-                               snap_to (where);
-                       }
-               }
+               /* TODO:  if splitting a single region, and snap-to is using
+                region boundaries, mabye we shouldn't pay attention to them? */
        } else {
-               if (snap_sample) {
-                       snap_to (where);
-               }
-
                frozen = true;
                EditorFreeze(); /* Emit Signal */
        }
@@ -281,22 +284,23 @@ Editor::split_regions_at (MusicSample where, RegionSelection& regions, bool snap
                EditorThaw(); /* Emit Signal */
        }
 
-       if (working_on_selection) {
-               // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
-
-               RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
-               /* There are three classes of regions that we might want selected after
-                  splitting selected regions:
-                   - regions selected before the split operation, and unaffected by it
-                   - newly-created regions before the split
-                   - newly-created regions after the split
-                */
-
-               if (rsas & Existing) {
-                       // region selections that existed before the split.
-                       selection->add ( pre_selected_regions );
-               }
+       RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
 
+       //if the user has "Clear Selection" as their post-split behavior, then clear the selection
+       if (!latest_regionviews.empty() && (rsas == None)) {
+               selection->clear_objects();
+               selection->clear_time();
+               //but leave track selection intact
+       }
+       
+       //if the user doesn't want to preserve the "Existing" selection, then clear the selection
+       if (!(rsas & Existing)) {
+               selection->clear_objects();
+               selection->clear_time();
+       }
+       
+       //if the user wants newly-created regions to be selected, then select them:
+       if (mouse_mode == MouseObject) {
                for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
                        if ((*ri)->region()->position() < where.sample) {
                                // new regions created before the split
@@ -310,10 +314,6 @@ Editor::split_regions_at (MusicSample where, RegionSelection& regions, bool snap
                                }
                        }
                }
-       } else {
-               if( working_on_selection ) {
-                       selection->add (latest_regionviews);  //these are the new regions created after the split
-               }
        }
 
        commit_reversible_command ();
@@ -457,7 +457,7 @@ Editor::nudge_forward (bool next, bool force_playhead)
                                                loc->set_end (max_samplepos, false, true, divisions);
                                        }
                                        if (loc->is_session_range()) {
-                                               _session->set_end_is_free (false);
+                                               _session->set_session_range_is_free (false);
                                        }
                                }
                                if (!in_command) {
@@ -551,7 +551,7 @@ Editor::nudge_backward (bool next, bool force_playhead)
                                                loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
                                        }
                                        if (loc->is_session_range()) {
-                                               _session->set_end_is_free (false);
+                                               _session->set_session_range_is_free (false);
                                        }
                                }
                                if (!in_command) {
@@ -711,6 +711,13 @@ Editor::move_to_end ()
 void
 Editor::build_region_boundary_cache ()
 {
+
+       /* TODO:  maybe set a timer so we don't recalutate when lots of changes are coming in */
+       /* TODO:  maybe somehow defer this until session is fully loaded.  */
+
+       if (!_region_boundary_cache_dirty)
+               return;
+
        samplepos_t pos = 0;
        vector<RegionPoint> interesting_points;
        boost::shared_ptr<Region> r;
@@ -725,36 +732,29 @@ Editor::build_region_boundary_cache ()
 
        bool maybe_first_sample = false;
 
-       switch (_snap_type) {
-       case SnapToRegionStart:
+       if (UIConfiguration::instance().get_snap_to_region_start()) {
                interesting_points.push_back (Start);
                maybe_first_sample = true;
-               break;
-       case SnapToRegionEnd:
+       }
+
+       if (UIConfiguration::instance().get_snap_to_region_end()) {
                interesting_points.push_back (End);
-               break;
-       case SnapToRegionSync:
+       }
+
+       if (UIConfiguration::instance().get_snap_to_region_sync()) {
                interesting_points.push_back (SyncPoint);
-               break;
-       case SnapToRegionBoundary:
-               interesting_points.push_back (Start);
-               interesting_points.push_back (End);
-               maybe_first_sample = true;
-               break;
-       default:
-               fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
-               abort(); /*NOTREACHED*/
+       }
+
+       /* if no snap selections are set, boundary cache should be left empty */
+       if ( interesting_points.empty() ) {
+               _region_boundary_cache_dirty = false;
                return;
        }
 
        TimeAxisView *ontrack = 0;
        TrackViewList tlist;
 
-       if (!selection->tracks.empty()) {
-               tlist = selection->tracks.filter_to_unique_playlists ();
-       } else {
-               tlist = track_views.filter_to_unique_playlists ();
-       }
+       tlist = track_views.filter_to_unique_playlists ();
 
        if (maybe_first_sample) {
                TrackViewList::const_iterator i;
@@ -767,10 +767,18 @@ Editor::build_region_boundary_cache ()
                }
        }
 
-       while (pos < _session->current_end_sample() && !at_end) {
+       //allow regions to snap to the video start (if any) as if it were a "region"
+       if (ARDOUR_UI::instance()->video_timeline) {
+               region_boundary_cache.push_back (ARDOUR_UI::instance()->video_timeline->get_video_start_offset());
+       }
+
+       std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
+       samplepos_t session_end = ext.second;
+
+       while (pos < session_end && !at_end) {
 
                samplepos_t rpos;
-               samplepos_t lpos = max_samplepos;
+               samplepos_t lpos = session_end;
 
                for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
 
@@ -826,6 +834,8 @@ Editor::build_region_boundary_cache ()
        /* finally sort to be sure that the order is correct */
 
        sort (region_boundary_cache.begin(), region_boundary_cache.end());
+
+       _region_boundary_cache_dirty = false;
 }
 
 boost::shared_ptr<Region>
@@ -837,7 +847,6 @@ Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, Tr
        samplepos_t rpos = 0;
 
        samplepos_t track_sample;
-       RouteTimeAxisView *rtav;
 
        for (i = tracks.begin(); i != tracks.end(); ++i) {
 
@@ -1749,7 +1758,7 @@ Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
        //zoom-behavior-tweaks
        //limit our maximum zoom to the session gui extents value
        std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
-       samplecnt_t session_extents_pp = ( ext.second - ext.first )  / _visible_canvas_width;
+       samplecnt_t session_extents_pp = (ext.second - ext.first)  / _visible_canvas_width;
        if (nspp > session_extents_pp)
                nspp = session_extents_pp;
 
@@ -1794,7 +1803,13 @@ Editor::temporal_zoom (samplecnt_t fpp)
        new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
        half_page_size = new_page_size / 2;
 
-       switch (zoom_focus) {
+       Editing::ZoomFocus zf = zoom_focus;
+
+       if (zf == ZoomFocusEdit && _edit_point == EditAtMouse) {
+               zf = ZoomFocusMouse;
+       }
+
+       switch (zf) {
        case ZoomFocusLeft:
                leftmost_after_zoom = current_leftmost;
                break;
@@ -1864,9 +1879,7 @@ Editor::temporal_zoom (samplecnt_t fpp)
        case ZoomFocusEdit:
                /* try to keep the edit point in the same place */
                where = get_preferred_edit_position ();
-
-               if (where > 0) {
-
+               {
                        double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
 
                        if (l < 0) {
@@ -1876,10 +1889,6 @@ Editor::temporal_zoom (samplecnt_t fpp)
                        } else {
                                leftmost_after_zoom = (samplepos_t) l;
                        }
-
-               } else {
-                       /* edit point not defined */
-                       return;
                }
                break;
 
@@ -1932,7 +1941,7 @@ Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
 
        //ToDo:  if control points are selected, set extents to that selection
 
-       if ( !selection->regions.empty() ) {
+       if (!selection->regions.empty()) {
                RegionSelection rs = get_regions_from_selection_and_entered ();
 
                for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
@@ -1966,6 +1975,31 @@ Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
 {
        if (!selection) return;
 
+       if (selection->regions.empty() && selection->time.empty()) {
+               if (axes == Horizontal || axes == Both) {
+                       temporal_zoom_step(true);
+               }
+               if (axes == Vertical || axes == Both) {
+                       if (!track_views.empty()) {
+
+                               TrackViewList tvl;
+
+                               //implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
+                               const double top = vertical_adjustment.get_value() - 10;
+                               const double btm = top + _visible_canvas_height + 10;
+
+                               for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
+                                       if ((*iter)->covered_by_y_range (top, btm)) {
+                                               tvl.push_back(*iter);
+                                       }
+                               }
+
+                               fit_tracks (tvl);
+                       }
+               }
+               return;
+       }
+
        //ToDo:  if notes are selected, zoom to that
 
        //ToDo:  if control points are selected, zoom to that
@@ -1982,6 +2016,10 @@ Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
        if (axes == Vertical || axes == Both) {
                fit_selection ();
        }
+
+       //normally, we don't do anything "automatic" to the user's selection.
+       //but in this case, we will clear the selection after a zoom-to-selection.
+       selection->clear();
 }
 
 void
@@ -1993,7 +2031,7 @@ Editor::temporal_zoom_session ()
                samplecnt_t start = _session->current_start_sample();
                samplecnt_t end = _session->current_end_sample();
 
-               if (_session->actively_recording () ) {
+               if (_session->actively_recording ()) {
                        samplepos_t cur = playhead_cursor->current_sample ();
                        if (cur > end) {
                                /* recording beyond the end marker; zoom out
@@ -2021,12 +2059,12 @@ Editor::temporal_zoom_extents ()
        ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
 
        if (_session) {
-               std::pair<samplepos_t, samplepos_t> ext = session_gui_extents( false );  //in this case we want to zoom to the extents explicitly; ignore the users prefs for extra padding
+               std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);  //in this case we want to zoom to the extents explicitly; ignore the users prefs for extra padding
 
                samplecnt_t start = ext.first;
                samplecnt_t end = ext.second;
 
-               if (_session->actively_recording () ) {
+               if (_session->actively_recording ()) {
                        samplepos_t cur = playhead_cursor->current_sample ();
                        if (cur > end) {
                                /* recording beyond the end marker; zoom out
@@ -2129,7 +2167,7 @@ Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
 
 
 bool
-Editor::choose_new_marker_name(string &name) {
+Editor::choose_new_marker_name(string &name, bool is_range) {
 
        if (!UIConfiguration::instance().get_name_new_markers()) {
                /* don't prompt user for a new name */
@@ -2140,7 +2178,11 @@ Editor::choose_new_marker_name(string &name) {
 
        dialog.set_prompt (_("New Name:"));
 
-       dialog.set_title (_("New Location Marker"));
+       if (is_range) {
+               dialog.set_title(_("New Range"));
+       } else {
+               dialog.set_title (_("New Location Marker"));
+       }
 
        dialog.set_name ("MarkNameWindow");
        dialog.set_size_request (250, -1);
@@ -2181,6 +2223,9 @@ Editor::add_location_from_selection ()
        samplepos_t end = selection->time[clicked_selection].end;
 
        _session->locations()->next_available_name(rangename,"selection");
+       if (!choose_new_marker_name(rangename, true)) {
+               return;
+       }
        Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
 
        begin_reversible_command (_("add marker"));
@@ -2223,11 +2268,11 @@ Editor::set_session_start_from_playhead ()
 
        Location* loc;
        if ((loc = _session->locations()->session_range_location()) == 0) {  //should never happen
-               _session->set_session_extents ( _session->audible_sample(), _session->audible_sample() );
+               _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
        } else {
                XMLNode &before = loc->get_state();
 
-               _session->set_session_extents ( _session->audible_sample(), loc->end() );
+               _session->set_session_extents (_session->audible_sample(), loc->end());
 
                XMLNode &after = loc->get_state();
 
@@ -2237,6 +2282,8 @@ Editor::set_session_start_from_playhead ()
 
                commit_reversible_command ();
        }
+
+       _session->set_session_range_is_free (false);
 }
 
 void
@@ -2247,11 +2294,11 @@ Editor::set_session_end_from_playhead ()
 
        Location* loc;
        if ((loc = _session->locations()->session_range_location()) == 0) {  //should never happen
-               _session->set_session_extents ( _session->audible_sample(), _session->audible_sample() );
+               _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
        } else {
                XMLNode &before = loc->get_state();
 
-               _session->set_session_extents ( loc->start(), _session->audible_sample() );
+               _session->set_session_extents (loc->start(), _session->audible_sample());
 
                XMLNode &after = loc->get_state();
 
@@ -2262,7 +2309,7 @@ Editor::set_session_end_from_playhead ()
                commit_reversible_command ();
        }
 
-       _session->set_end_is_free (false);
+       _session->set_session_range_is_free (false);
 }
 
 
@@ -2411,9 +2458,9 @@ Editor::jump_backward_to_mark ()
        samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
 
        //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_sample() - pos) < _session->sample_rate()/2 ) {
-                       samplepos_t prior = _session->locations()->first_mark_before ( pos );
+       if (_session->transport_rolling()) {
+               if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
+                       samplepos_t prior = _session->locations()->first_mark_before (pos);
                        pos = prior;
                }
        }
@@ -2510,7 +2557,7 @@ Editor::unhide_ranges ()
 /* INSERT/REPLACE */
 
 void
-Editor::insert_region_list_selection (float times)
+Editor::insert_source_list_selection (float times)
 {
        RouteTimeAxisView *tv = 0;
        boost::shared_ptr<Playlist> playlist;
@@ -2533,7 +2580,7 @@ Editor::insert_region_list_selection (float times)
                return;
        }
 
-       boost::shared_ptr<Region> region = _regions->get_single_selection ();
+       boost::shared_ptr<Region> region = _sources->get_single_selection ();
        if (region == 0) {
                return;
        }
@@ -2573,7 +2620,7 @@ Editor::transition_to_rolling (bool fwd)
        }
 
        if (_session->config.get_external_sync()) {
-               switch (Config->get_sync_source()) {
+               switch (TransportMasterManager::instance().current()->type()) {
                case Engine:
                        break;
                default:
@@ -2608,7 +2655,7 @@ Editor::play_from_edit_point_and_return ()
        samplepos_t start_sample;
        samplepos_t return_sample;
 
-       start_sample = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
+       start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
 
        if (_session->transport_rolling()) {
                _session->request_locate (start_sample, false);
@@ -2630,7 +2677,7 @@ void
 Editor::play_selection ()
 {
        samplepos_t start, end;
-       if (!get_selection_extents ( start, end))
+       if (!get_selection_extents (start, end))
                return;
 
        AudioRange ar (start, end, 0);
@@ -2644,7 +2691,7 @@ Editor::play_selection ()
 void
 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
 {
-       if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
+       if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
                return;
 
        location -= _session->preroll_samples (location);
@@ -2655,18 +2702,18 @@ Editor::maybe_locate_with_edit_preroll (samplepos_t location)
        }
 
        //if follow_playhead is on, keep the playhead on the screen
-       if ( _follow_playhead )
-               if ( location < _leftmost_sample )
+       if (_follow_playhead)
+               if (location < _leftmost_sample)
                        location = _leftmost_sample;
 
-       _session->request_locate( location );
+       _session->request_locate (location);
 }
 
 void
 Editor::play_with_preroll ()
 {
        samplepos_t start, end;
-       if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
+       if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
                const samplepos_t preroll = _session->preroll_samples (start);
 
                samplepos_t ret = start;
@@ -3178,8 +3225,18 @@ Editor::separate_regions_between (const TimeSelection& ts)
                }
        }
 
-       if (in_command) {
-//             selection->set (new_selection);
+       if (in_command) {
+
+               RangeSelectionAfterSplit rsas = Config->get_range_selection_after_split();
+
+               //if our config preference says to clear the selection, clear the Range selection
+               if (rsas == ClearSel) {
+                       selection->clear_time();
+                       //but leave track selection intact
+               } else if (rsas == ForceSel) {
+                       //note: forcing the regions to be selected *might* force a tool-change to Object here
+                       selection->set(new_selection);  
+               }
 
                commit_reversible_command ();
        }
@@ -3318,7 +3375,7 @@ Editor::separate_under_selected_regions ()
                playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
 
                //Re-add region that was just removed due to the partition operation
-               playlist->add_region( (*rl), (*rl)->first_sample() );
+               playlist->add_region ((*rl), (*rl)->first_sample());
        }
 
        vector<PlaylistState>::iterator pl;
@@ -3935,7 +3992,7 @@ Editor::freeze_route ()
        }
 
        if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
-               MessageDialog d (
+               ArdourMessageDialog d (
                        _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
                          "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
                        );
@@ -3945,9 +4002,9 @@ 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);
+               ArdourMessageDialog 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);
 
                d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
                d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
@@ -3996,7 +4053,7 @@ Editor::bounce_range_selection (bool replace, bool enable_processing)
                        RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
 
                        if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
-                               MessageDialog d (
+                               ArdourMessageDialog d (
                                        _("You can't perform this operation because the processing of the signal "
                                          "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
                                          "You can do this without processing, which is a different operation.")
@@ -4074,7 +4131,7 @@ Editor::delete_ ()
        //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
        //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
        bool deleted = false;
-       if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
+       if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
                deleted = current_mixer_strip->delete_processors ();
 
        if (!deleted)
@@ -4144,7 +4201,7 @@ 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 ();
        }
 
@@ -4415,12 +4472,15 @@ Editor::remove_clicked_region ()
        begin_reversible_command (_("remove region"));
 
        boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
+       boost::shared_ptr<Region> region = clicked_regionview->region();
 
        playlist->clear_changes ();
        playlist->clear_owned_changes ();
-       playlist->remove_region (clicked_regionview->region());
-       if (Config->get_edit_mode() == Ripple)
-               playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
+       playlist->remove_region (region);
+
+       if (Config->get_edit_mode() == Ripple) {
+               playlist->ripple (region->position(), - region->length(), boost::shared_ptr<Region>());
+       }
 
        /* We might have removed regions, which alters other regions' layering_index,
           so we need to do a recursive diff here.
@@ -4434,6 +4494,32 @@ Editor::remove_clicked_region ()
 }
 
 
+void
+Editor::recover_regions (ARDOUR::RegionList regions)
+{
+#ifdef RECOVER_REGIONS_IS_WORKING
+       begin_reversible_command (_("recover regions"));
+
+       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+               boost::shared_ptr<ARDOUR::Source> source = (*i)->source();
+
+               RouteList routes = _session->get_routelist();
+               for (RouteList::iterator it = routes.begin(); it != routes.end(); ++it) {
+                       boost::shared_ptr<ARDOUR::Track> track = boost::dynamic_pointer_cast<Track>(*it);
+                       if (track) {
+                               //ToDo
+                               if (source->captured_for() == track->) {
+                                       //_session->add_command(new StatefulDiffCommand (playlist));    
+                               }
+                       }
+               }
+       }
+
+       commit_reversible_command ();
+#endif
+}
+
+
 /** Remove the selected regions */
 void
 Editor::remove_selected_regions ()
@@ -4746,10 +4832,7 @@ Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num
                DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
        }
 
-       if (position == last_paste_pos) {
-               /* repeated paste in the same position */
-               ++paste_count;
-       } else {
+       if (position != last_paste_pos) {
                /* paste in new location, reset repeated paste state */
                paste_count = 0;
                last_paste_pos = position;
@@ -4838,6 +4921,8 @@ Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num
                }
        }
 
+       ++paste_count;
+
        commit_reversible_command ();
 }
 
@@ -4856,17 +4941,40 @@ Editor::duplicate_some_regions (RegionSelection& regions, float times)
        }
 
        boost::shared_ptr<Playlist> playlist;
+       std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
        RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
        RegionSelection foo;
 
        samplepos_t const start_sample = regions.start ();
        samplepos_t const end_sample = regions.end_sample ();
-       samplecnt_t const gap = end_sample - start_sample + 1;
+       samplecnt_t const span = end_sample - start_sample + 1;
 
        begin_reversible_command (Operations::duplicate_region);
 
        selection->clear_regions ();
 
+       /* ripple first so that we don't move the duplicates that will be added */
+
+       if (Config->get_edit_mode() == Ripple) {
+
+               /* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
+
+               RegionList exclude;
+
+               for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
+                       exclude.push_back ((*i)->region());
+                       playlist = (*i)->region()->playlist();
+                       if (playlists.insert (playlist).second) {
+                               /* successfully inserted into set, so it's the first time we've seen this playlist */
+                               playlist->clear_changes ();
+                       }
+               }
+
+               for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
+                       (*p)->ripple (start_sample, span * times, &exclude);
+               }
+       }
+
        for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
 
                boost::shared_ptr<Region> r ((*i)->region());
@@ -4878,15 +4986,27 @@ Editor::duplicate_some_regions (RegionSelection& regions, float times)
 
                samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
                playlist = (*i)->region()->playlist();
-               playlist->clear_changes ();
-               playlist->duplicate (r, position, gap, times);
-               _session->add_command(new StatefulDiffCommand (playlist));
+
+               if (Config->get_edit_mode() != Ripple) {
+                       if (playlists.insert (playlist).second) {
+                               playlist->clear_changes ();
+                       }
+               }
+
+               playlist->duplicate (r, position, span, times);
 
                c.disconnect ();
 
                foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
        }
 
+       for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
+               _session->add_command (new StatefulDiffCommand (*p));
+               vector<Command*> cmds;
+               (*p)->rdiff (cmds);
+               _session->add_commands (cmds);
+       }
+
        if (!foo.empty()) {
                selection->set (foo);
        }
@@ -5061,6 +5181,95 @@ Editor::remove_last_capture ()
        }
 }
 
+void
+Editor::tag_regions (RegionList regions)
+{
+       ArdourDialog d (_("Tag Last Capture"), true, false);
+       Entry entry;
+       Label label (_("Tag:"));
+       HBox hbox;
+
+       hbox.set_spacing (6);
+       hbox.pack_start (label, false, false);
+       hbox.pack_start (entry, true, true);
+
+       d.get_vbox()->set_border_width (12);
+       d.get_vbox()->pack_start (hbox, false, false);
+
+       d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+       d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
+
+       d.set_size_request (300, -1);
+
+       entry.set_text (_("Good"));
+       entry.select_region (0, -1);
+
+       entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
+
+       d.show_all ();
+
+       entry.grab_focus();
+
+       int const ret = d.run();
+
+       d.hide ();
+
+       if (ret != RESPONSE_OK) {
+               return;
+       }
+
+       std::string tagstr = entry.get_text();
+       strip_whitespace_edges (tagstr);
+       
+       if (!tagstr.empty()) {
+               for (RegionList::iterator r = regions.begin(); r != regions.end(); r++) {
+                       (*r)->set_tags(tagstr);
+               }
+                       
+               _regions->redisplay ();
+       }
+}
+
+void
+Editor::tag_selected_region ()
+{
+       std::list<boost::shared_ptr<Region> > rlist;
+
+       RegionSelection rs = get_regions_from_selection_and_entered ();
+       for (RegionSelection::iterator r = rs.begin(); r != rs.end(); r++) {
+               rlist.push_back((*r)->region());
+       }
+
+       tag_regions(rlist);
+}
+
+void
+Editor::tag_last_capture ()
+{
+       if (!_session) {
+               return;
+       }
+
+       std::list<boost::shared_ptr<Region> > rlist;
+
+       std::list<boost::shared_ptr<Source> > srcs;
+       _session->get_last_capture_sources (srcs);
+       for (std::list<boost::shared_ptr<Source> >::iterator i = srcs.begin(); i != srcs.end(); ++i) {
+               boost::shared_ptr<ARDOUR::Source> source = (*i);
+               if (source) {
+
+                       set<boost::shared_ptr<Region> > regions;
+                       RegionFactory::get_regions_using_source (source, regions);
+                       for (set<boost::shared_ptr<Region> >::iterator r = regions.begin(); r != regions.end(); r++) {
+                               rlist.push_back(*r);
+                       }
+
+               }
+       }
+       
+       tag_regions(rlist);
+}
+
 void
 Editor::normalize_region ()
 {
@@ -5864,6 +6073,54 @@ Editor::toggle_record_enable ()
        }
 }
 
+StripableList
+tracklist_to_stripables (TrackViewList list)
+{
+       StripableList ret;
+
+       for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
+               RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
+
+               if (rtv && rtv->is_track()) {
+                       ret.push_back (rtv->track());
+               }
+       }
+
+       return ret;
+}
+
+void
+Editor::play_solo_selection (bool restart)
+{
+       //note: session::solo_selection takes care of invalidating the region playlist
+
+       if ((!selection->tracks.empty()) && selection->time.length() > 0) {  //a range is selected; solo the tracks and roll
+
+               StripableList sl = tracklist_to_stripables (selection->tracks);
+               _session->solo_selection (sl, true);
+
+               if (restart) {
+                       samplepos_t start = selection->time.start();
+                       samplepos_t end = selection->time.end_sample();
+                       _session->request_bounded_roll (start, end);
+               }
+       } else if (! selection->tracks.empty()) {  //no range is selected, but tracks are selected; solo the tracks and roll
+               StripableList sl = tracklist_to_stripables (selection->tracks);
+               _session->solo_selection (sl, true);
+               _session->request_cancel_play_range();
+               transition_to_rolling (true);
+
+       } else if (! selection->regions.empty()) {  //solo any tracks with selected regions, and roll
+               StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
+               _session->solo_selection (sl, true);
+               _session->request_cancel_play_range();
+               transition_to_rolling (true);
+       } else {
+               _session->request_cancel_play_range();
+               transition_to_rolling (true);  //no selection.  just roll.
+       }
+}
+
 void
 Editor::toggle_solo ()
 {
@@ -5908,7 +6165,9 @@ Editor::toggle_mute ()
                        first = false;
                }
 
-               cl->push_back (stav->stripable()->mute_control());
+               boost::shared_ptr<MuteControl> mc = stav->stripable()->mute_control();
+               cl->push_back (mc);
+               mc->start_touch (_session->audible_sample ());
        }
 
        _session->set_controls (cl, new_state, Controllable::UseGroup);
@@ -6287,20 +6546,47 @@ Editor::set_playhead_cursor ()
 void
 Editor::split_region ()
 {
-       if (_drags->active ()) {
+       if (_dragging_playhead) {
+               /*continue*/
+       } else if (_drags->active ()) {
+               /*any other kind of drag, bail out so we avoid Undo snafu*/
                return;
        }
 
        //if a range is selected, separate it
-       if ( !selection->time.empty()) {
+       if (!selection->time.empty()) {
                separate_regions_between (selection->time);
                return;
        }
 
        //if no range was selected, try to find some regions to split
-       if (current_mouse_mode() == MouseObject) {  //don't try this for Internal Edit, Stretch, Draw, etc.
+       if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) {  //don't try this for Internal Edit, Stretch, Draw, etc.
+
+               RegionSelection rs;
+
+               //new behavior:  the Split action will prioritize the entered_regionview rather than selected regions.
+               //this fixes the unexpected case where you point at a region, but
+               //  * nothing happens OR
+               //  * some other region (maybe off-screen) is split.
+               //NOTE:  if the entered_regionview is /part of the selection/ then we should operate on the selection as usual
+               if (_edit_point == EditAtMouse && entered_regionview && !entered_regionview->selected()) {
+                       rs.add (entered_regionview);
+               } else {
+                       rs = selection->regions;   //might be empty
+               }
+
+               if (rs.empty()) {
+                       TrackViewList tracks = selection->tracks;
+
+                       if (!tracks.empty()) {
+                               /* no region selected or entered, but some selected tracks:
+                                * act on all regions on the selected tracks at the edit point
+                                */
+                               samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, false);
+                               get_regions_at(rs, where, tracks);
+                       }
+               }
 
-               RegionSelection rs = get_regions_from_selection_and_edit_point ();
                const samplepos_t pos = get_preferred_edit_position();
                const int32_t division = get_grid_music_divisions (0);
                MusicSample where (pos, division);
@@ -6310,86 +6596,19 @@ Editor::split_region ()
                }
 
                split_regions_at (where, rs);
-
        }
 }
 
 void
 Editor::select_next_stripable (bool routes_only)
 {
-       if (selection->tracks.empty()) {
-               selection->set (track_views.front());
-               return;
-       }
-
-       TimeAxisView* current = selection->tracks.front();
-
-       bool valid;
-       do {
-               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
-
-                       if (*i == current) {
-                               ++i;
-                               if (i != track_views.end()) {
-                                       current = (*i);
-                               } else {
-                                       current = (*(track_views.begin()));
-                                       //selection->set (*(track_views.begin()));
-                               }
-                               break;
-                       }
-               }
-
-               if (routes_only) {
-                       RouteUI* rui = dynamic_cast<RouteUI *>(current);
-                       valid = rui && rui->route()->active();
-               } else {
-                       valid = 0 != current->stripable ().get();
-               }
-
-       } while (current->hidden() || !valid);
-
-       selection->set (current);
-
-       ensure_time_axis_view_is_visible (*current, false);
+       _session->selection().select_next_stripable (false, routes_only);
 }
 
 void
 Editor::select_prev_stripable (bool routes_only)
 {
-       if (selection->tracks.empty()) {
-               selection->set (track_views.front());
-               return;
-       }
-
-       TimeAxisView* current = selection->tracks.front();
-
-       bool valid;
-       do {
-               for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
-
-                       if (*i == current) {
-                               ++i;
-                               if (i != track_views.rend()) {
-                                       current = (*i);
-                               } else {
-                                       current = *(track_views.rbegin());
-                               }
-                               break;
-                       }
-               }
-               if (routes_only) {
-                       RouteUI* rui = dynamic_cast<RouteUI *>(current);
-                       valid = rui && rui->route()->active();
-               } else {
-                       valid = 0 != current->stripable ().get();
-               }
-
-       } while (current->hidden() || !valid);
-
-       selection->set (current);
-
-       ensure_time_axis_view_is_visible (*current, false);
+       _session->selection().select_prev_stripable (false, routes_only);
 }
 
 void
@@ -6400,7 +6619,7 @@ Editor::set_loop_from_selection (bool play)
        }
 
        samplepos_t start, end;
-       if (!get_selection_extents ( start, end))
+       if (!get_selection_extents (start, end))
                return;
 
        set_loop_range (start, end,  _("set loop range from selection"));
@@ -6414,7 +6633,7 @@ void
 Editor::set_loop_from_region (bool play)
 {
        samplepos_t start, end;
-       if (!get_selection_extents ( start, end))
+       if (!get_selection_extents (start, end))
                return;
 
        set_loop_range (start, end, _("set loop range from region"));
@@ -6433,7 +6652,7 @@ Editor::set_punch_from_selection ()
        }
 
        samplepos_t start, end;
-       if (!get_selection_extents ( start, end))
+       if (!get_selection_extents (start, end))
                return;
 
        set_punch_range (start, end,  _("set punch range from selection"));
@@ -6474,7 +6693,7 @@ Editor::set_auto_punch_range ()
                        set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
                        _session->config.set_punch_out(true);
                }
-       } else  {
+       } else {
                if (_session->config.get_punch_out()) {
                        _session->config.set_punch_out(false);
                }
@@ -6500,7 +6719,7 @@ Editor::set_session_extents_from_selection ()
        }
 
        samplepos_t start, end;
-       if (!get_selection_extents ( start, end))
+       if (!get_selection_extents (start, end))
                return;
 
        Location* loc;
@@ -6520,7 +6739,7 @@ Editor::set_session_extents_from_selection ()
                commit_reversible_command ();
        }
 
-       _session->set_end_is_free (false);
+       _session->set_session_range_is_free (false);
 }
 
 void
@@ -6543,11 +6762,8 @@ Editor::set_punch_start_from_edit_point ()
                        start.sample = 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.sample > end ) {
+               if (start.sample > end) {
                        end = max_samplepos;
                }
 
@@ -6576,9 +6792,6 @@ Editor::set_punch_end_from_edit_point ()
                        end.sample = get_preferred_edit_position();
                }
 
-               //snap the selection start/end
-               snap_to (end);
-
                set_punch_range (start, end.sample, _("set punch end from EP"));
 
        }
@@ -6604,11 +6817,8 @@ Editor::set_loop_start_from_edit_point ()
                        start.sample = 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.sample > end ) {
+               if (start.sample > end) {
                        end = max_samplepos;
                }
 
@@ -6637,9 +6847,6 @@ Editor::set_loop_end_from_edit_point ()
                        end.sample = get_preferred_edit_position();
                }
 
-               //snap the selection start/end
-               snap_to(end);
-
                set_loop_range (start, end.sample, _("set loop end from EP"));
        }
 }
@@ -6648,7 +6855,7 @@ void
 Editor::set_punch_from_region ()
 {
        samplepos_t start, end;
-       if (!get_selection_extents ( start, end))
+       if (!get_selection_extents (start, end))
                return;
 
        set_punch_range (start, end, _("set punch range from region"));
@@ -6842,10 +7049,10 @@ Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList
 
        if (positions.size() > 20 && can_ferret) {
                std::string msgstr = string_compose (_("You are about to split\n%1\ninto %2 pieces.\nThis could take a long time."), r->name(), positions.size() + 1);
-               MessageDialog msg (msgstr,
-                                  false,
-                                  Gtk::MESSAGE_INFO,
-                                  Gtk::BUTTONS_OK_CANCEL);
+               ArdourMessageDialog msg (msgstr,
+                                        false,
+                                        Gtk::MESSAGE_INFO,
+                                        Gtk::BUTTONS_OK_CANCEL);
 
                if (can_ferret) {
                        msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
@@ -6855,8 +7062,6 @@ Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList
                }
 
                msg.set_title (_("Excessive split?"));
-               msg.present ();
-
                int response = msg.run();
                msg.hide ();
 
@@ -7058,7 +7263,7 @@ Editor::snap_regions_to_grid ()
                (*r)->region()->clear_changes ();
 
                MusicSample start ((*r)->region()->first_sample (), 0);
-               snap_to (start);
+               snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
                (*r)->region()->set_position (start.sample, start.division);
                _session->add_command(new StatefulDiffCommand ((*r)->region()));
        }
@@ -7155,10 +7360,10 @@ Editor::close_region_gaps ()
                }
 
                (*r)->region()->clear_changes ();
-               (*r)->region()->trim_front( (position - pull_back_samples));
+               (*r)->region()->trim_front((position - pull_back_samples));
 
                last_region->clear_changes ();
-               last_region->trim_end(position - pull_back_samples + crossfade_len));
+               last_region->trim_end ((position - pull_back_samples + crossfade_len));
 
                _session->add_command (new StatefulDiffCommand ((*r)->region()));
                _session->add_command (new StatefulDiffCommand (last_region));
@@ -7263,12 +7468,28 @@ Editor::playhead_forward_to_grid ()
                return;
        }
 
-       MusicSample pos (playhead_cursor->current_sample (), 0);
+       MusicSample pos  (playhead_cursor->current_sample (), 0);
+
+       if ( _grid_type == GridTypeNone) {
+               if (pos.sample < max_samplepos - current_page_samples()*0.1) {
+                       pos.sample += current_page_samples()*0.1;
+                       _session->request_locate (pos.sample);
+               } else {
+                       _session->request_locate (0);
+               }
+       } else {
+
+               if (pos.sample < max_samplepos - 1) {
+                       pos.sample += 2;
+                       pos = snap_to_grid (pos, RoundUpAlways, SnapToGrid_Scaled);
+                       _session->request_locate (pos.sample);
+               }
+       }
+
 
-       if (pos.sample < max_samplepos - 1) {
-               pos.sample += 2;
-               snap_to_internal (pos, RoundUpAlways, false, true);
-               _session->request_locate (pos.sample);
+       /* keep PH visible in window */
+       if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
+               reset_x_origin (pos.sample - (current_page_samples()*0.9));
        }
 }
 
@@ -7282,10 +7503,34 @@ Editor::playhead_backward_to_grid ()
 
        MusicSample pos  (playhead_cursor->current_sample (), 0);
 
-       if (pos.sample > 2) {
-               pos.sample -= 2;
-               snap_to_internal (pos, RoundDownAlways, false, true);
-               _session->request_locate (pos.sample);
+       if ( _grid_type == GridTypeNone) {
+               if ( pos.sample > current_page_samples()*0.1 ) {
+                       pos.sample -= current_page_samples()*0.1;
+                       _session->request_locate (pos.sample);
+               } else {
+                       _session->request_locate (0);
+               }
+       } else {
+
+               if (pos.sample > 2) {
+                       pos.sample -= 2;
+                       pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
+               }
+
+               //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...
+               //also see:  jump_backward_to_mark
+               if (_session->transport_rolling()) {
+                       if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
+                               pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
+                       }
+               }
+
+               _session->request_locate (pos.sample, _session->transport_rolling());
+       }
+
+       /* keep PH visible in window */
+       if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
+               reset_x_origin (pos.sample - (current_page_samples()*0.1));
        }
 }
 
@@ -7350,6 +7595,10 @@ Editor::_remove_tracks ()
                return;
        }
 
+       if (!ARDOUR_UI_UTILS::engine_is_running ()) {
+               return;
+       }
+
        vector<string> choices;
        string prompt;
        int ntracks = 0;
@@ -7386,19 +7635,17 @@ Editor::_remove_tracks ()
        }
 
        if (special_bus && !Config->get_allow_special_bus_removal()) {
-               MessageDialog msg (_("That would be bad news ...."),
-                                  false,
-                                  Gtk::MESSAGE_INFO,
-                                  Gtk::BUTTONS_OK);
-               msg.set_secondary_text (string_compose (_(
-                                                               "Removing the master or monitor bus is such a bad idea\n\
+               ArdourMessageDialog msg (_("That would be bad news ...."),
+                                        false,
+                                        Gtk::MESSAGE_INFO,
+                                        Gtk::BUTTONS_OK);
+               msg.set_secondary_text (string_compose (_("Removing the master or monitor bus is such a bad idea\n\
 that %1 is not going to allow it.\n\
 \n\
 If you really want to do this sort of thing\n\
 edit your ardour.rc file to set the\n\
 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
 
-               msg.present ();
                msg.run ();
                return;
        }
@@ -7475,7 +7722,7 @@ edit your ardour.rc file to set the\n\
                /* 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() ).
+                * 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.
                 *
@@ -7514,6 +7761,16 @@ void
 Editor::do_insert_time ()
 {
        if (selection->tracks.empty()) {
+               ArdourMessageDialog msg (_("You must first select some tracks to Insert Time."),
+                                  true, MESSAGE_INFO, BUTTONS_OK, true);
+               msg.run ();
+               return;
+       }
+
+       if (Config->get_edit_mode() == Lock) {
+               ArdourMessageDialog msg (_("You cannot insert time in Lock Edit mode."),
+                                        true, MESSAGE_INFO, BUTTONS_OK, true);
+               msg.run ();
                return;
        }
 
@@ -7569,7 +7826,7 @@ Editor::insert_time (
                if (all_playlists) {
                        RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
                        if (rtav && rtav->track ()) {
-                               vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
+                               vector<boost::shared_ptr<Playlist> > all = _session->playlists()->playlists_for_track (rtav->track ());
                                for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
                                        pl.insert (*p);
                                }
@@ -7677,6 +7934,16 @@ void
 Editor::do_remove_time ()
 {
        if (selection->tracks.empty()) {
+               ArdourMessageDialog msg (_("You must first select some tracks to Remove Time."),
+                                        true, MESSAGE_INFO, BUTTONS_OK, true);
+               msg.run ();
+               return;
+       }
+
+       if (Config->get_edit_mode() == Lock) {
+               ArdourMessageDialog msg (_("You cannot remove time in Lock Edit mode."),
+                                        true, MESSAGE_INFO, BUTTONS_OK, true);
+               msg.run ();
                return;
        }
 
@@ -7798,7 +8065,7 @@ Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
                                                }
 
                                        }
-                               } else if ((*i)->start() >= pos && (*i)->start() < pos+samples ) {
+                               } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
                                        loc_kill_list.push_back(*i);
                                        moved = true;
                                } else if ((*i)->start() >= pos) {
@@ -7813,7 +8080,7 @@ Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
                }
 
                for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
-                       _session->locations()->remove( *i );
+                       _session->locations()->remove (*i);
                }
 
                if (moved) {
@@ -7829,7 +8096,7 @@ Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
        if (tempo_too) {
                XMLNode& before (_session->tempo_map().get_state());
 
-               if (_session->tempo_map().remove_time (pos, samples) ) {
+               if (_session->tempo_map().remove_time (pos, samples)) {
                        if (!in_command) {
                                begin_reversible_command (_("remove time"));
                                in_command = true;
@@ -7904,7 +8171,8 @@ Editor::fit_tracks (TrackViewList & tracks)
        double first_y_pos = DBL_MAX;
 
        if (h < TimeAxisView::preset_height (HeightSmall)) {
-               MessageDialog msg (_("There are too many tracks to fit in the current window"));
+               ArdourMessageDialog msg (_("There are too many tracks to fit in the current window"));
+               msg.run ();
                /* too small to be displayed */
                return;
        }