Move Diskstream ownership to Track, so that Session no longer holds lists of Diskstre...
[ardour.git] / gtk2_ardour / editor_drag.cc
index 4de1722ed8f9399e609d3f4e10aa49588e423810..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,16 +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)
-       , _had_movement (false)
        , _move_threshold_passed (false)
        , _grab_frame (0)
        , _last_pointer_frame (0)
-       , _current_pointer_frame (0)
 {
 
 }
@@ -103,16 +216,10 @@ 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,30 +249,24 @@ 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, _had_movement);
+       finished (event, _move_threshold_passed);
 
        _editor->hide_verbose_canvas_cursor();
 
-       _ending = false;
-
-       return _had_movement;
+       return _move_threshold_passed;
 }
 
 nframes64_t
-Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
+Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
 {
        nframes64_t pos = 0;
 
-       if (_current_pointer_frame > _pointer_frame_offset) {
-               pos = _current_pointer_frame - _pointer_frame_offset;
+       if (f > _pointer_frame_offset) {
+               pos = f - _pointer_frame_offset;
        }
 
        if (snap) {
@@ -175,40 +276,46 @@ Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
        return pos;
 }
 
+nframes64_t
+Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
+{
+       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);
-
-       if (!from_autoscroll && !_move_threshold_passed) {
+       /* 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;
+       }
 
-               bool const xp = (::llabs ((nframes64_t) (_current_pointer_x - _grab_x)) > 4LL);
-               bool const yp = (::llabs ((nframes64_t) (_current_pointer_y - _grab_y)) > 4LL);
+       pair<nframes64_t, int> const threshold = move_threshold ();
 
-               _move_threshold_passed = (xp || yp);
-       }
+       bool const old_move_threshold_passed = _move_threshold_passed;
 
-       bool old_had_movement = _had_movement;
+       if (!from_autoscroll && !_move_threshold_passed) {
 
-       /* a motion event has happened, so we've had movement... */
-       _had_movement = true;
+               bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
+               bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
 
-       /* ... unless we're using a move threshold and we've not yet passed it */
-       if (apply_move_threshold() && !_move_threshold_passed) {
-               _had_movement = false;
+               _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
        }
 
-       if (active (_editor->mouse_mode) && _had_movement) {
+       if (active (_editor->mouse_mode) && _move_threshold_passed) {
 
                if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
                        if (!from_autoscroll) {
                                _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
                        }
 
-                       motion (event, _had_movement != old_had_movement);
+                       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)
@@ -2880,8 +3176,7 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
                if (_editor->clicked_axisview) {
                        _editor->clicked_axisview->order_selection_trims (_item, true);
                }
-               Drag::start_grab (event, cursor);
-               cursor = _editor->trimmer_cursor;
+               Drag::start_grab (event, _editor->trimmer_cursor);
                start = _editor->selection->time[_editor->clicked_selection].start;
                _pointer_frame_offset = grab_frame() - start;
                break;
@@ -2890,8 +3185,7 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
                if (_editor->clicked_axisview) {
                        _editor->clicked_axisview->order_selection_trims (_item, false);
                }
-               Drag::start_grab (event, cursor);
-               cursor = _editor->trimmer_cursor;
+               Drag::start_grab (event, _editor->trimmer_cursor);
                end = _editor->selection->time[_editor->clicked_selection].end;
                _pointer_frame_offset = grab_frame() - end;
                break;
@@ -2909,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
@@ -2919,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;
        }
@@ -2955,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);
@@ -3006,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;
 
@@ -3023,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;
 
@@ -3041,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;
 
@@ -3086,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);
@@ -3116,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),
@@ -3185,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);
@@ -3318,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)
@@ -3345,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);
 
@@ -3396,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)
 {
@@ -3414,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);
 
@@ -3452,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);
 
@@ -3523,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)
@@ -3554,32 +3836,43 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
                boost::shared_ptr<AutomationList> the_list = _line->the_list ();
                for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
+
+                       /* fade into and out of the region that we're dragging;
+                          64 samples length plucked out of thin air.
+                       */
+                       nframes64_t const h = (j->start + j->end) / 2;
+                       nframes64_t a = j->start + 64;
+                       if (a > h) {
+                               a = h;
+                       }
+                       nframes64_t b = j->end - 64;
+                       if (b < h) {
+                               b = h;
+                       }
+                       
                        the_list->add (j->start, the_list->eval (j->start));
                        _line->add_always_in_view (j->start);
-                       the_list->add (j->start + 1, the_list->eval (j->start + 1));
-                       _line->add_always_in_view (j->start + 1);
-                       the_list->add (j->end - 1, the_list->eval (j->end - 1));
-                       _line->add_always_in_view (j->end - 1);
+                       the_list->add (a, the_list->eval (a));
+                       _line->add_always_in_view (a);
+                       the_list->add (b, the_list->eval (b));
+                       _line->add_always_in_view (b);
                        the_list->add (j->end, the_list->eval (j->end));
                        _line->add_always_in_view (j->end);
                }
 
                uint32_t const N = _line->npoints ();
-               AutomationList::const_iterator j = the_list->begin ();
                for (uint32_t i = 0; i < N; ++i) {
 
                        ControlPoint* p = _line->nth (i);
 
-                       list<AudioRange>::const_iterator k = _ranges.begin ();
-                       while (k != _ranges.end() && (k->start >= (*j)->when || k->end <= (*j)->when)) {
-                               ++k;
+                       list<AudioRange>::const_iterator j = _ranges.begin ();
+                       while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
+                               ++j;
                        }
 
-                       if (k != _ranges.end()) {
+                       if (j != _ranges.end()) {
                                points.push_back (p);
                        }
-
-                       ++j;
                }
        }
 
@@ -3588,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
@@ -3598,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
@@ -3615,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 ();
+}