Only show user-presets in favorite sidebar
[ardour.git] / gtk2_ardour / editor_ops.cc
index be52230ff8d120df39f02cf6a9663083478f7700..7a528929a8143453a4b759e6cee6f71d6c30ce3f 100644 (file)
 #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/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_ui.h"
 #include "audio_region_view.h"
 #include "audio_streamview.h"
 #include "audio_time_axis.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"
@@ -173,7 +177,7 @@ 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;
 
@@ -189,25 +193,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 */
        }
@@ -294,7 +284,7 @@ Editor::split_regions_at (MusicSample where, RegionSelection& regions, bool snap
 
                if (rsas & Existing) {
                        // region selections that existed before the split.
-                       selection->add ( pre_selected_regions );
+                       selection->add (pre_selected_regions);
                }
 
                for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
@@ -310,10 +300,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 ();
@@ -711,6 +697,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 +718,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 +753,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 +820,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>
@@ -1748,7 +1744,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;
 
@@ -1931,7 +1927,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) {
@@ -1965,6 +1961,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
@@ -1981,6 +2002,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
@@ -1992,7 +2017,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
@@ -2020,12 +2045,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
@@ -2222,11 +2247,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();
 
@@ -2246,11 +2271,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();
 
@@ -2410,9 +2435,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;
                }
        }
@@ -2572,7 +2597,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:
@@ -2607,7 +2632,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);
@@ -2629,7 +2654,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);
@@ -2643,7 +2668,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);
@@ -2654,18 +2679,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;
@@ -3317,7 +3342,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;
@@ -4073,7 +4098,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)
@@ -4143,7 +4168,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 ();
        }
 
@@ -4745,10 +4770,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;
@@ -4837,6 +4859,8 @@ Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num
                }
        }
 
+       ++paste_count;
+
        commit_reversible_command ();
 }
 
@@ -4855,17 +4879,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());
@@ -4877,15 +4924,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);
        }
@@ -5863,6 +5922,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 ()
 {
@@ -6286,18 +6393,21 @@ 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 = get_regions_from_selection_and_edit_point ();
                const samplepos_t pos = get_preferred_edit_position();
@@ -6316,79 +6426,13 @@ Editor::split_region ()
 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
@@ -6399,7 +6443,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"));
@@ -6413,7 +6457,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"));
@@ -6432,7 +6476,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"));
@@ -6499,7 +6543,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;
@@ -6542,11 +6586,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;
                }
 
@@ -6575,9 +6616,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"));
 
        }
@@ -6603,11 +6641,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;
                }
 
@@ -6636,9 +6671,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"));
        }
 }
@@ -6647,7 +6679,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"));
@@ -7057,7 +7089,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()));
        }
@@ -7154,10 +7186,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));
@@ -7262,12 +7294,28 @@ Editor::playhead_forward_to_grid ()
                return;
        }
 
-       MusicSample pos (playhead_cursor->current_sample (), 0);
+       MusicSample pos  (playhead_cursor->current_sample (), 0);
 
-       if (pos.sample < max_samplepos - 1) {
-               pos.sample += 2;
-               snap_to_internal (pos, RoundUpAlways, false, true);
-               _session->request_locate (pos.sample);
+       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);
+               }
+       }
+
+
+       /* 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));
        }
 }
 
@@ -7281,10 +7329,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));
        }
 }
 
@@ -7349,6 +7421,10 @@ Editor::_remove_tracks ()
                return;
        }
 
+       if (!ARDOUR_UI_UTILS::engine_is_running ()) {
+               return;
+       }
+
        vector<string> choices;
        string prompt;
        int ntracks = 0;
@@ -7474,7 +7550,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.
                 *
@@ -7513,6 +7589,18 @@ void
 Editor::do_insert_time ()
 {
        if (selection->tracks.empty()) {
+               MessageDialog msg (_("You must first select some tracks to Insert Time."),
+                                  true, MESSAGE_INFO, BUTTONS_OK, true);
+               msg.set_position (WIN_POS_MOUSE);
+               msg.run ();
+               return;
+       }
+
+       if (Config->get_edit_mode() == Lock) {
+               MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
+                                  true, MESSAGE_INFO, BUTTONS_OK, true);
+               msg.set_position (WIN_POS_MOUSE);
+               msg.run ();
                return;
        }
 
@@ -7676,6 +7764,18 @@ void
 Editor::do_remove_time ()
 {
        if (selection->tracks.empty()) {
+               MessageDialog msg (_("You must first select some tracks to Remove Time."),
+                                  true, MESSAGE_INFO, BUTTONS_OK, true);
+               msg.set_position (WIN_POS_MOUSE);
+               msg.run ();
+               return;
+       }
+
+       if (Config->get_edit_mode() == Lock) {
+               MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
+                                  true, MESSAGE_INFO, BUTTONS_OK, true);
+               msg.set_position (WIN_POS_MOUSE);
+               msg.run ();
                return;
        }
 
@@ -7797,7 +7897,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) {
@@ -7812,7 +7912,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) {
@@ -7828,7 +7928,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;