Move Diskstream ownership to Track, so that Session no longer holds lists of Diskstre...
[ardour.git] / gtk2_ardour / editor_drag.cc
index 569f62cc78b5cdb25d5f12cabbb0fd503a75dd18..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)
-       , _ending (false)
        , _move_threshold_passed (false)
        , _grab_frame (0)
        , _last_pointer_frame (0)
-       , _current_pointer_frame (0)
 {
 
 }
@@ -105,11 +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;
+       _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,
@@ -139,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;
 }
 
@@ -175,20 +279,15 @@ 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 == _current_pointer_y) ) {
+            (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
                return false;
        }
 
@@ -199,7 +298,7 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
        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()));
        }
@@ -212,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;
                }
        }
@@ -222,8 +326,6 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
 void
 Drag::break_drag ()
 {
-       _ending = true;
-       
        if (_item) {
                _item->ungrab (0);
        }
@@ -232,41 +334,32 @@ Drag::break_drag ()
 
        _editor->stop_canvas_autoscroll ();
        _editor->hide_verbose_canvas_cursor ();
-       
-       _ending = false;
-}
-
-pair<nframes64_t, nframes64_t>
-Drag::extent () const
-{
-       nframes64_t const f = adjusted_current_frame (0);
-       return make_pair (f, f);
 }
 
 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)
 {
-       if (!ending ()) {
-               _views.remove (v);
+       list<DraggingView>::iterator i = _views.begin ();
+       while (i != _views.end() && i->view != v) {
+               ++i;
        }
-}
 
-pair<nframes64_t, nframes64_t>
-RegionDrag::extent () const
-{
-       nframes64_t const f = adjusted_current_frame (0);
-       return make_pair (f, f + _primary->region()->length ());
+       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),
@@ -369,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;
@@ -460,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.
 
@@ -545,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;
@@ -731,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;
@@ -743,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 */
@@ -753,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);
                        }
                }
 
@@ -783,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());
 
@@ -795,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;
@@ -832,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;
@@ -846,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);
@@ -865,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).
@@ -872,31 +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) {
@@ -906,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 */
@@ -929,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
@@ -952,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();
                        }
@@ -964,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);
        }
@@ -978,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 ();
 
@@ -994,8 +1131,8 @@ RegionMoveDrag::aborted ()
 {
        if (_copy) {
 
-               for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                       delete *i;
+               for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                       delete i->view;
                }
 
                _views.clear ();
@@ -1008,16 +1145,17 @@ RegionMoveDrag::aborted ()
 void
 RegionMotionDrag::aborted ()
 {
-       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-               TimeAxisView* tv = &(*i)->get_time_axis_view ();
+       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);
-               (*i)->get_canvas_group()->reparent (*rtv->view()->canvas_item());
-               (*i)->get_canvas_group()->property_y() = 0;
-               (*i)->get_time_axis_view().reveal_dependent_views (**i);
-               (*i)->fake_set_opaque (false);
-               (*i)->move (-_total_x_delta, 0);
-               (*i)->set_height (rtv->view()->child_height ());
+               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 ();
@@ -1040,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
@@ -1065,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 */
 
@@ -1086,7 +1226,7 @@ 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
@@ -1103,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;
 
@@ -1228,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);
@@ -1252,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;
 
@@ -1266,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;
@@ -1296,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;
@@ -1336,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;
@@ -1502,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);
        }
 }
 
@@ -1511,7 +1652,7 @@ 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);
        }
 }
 
@@ -1541,6 +1682,7 @@ RegionGainDrag::aborted ()
 
 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
        : RegionDrag (e, i, p, v)
+       , _have_transaction (false)
 {
 
 }
@@ -1553,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);
@@ -1610,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);
@@ -1640,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 {
@@ -1685,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;
                }
@@ -1696,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;
                }
@@ -1710,8 +1846,8 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                                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;
@@ -1741,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 ();
@@ -1761,7 +1897,7 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
 
        } else {
                /* no mouse movement */
-               _editor->point_trim (event);
+               _editor->point_trim (event, adjusted_current_frame (event));
        }
 }
 
@@ -1824,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);
@@ -1917,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);
 }
 
@@ -2089,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;
@@ -2126,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;
@@ -2150,8 +2274,8 @@ FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
 void
 FadeInDrag::aborted ()
 {
-       for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
-               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
+       for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
 
                if (!tmp) {
                        continue;
@@ -2198,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;
@@ -2237,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;
@@ -2261,8 +2385,8 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
 void
 FadeOutDrag::aborted ()
 {
-       for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
-               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
+       for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
 
                if (!tmp) {
                        continue;
@@ -2365,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));
        }
 }
@@ -2380,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);
@@ -2466,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);
 
@@ -2619,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);
@@ -2634,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;
@@ -2679,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));
 }
@@ -2773,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;
@@ -2796,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));
 }
@@ -2830,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);
@@ -2853,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();
        }
 
@@ -2887,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();
                }
 
@@ -2944,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);
        }
@@ -3008,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
@@ -3090,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
@@ -3100,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;
        }
@@ -3136,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);
@@ -3187,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;
 
@@ -3204,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;
 
@@ -3222,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;
 
@@ -3267,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);
@@ -3372,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);
@@ -3536,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);
 
@@ -3611,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);
 
@@ -3649,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);
 
@@ -3802,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
@@ -3812,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
@@ -3836,3 +3915,9 @@ AutomationRangeDrag::aborted ()
        _line->clear_always_in_view ();
        _line->reset ();
 }
+
+DraggingView::DraggingView (RegionView* v)
+       : view (v)
+{
+       initial_y = v->get_canvas_group()->property_y ();
+}