Move Diskstream ownership to Track, so that Session no longer holds lists of Diskstre...
[ardour.git] / gtk2_ardour / editor_drag.cc
index d3ada92ff71b08d95f2b357eed77928a80fa1f12..d9da60e0c8705f23242adea60209722ded17b62e 100644 (file)
 
 */
 
+#define __STDC_LIMIT_MACROS 1
+#include <stdint.h>
 #include "pbd/memento_command.h"
 #include "pbd/basename.h"
-#include "ardour/diskstream.h"
+#include "pbd/stateful_diff_command.h"
 #include "ardour/session.h"
 #include "ardour/dB.h"
 #include "ardour/region_factory.h"
-#include "ardour/midi_diskstream.h"
 #include "editor.h"
 #include "i18n.h"
 #include "keyboard.h"
@@ -53,15 +54,128 @@ using Gtkmm2ext::Keyboard;
 
 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
 
+DragManager::DragManager (Editor* e)
+       : _editor (e)
+       , _ending (false)
+       , _current_pointer_frame (0)
+{
+
+}
+
+DragManager::~DragManager ()
+{
+       abort ();
+}
+
+void
+DragManager::abort ()
+{
+       for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
+               (*i)->end_grab (0);
+               delete *i;
+       }
+
+       _drags.clear ();
+}
+
+void
+DragManager::break_drag ()
+{
+       _ending = true;
+       
+       for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
+               (*i)->break_drag ();
+               delete *i;
+       }
+
+       _drags.clear ();
+
+       _ending = false;
+}
+
+void
+DragManager::add (Drag* d)
+{
+       d->set_manager (this);
+       _drags.push_back (d);
+}
+
+void
+DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
+{
+       assert (_drags.empty ());
+       d->set_manager (this);
+       _drags.push_back (d);
+       start_grab (e);
+}
+
+void
+DragManager::start_grab (GdkEvent* e)
+{
+       _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
+       
+       for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
+               (*i)->start_grab (e);
+       }
+}
+
+bool
+DragManager::end_grab (GdkEvent* e)
+{
+       _ending = true;
+       
+       bool r = false;
+       for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
+               bool const t = (*i)->end_grab (e);
+               if (t) {
+                       r = true;
+               }
+               delete *i;
+       }
+
+       _drags.clear ();
+
+       _ending = false;
+       
+       return r;
+}
+
+bool
+DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
+{
+       bool r = false;
+
+       _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
+       
+       for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
+               bool const t = (*i)->motion_handler (e, from_autoscroll);
+               if (t) {
+                       r = true;
+               }
+               
+       }
+
+       return r;
+}
+
+bool
+DragManager::have_item (ArdourCanvas::Item* i) const
+{
+       list<Drag*>::const_iterator j = _drags.begin ();
+       while (j != _drags.end() && (*j)->item () != i) {
+               ++j;
+       }
+
+       return j != _drags.end ();
+}
+
 Drag::Drag (Editor* e, ArdourCanvas::Item* i) 
        : _editor (e)
        , _item (i)
        , _pointer_frame_offset (0)
-       , _have_transaction (false)
        , _move_threshold_passed (false)
        , _grab_frame (0)
        , _last_pointer_frame (0)
-       , _current_pointer_frame (0)
 {
 
 }
@@ -104,15 +218,8 @@ Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
        _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
        _grab_frame = adjusted_frame (_grab_frame, event);
        _last_pointer_frame = _grab_frame;
-       _current_pointer_frame = _grab_frame;
-       _current_pointer_x = _grab_x;
-       _current_pointer_y = _grab_y;
-       _last_pointer_x = _current_pointer_x;
-       _last_pointer_y = _current_pointer_y;
-
-       _original_x = 0;
-       _original_y = 0;
-       _item->i2w (_original_x, _original_y);
+       _last_pointer_x = _grab_x;
+       _last_pointer_y = _grab_y;
 
        _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
                              *cursor,
@@ -142,20 +249,14 @@ Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
 bool
 Drag::end_grab (GdkEvent* event)
 {
-       _ending = true;
-
        _editor->stop_canvas_autoscroll ();
 
        _item->ungrab (event ? event->button.time : 0);
 
-       _last_pointer_x = _current_pointer_x;
-       _last_pointer_y = _current_pointer_y;
        finished (event, _move_threshold_passed);
 
        _editor->hide_verbose_canvas_cursor();
 
-       _ending = false;
-
        return _move_threshold_passed;
 }
 
@@ -178,25 +279,26 @@ Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
 nframes64_t
 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
 {
-       return adjusted_frame (_current_pointer_frame, event, snap);
+       return adjusted_frame (_drags->current_pointer_frame (), event, snap);
 }
 
 bool
 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
 {
-       _last_pointer_x = _current_pointer_x;
-       _last_pointer_y = _current_pointer_y;
-       _last_pointer_frame = adjusted_current_frame (event);
-       _current_pointer_frame = _editor->event_frame (event, &_current_pointer_x, &_current_pointer_y);
+       /* check to see if we have moved in any way that matters since the last motion event */
+       if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
+            (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
+               return false;
+       }
 
        pair<nframes64_t, int> const threshold = move_threshold ();
 
        bool const old_move_threshold_passed = _move_threshold_passed;
-       
+
        if (!from_autoscroll && !_move_threshold_passed) {
 
                bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
-               bool const yp = (::fabs ((_current_pointer_y - _grab_y)) >= threshold.second);
+               bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
 
                _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
        }
@@ -209,6 +311,11 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
                        }
 
                        motion (event, _move_threshold_passed != old_move_threshold_passed);
+
+                       _last_pointer_x = _drags->current_pointer_x ();
+                       _last_pointer_y = _drags->current_pointer_y ();
+                       _last_pointer_frame = adjusted_current_frame (event);
+                       
                        return true;
                }
        }
@@ -216,46 +323,49 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
        return false;
 }
 
-
 void
 Drag::break_drag ()
 {
-       _editor->stop_canvas_autoscroll ();
-       _editor->hide_verbose_canvas_cursor ();
-
        if (_item) {
                _item->ungrab (0);
+       }
 
-               /* put it back where it came from */
+       aborted ();
 
-               double cxw, cyw;
-               cxw = 0;
-               cyw = 0;
-               _item->i2w (cxw, cyw);
-               _item->move (_original_x - cxw, _original_y - cyw);
-       }
+       _editor->stop_canvas_autoscroll ();
+       _editor->hide_verbose_canvas_cursor ();
 }
 
-
 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
        : Drag (e, i),
-         _primary (p),
-         _views (v)
+         _primary (p)
 {
-       RegionView::RegionViewGoingAway.connect (death_connection, ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
+       for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
+               _views.push_back (DraggingView (*i));
+       }
+       
+       RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
 }
 
 void
 RegionDrag::region_going_away (RegionView* v)
 {
-       _views.remove (v);
+       list<DraggingView>::iterator i = _views.begin ();
+       while (i != _views.end() && i->view != v) {
+               ++i;
+       }
+
+       if (i != _views.end()) {
+               _views.erase (i);
+       }
 }
 
 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)
+         _brushing (b),
+         _total_x_delta (0)
 {
 
 }
@@ -352,9 +462,9 @@ RegionMotionDrag::compute_y_delta (
                        }
                }
 
-               for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+               for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
 
-                       RegionView* rv = (*i);
+                       RegionView* rv = i->view;
 
                        if (rv->region()->locked()) {
                                continue;
@@ -443,9 +553,9 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_
 
                if (*pending_region_position <= _last_frame_position) {
 
-                       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
 
-                               RegionView* rv = (*i);
+                               RegionView* rv = i->view;
 
                                // If any regionview is at zero, we need to know so we can stop further leftward motion.
 
@@ -528,9 +638,9 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
 
        pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
 
-       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
 
-               RegionView* rv = (*i);
+               RegionView* rv = i->view;
 
                if (rv->region()->locked()) {
                        continue;
@@ -689,6 +799,8 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
 
        } /* foreach region */
 
+       _total_x_delta += x_delta;
+       
        if (first_move) {
                _editor->cursor_group->raise_to_top();
        }
@@ -712,8 +824,9 @@ void
 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
 {
        vector<RegionView*> copies;
-       boost::shared_ptr<Diskstream> ds;
+       boost::shared_ptr<Track> tr;
        boost::shared_ptr<Playlist> from_playlist;
+       boost::shared_ptr<Playlist> to_playlist;
        RegionSelection new_views;
        typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
        PlaylistSet modified_playlists;
@@ -724,6 +837,7 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
        bool changed_tracks, changed_position;
        map<RegionView*, pair<RouteTimeAxisView*, int> > final;
        RouteTimeAxisView* source_tv;
+        vector<StatefulDiffCommand*> sdc;
 
        if (!movement_occurred) {
                /* just a click */
@@ -734,8 +848,8 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
                /* all changes were made during motion event handlers */
 
                if (_copy) {
-                       for (list<RegionView*>::iterator i = _views.begin(); i != _views.end(); ++i) {
-                               copies.push_back (*i);
+                       for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
+                               copies.push_back (i->view);
                        }
                }
 
@@ -764,8 +878,6 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
                }
        }
 
-       _have_transaction = true;
-
        changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
        changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
 
@@ -776,14 +888,19 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
        /* make a list of where each region ended up */
        final = find_time_axis_views_and_layers ();
 
-       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
+        cerr << "Iterate over " << _views.size() << " views\n";
+
+       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
 
-               RegionView* rv = (*i);
-               RouteTimeAxisView* dest_rtv = final[*i].first;
-               layer_t dest_layer = final[*i].second;
+               RegionView* rv = i->view;
+               RouteTimeAxisView* dest_rtv = final[rv].first;
+               layer_t dest_layer = final[rv].second;
 
                nframes64_t where;
 
+                from_playlist.reset ();
+                to_playlist.reset ();
+
                if (rv->region()->locked()) {
                        ++i;
                        continue;
@@ -813,7 +930,7 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
 
                if (changed_tracks || _copy) {
 
-                       boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
+                       to_playlist = dest_rtv->playlist();
 
                        if (!to_playlist) {
                                ++i;
@@ -827,10 +944,18 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
                        insert_result = modified_playlists.insert (to_playlist);
 
                        if (insert_result.second) {
-                               _editor->session()->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
+                                to_playlist->clear_history ();
                        }
 
+                        cerr << "To playlist " << to_playlist->name() << " region history contains "
+                             << to_playlist->region_list().change().added.size() << " adds and " 
+                             << to_playlist->region_list().change().removed.size() << " removes\n";
+
+                        cerr << "Adding new region " << new_region->id() << " based on "
+                             << rv->region()->id() << endl;
+                        
                        to_playlist->add_region (new_region, where);
+
                        if (dest_rtv->view()->layer_display() == Stacked) {
                                new_region->set_layer (dest_layer);
                                new_region->set_pending_explicit_relayer (true);
@@ -846,6 +971,8 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
                        }
 
                } else {
+                       rv->region()->clear_history ();
+
                        /*
                           motion on the same track. plonk the previously reparented region
                           back to its original canvas group (its streamview).
@@ -853,30 +980,31 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
                        */
 
                        rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
-                       rv->get_canvas_group()->property_y() = 0;
+                       rv->get_canvas_group()->property_y() = i->initial_y;
+                       rv->get_time_axis_view().reveal_dependent_views (*rv);
 
                        /* just change the model */
 
                        boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
-
+                       
                        if (dest_rtv->view()->layer_display() == Stacked) {
                                rv->region()->set_layer (dest_layer);
                                rv->region()->set_pending_explicit_relayer (true);
                        }
+                       
+                       /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
 
-                       insert_result = modified_playlists.insert (playlist);
-
-                       if (insert_result.second) {
-                               _editor->session()->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
-                       }
-                       /* freeze to avoid lots of relayering in the case of a multi-region drag */
                        frozen_insert_result = frozen_playlists.insert(playlist);
 
                        if (frozen_insert_result.second) {
                                playlist->freeze();
                        }
 
+                        cerr << "Moving region " << rv->region()->id() << endl;
+
                        rv->region()->set_position (where, (void*) this);
+
+                       sdc.push_back (new StatefulDiffCommand (rv->region()));
                }
 
                if (changed_tracks && !_copy) {
@@ -886,11 +1014,11 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
                        */
 
                        source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
-                       ds = source_tv->get_diskstream();
-                       from_playlist = ds->playlist();
+                       tr = source_tv->track();
+                       from_playlist = tr->playlist();
 
                        assert (source_tv);
-                       assert (ds);
+                       assert (tr);
                        assert (from_playlist);
 
                        /* moved to a different audio track, without copying */
@@ -909,9 +1037,15 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
                        insert_result = modified_playlists.insert (from_playlist);
 
                        if (insert_result.second) {
-                               _editor->session()->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
+                               from_playlist->clear_history ();
                        }
+                        
+                        cerr << "From playlist " << from_playlist->name() << " region history contains "
+                             << from_playlist->region_list().change().added.size() << " adds and " 
+                             << from_playlist->region_list().change().removed.size() << " removes\n";
 
+                        cerr << "removing region " << rv->region() << endl;
+                        
                        from_playlist->remove_region (rv->region());
 
                        /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
@@ -932,7 +1066,16 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
                        */
 
                        if (_views.empty()) {
-                               break;
+                                if (to_playlist) {
+                                        sdc.push_back (new StatefulDiffCommand (to_playlist));
+                                        cerr << "Saved diff for to:" << to_playlist->name() << endl;
+                                }
+                                
+                                if (from_playlist && (from_playlist != to_playlist)) {
+                                        sdc.push_back (new StatefulDiffCommand (from_playlist));
+                                        cerr << "Saved diff for from:" << from_playlist->name() << endl;
+                                }                              
+                                break;
                        } else {
                                i = _views.begin();
                        }
@@ -944,11 +1087,25 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
                if (_copy) {
                        copies.push_back (rv);
                }
+
+                cerr << "Done with TV, top = " << to_playlist << " from = " << from_playlist << endl;
+
+                if (to_playlist) {
+                        sdc.push_back (new StatefulDiffCommand (to_playlist));
+                        cerr << "Saved diff for to:" << to_playlist->name() << endl;
+                }
+
+                if (from_playlist && (from_playlist != to_playlist)) {
+                        sdc.push_back (new StatefulDiffCommand (from_playlist));
+                        cerr << "Saved diff for from:" << from_playlist->name() << endl;
+                }
        }
+
        /*
           if we've created new regions either by copying or moving 
           to a new track, we want to replace the old selection with the new ones 
        */
+
        if (new_views.size() > 0) {
                _editor->selection->set (new_views);
        }
@@ -958,9 +1115,9 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
        }
 
   out:
-       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()));
-       }
+        for (vector<StatefulDiffCommand*>::iterator i = sdc.begin(); i != sdc.end(); ++i) {
+               _editor->session()->add_command (*i);
+        }
 
        _editor->commit_reversible_command ();
 
@@ -969,6 +1126,41 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
        }
 }
 
+void
+RegionMoveDrag::aborted ()
+{
+       if (_copy) {
+
+               for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                       delete i->view;
+               }
+
+               _views.clear ();
+
+       } else {
+               RegionMotionDrag::aborted ();
+       }
+}
+
+void
+RegionMotionDrag::aborted ()
+{
+       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+               RegionView* rv = i->view;
+               TimeAxisView* tv = &(rv->get_time_axis_view ());
+               RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
+               assert (rtv);
+               rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
+               rv->get_canvas_group()->property_y() = 0;
+               rv->get_time_axis_view().reveal_dependent_views (*rv);
+               rv->fake_set_opaque (false);
+               rv->move (-_total_x_delta, 0);
+               rv->set_height (rtv->view()->child_height ());
+       }
+
+       _editor->update_canvas_now ();
+}
+                                     
 
 bool
 RegionMotionDrag::x_move_allowed () const
@@ -986,21 +1178,23 @@ RegionMotionDrag::copy_regions (GdkEvent* event)
 {
        /* duplicate the regionview(s) and region(s) */
 
-       list<RegionView*> new_regionviews;
+       list<DraggingView> new_regionviews;
 
-       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
 
-               RegionView* rv = (*i);
+               RegionView* rv = i->view;
                AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
                MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
 
                const boost::shared_ptr<const Region> original = rv->region();
                boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
+                region_copy->set_position (original->position(), this);
 
                RegionView* nrv;
                if (arv) {
                        boost::shared_ptr<AudioRegion> audioregion_copy
                                = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
+
                        nrv = new AudioRegionView (*arv, audioregion_copy);
                } else if (mrv) {
                        boost::shared_ptr<MidiRegion> midiregion_copy
@@ -1011,7 +1205,7 @@ RegionMotionDrag::copy_regions (GdkEvent* event)
                }
 
                nrv->get_canvas_group()->show ();
-               new_regionviews.push_back (nrv);
+               new_regionviews.push_back (DraggingView (nrv));
 
                /* swap _primary to the copy */
 
@@ -1032,14 +1226,14 @@ RegionMotionDrag::copy_regions (GdkEvent* event)
 
        _views = new_regionviews;
 
-       swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
+       swap_grab (new_regionviews.front().view->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
           "forget" to update properly after the upcoming reparent()
           ..only if the mouse is in rapid motion at the time of the grab.
-          something to do with regionview creation raking so long?
+          something to do with regionview creation taking so long?
        */
        _editor->update_canvas_now();
 }
@@ -1049,7 +1243,7 @@ RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
 {
        /* Which trackview is this ? */
 
-       pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
+       pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
        (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
        (*layer) = tvp.second;
 
@@ -1174,7 +1368,7 @@ RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p,
        double speed = 1;
        RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
        if (rtv && rtv->is_track()) {
-               speed = rtv->get_diskstream()->speed ();
+               speed = rtv->track()->speed ();
        }
 
        _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
@@ -1198,7 +1392,7 @@ RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, Rout
 
        _primary->get_canvas_group()->show ();
        _primary->set_position (pos, 0);
-       _views.push_back (_primary);
+       _views.push_back (DraggingView (_primary));
 
        _last_frame_position = pos;
 
@@ -1212,15 +1406,16 @@ RegionMotionDrag::find_time_axis_views_and_layers ()
 {
        map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
 
-       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+       for (list<DraggingView>::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);
+               RegionView* rv = i->view;
+               rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
+               rv->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] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
+               tav[rv] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
        }
 
        return tav;
@@ -1242,9 +1437,9 @@ RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
        boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
 
        _editor->begin_reversible_command (_("insert region"));
-       XMLNode& before = playlist->get_state ();
+        playlist->clear_history ();
        playlist->add_region (_primary->region (), _last_frame_position);
-       _editor->session()->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
+       _editor->session()->add_command (new StatefulDiffCommand (playlist));
        _editor->commit_reversible_command ();
 
        delete _primary;
@@ -1252,6 +1447,12 @@ RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
        _views.clear ();
 }
 
+void
+RegionInsertDrag::aborted ()
+{
+       /* XXX: TODO */
+}
+
 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
        : RegionMoveDrag (e, i, p, v, false, false)
 {
@@ -1276,7 +1477,7 @@ RegionSpliceDrag::motion (GdkEvent* event, bool)
 
        int dir;
 
-       if ((current_pointer_x() - last_pointer_x()) > 0) {
+       if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
                dir = 1;
        } else {
                dir = -1;
@@ -1328,6 +1529,11 @@ RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
 
 }
 
+void
+RegionSpliceDrag::aborted ()
+{
+       /* XXX: TODO */
+}
 
 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
        : Drag (e, i),
@@ -1372,6 +1578,12 @@ RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
        }
 }
 
+void
+RegionCreateDrag::aborted ()
+{
+       /* XXX: TODO */
+}
+
 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
        : Drag (e, i)
        , region (0)
@@ -1431,7 +1643,7 @@ NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
 {
        MidiRegionSelection& ms (_editor->get_selection().midi_regions);
        for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
-               (*r)->update_resizing (at_front, current_pointer_x() - grab_x(), relative);
+               (*r)->update_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
        }
 }
 
@@ -1440,10 +1652,16 @@ NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
 {
        MidiRegionSelection& ms (_editor->get_selection().midi_regions);
        for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
-               (*r)->commit_resizing (at_front, current_pointer_x() - grab_x(), relative);
+               (*r)->commit_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
        }
 }
 
+void
+NoteResizeDrag::aborted ()
+{
+       /* XXX: TODO */
+}
+
 void
 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
 {
@@ -1456,8 +1674,15 @@ RegionGainDrag::finished (GdkEvent *, bool)
 
 }
 
+void
+RegionGainDrag::aborted ()
+{
+       /* XXX: TODO */
+}
+
 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
        : RegionDrag (e, i, p, v)
+       , _have_transaction (false)
 {
 
 }
@@ -1470,7 +1695,7 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
        RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
 
        if (tv && tv->is_track()) {
-               speed = tv->get_diskstream()->speed();
+               speed = tv->track()->speed();
        }
 
        nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
@@ -1514,7 +1739,7 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
        nframes64_t frame_delta = 0;
 
        bool left_direction;
-       bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
+       bool obey_snap = event ? !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) : false;
 
        /* snap modifier works differently here..
           its current state has to be passed to the
@@ -1527,7 +1752,7 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
        pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
 
        if (tv && tv->is_track()) {
-               speed = tv->get_diskstream()->speed();
+               speed = tv->track()->speed();
        }
 
        nframes64_t const pf = adjusted_current_frame (event);
@@ -1557,33 +1782,27 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                _editor->begin_reversible_command (trim_type);
                _have_transaction = true;
 
-               for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                       (*i)->fake_set_opaque(false);
-                       (*i)->region()->freeze ();
+               for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                       RegionView* rv = i->view;
+                       rv->fake_set_opaque(false);
+                        rv->region()->clear_history ();
+                       rv->region()->suspend_property_changes ();
 
-                       AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
+                       AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
 
                        if (arv){
                                arv->temporarily_hide_envelope ();
                        }
 
-                       boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
+                       boost::shared_ptr<Playlist> pl = rv->region()->playlist();
                        insert_result = _editor->motion_frozen_playlists.insert (pl);
 
                        if (insert_result.second) {
-                               _editor->session()->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
                                pl->freeze();
                        }
                }
        }
 
-       if (pf == last_pointer_frame()) {
-               return;
-       }
-
-       /* XXX i hope to god that we can really conclude this ... */
-       _have_transaction = true;
-
        if (left_direction) {
                frame_delta = (last_pointer_frame() - pf);
        } else {
@@ -1592,7 +1811,7 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
 
        bool non_overlap_trim = false;
 
-       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
+       if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
                non_overlap_trim = true;
        }
 
@@ -1602,8 +1821,8 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                        break;
                } else {
 
-                       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                               _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
+                       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                               _editor->single_start_trim (*i->view, frame_delta, left_direction, obey_snap, non_overlap_trim);
                        }
                        break;
                }
@@ -1613,8 +1832,8 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                        break;
                } else {
 
-                       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                               _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
+                       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                               _editor->single_end_trim (*i->view, frame_delta, left_direction, obey_snap, non_overlap_trim);
                        }
                        break;
                }
@@ -1623,12 +1842,12 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                {
                        bool swap_direction = false;
 
-                       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
+                       if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
                                swap_direction = true;
                        }
 
-                       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                               _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
+                       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                               _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction, obey_snap);
                        }
                }
                break;
@@ -1658,16 +1877,16 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
                        _editor->thaw_region_after_trim (*_primary);
                } else {
 
-                       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                               _editor->thaw_region_after_trim (**i);
-                               (*i)->fake_set_opaque (true);
+                       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                               _editor->thaw_region_after_trim (*i->view);
+                               i->view->fake_set_opaque (true);
+                                if (_have_transaction) {
+                                        _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
+                                }
                        }
                }
                for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
                        (*p)->thaw ();
-                       if (_have_transaction) {
-                               _editor->session()->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
-                       }
                }
 
                _editor->motion_frozen_playlists.clear ();
@@ -1678,7 +1897,22 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
 
        } else {
                /* no mouse movement */
-               _editor->point_trim (event);
+               _editor->point_trim (event, adjusted_current_frame (event));
+       }
+}
+
+void
+TrimDrag::aborted ()
+{
+       /* Our motion method is changing model state, so use the Undo system
+          to cancel.  Perhaps not ideal, as this will leave an Undo point
+          behind which may be slightly odd from the user's point of view.
+       */
+
+       finished (0, true);
+       
+       if (_have_transaction) {
+               _editor->undo ();
        }
 }
 
@@ -1726,10 +1960,6 @@ MeterMarkerDrag::motion (GdkEvent* event, bool)
 {
        nframes64_t const pf = adjusted_current_frame (event);
 
-       if (pf == last_pointer_frame()) {
-               return;
-       }
-
        _marker->set_position (pf);
        
        _editor->show_verbose_time_cursor (pf, 10);
@@ -1770,6 +2000,12 @@ MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
        }
 }
 
+void
+MeterMarkerDrag::aborted ()
+{
+       _marker->set_position (_marker->meter().frame ());
+}
+
 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
        : Drag (e, i),
          _copy (c)
@@ -1813,15 +2049,7 @@ void
 TempoMarkerDrag::motion (GdkEvent* event, bool)
 {
        nframes64_t const pf = adjusted_current_frame (event);
-
-       if (pf == last_pointer_frame()) {
-               return;
-       }
-
-       /* OK, we've moved far enough to make it worth actually move the thing. */
-
        _marker->set_position (pf);
-
        _editor->show_verbose_time_cursor (pf, 10);
 }
 
@@ -1860,6 +2088,11 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
        }
 }
 
+void
+TempoMarkerDrag::aborted ()
+{
+       _marker->set_position (_marker->tempo().frame());
+}
 
 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
        : Drag (e, i),
@@ -1895,6 +2128,8 @@ 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);
 }
 
@@ -1936,6 +2171,13 @@ CursorDrag::finished (GdkEvent* event, bool movement_occurred)
        }
 }
 
+void
+CursorDrag::aborted ()
+{
+       _editor->_dragging_playhead = false;
+       _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
+}
+
 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
        : RegionDrag (e, i, p, v)
 {
@@ -1971,9 +2213,9 @@ FadeInDrag::motion (GdkEvent* event, bool)
                fade_length = pos - region->position();
        }
 
-       for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
+       for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
 
-               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
 
                if (!tmp) {
                        continue;
@@ -2008,9 +2250,9 @@ FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
 
        _editor->begin_reversible_command (_("change fade in length"));
 
-       for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
+       for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
 
-               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
 
                if (!tmp) {
                        continue;
@@ -2029,6 +2271,20 @@ FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
        _editor->commit_reversible_command ();
 }
 
+void
+FadeInDrag::aborted ()
+{
+       for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
+
+               if (!tmp) {
+                       continue;
+               }
+
+               tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
+       }
+}
+
 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
        : RegionDrag (e, i, p, v)
 {
@@ -2066,9 +2322,9 @@ FadeOutDrag::motion (GdkEvent* event, bool)
                fade_length = region->last_frame() - pos;
        }
 
-       for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
+       for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
 
-               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
 
                if (!tmp) {
                        continue;
@@ -2105,9 +2361,9 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
 
        _editor->begin_reversible_command (_("change fade out length"));
 
-       for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
+       for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
 
-               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
 
                if (!tmp) {
                        continue;
@@ -2126,6 +2382,20 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
        _editor->commit_reversible_command ();
 }
 
+void
+FadeOutDrag::aborted ()
+{
+       for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
+
+               if (!tmp) {
+                       continue;
+               }
+
+               tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
+       }
+}
+
 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
        : Drag (e, i)
 {
@@ -2219,10 +2489,10 @@ MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
                break;
        }
 
-       /* set up copies for us to manipulate during the drag */
+       /* Set up copies for us to manipulate during the drag */
 
        for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
-               Location *l = _editor->find_location_from_marker (*i, is_start);
+               Locationl = _editor->find_location_from_marker (*i, is_start);
                _copied_locations.push_back (new Location (*l));
        }
 }
@@ -2234,7 +2504,7 @@ MarkerDrag::motion (GdkEvent* event, bool)
        bool is_start;
        bool move_both = false;
        Marker* marker;
-       Location  *real_location;
+       Location *real_location;
        Location *copy_location = 0;
 
        nframes64_t const newframe = adjusted_current_frame (event);
@@ -2320,7 +2590,7 @@ MarkerDrag::motion (GdkEvent* event, bool)
 
                if (copy_location->is_mark()) {
 
-                       /* just move it */
+                       /* now move it */
 
                        copy_location->set_start (copy_location->start() + f_delta);
 
@@ -2438,6 +2708,12 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
        _line->hide();
 }
 
+void
+MarkerDrag::aborted ()
+{
+       /* XXX: TODO */
+}
+
 void
 MarkerDrag::update_item (Location* location)
 {
@@ -2467,11 +2743,10 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
        // the point doesn't 'jump' to the mouse after the first drag
        _time_axis_view_grab_x = _point->get_x();
        _time_axis_view_grab_y = _point->get_y();
-       nframes64_t grab_frame = _editor->pixel_to_frame (_time_axis_view_grab_x);
 
        float const fraction = 1 - (_point->get_y() / _point->line().height());
 
-       _point->line().start_drag_single (_point, grab_frame, fraction);
+       _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
 
        _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
                                            event->button.x + 10, event->button.y + 10);
@@ -2482,8 +2757,8 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 void
 ControlPointDrag::motion (GdkEvent* event, bool)
 {
-       double dx = current_pointer_x() - last_pointer_x();
-       double dy = current_pointer_y() - last_pointer_y();
+       double dx = _drags->current_pointer_x() - last_pointer_x();
+       double dy = _drags->current_pointer_y() - last_pointer_y();
 
        if (event->button.state & Keyboard::SecondaryModifier) {
                dx *= 0.1;
@@ -2527,7 +2802,7 @@ ControlPointDrag::motion (GdkEvent* event, bool)
 
        bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
 
-       _point->line().drag_motion (cx_frames, fraction, push);
+       _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
 
        _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
 }
@@ -2549,6 +2824,12 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
        _point->line().end_drag ();
 }
 
+void
+ControlPointDrag::aborted ()
+{
+       _point->line().reset ();
+}
+
 bool
 ControlPointDrag::active (Editing::MouseMode m)
 {
@@ -2615,7 +2896,7 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 void
 LineDrag::motion (GdkEvent* event, bool)
 {
-       double dy = current_pointer_y() - last_pointer_y();
+       double dy = _drags->current_pointer_y() - last_pointer_y();
 
        if (event->button.state & Keyboard::SecondaryModifier) {
                dy *= 0.1;
@@ -2638,8 +2919,8 @@ LineDrag::motion (GdkEvent* event, bool)
                push = true;
        }
 
-       /* we are ignoring x position for this drag, so we can just pass in 0 */
-       _line->drag_motion (0, fraction, push);
+       /* we are ignoring x position for this drag, so we can just pass in anything */
+       _line->drag_motion (0, fraction, true, push);
 
        _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
 }
@@ -2651,6 +2932,12 @@ LineDrag::finished (GdkEvent* event, bool)
        _line->end_drag ();
 }
 
+void
+LineDrag::aborted ()
+{
+       _line->reset ();
+}
+
 void
 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 {
@@ -2666,14 +2953,8 @@ RubberbandSelectDrag::motion (GdkEvent* event, bool)
        double y1;
        double y2;
 
-       /* use a bigger drag threshold than the default */
-
        nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
 
-       if (abs ((int) (pf - grab_frame())) < 8) {
-               return;
-       }
-
        nframes64_t grab = grab_frame ();
        if (Config->get_rubberbanding_snaps_to_grid ()) {
                _editor->snap_to_with_modifier (grab, event);
@@ -2689,11 +2970,11 @@ RubberbandSelectDrag::motion (GdkEvent* event, bool)
                start = grab;
        }
 
-       if (current_pointer_y() < grab_y()) {
-               y1 = current_pointer_y();
+       if (_drags->current_pointer_y() < grab_y()) {
+               y1 = _drags->current_pointer_y();
                y2 = grab_y();
        } else {
-               y2 = current_pointer_y();
+               y2 = _drags->current_pointer_y();
                y1 = grab_y();
        }
 
@@ -2723,11 +3004,11 @@ RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
                motion (event, false);
 
                double y1,y2;
-               if (current_pointer_y() < grab_y()) {
-                       y1 = current_pointer_y();
+               if (_drags->current_pointer_y() < grab_y()) {
+                       y1 = _drags->current_pointer_y();
                        y2 = grab_y();
                } else {
-                       y2 = current_pointer_y();
+                       y2 = _drags->current_pointer_y();
                        y1 = grab_y();
                }
 
@@ -2759,6 +3040,12 @@ RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
        _editor->rubberband_rect->hide();
 }
 
+void
+RubberbandSelectDrag::aborted ()
+{
+       _editor->rubberband_rect->hide ();
+}
+
 void
 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 {
@@ -2774,10 +3061,6 @@ TimeFXDrag::motion (GdkEvent* event, bool)
 
        nframes64_t const pf = adjusted_current_frame (event);
 
-       if (pf == last_pointer_frame()) {
-               return;
-       }
-
        if (pf > rv->region()->position()) {
                rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
        }
@@ -2822,6 +3105,13 @@ TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
        }
 }
 
+void
+TimeFXDrag::aborted ()
+{
+       _primary->get_time_axis_view().hide_timestretch ();
+}
+
+
 void
 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 {
@@ -2831,7 +3121,7 @@ ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 void
 ScrubDrag::motion (GdkEvent* /*event*/, bool)
 {
-       _editor->scrub ();
+       _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
 }
 
 void
@@ -2843,6 +3133,12 @@ ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
        }
 }
 
+void
+ScrubDrag::aborted ()
+{
+       /* XXX: TODO */
+}
+
 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
        : Drag (e, i)
        , _operation (o)
@@ -2907,7 +3203,7 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
                _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
        }
 
-       _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
+       _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
 }
 
 void
@@ -2917,7 +3213,7 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
        nframes64_t end = 0;
        nframes64_t length;
 
-       pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (current_pointer_y ());
+       pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
        if (pending_time_axis.first == 0) {
                return;
        }
@@ -2953,9 +3249,6 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
 
                if (first_move) {
 
-                       _editor->begin_reversible_command (_("range selection"));
-                       _have_transaction = true;
-
                        if (_copy) {
                                /* adding to the selection */
                                _editor->selection->add (_editor->clicked_axisview);
@@ -3004,11 +3297,6 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
 
        case SelectionStartTrim:
 
-               if (first_move) {
-                       _editor->begin_reversible_command (_("trim selection start"));
-                       _have_transaction = true;
-               }
-               
                start = _editor->selection->time[_editor->clicked_selection].start;
                end = _editor->selection->time[_editor->clicked_selection].end;
 
@@ -3021,11 +3309,6 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
 
        case SelectionEndTrim:
 
-               if (first_move) {
-                       _editor->begin_reversible_command (_("trim selection end"));
-                       _have_transaction = true;
-               }
-
                start = _editor->selection->time[_editor->clicked_selection].start;
                end = _editor->selection->time[_editor->clicked_selection].end;
 
@@ -3039,11 +3322,6 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
 
        case SelectionMove:
 
-               if (first_move) {
-                       _editor->begin_reversible_command (_("move selection"));
-                       _have_transaction = true;
-               }
-
                start = _editor->selection->time[_editor->clicked_selection].start;
                end = _editor->selection->time[_editor->clicked_selection].end;
 
@@ -3084,10 +3362,6 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
                        _editor->selection->TimeChanged ();
                }
 
-               if (_have_transaction) {
-                       _editor->commit_reversible_command ();
-               }
-
                /* XXX what if its a music time selection? */
                if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
                        s->request_play_range (&_editor->selection->time, true);
@@ -3114,6 +3388,12 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
        _editor->stop_canvas_autoscroll ();
 }
 
+void
+SelectionDrag::aborted ()
+{
+       /* XXX: TODO */
+}
+
 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
        : Drag (e, i),
          _operation (o),
@@ -3183,14 +3463,6 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
 
        nframes64_t const pf = adjusted_current_frame (event);
 
-       /* only alter selection if the current frame is
-          different from the last frame position.
-        */
-
-       if (pf == last_pointer_frame()) {
-               return;
-       }
-
        if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
                nframes64_t grab = grab_frame ();
                _editor->snap_to (grab);
@@ -3316,7 +3588,11 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
        _editor->stop_canvas_autoscroll ();
 }
 
-
+void
+RangeMarkerBarDrag::aborted ()
+{
+       /* XXX: TODO */
+}
 
 void
 RangeMarkerBarDrag::update_item (Location* location)
@@ -3343,10 +3619,6 @@ MouseZoomDrag::motion (GdkEvent* event, bool first_move)
 
        nframes64_t const pf = adjusted_current_frame (event);
 
-       if (pf == last_pointer_frame()) {
-               return;
-       }
-
        nframes64_t grab = grab_frame ();
        _editor->snap_to_with_modifier (grab, event);
 
@@ -3394,6 +3666,12 @@ MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
        _editor->zoom_rect->hide();
 }
 
+void
+MouseZoomDrag::aborted ()
+{
+       _editor->zoom_rect->hide ();
+}
+
 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
        : Drag (e, i)
 {
@@ -3412,8 +3690,8 @@ NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
        double event_x;
        double event_y;
 
-       event_x = current_pointer_x();
-       event_y = current_pointer_y();
+       event_x = _drags->current_pointer_x();
+       event_y = _drags->current_pointer_y();
 
        _item->property_parent().get_value()->w2i(event_x, event_y);
 
@@ -3450,8 +3728,8 @@ NoteDrag::motion (GdkEvent*, bool)
        double event_x;
        double event_y;
 
-       event_x = current_pointer_x();
-       event_y = current_pointer_y();
+       event_x = _drags->current_pointer_x();
+       event_y = _drags->current_pointer_y();
 
        _item->property_parent().get_value()->w2i(event_x, event_y);
 
@@ -3521,6 +3799,12 @@ NoteDrag::finished (GdkEvent* ev, bool moved)
        }
 }
 
+void
+NoteDrag::aborted ()
+{
+       /* XXX: TODO */
+}
+
 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
        : Drag (e, i)
        , _ranges (r)
@@ -3597,7 +3881,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
                return;
        }
 
-       _line->start_drag_multiple (points, 1 - (current_pointer_y() / _line->height ()), state);
+       _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
 }
 
 void
@@ -3607,10 +3891,10 @@ AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
                return;
        }
        
-       float const f = 1 - (current_pointer_y() / _line->height());
+       float const f = 1 - (_drags->current_pointer_y() / _line->height());
 
-       /* we are ignoring x position for this drag, so we can just pass in 0 */
-       _line->drag_motion (0, f, false);
+       /* we are ignoring x position for this drag, so we can just pass in anything */
+       _line->drag_motion (0, f, true, false);
 }
 
 void
@@ -3624,3 +3908,16 @@ AutomationRangeDrag::finished (GdkEvent* event, bool)
        _line->end_drag ();
        _line->clear_always_in_view ();
 }
+
+void
+AutomationRangeDrag::aborted ()
+{
+       _line->clear_always_in_view ();
+       _line->reset ();
+}
+
+DraggingView::DraggingView (RegionView* v)
+       : view (v)
+{
+       initial_y = v->get_canvas_group()->property_y ();
+}