Alter snap modifier so that it turns the grid on when it's off as well as vice-versa.
[ardour.git] / gtk2_ardour / editor_drag.cc
index e58c4f642b18d70e6a445ebbeb128344bec103f2..73296af8640edd0024860e3a3c2ebbb309f8e390 100644 (file)
@@ -33,6 +33,8 @@
 #include "utils.h"
 #include "region_gain_line.h"
 #include "editor_drag.h"
+#include "audio_time_axis.h"
+#include "midi_time_axis.h"
 
 using namespace std;
 using namespace ARDOUR;
@@ -50,7 +52,8 @@ Drag::Drag (Editor* e, ArdourCanvas::Item* i) :
        _grab_frame (0),
        _last_pointer_frame (0),
        _current_pointer_frame (0),
-       _copy (false)
+       _had_movement (false),
+       _move_threshold_passed (false)
 {
 
 }
@@ -97,9 +100,6 @@ Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
        _current_pointer_y = _grab_y;
        _last_pointer_x = _current_pointer_x;
        _last_pointer_y = _current_pointer_y;
-       _first_move = true;
-       _move_threshold_passed = false;
-       _want_move_threshold = false;
 
        _original_x = 0;
        _original_y = 0;
@@ -127,43 +127,41 @@ Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
        }
 }
 
-/*  @param event GDK event, or 0 */
+/** @param event GDK event, or 0.
+ *  @return true if some movement occurred, otherwise false.
+ */
 bool
 Drag::end_grab (GdkEvent* event)
 {
        _ending = true;
        
-       bool did_drag = false;
-
        _editor->stop_canvas_autoscroll ();
 
        _item->ungrab (event ? event->button.time : 0);
 
-       if (event) {
-               _last_pointer_x = _current_pointer_x;
-               _last_pointer_y = _current_pointer_y;
-               finished (event);
-       }
-
-       did_drag = !_first_move;
+       _last_pointer_x = _current_pointer_x;
+       _last_pointer_y = _current_pointer_y;
+       finished (event, _had_movement);
 
        _editor->hide_verbose_canvas_cursor();
 
        _ending = false;
 
-       update_selection ();
-       
-       return did_drag;
+       return _had_movement;
 }
 
 nframes64_t
-Drag::adjusted_current_frame () const
+Drag::adjusted_current_frame (GdkEvent* event) const
 {
+       nframes64_t pos = 0;
+       
        if (_current_pointer_frame > _pointer_frame_offset) {
-               return _current_pointer_frame - _pointer_frame_offset;
+               pos = _current_pointer_frame - _pointer_frame_offset;
        }
 
-       return 0;
+       _editor->snap_to_with_modifier (pos, event);
+       
+       return pos;
 }
 
 bool
@@ -174,28 +172,41 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
        _current_pointer_frame = _editor->event_frame (event, &_current_pointer_x, &_current_pointer_y);
 
        if (!from_autoscroll && !_move_threshold_passed) {
-               bool xp = (::llabs ((nframes64_t) (_current_pointer_x - _grab_x)) > 4LL);
-               bool yp = (::llabs ((nframes64_t) (_current_pointer_y - _grab_y)) > 4LL);
+               
+               bool const xp = (::llabs ((nframes64_t) (_current_pointer_x - _grab_x)) > 4LL);
+               bool const yp = (::llabs ((nframes64_t) (_current_pointer_y - _grab_y)) > 4LL);
                        
                _move_threshold_passed = (xp || yp);
+
+               if (apply_move_threshold() && _move_threshold_passed) {
                        
-               if (_want_move_threshold && _move_threshold_passed) {
                        _grab_frame = _current_pointer_frame;
                        _grab_x = _current_pointer_x;
                        _grab_y = _current_pointer_y;
                        _last_pointer_frame = _grab_frame;
                        _pointer_frame_offset = _grab_frame - _last_frame_position;
+                       
                }
        }
 
+       bool old_had_movement = _had_movement;
+
+       /* a motion event has happened, so we've had movement... */
+       _had_movement = true;
+
+       /* ... unless we're using a move threshold and we've not yet passed it */
+       if (apply_move_threshold() && !_move_threshold_passed) {
+               _had_movement = false;
+       }
+
        if (active (_editor->mouse_mode)) {
 
                if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
                        if (!from_autoscroll) {
-                               _editor->maybe_autoscroll (&event->motion);
+                               _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
                        }
 
-                       motion (event);
+                       motion (event, _had_movement != old_had_movement);
                        return true;
                }
        }
@@ -238,157 +249,108 @@ RegionDrag::region_going_away (RegionView* v)
        _views.remove (v);
 }
 
-void
-RegionDrag::update_selection ()
-{
-       list<Selectable*> s;
-       copy (_views.begin(), _views.end(), back_inserter (s));
-       _editor->selection->set (s);
-}
-
-RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
+RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
        : RegionDrag (e, i, p, v),
+         _dest_trackview (0),
+         _dest_layer (0),
          _brushing (b)
 {
-       _want_move_threshold = true;
-       _copy = c;
        
-       _source_trackview = &_primary->get_time_axis_view ();
-       _source_layer = _primary->region()->layer ();
-       _dest_trackview = _source_trackview;
-       _dest_layer = _source_layer;
-
-       double speed = 1;
-       RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (_source_trackview);
-       if (tv && tv->is_track()) {
-               speed = tv->get_diskstream()->speed ();
-       }
-
-       _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
 }
 
+
 void
-RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
+RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 {
        Drag::start_grab (event);
        
-       _pointer_frame_offset = _grab_frame - _last_frame_position;
        _editor->show_verbose_time_cursor (_last_frame_position, 10);
 }
 
-void
-RegionMoveDrag::motion (GdkEvent* event)
+RegionMotionDrag::TimeAxisViewSummary
+RegionMotionDrag::get_time_axis_view_summary ()
 {
-       double x_delta;
-       double y_delta = 0;
-       nframes64_t pending_region_position = 0;
-       int32_t pointer_order_span = 0, canvas_pointer_order_span = 0;
-       int32_t pointer_layer_span = 0;
-       
-       bool clamp_y_axis = false;
-       vector<int32_t>::iterator j;
+       int32_t children = 0;
+       TimeAxisViewSummary sum;
 
-       if (_copy && _move_threshold_passed && _want_move_threshold) {
-               copy_regions (event);
-               _want_move_threshold = false; // don't copy again
-       }
-       
-       /* *pointer* variables reflect things about the pointer; as we may be moving
-          multiple regions, much detail must be computed per-region */
+       _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
+               
+       /* get a bitmask representing the visible tracks */
 
-       /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
-          current_pointer_layer the current layer on that TimeAxisView */
-       RouteTimeAxisView* current_pointer_view;
-       layer_t current_pointer_layer;
-       if (!check_possible (&current_pointer_view, &current_pointer_layer)) {
-               return;
+       for (Editor::TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
+               RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
+               TimeAxisView::Children children_list;
+               
+               /* zeroes are audio/MIDI tracks. ones are other types. */
+               
+               if (!rtv->hidden()) {
+                       
+                       if (!rtv->is_track()) {
+                               /* not an audio nor MIDI track */
+                               sum.tracks = sum.tracks |= (0x01 << rtv->order());
+                       }
+                       
+                       sum.height_list[rtv->order()] = (*i)->current_height();
+                       children = 1;
+
+                       if ((children_list = rtv->get_child_list()).size() > 0) {
+                               for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
+                                       sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
+                                       sum.height_list[rtv->order() + children] = (*j)->current_height();
+                                       children++;     
+                               }
+                       }
+               }
        }
 
-       /* TimeAxisView that we were pointing at last time we entered this method */
-       TimeAxisView const * const last_pointer_view = _dest_trackview;
-       /* the order of the track that we were pointing at last time we entered this method */
-       int32_t const last_pointer_order = last_pointer_view->order ();
-       /* the layer that we were pointing at last time we entered this method */
-       layer_t const last_pointer_layer = _dest_layer;
-
-       /************************************************************
-            Y DELTA COMPUTATION
-       ************************************************************/   
+       return sum;
+}
 
-       /* Height of TimeAxisViews, indexed by order */
-       /* XXX: hard-coded limit of TimeAxisViews */
-       vector<int32_t> height_list (512);
-       
+bool
+RegionMotionDrag::compute_y_delta (
+       TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
+       int32_t last_pointer_layer, int32_t current_pointer_layer,
+       TimeAxisViewSummary const & tavs,
+       int32_t* pointer_order_span, int32_t* pointer_layer_span,
+       int32_t* canvas_pointer_order_span
+       )
+{
        if (_brushing) {
-               clamp_y_axis = true;
-               pointer_order_span = 0;
-               goto y_axis_done;
+               *pointer_order_span = 0;
+               *pointer_layer_span = 0;
+               return true;
        }
 
+       bool clamp_y_axis = false;
+               
        /* the change in track order between this callback and the last */
-       pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
+       *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
        /* the change in layer between this callback and the last;
           only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
-       pointer_layer_span = last_pointer_layer - current_pointer_layer;
+       *pointer_layer_span = last_pointer_layer - current_pointer_layer;
 
-       if (pointer_order_span != 0) {
-
-               int32_t children = 0;
-               /* XXX: hard-coded limit of tracks */
-               bitset <512> tracks (0x00);
-
-               int visible_y_high;
-               int visible_y_low;
-               _editor->visible_order_range (&visible_y_low, &visible_y_high);
-               
-               /* get a bitmask representing the visible tracks */
-
-               for (Editor::TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
-                       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
-                       TimeAxisView::Children children_list;
-             
-                       /* zeroes are audio/MIDI tracks. ones are other types. */
-             
-                       if (!rtv->hidden()) {
-                               
-                               if (!rtv->is_track()) {
-                                       /* not an audio nor MIDI track */
-                                       tracks = tracks |= (0x01 << rtv->order());
-                               }
-       
-                               height_list[rtv->order()] = (*i)->current_height();
-                               children = 1;
+       if (*pointer_order_span != 0) {
 
-                               if ((children_list = rtv->get_child_list()).size() > 0) {
-                                       for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
-                                               tracks = tracks |= (0x01 << (rtv->order() + children));
-                                               height_list[rtv->order() + children] = (*j)->current_height();
-                                               children++;     
-                                       }
-                               }
-                       }
-               }
-               
                /* find the actual pointer span, in terms of the number of visible tracks;
                   to do this, we reduce |pointer_order_span| by the number of hidden tracks
                   over the span */
 
-               canvas_pointer_order_span = pointer_order_span;
+               *canvas_pointer_order_span = *pointer_order_span;
                if (last_pointer_view->order() >= current_pointer_view->order()) {
                        for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
-                               if (height_list[y] == 0) {
-                                       canvas_pointer_order_span--;
+                               if (tavs.height_list[y] == 0) {
+                                       *canvas_pointer_order_span--;
                                }
                        }
                } else {
                        for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
-                               if (height_list[y] == 0) {
-                                       canvas_pointer_order_span++;
+                               if (tavs.height_list[y] == 0) {
+                                       *canvas_pointer_order_span++;
                                }
                        }
                }
 
-               for (list<RegionView*>::const_iterator i = _editor->selection->regions.by_layer().begin(); i != _editor->selection->regions.by_layer().end(); ++i) {
+               for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
                        
                        RegionView* rv = (*i);
 
@@ -406,18 +368,11 @@ RegionMoveDrag::motion (GdkEvent* event)
                        assert (tvp.first);
                        RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
 
-                       /* I know this method has a slightly excessive argument list, but I think
-                          it's nice to separate the code out all the same, since it has such a
-                          simple result, and it makes it clear that there are no other
-                          side-effects.
-                       */
-
                        /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
                           as surely this is a per-region thing... */
                        
                        clamp_y_axis = y_movement_disallowed (
-                               rtv->order(), last_pointer_order, canvas_pointer_order_span, visible_y_low, visible_y_high,
-                               tracks, height_list
+                               rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
                                );
 
                        if (clamp_y_axis) {
@@ -433,111 +388,133 @@ RegionMoveDrag::motion (GdkEvent* event)
                } 
        }
 
-  y_axis_done:
        if (!clamp_y_axis) {
                _dest_trackview = current_pointer_view;
                _dest_layer = current_pointer_layer;
        }
-         
-       /************************************************************
-           X DELTA COMPUTATION
-       ************************************************************/
 
+       return clamp_y_axis;
+}
+
+
+double
+RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
+{
+       *pending_region_position = 0;
+       
        /* compute the amount of pointer motion in frames, and where
           the region would be if we moved it by that much.
        */
-       if (_move_threshold_passed) {
+       if (_current_pointer_frame >= _pointer_frame_offset) {
 
-               if (_current_pointer_frame >= _pointer_frame_offset) {
-
-                       nframes64_t sync_frame;
-                       nframes64_t sync_offset;
-                       int32_t sync_dir;
-
-                       pending_region_position = _current_pointer_frame - _pointer_frame_offset;
-
-                       sync_offset = _primary->region()->sync_offset (sync_dir);
-
-                       /* we don't handle a sync point that lies before zero.
-                        */
-                       if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
-                               sync_frame = pending_region_position + (sync_dir*sync_offset);
-
-                               /* we snap if the snap modifier is not enabled.
-                                */
-           
-                               if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-                                       _editor->snap_to (sync_frame);  
-                               }
-           
-                               pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
-
-                       } else {
-                               pending_region_position = _last_frame_position;
-                       }
-           
+               nframes64_t sync_frame;
+               nframes64_t sync_offset;
+               int32_t sync_dir;
+               
+               *pending_region_position = _current_pointer_frame - _pointer_frame_offset;
+               
+               sync_offset = _primary->region()->sync_offset (sync_dir);
+               
+               /* we don't handle a sync point that lies before zero.
+                */
+               if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
+                       
+                       sync_frame = *pending_region_position + (sync_dir*sync_offset);
+                       
+                       _editor->snap_to_with_modifier (sync_frame, event);
+                       
+                       *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
+                       
                } else {
-                       pending_region_position = 0;
-               }
-         
-               if (pending_region_position > max_frames - _primary->region()->length()) {
-                       pending_region_position = _last_frame_position;
+                       *pending_region_position = _last_frame_position;
                }
+               
+       }
+       
+       if (*pending_region_position > max_frames - _primary->region()->length()) {
+               *pending_region_position = _last_frame_position;
+       }
 
-               bool x_move_allowed;
+       double x_delta = 0;
+       
+       if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
                
-               if (Config->get_edit_mode() == Lock) {
-                       if (_copy) {
-                               x_move_allowed = !_x_constrained;
-                       } else {
-                               /* in locked edit mode, reverse the usual meaning of _x_constrained */
-                               x_move_allowed = _x_constrained;
+               /* now compute the canvas unit distance we need to move the regionview
+                  to make it appear at the new location.
+               */
+
+               x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
+               
+               if (*pending_region_position <= _last_frame_position) {
+                       
+                       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                               
+                               RegionView* rv = (*i);
+                               
+                               // If any regionview is at zero, we need to know so we can stop further leftward motion.
+                               
+                               double ix1, ix2, iy1, iy2;
+                               rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
+                               rv->get_canvas_frame()->i2w (ix1, iy1);
+                               
+                               if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
+                                       x_delta = 0;
+                                       *pending_region_position = _last_frame_position;
+                                       break;
+                               }
                        }
-               } else {
-                       x_move_allowed = !_x_constrained;
+                       
                }
+               
+               _last_frame_position = *pending_region_position;
+       }
 
-               if (( pending_region_position != _last_frame_position) && x_move_allowed ) {
+       return x_delta;
+}
 
-                       /* now compute the canvas unit distance we need to move the regionview
-                          to make it appear at the new location.
-                       */
+void
+RegionMotionDrag::motion (GdkEvent* event, bool first_move)
+{
+       double y_delta = 0;
 
-                       if (pending_region_position > _last_frame_position) {
-                               x_delta = ((double) (pending_region_position - _last_frame_position) / _editor->frames_per_unit);
-                       } else {
-                               x_delta = -((double) (_last_frame_position - pending_region_position) / _editor->frames_per_unit);
-                               for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+       TimeAxisViewSummary tavs = get_time_axis_view_summary ();
 
-                                       RegionView* rv = (*i);
+       vector<int32_t>::iterator j;
 
-                                       // If any regionview is at zero, we need to know so we can stop further leftward motion.
-       
-                                       double ix1, ix2, iy1, iy2;
-                                       rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
-                                       rv->get_canvas_frame()->i2w (ix1, iy1);
+       /* *pointer* variables reflect things about the pointer; as we may be moving
+          multiple regions, much detail must be computed per-region */
 
-                                       if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
-                                               x_delta = 0;
-                                               pending_region_position = _last_frame_position;
-                                               break;
-                                       }
-                               }
+       /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
+          current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
+          are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
+          is always 0 regardless of what the region's "real" layer is */
+       RouteTimeAxisView* current_pointer_view;
+       layer_t current_pointer_layer;
+       if (!check_possible (&current_pointer_view, &current_pointer_layer)) {
+               return;
+       }
 
-                       }
-               
-                       _last_frame_position = pending_region_position;
+       /* TimeAxisView that we were pointing at last time we entered this method */
+       TimeAxisView const * const last_pointer_view = _dest_trackview;
+       /* the order of the track that we were pointing at last time we entered this method */
+       int32_t const last_pointer_order = last_pointer_view->order ();
+       /* the layer that we were pointing at last time we entered this method */
+       layer_t const last_pointer_layer = _dest_layer;
 
-               } else {
-                       x_delta = 0;
-               }
+       int32_t pointer_order_span;
+       int32_t pointer_layer_span;
+       int32_t canvas_pointer_order_span;
+       
+       bool const clamp_y_axis = compute_y_delta (
+               last_pointer_view, current_pointer_view,
+               last_pointer_layer, current_pointer_layer, tavs,
+               &pointer_order_span, &pointer_layer_span,
+               &canvas_pointer_order_span
+               );
 
-       } else {
-               /* threshold not passed */
+       nframes64_t pending_region_position;
+       double const x_delta = compute_x_delta (event, &pending_region_position);
 
-               x_delta = 0;
-       }
-       
        /*************************************************************
            PREPARE TO MOVE
        ************************************************************/
@@ -552,196 +529,193 @@ RegionMoveDrag::motion (GdkEvent* event)
        /*************************************************************
            MOTION                                                                    
        ************************************************************/
-       bool do_move = true;
-       if (_first_move) {
-               if (!_move_threshold_passed) {
-                       do_move = false;
-               }
-       }
-
-       if (do_move) {
 
-               pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
+       pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
+       
+       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
                
-               for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-
-                       RegionView* rv = (*i);
-
-                       if (rv->region()->locked()) {
-                               continue;
-                       }
-
-                       /* here we are calculating the y distance from the
-                          top of the first track view to the top of the region
-                          area of the track view that we're working on */
-
-                       /* this x value is just a dummy value so that we have something
-                          to pass to i2w () */
-
-                       double ix1 = 0;
-
-                       /* distance from the top of this track view to the region area
-                          of our track view is always 1 */
+               RegionView* rv = (*i);
+               
+               if (rv->region()->locked()) {
+                       continue;
+               }
+               
+               /* here we are calculating the y distance from the
+                  top of the first track view to the top of the region
+                  area of the track view that we're working on */
+               
+               /* this x value is just a dummy value so that we have something
+                  to pass to i2w () */
+               
+               double ix1 = 0;
+               
+               /* distance from the top of this track view to the region area
+                  of our track view is always 1 */
+               
+               double iy1 = 1;
+               
+               /* convert to world coordinates, ie distance from the top of
+                  the ruler section */
+               
+               rv->get_canvas_frame()->i2w (ix1, iy1);
+               
+               /* compensate for the ruler section and the vertical scrollbar position */
+               iy1 += _editor->get_trackview_group_vertical_offset ();
+               
+               if (first_move) {
                        
-                       double iy1 = 1;
-
-                       /* convert to world coordinates, ie distance from the top of
-                          the ruler section */
+                       // hide any dependent views 
                        
-                       rv->get_canvas_frame()->i2w (ix1, iy1);
-
-                       /* compensate for the ruler section and the vertical scrollbar position */
-                       iy1 += _editor->get_trackview_group_vertical_offset ();
-
-                       if (_first_move) {
-
-                               // hide any dependent views 
-       
-                               rv->get_time_axis_view().hide_dependent_views (*rv);
-
-                               /* 
-                                  reparent to a non scrolling group so that we can keep the 
-                                  region selection above all time axis views.
-                                  reparenting means we have to move the rv as the two 
-                                  parent groups have different coordinates.
-                               */
-
-                               rv->get_canvas_group()->property_y() = iy1 - 1;
-                               rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
-
-                               rv->fake_set_opaque (true);
+                       rv->get_time_axis_view().hide_dependent_views (*rv);
+                       
+                       /* 
+                          reparent to a non scrolling group so that we can keep the 
+                          region selection above all time axis views.
+                          reparenting means we have to move the rv as the two 
+                          parent groups have different coordinates.
+                       */
+                       
+                       rv->get_canvas_group()->property_y() = iy1 - 1;
+                       rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
+                       
+                       rv->fake_set_opaque (true);
+               }
+               
+               /* current view for this particular region */
+               pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
+               RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
+               
+               if (pointer_order_span != 0 && !clamp_y_axis) {
+                       
+                       /* INTER-TRACK MOVEMENT */
+                       
+                       /* move through the height list to the track that the region is currently on */
+                       vector<int32_t>::iterator j = tavs.height_list.begin ();
+                       int32_t x = 0;
+                       while (j != tavs.height_list.end () && x != rtv->order ()) {
+                               ++x;
+                               ++j;
                        }
-
-                       /* current view for this particular region */
-                       pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
-                       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
-
-                       if (pointer_order_span != 0 && !clamp_y_axis) {
-
-                               /* INTER-TRACK MOVEMENT */
-
-                               /* move through the height list to the track that the region is currently on */
-                               vector<int32_t>::iterator j = height_list.begin ();
-                               int32_t x = 0;
-                               while (j != height_list.end () && x != rtv->order ()) {
-                                       ++x;
-                                       ++j;
+                       
+                       y_delta = 0;
+                       int32_t temp_pointer_order_span = canvas_pointer_order_span;
+                       
+                       if (j != tavs.height_list.end ()) {
+                               
+                               /* Account for layers in the original and
+                                  destination tracks.  If we're moving around in layers we assume
+                                  that only one track is involved, so it's ok to use *pointer*
+                                  variables here. */
+                               
+                               StreamView* lv = last_pointer_view->view ();
+                               assert (lv);
+                               
+                               /* move to the top of the last trackview */
+                               if (lv->layer_display () == Stacked) {
+                                       y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
                                }
-
-                               y_delta = 0;
-                               int32_t temp_pointer_order_span = canvas_pointer_order_span;
-
-                               if (j != height_list.end ()) {
-
-                                       /* Account for layers in the original and
-                                          destination tracks.  If we're moving around in layers we assume
-                                          that only one track is involved, so it's ok to use *pointer*
-                                          variables here. */
-
-                                       StreamView* lv = last_pointer_view->view ();
-                                       assert (lv);
-
-                                       /* move to the top of the last trackview */
-                                       if (lv->layer_display () == Stacked) {
-                                               y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
-                                       }
                                        
-                                       StreamView* cv = current_pointer_view->view ();
-                                       assert (cv);
+                               StreamView* cv = current_pointer_view->view ();
+                               assert (cv);
 
-                                       /* move to the right layer on the current trackview */
-                                       if (cv->layer_display () == Stacked) {
-                                               y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
+                               /* move to the right layer on the current trackview */
+                               if (cv->layer_display () == Stacked) {
+                                       y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
+                               }
+                               
+                               /* And for being on a non-topmost layer on the new
+                                  track */
+                               
+                               while (temp_pointer_order_span > 0) {
+                                       /* we're moving up canvas-wise,
+                                          so we need to find the next track height
+                                       */
+                                       if (j != tavs.height_list.begin()) {              
+                                               j--;
                                        }
-
-                                       /* And for being on a non-topmost layer on the new
-                                          track */
-
-                                       while (temp_pointer_order_span > 0) {
-                                               /* we're moving up canvas-wise,
-                                                  so we need to find the next track height
-                                               */
-                                               if (j != height_list.begin()) {           
-                                                       j--;
-                                               }
-
-                                               if (x != last_pointer_order) {
-                                                       if ((*j) == 0) {
-                                                               ++temp_pointer_order_span;
-                                                       }
+                                       
+                                       if (x != last_pointer_order) {
+                                               if ((*j) == 0) {
+                                                       ++temp_pointer_order_span;
                                                }
-
-                                               y_delta -= (*j);
-                                               temp_pointer_order_span--;
                                        }
-
-                                       while (temp_pointer_order_span < 0) {
-
-                                               y_delta += (*j);
-
-                                               if (x != last_pointer_order) {
-                                                       if ((*j) == 0) {
-                                                               --temp_pointer_order_span;
-                                                       }
-                                               }
-                                               
-                                               if (j != height_list.end()) {                 
-                                                       j++;
+                                       
+                                       y_delta -= (*j);
+                                       temp_pointer_order_span--;
+                               }
+                               
+                               while (temp_pointer_order_span < 0) {
+                                       
+                                       y_delta += (*j);
+                                       
+                                       if (x != last_pointer_order) {
+                                               if ((*j) == 0) {
+                                                       --temp_pointer_order_span;
                                                }
-
-                                               temp_pointer_order_span++;
                                        }
-
                                        
-                                       /* find out where we'll be when we move and set height accordingly */
-
-                                       pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
-                                       RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
-                                       rv->set_height (temp_rtv->view()->child_height());
-                               
-                                       /* if you un-comment the following, the region colours will follow
-                                          the track colours whilst dragging; personally
-                                          i think this can confuse things, but never mind.
-                                       */
+                                       if (j != tavs.height_list.end()) {                    
+                                               j++;
+                                       }
                                        
-                                       //const GdkColor& col (temp_rtv->view->get_region_color());
-                                       //rv->set_color (const_cast<GdkColor&>(col));
+                                       temp_pointer_order_span++;
                                }
+                               
+                               
+                               /* find out where we'll be when we move and set height accordingly */
+                               
+                               pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
+                               RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
+                               rv->set_height (temp_rtv->view()->child_height());
+                               
+                               /* if you un-comment the following, the region colours will follow
+                                  the track colours whilst dragging; personally
+                                  i think this can confuse things, but never mind.
+                               */
+                               
+                               //const GdkColor& col (temp_rtv->view->get_region_color());
+                               //rv->set_color (const_cast<GdkColor&>(col));
                        }
+               }
+               
+               if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
+                       
+                       /* INTER-LAYER MOVEMENT in the same track */
+                       y_delta = rtv->view()->child_height () * pointer_layer_span;
+               }
+               
+               
+               if (_brushing) {
+                       _editor->mouse_brush_insert_region (rv, pending_region_position);
+               } else {
+                       rv->move (x_delta, y_delta);
+               }
+               
+       } /* foreach region */
 
-                       if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
-
-                               /* INTER-LAYER MOVEMENT in the same track */
-                               y_delta = rtv->view()->child_height () * pointer_layer_span;
-                       }
-
-
-                       if (_brushing) {
-                               _editor->mouse_brush_insert_region (rv, pending_region_position);
-                       } else {
-                               rv->move (x_delta, y_delta);
-                       }
-
-               } /* foreach region */
-
-       } /* if do_move */
-
-       if (_first_move && _move_threshold_passed) {
+       if (first_move) {
                _editor->cursor_group->raise_to_top();
-               _first_move = false;
        }
 
        if (x_delta != 0 && !_brushing) {
                _editor->show_verbose_time_cursor (_last_frame_position, 10);
        }
-} 
+}
+
+void
+RegionMoveDrag::motion (GdkEvent* event, bool first_move)
+{
+       if (_copy && first_move) {
+               copy_regions (event);
+       }
+
+       RegionMotionDrag::motion (event, first_move);
+}
 
 void
-RegionMoveDrag::finished (GdkEvent* event)
+RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
 {
-       bool nocommit = true;
        vector<RegionView*> copies;
-       RouteTimeAxisView* source_tv;
        boost::shared_ptr<Diskstream> ds;
        boost::shared_ptr<Playlist> from_playlist;
        list<RegionView*> new_views;
@@ -752,20 +726,14 @@ RegionMoveDrag::finished (GdkEvent* event)
        pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
        nframes64_t drag_delta;
        bool changed_tracks, changed_position;
-       pair<TimeAxisView*, int> tvp;
        map<RegionView*, RouteTimeAxisView*> final;
+       RouteTimeAxisView* source_tv;
 
-       /* _first_move is set to false if the regionview has been moved in the 
-          motion handler. 
-       */
-
-       if (_first_move) {
+       if (!movement_occurred) {
                /* just a click */
-               goto out;
+               return;
        }
 
-       nocommit = false;
-
        if (Config->get_edit_mode() == Splice && !_editor->pre_drag_region_selection.empty()) {
                _editor->selection->set (_editor->pre_drag_region_selection);
                _editor->pre_drag_region_selection.clear ();
@@ -783,8 +751,6 @@ RegionMoveDrag::finished (GdkEvent* event)
                goto out;
        }
 
-       char* op_string;
-
        /* reverse this here so that we have the correct logic to finalize
           the drag.
        */
@@ -795,38 +761,27 @@ RegionMoveDrag::finished (GdkEvent* event)
 
        if (_copy) {
                if (_x_constrained) {
-                       op_string = _("fixed time region copy");
+                       _editor->begin_reversible_command (_("fixed time region copy"));
                } else {
-                       op_string = _("region copy");
+                       _editor->begin_reversible_command (_("region copy"));
                } 
        } else {
                if (_x_constrained) {
-                       op_string = _("fixed time region drag");
+                       _editor->begin_reversible_command (_("fixed time region drag"));
                } else {
-                       op_string = _("region drag");
+                       _editor->begin_reversible_command (_("region drag"));
                }
        }
 
-       _editor->begin_reversible_command (op_string);
        changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
-       tvp = _editor->trackview_by_y_position (_current_pointer_y);
-       changed_tracks = (tvp.first != &_primary->get_time_axis_view());
+       changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
 
        drag_delta = _primary->region()->position() - _last_frame_position;
 
-       _editor->track_canvas->update_now ();
+       _editor->update_canvas_now ();
 
        /* make a list of where each region ended up */
-       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-
-               double ix1, ix2, iy1, iy2;
-               (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
-               (*i)->get_canvas_frame()->i2w (ix1, iy1);
-               iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
-
-               pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
-               final[*i] = dynamic_cast<RouteTimeAxisView*> (tv.first);
-       }
+       final = find_time_axis_views ();
 
        for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
 
@@ -926,10 +881,14 @@ RegionMoveDrag::finished (GdkEvent* event)
                        /* get the playlist where this drag started. we can't use rv->region()->playlist()
                           because we may have copied the region and it has not been attached to a playlist.
                        */
+                       
+                       source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
+                       ds = source_tv->get_diskstream();
+                       from_playlist = ds->playlist();
 
-                       assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
-                       assert ((ds = source_tv->get_diskstream()));
-                       assert ((from_playlist = ds->playlist()));
+                       assert (source_tv);
+                       assert (ds);
+                       assert (from_playlist);
 
                        /* moved to a different audio track, without copying */
 
@@ -1001,22 +960,45 @@ RegionMoveDrag::finished (GdkEvent* event)
        }
                        
   out:
-       if (!nocommit) {
-               for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
-                       _editor->session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));     
-               }
-
-               _editor->commit_reversible_command ();
+       for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
+               _editor->session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));     
        }
+       
+       _editor->commit_reversible_command ();
 
        for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
                delete *x;
        }
 }
+
+
+bool
+RegionMoveDrag::x_move_allowed () const
+{
+       if (Config->get_edit_mode() == Lock) {
+               if (_copy) {
+                       return !_x_constrained;
+               } else {
+                       /* in locked edit mode, reverse the usual meaning of _x_constrained */
+                       return _x_constrained;
+               }
+       }
        
+       return !_x_constrained;
+}
+
+bool
+RegionInsertDrag::x_move_allowed () const
+{
+       if (Config->get_edit_mode() == Lock) {
+               return _x_constrained;
+       }
+
+       return !_x_constrained;
+}
 
 void
-RegionMoveDrag::copy_regions (GdkEvent* event)
+RegionMotionDrag::copy_regions (GdkEvent* event)
 {
        /* duplicate the regionview(s) and region(s) */
 
@@ -1057,7 +1039,8 @@ RegionMoveDrag::copy_regions (GdkEvent* event)
        _primary = new_regionviews.front();
        _views = new_regionviews;
                
-       swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
+       swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
+       
        /* 
           sync the canvas to what we think is its current state 
           without it, the canvas seems to 
@@ -1065,11 +1048,11 @@ RegionMoveDrag::copy_regions (GdkEvent* event)
           ..only if the mouse is in rapid motion at the time of the grab. 
           something to do with regionview creation raking so long?
        */
-       _editor->track_canvas->update_now();
+       _editor->update_canvas_now();
 }
 
 bool
-RegionMoveDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
+RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
 {
        /* Which trackview is this ? */
 
@@ -1077,6 +1060,10 @@ RegionMoveDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
        (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
        (*layer) = tvp.second;
 
+       if (*tv && (*tv)->layer_display() == Overlaid) {
+               *layer = 0;
+       }
+
        /* The region motion is only processed if the pointer is over
           an audio track.
        */
@@ -1095,16 +1082,10 @@ RegionMoveDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
 /** @param new_order New track order.
  *  @param old_order Old track order.
  *  @param visible_y_low Lowest visible order.
- *  @param visible_y_high Highest visible order.
- *  @param tracks Bitset of tracks indexed by order; 0 means a audio/MIDI track, 1 means something else.
- *  @param heigh_list Heights of tracks indexed by order.
  *  @return true if y movement should not happen, otherwise false.
  */
 bool
-RegionMoveDrag::y_movement_disallowed (
-       int new_order, int old_order, int y_span, int visible_y_low, int visible_y_high,
-       bitset<512> const & tracks, vector<int32_t> const & height_list
-       ) const
+RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
 {
        if (new_order != old_order) {
 
@@ -1113,7 +1094,7 @@ RegionMoveDrag::y_movement_disallowed (
                if (y_span > 0) {
 
                        /* moving up the canvas */
-                       if ( (new_order - y_span) >= visible_y_low) {
+                       if ( (new_order - y_span) >= tavs.visible_y_low) {
 
                                int32_t n = 0;
 
@@ -1121,13 +1102,13 @@ RegionMoveDrag::y_movement_disallowed (
                                int32_t visible_tracks = 0;
                                while (visible_tracks < y_span ) {
                                        visible_tracks++;
-                                       while (height_list[new_order - (visible_tracks - n)] == 0) {
+                                       while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
                                                /* passing through a hidden track */
                                                n--;
                                        }                 
                                }
                 
-                               if (tracks[new_order - (y_span - n)] != 0x00) {
+                               if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
                                        /* moving to a non-track; disallow */
                                        return true;
                                }
@@ -1141,20 +1122,20 @@ RegionMoveDrag::y_movement_disallowed (
                } else if (y_span < 0) {
 
                        /* moving down the canvas */
-                       if ((new_order - y_span) <= visible_y_high) {
+                       if ((new_order - y_span) <= tavs.visible_y_high) {
 
                                int32_t visible_tracks = 0;
                                int32_t n = 0;
                                while (visible_tracks > y_span ) {
                                        visible_tracks--;
                      
-                                       while (height_list[new_order - (visible_tracks - n)] == 0) {
+                                       while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
                                                /* passing through a hidden track */
                                                n++;
                                        }                
                                }
                                                
-                               if (tracks[new_order - (y_span - n)] != 0x00) {
+                               if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
                                        /* moving to a non-track; disallow */
                                        return true;
                                }
@@ -1171,10 +1152,10 @@ RegionMoveDrag::y_movement_disallowed (
                
                /* this is the pointer's track */
                
-               if ((new_order - y_span) > visible_y_high) {
+               if ((new_order - y_span) > tavs.visible_y_high) {
                        /* we will overflow */
                        return true;
-               } else if ((new_order - y_span) < visible_y_low) {
+               } else if ((new_order - y_span) < tavs.visible_y_low) {
                        /* we will overflow */
                        return true;
                }
@@ -1183,6 +1164,101 @@ RegionMoveDrag::y_movement_disallowed (
        return false;
 }
 
+
+RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
+       : RegionMotionDrag (e, i, p, v, b),
+         _copy (c)
+{
+       TimeAxisView* const tv = &_primary->get_time_axis_view ();
+       
+       _dest_trackview = tv;
+       if (tv->layer_display() == Overlaid) {
+               _dest_layer = 0;
+       } else {
+               _dest_layer = _primary->region()->layer ();
+       }
+
+       double speed = 1;
+       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
+       if (rtv && rtv->is_track()) {
+               speed = rtv->get_diskstream()->speed ();
+       }
+
+       _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
+}
+
+void
+RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
+{
+       RegionMotionDrag::start_grab (event, c);
+       
+       _pointer_frame_offset = _grab_frame - _last_frame_position;
+}
+
+RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
+       : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
+{
+       assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
+               (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
+
+       _primary = v->view()->create_region_view (r, false, false);
+       
+       _primary->get_canvas_group()->show ();
+       _primary->set_position (pos, 0);
+       _views.push_back (_primary);
+
+       _last_frame_position = pos;
+
+       _item = _primary->get_canvas_group ();
+       _dest_trackview = v;
+       _dest_layer = _primary->region()->layer ();
+}
+
+map<RegionView*, RouteTimeAxisView*>
+RegionMotionDrag::find_time_axis_views ()
+{
+       map<RegionView*, RouteTimeAxisView*> tav;
+       
+       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+
+               double ix1, ix2, iy1, iy2;
+               (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
+               (*i)->get_canvas_frame()->i2w (ix1, iy1);
+               iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
+
+               pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
+               tav[*i] = dynamic_cast<RouteTimeAxisView*> (tv.first);
+       }
+
+       return tav;
+}
+
+
+void
+RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
+{
+       _editor->update_canvas_now ();
+
+       map<RegionView*, RouteTimeAxisView*> final = find_time_axis_views ();
+       
+       RouteTimeAxisView* dest_rtv = final[_primary];
+
+       _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
+       _primary->get_canvas_group()->property_y() = 0;
+
+       boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
+
+       _editor->begin_reversible_command (_("insert region"));
+       XMLNode& before = playlist->get_state ();
+       playlist->add_region (_primary->region (), _last_frame_position);
+       _editor->session->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
+       _editor->commit_reversible_command ();
+
+       delete _primary;
+       _primary = 0;
+       _views.clear ();
+}
+
 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
        : RegionMoveDrag (e, i, p, v, false, false)
 {
@@ -1196,7 +1272,7 @@ struct RegionSelectionByPosition {
 };
 
 void
-RegionSpliceDrag::motion (GdkEvent* event)
+RegionSpliceDrag::motion (GdkEvent* /*event*/, bool)
 {
        RouteTimeAxisView* tv;
        layer_t layer;
@@ -1205,10 +1281,6 @@ RegionSpliceDrag::motion (GdkEvent* event)
                return;
        }
 
-       if (!_move_threshold_passed) {
-               return;
-       }
-
        int dir;
 
        if (_current_pointer_x - _grab_x > 0) {
@@ -1258,7 +1330,7 @@ RegionSpliceDrag::motion (GdkEvent* event)
 }
 
 void
-RegionSpliceDrag::finished (GdkEvent* event)
+RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
 {
        
 }
@@ -1274,29 +1346,24 @@ RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisVi
 void
 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 {
-       _source_trackview = _view;
        _dest_trackview = _view;
-       _dest_layer = _source_layer;
        
        Drag::start_grab (event);
 }
 
 
 void
-RegionCreateDrag::motion (GdkEvent* event)
+RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
 {
-       if (_move_threshold_passed) {
-               if (_first_move) {
-                       // TODO: create region-create-drag region view here
-                       _first_move = false;
-               }
-
-               // TODO: resize region-create-drag region view here
+       if (first_move) {
+               // TODO: create region-create-drag region view here
        }
+
+       // TODO: resize region-create-drag region view here
 } 
 
 void
-RegionCreateDrag::finished (GdkEvent* event)
+RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
 {
        MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
        if (!mtv) {
@@ -1311,7 +1378,7 @@ RegionCreateDrag::finished (GdkEvent* event)
                return;
        }
 
-       if (_first_move) {
+       if (!movement_occurred) {
                _editor->begin_reversible_command (_("create region"));
                XMLNode &before = mtv->playlist()->get_state();
 
@@ -1331,7 +1398,7 @@ RegionCreateDrag::finished (GdkEvent* event)
                _editor->commit_reversible_command();
 
        } else {
-               motion (event);
+               motion (event, false);
                // TODO: create region-create-drag region here
        }
 }
@@ -1340,15 +1407,13 @@ RegionCreateDrag::finished (GdkEvent* event)
 
 
 void
-RegionGainDrag::motion (GdkEvent* event)
+RegionGainDrag::motion (GdkEvent* /*event*/, bool)
 {
-       if (_first_move && _move_threshold_passed) {
-               _first_move = false;
-       }
+       
 }
 
 void
-RegionGainDrag::finished (GdkEvent *)
+RegionGainDrag::finished (GdkEvent *, bool)
 {
 
 }
@@ -1403,7 +1468,7 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 }
 
 void
-TrimDrag::motion (GdkEvent* event)
+TrimDrag::motion (GdkEvent* event, bool first_move)
 {
        RegionView* rv = _primary;
        nframes64_t frame_delta = 0;
@@ -1431,16 +1496,10 @@ TrimDrag::motion (GdkEvent* event)
                left_direction = false;
        }
 
-       if (obey_snap) {
-               _editor->snap_to (_current_pointer_frame);
-       }
+       _editor->snap_to_with_modifier (_current_pointer_frame, event);
 
-       if (_current_pointer_frame == _last_pointer_frame) {
-               return;
-       }
+       if (first_move) {
 
-       if (_first_move) {
-       
                string trim_type;
 
                switch (_operation) {
@@ -1477,6 +1536,10 @@ TrimDrag::motion (GdkEvent* event)
                }
        }
 
+       if (_current_pointer_frame == _last_pointer_frame) {
+               return;
+       }
+
        if (left_direction) {
                frame_delta = (_last_pointer_frame - _current_pointer_frame);
        } else {
@@ -1541,15 +1604,14 @@ TrimDrag::motion (GdkEvent* event)
        }
 
        _last_pointer_frame = _current_pointer_frame;
-       _first_move = false;
 }
 
 
 void
-TrimDrag::finished (GdkEvent* event)
+TrimDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       if (!_first_move) {
-               motion (event);
+       if (movement_occurred) {
+               motion (event, false);
                
                if (!_editor->selection->selected (_primary)) {
                        _editor->thaw_region_after_trim (*_primary);            
@@ -1576,10 +1638,9 @@ TrimDrag::finished (GdkEvent* event)
 }
 
 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
-       : Drag (e, i)
+       : Drag (e, i),
+         _copy (c)
 {
-       _copy = c;
-
        _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
        assert (_marker);
 }
@@ -1616,13 +1677,9 @@ MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 }
 
 void
-MeterMarkerDrag::motion (GdkEvent* event)
+MeterMarkerDrag::motion (GdkEvent* event, bool)
 {
-       nframes64_t adjusted_frame = adjusted_current_frame ();
-       
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               _editor->snap_to (adjusted_frame);
-       }
+       nframes64_t const adjusted_frame = adjusted_current_frame (event);
        
        if (adjusted_frame == _last_pointer_frame) {
                return;
@@ -1631,19 +1688,18 @@ MeterMarkerDrag::motion (GdkEvent* event)
        _marker->set_position (adjusted_frame);
        
        _last_pointer_frame = adjusted_frame;
-       _first_move = false;
 
        _editor->show_verbose_time_cursor (adjusted_frame, 10);
 }
 
 void
-MeterMarkerDrag::finished (GdkEvent* event)
+MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       if (_first_move) {
+       if (!movement_occurred) {
                return;
        }
 
-       motion (event);
+       motion (event, false);
        
        BBT_Time when;
        
@@ -1672,11 +1728,10 @@ MeterMarkerDrag::finished (GdkEvent* event)
 }
 
 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
-       : Drag (e, i)
+       : Drag (e, i),
+         _copy (c)
 {
-       _copy = c;
-
-       TempoMarker* _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
+       _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
        assert (_marker);
 }
 
@@ -1712,13 +1767,9 @@ TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 }
 
 void
-TempoMarkerDrag::motion (GdkEvent* event)
+TempoMarkerDrag::motion (GdkEvent* event, bool)
 {
-       nframes64_t adjusted_frame = adjusted_current_frame ();
-
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               _editor->snap_to (adjusted_frame);
-       }
+       nframes64_t const adjusted_frame = adjusted_current_frame (event);
        
        if (adjusted_frame == _last_pointer_frame) {
                return;
@@ -1731,17 +1782,16 @@ TempoMarkerDrag::motion (GdkEvent* event)
        _editor->show_verbose_time_cursor (adjusted_frame, 10);
 
        _last_pointer_frame = adjusted_frame;
-       _first_move = false;
 }
 
 void
-TempoMarkerDrag::finished (GdkEvent* event)
+TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       if (_first_move) {
+       if (!movement_occurred) {
                return;
        }
        
-       motion (event);
+       motion (event, false);
        
        BBT_Time when;
        
@@ -1787,7 +1837,7 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
                
                nframes64_t where = _editor->event_frame (event, 0, 0);
 
-               _editor->snap_to (where);
+               _editor->snap_to_with_modifier (where, event);
                _editor->playhead_cursor->set_position (where);
 
        }
@@ -1804,22 +1854,14 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
                }
        }
 
-       _pointer_frame_offset = _grab_frame - _cursor->current_frame;   
-       
        _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
 }
 
 void
-CursorDrag::motion (GdkEvent* event)
+CursorDrag::motion (GdkEvent* event, bool)
 {
-       nframes64_t adjusted_frame = adjusted_current_frame ();
-       
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               if (_cursor == _editor->playhead_cursor) {
-                       _editor->snap_to (adjusted_frame);
-               }
-       }
-       
+       nframes64_t const adjusted_frame = adjusted_current_frame (event);
+
        if (adjusted_frame == _last_pointer_frame) {
                return;
        }
@@ -1829,24 +1871,23 @@ CursorDrag::motion (GdkEvent* event)
        _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
 
 #ifdef GTKOSX
-       _editor->track_canvas->update_now ();
+       _editor->update_canvas_now ();
 #endif
        _editor->UpdateAllTransportClocks (_cursor->current_frame);
 
        _last_pointer_frame = adjusted_frame;
-       _first_move = false;
 }
 
 void
-CursorDrag::finished (GdkEvent* event)
+CursorDrag::finished (GdkEvent* event, bool movement_occurred)
 {
        _editor->_dragging_playhead = false;
 
-       if (_first_move && _stop) {
+       if (!movement_occurred && _stop) {
                return;
        }
        
-       motion (event);
+       motion (event, false);
        
        if (_item == &_editor->playhead_cursor->canvas_item) {
                if (_editor->session) {
@@ -1874,15 +1915,11 @@ FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 }
 
 void
-FadeInDrag::motion (GdkEvent* event)
+FadeInDrag::motion (GdkEvent* event, bool)
 {
        nframes64_t fade_length;
 
-       nframes64_t pos = adjusted_current_frame ();
-
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               _editor->snap_to (pos);
-       }
+       nframes64_t const pos = adjusted_current_frame (event);
        
        boost::shared_ptr<Region> region = _primary->region ();
 
@@ -1906,21 +1943,19 @@ FadeInDrag::motion (GdkEvent* event)
        }
 
        _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
-
-       _first_move = false;
 }
 
 void
-FadeInDrag::finished (GdkEvent* event)
+FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       nframes64_t fade_length;
-
-       if (_first_move) {
+       if (!movement_occurred) {
                return;
        }
 
-       nframes64_t const pos = adjusted_current_frame ();
+       nframes64_t fade_length;
 
+       nframes64_t const pos = adjusted_current_frame (event);
+       
        boost::shared_ptr<Region> region = _primary->region ();
 
        if (pos < (region->position() + 64)) {
@@ -1972,15 +2007,11 @@ FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 }
 
 void
-FadeOutDrag::motion (GdkEvent* event)
+FadeOutDrag::motion (GdkEvent* event, bool)
 {
        nframes64_t fade_length;
 
-       nframes64_t pos = adjusted_current_frame ();
-
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               _editor->snap_to (pos);
-       }
+       nframes64_t const pos = adjusted_current_frame (event);
 
        boost::shared_ptr<Region> region = _primary->region ();
        
@@ -2006,24 +2037,18 @@ FadeOutDrag::motion (GdkEvent* event)
        }
 
        _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
-
-       _first_move = false;
 }
 
 void
-FadeOutDrag::finished (GdkEvent* event)
+FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       if (_first_move) {
+       if (!movement_occurred) {
                return;
        }
 
        nframes64_t fade_length;
 
-       nframes64_t pos = adjusted_current_frame ();
-
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               _editor->snap_to (pos);
-       }
+       nframes64_t const pos = adjusted_current_frame (event);
 
        boost::shared_ptr<Region> region = _primary->region ();
 
@@ -2162,7 +2187,7 @@ MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 }
 
 void
-MarkerDrag::motion (GdkEvent* event)
+MarkerDrag::motion (GdkEvent* event, bool)
 {
        nframes64_t f_delta = 0;
        bool is_start;
@@ -2171,13 +2196,9 @@ MarkerDrag::motion (GdkEvent* event)
        Location  *real_location;
        Location *copy_location = 0;
 
-       nframes64_t newframe = adjusted_current_frame ();
+       nframes64_t const newframe = adjusted_current_frame (event);
 
        nframes64_t next = newframe;
-
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               _editor->snap_to (newframe, 0, true);
-       }
        
        if (_current_pointer_frame == _last_pointer_frame) { 
                return;
@@ -2305,7 +2326,6 @@ MarkerDrag::motion (GdkEvent* event)
        }
 
        _last_pointer_frame = _current_pointer_frame;
-       _first_move = false;
 
        assert (!_copied_locations.empty());
 
@@ -2313,15 +2333,15 @@ MarkerDrag::motion (GdkEvent* event)
        _editor->show_verbose_time_cursor (newframe, 10);
 
 #ifdef GTKOSX
-       _editor->track_canvas->update_now ();
+       _editor->update_canvas_now ();
 #endif
        _editor->edit_point_clock.set (copy_location->start());
 }
 
 void
-MarkerDrag::finished (GdkEvent* event)
+MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       if (_first_move) {
+       if (!movement_occurred) {
 
                /* just a click, do nothing but finish
                   off the selection process
@@ -2347,7 +2367,6 @@ MarkerDrag::finished (GdkEvent* event)
 
        _editor->_dragging_edit_point = false;
        
-
        _editor->begin_reversible_command ( _("move marker") );
        XMLNode &before = _editor->session->locations()->get_state();
 
@@ -2397,13 +2416,13 @@ ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
          _cumulative_x_drag (0),
          _cumulative_y_drag (0)
 {
-       ControlPoint* _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
+       _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
        assert (_point);
 }
 
 
 void
-ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
+ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 {
        Drag::start_grab (event, _editor->fader_cursor);
 
@@ -2427,7 +2446,7 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 }
 
 void
-ControlPointDrag::motion (GdkEvent* event)
+ControlPointDrag::motion (GdkEvent* event, bool)
 {
        double dx = _current_pointer_x - _last_pointer_x;
        double dy = _current_pointer_y - _last_pointer_y;
@@ -2471,8 +2490,8 @@ ControlPointDrag::motion (GdkEvent* event)
        //translate cx to frames
        nframes64_t cx_frames = _editor->unit_to_frame (cx);
 
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !_x_constrained) {
-               _editor->snap_to (cx_frames);
+       if (!_x_constrained) {
+               _editor->snap_to_with_modifier (cx_frames, event);
        }
 
        float const fraction = 1.0 - (cy / _point->line().height());
@@ -2482,14 +2501,12 @@ ControlPointDrag::motion (GdkEvent* event)
        _point->line().point_drag (*_point, cx_frames, fraction, push);
        
        _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
-
-       _first_move = false;
 }
 
 void
-ControlPointDrag::finished (GdkEvent* event)
+ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       if (_first_move) {
+       if (!movement_occurred) {
 
                /* just a click */
                
@@ -2498,11 +2515,23 @@ ControlPointDrag::finished (GdkEvent* event)
                }
 
        } else {
-               motion (event);
+               motion (event, false);
        }
        _point->line().end_drag (_point);
 }
 
+bool
+ControlPointDrag::active (Editing::MouseMode m)
+{
+       if (m == Editing::MouseGain) {
+               /* always active in mouse gain */
+               return true;
+       }
+
+       /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
+       return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
+}
+
 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
        : Drag (e, i),
          _line (0),
@@ -2511,7 +2540,7 @@ LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
 
 }
 void
-LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
+LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 {
        _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
        assert (_line);
@@ -2552,7 +2581,7 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 }
 
 void
-LineDrag::motion (GdkEvent* event)
+LineDrag::motion (GdkEvent* event, bool)
 {
        double dy = _current_pointer_y - _last_pointer_y;
        
@@ -2583,9 +2612,9 @@ LineDrag::motion (GdkEvent* event)
 }
 
 void
-LineDrag::finished (GdkEvent* event)
+LineDrag::finished (GdkEvent* event, bool)
 {
-       motion (event);
+       motion (event, false);
        _line->end_drag (0);
 }
 
@@ -2597,7 +2626,7 @@ RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 }
 
 void
-RubberbandSelectDrag::motion (GdkEvent* event)
+RubberbandSelectDrag::motion (GdkEvent* event, bool first_move)
 {
        nframes64_t start;
        nframes64_t end;
@@ -2610,11 +2639,11 @@ RubberbandSelectDrag::motion (GdkEvent* event)
                return;
        }
 
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
-               if (_first_move) {
-                       _editor->snap_to (_grab_frame);
+       if (Config->get_rubberbanding_snaps_to_grid()) {
+               if (first_move) {
+                       _editor->snap_to_with_modifier (_grab_frame, event);
                } 
-               _editor->snap_to (_current_pointer_frame);
+               _editor->snap_to_with_modifier (_current_pointer_frame, event);
        }
 
        /* base start and end on initial click position */
@@ -2650,18 +2679,17 @@ RubberbandSelectDrag::motion (GdkEvent* event)
                _editor->rubberband_rect->raise_to_top();
                
                _last_pointer_frame = _current_pointer_frame;
-               _first_move = false;
 
                _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
        }
 }
 
 void
-RubberbandSelectDrag::finished (GdkEvent* event)
+RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       if (!_first_move) {
+       if (movement_occurred) {
 
-               motion (event);
+               motion (event, false);
 
                double y1,y2;
                if (_current_pointer_y < _grab_y) {
@@ -2674,17 +2702,17 @@ RubberbandSelectDrag::finished (GdkEvent* event)
 
 
                Selection::Operation op = Keyboard::selection_type (event->button.state);
-               bool commit;
+               bool committed;
 
                _editor->begin_reversible_command (_("rubberband selection"));
 
                if (_grab_frame < _last_pointer_frame) {
-                       commit = _editor->select_all_within (_grab_frame, _last_pointer_frame, y1, y2, _editor->track_views, op);
+                       committed = _editor->select_all_within (_grab_frame, _last_pointer_frame, y1, y2, _editor->track_views, op);
                } else {
-                       commit = _editor->select_all_within (_last_pointer_frame, _grab_frame, y1, y2, _editor->track_views, op);
+                       committed = _editor->select_all_within (_last_pointer_frame, _grab_frame, y1, y2, _editor->track_views, op);
                }               
 
-               if (commit) {
+               if (!committed) {
                        _editor->commit_reversible_command ();
                }
                
@@ -2709,13 +2737,11 @@ TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 }
 
 void
-TimeFXDrag::motion (GdkEvent* event)
+TimeFXDrag::motion (GdkEvent* event, bool)
 {
        RegionView* rv = _primary;
 
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               _editor->snap_to (_current_pointer_frame);
-       }
+       _editor->snap_to_with_modifier (_current_pointer_frame, event);
 
        if (_current_pointer_frame == _last_pointer_frame) {
                return;
@@ -2726,17 +2752,16 @@ TimeFXDrag::motion (GdkEvent* event)
        }
 
        _last_pointer_frame = _current_pointer_frame;
-       _first_move = false;
 
        _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
 }
 
 void
-TimeFXDrag::finished (GdkEvent* event)
+TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
 {
        _primary->get_time_axis_view().hide_timestretch ();
 
-       if (_first_move) {
+       if (!movement_occurred) {
                return;
        }
 
@@ -2768,9 +2793,31 @@ TimeFXDrag::finished (GdkEvent* event)
        }
 }
 
+void
+ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
+{
+       Drag::start_grab (event);
+}
+
+void
+ScrubDrag::motion (GdkEvent* /*event*/, bool)
+{
+       _editor->scrub ();
+}
+
+void
+ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
+{
+       if (movement_occurred && _editor->session) {
+               /* make sure we stop */
+               _editor->session->request_transport_speed (0.0);
+       } 
+}
+
 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
        : Drag (e, i),
-         _operation (o)
+         _operation (o),
+         _copy (false)
 {
 
 }
@@ -2833,18 +2880,14 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
 }
 
 void
-SelectionDrag::motion (GdkEvent* event)
+SelectionDrag::motion (GdkEvent* event, bool first_move)
 {
        nframes64_t start = 0;
        nframes64_t end = 0;
        nframes64_t length;
 
-       nframes64_t pending_position = adjusted_current_frame ();
+       nframes64_t const pending_position = adjusted_current_frame (event);
        
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               _editor->snap_to (pending_position);
-       }
-
        /* only alter selection if the current frame is 
           different from the last frame position (adjusted)
         */
@@ -2856,7 +2899,7 @@ SelectionDrag::motion (GdkEvent* event)
        switch (_operation) {
        case CreateSelection:
                
-               if (_first_move) {
+               if (first_move) {
                        _editor->snap_to (_grab_frame);
                }
                
@@ -2872,7 +2915,7 @@ SelectionDrag::motion (GdkEvent* event)
                   or create a new selection->
                */
                
-               if (_first_move) {
+               if (first_move) {
                        
                        _editor->begin_reversible_command (_("range selection"));
                        
@@ -2889,7 +2932,7 @@ SelectionDrag::motion (GdkEvent* event)
                
        case SelectionStartTrim:
                
-               if (_first_move) {
+               if (first_move) {
                        _editor->begin_reversible_command (_("trim selection start"));
                }
                
@@ -2905,7 +2948,7 @@ SelectionDrag::motion (GdkEvent* event)
                
        case SelectionEndTrim:
                
-               if (_first_move) {
+               if (first_move) {
                        _editor->begin_reversible_command (_("trim selection end"));
                }
                
@@ -2922,7 +2965,7 @@ SelectionDrag::motion (GdkEvent* event)
                
        case SelectionMove:
                
-               if (_first_move) {
+               if (first_move) {
                        _editor->begin_reversible_command (_("move selection"));
                }
                
@@ -2939,7 +2982,7 @@ SelectionDrag::motion (GdkEvent* event)
                break;
        }
        
-       if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->canvas_width) {
+       if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
                _editor->start_canvas_autoscroll (1, 0);
        }
 
@@ -2948,7 +2991,6 @@ SelectionDrag::motion (GdkEvent* event)
        }
 
        _last_pointer_frame = pending_position;
-       _first_move = false;
 
        if (_operation == SelectionMove) {
                _editor->show_verbose_time_cursor(start, 10);   
@@ -2958,10 +3000,10 @@ SelectionDrag::motion (GdkEvent* event)
 }
 
 void
-SelectionDrag::finished (GdkEvent* event)
+SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       if (!_first_move) {
-               motion (event);
+       if (movement_occurred) {
+               motion (event, false);
                /* XXX this is not object-oriented programming at all. ick */
                if (_editor->selection->time.consolidate()) {
                        _editor->selection->TimeChanged ();
@@ -2984,7 +3026,8 @@ SelectionDrag::finished (GdkEvent* event)
 
 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
        : Drag (e, i),
-         _operation (o)
+         _operation (o),
+         _copy (false)
 {
        _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
        _drag_rect->hide ();
@@ -3026,7 +3069,7 @@ RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 }
 
 void
-RangeMarkerBarDrag::motion (GdkEvent* event)
+RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
 {
        nframes64_t start = 0;
        nframes64_t end = 0;
@@ -3048,9 +3091,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event)
                break;
        }
        
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               _editor->snap_to (_current_pointer_frame);
-       }
+       _editor->snap_to_with_modifier (_current_pointer_frame, event);
 
        /* only alter selection if the current frame is 
           different from the last frame position.
@@ -3064,7 +3105,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event)
        case CreateRangeMarker:
        case CreateTransportMarker:
        case CreateCDMarker:
-               if (_first_move) {
+               if (first_move) {
                        _editor->snap_to (_grab_frame);
                }
                
@@ -3080,7 +3121,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event)
                   or create a new selection.
                */
                
-               if (_first_move) {
+               if (first_move) {
                        
                        _editor->temp_location->set (start, end);
                        
@@ -3094,7 +3135,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event)
                break;          
        }
        
-       if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->canvas_width) {
+       if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
                _editor->start_canvas_autoscroll (1, 0);
        }
        
@@ -3110,21 +3151,21 @@ RangeMarkerBarDrag::motion (GdkEvent* event)
        }
 
        _last_pointer_frame = _current_pointer_frame;
-       _first_move = false;
 
        _editor->show_verbose_time_cursor (_current_pointer_frame, 10); 
        
 }
 
 void
-RangeMarkerBarDrag::finished (GdkEvent* event)
+RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
 {
        Location * newloc = 0;
        string rangename;
        int flags;
        
-       if (!_first_move) {
-               motion (event);
+       if (movement_occurred) {
+               motion (event, false);
+               _drag_rect->hide();
 
                switch (_operation) {
                case CreateRangeMarker:
@@ -3146,15 +3187,12 @@ RangeMarkerBarDrag::finished (GdkEvent* event)
                        XMLNode &after = _editor->session->locations()->get_state();
                        _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
                        _editor->commit_reversible_command ();
-                       
-                       _drag_rect->hide();
                        break;
                    }
 
                case CreateTransportMarker:
                        // popup menu to pick loop or punch
                        _editor->new_transport_marker_context_menu (&event->button, _item);
-                       
                        break;
                }
        } else {
@@ -3216,17 +3254,15 @@ MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 }
 
 void
-MouseZoomDrag::motion (GdkEvent* event)
+MouseZoomDrag::motion (GdkEvent* event, bool first_move)
 {
        nframes64_t start;
        nframes64_t end;
 
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               _editor->snap_to (_current_pointer_frame);
-               
-               if (_first_move) {
-                       _editor->snap_to (_grab_frame);
-               }
+       _editor->snap_to_with_modifier (_current_pointer_frame, event);
+       
+       if (first_move) {
+               _editor->snap_to_with_modifier (_grab_frame, event);
        }
                
        if (_current_pointer_frame == _last_pointer_frame) {
@@ -3244,7 +3280,7 @@ MouseZoomDrag::motion (GdkEvent* event)
        
        if (start != end) {
 
-               if (_first_move) {
+               if (first_move) {
                        _editor->zoom_rect->show();
                        _editor->zoom_rect->raise_to_top();
                }
@@ -3252,17 +3288,16 @@ MouseZoomDrag::motion (GdkEvent* event)
                _editor->reposition_zoom_rect(start, end);
 
                _last_pointer_frame = _current_pointer_frame;
-               _first_move = false;
 
                _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
        }
 }
 
 void
-MouseZoomDrag::finished (GdkEvent* event)
+MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       if (!_first_move) {
-               motion (event);
+       if (movement_occurred) {
+               motion (event, false);
                
                if (_grab_frame < _last_pointer_frame) {
                        _editor->temporal_zoom_by_frame (_grab_frame, _last_pointer_frame, "mouse zoom");