Move Diskstream ownership to Track, so that Session no longer holds lists of Diskstre...
[ardour.git] / gtk2_ardour / editor_drag.cc
index 68a73fef7b6096a0c1f42020631893adcfe42fda..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"
 #include "audio_region_view.h"
 #include "midi_region_view.h"
 #include "ardour_ui.h"
+#include "gui_thread.h"
 #include "control_point.h"
 #include "utils.h"
 #include "region_gain_line.h"
 #include "canvas-note.h"
 #include "selection.h"
 #include "midi_selection.h"
+#include "automation_time_axis.h"
 
 using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
-using namespace sigc;
 using namespace Gtk;
 using namespace Editing;
 using namespace ArdourCanvas;
@@ -52,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)
+       , _move_threshold_passed (false)
        , _grab_frame (0)
        , _last_pointer_frame (0)
-       , _current_pointer_frame (0)
-       , _have_transaction (false)
-       , _had_movement (false)
-       , _move_threshold_passed (false)
 {
 
 }
@@ -102,22 +216,16 @@ 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,
                              event->button.time);
 
-       if (_editor->session && _editor->session->transport_rolling()) {
+       if (_editor->session() && _editor->session()->transport_rolling()) {
                _was_rolling = true;
        } else {
                _was_rolling = false;
@@ -141,70 +249,73 @@ 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* event) 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;
        }
 
-       _editor->snap_to_with_modifier (pos, event);
+       if (snap) {
+               _editor->snap_to_with_modifier (pos, event);
+       }
 
        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;
-       _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;
                }
        }
@@ -212,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 (sigc::mem_fun (*this, &RegionDrag::region_going_away));
+       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)
 {
 
 }
@@ -275,7 +389,7 @@ RegionMotionDrag::get_time_axis_view_summary ()
 
        /* get a bitmask representing the visible tracks */
 
-       for (Editor::TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
+       for (TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
                RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
                TimeAxisView::Children children_list;
 
@@ -348,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;
@@ -398,35 +512,29 @@ RegionMotionDrag::compute_y_delta (
 double
 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
 {
-       *pending_region_position = 0;
-
        /* compute the amount of pointer motion in frames, and where
           the region would be if we moved it by that much.
        */
-       if (_current_pointer_frame >= _pointer_frame_offset) {
-
-               nframes64_t sync_frame;
-               nframes64_t sync_offset;
-               int32_t sync_dir;
-
-               *pending_region_position = _current_pointer_frame - _pointer_frame_offset;
-
-               sync_offset = _primary->region()->sync_offset (sync_dir);
-
-               /* we don't handle a sync point that lies before zero.
-                */
-               if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
-
-                       sync_frame = *pending_region_position + (sync_dir*sync_offset);
-
-                       _editor->snap_to_with_modifier (sync_frame, event);
-
-                       *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
-
-               } else {
-                       *pending_region_position = _last_frame_position;
-               }
-
+       *pending_region_position = adjusted_current_frame (event);
+       
+       nframes64_t sync_frame;
+       nframes64_t sync_offset;
+       int32_t sync_dir;
+       
+       sync_offset = _primary->region()->sync_offset (sync_dir);
+       
+       /* we don't handle a sync point that lies before zero.
+        */
+       if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
+               
+               sync_frame = *pending_region_position + (sync_dir*sync_offset);
+               
+               _editor->snap_to_with_modifier (sync_frame, event);
+               
+               *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
+               
+       } else {
+               *pending_region_position = _last_frame_position;
        }
 
        if (*pending_region_position > max_frames - _primary->region()->length()) {
@@ -445,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.
 
@@ -530,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;
@@ -691,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();
        }
@@ -714,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;
@@ -726,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 */
@@ -736,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);
                        }
                }
 
@@ -766,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());
 
@@ -778,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;
@@ -815,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;
@@ -829,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);
@@ -848,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).
@@ -855,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) {
@@ -888,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 */
@@ -911,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
@@ -934,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();
                        }
@@ -946,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);
        }
@@ -960,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 ();
 
@@ -971,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
@@ -988,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
@@ -1013,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 */
 
@@ -1034,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();
 }
@@ -1051,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;
 
@@ -1176,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);
@@ -1187,7 +1379,7 @@ RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
 {
        RegionMotionDrag::start_grab (event, c);
 
-       _pointer_frame_offset = _grab_frame - _last_frame_position;
+       _pointer_frame_offset = grab_frame() - _last_frame_position;
 }
 
 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
@@ -1200,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;
 
@@ -1214,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;
@@ -1244,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;
@@ -1254,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)
 {
@@ -1267,7 +1466,7 @@ struct RegionSelectionByPosition {
 };
 
 void
-RegionSpliceDrag::motion (GdkEvent* /*event*/, bool)
+RegionSpliceDrag::motion (GdkEvent* event, bool)
 {
        RouteTimeAxisView* tv;
        layer_t layer;
@@ -1278,7 +1477,7 @@ RegionSpliceDrag::motion (GdkEvent* /*event*/, bool)
 
        int dir;
 
-       if (_current_pointer_x - _grab_x > 0) {
+       if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
                dir = 1;
        } else {
                dir = -1;
@@ -1289,6 +1488,8 @@ RegionSpliceDrag::motion (GdkEvent* /*event*/, bool)
        RegionSelectionByPosition cmp;
        copy.sort (cmp);
 
+       nframes64_t const pf = adjusted_current_frame (event);
+
        for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
 
                RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
@@ -1308,19 +1509,17 @@ RegionSpliceDrag::motion (GdkEvent* /*event*/, bool)
                }
 
                if (dir > 0) {
-                       if (_current_pointer_frame < (*i)->region()->last_frame() + 1) {
+                       if (pf < (*i)->region()->last_frame() + 1) {
                                continue;
                        }
                } else {
-                       if (_current_pointer_frame > (*i)->region()->first_frame()) {
+                       if (pf > (*i)->region()->first_frame()) {
                                continue;
                        }
                }
 
 
                playlist->shuffle ((*i)->region(), dir);
-
-               _grab_x = _current_pointer_x;
        }
 }
 
@@ -1330,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),
@@ -1367,13 +1571,19 @@ RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
        }
 
        if (!movement_occurred) {
-               mtv->add_region (_grab_frame);
+               mtv->add_region (grab_frame ());
        } else {
                motion (event, false);
                // TODO: create region-create-drag region here
        }
 }
 
+void
+RegionCreateDrag::aborted ()
+{
+       /* XXX: TODO */
+}
+
 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
        : Drag (e, i)
        , region (0)
@@ -1394,7 +1604,7 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
        double region_start = region->get_position_pixels();
        double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
 
-       if (_grab_x <= middle_point) {
+       if (grab_x() <= middle_point) {
                cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
                at_front = true;
        } else {
@@ -1433,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);
        }
 }
 
@@ -1442,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)
 {
@@ -1458,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)
 {
 
 }
@@ -1472,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);
@@ -1481,14 +1704,16 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 
        Drag::start_grab (event, _editor->trimmer_cursor);
 
+       nframes64_t const pf = adjusted_current_frame (event);
+
        if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
                _operation = ContentsTrim;
        } else {
                /* These will get overridden for a point trim.*/
-               if (_current_pointer_frame < (region_start + region_length/2)) {
+               if (pf < (region_start + region_length/2)) {
                        /* closer to start */
                        _operation = StartTrim;
-               } else if (_current_pointer_frame > (region_end - region_length/2)) {
+               } else if (pf > (region_end - region_length/2)) {
                        /* closer to end */
                        _operation = EndTrim;
                }
@@ -1502,7 +1727,7 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
                _editor->show_verbose_time_cursor (region_end, 10);
                break;
        case ContentsTrim:
-               _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
+               _editor->show_verbose_time_cursor (pf, 10);
                break;
        }
 }
@@ -1514,10 +1739,10 @@ 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
+          its current state has to be passed to the
           various trim functions in order to work properly
        */
 
@@ -1527,17 +1752,17 @@ 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();
        }
 
-       if (_last_pointer_frame > _current_pointer_frame) {
+       nframes64_t const pf = adjusted_current_frame (event);
+
+       if (last_pointer_frame() > pf) {
                left_direction = true;
        } else {
                left_direction = false;
        }
 
-       _editor->snap_to_with_modifier (_current_pointer_frame, event);
-
        if (first_move) {
 
                string trim_type;
@@ -1557,64 +1782,58 @@ 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 (_current_pointer_frame == _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 - _current_pointer_frame);
+               frame_delta = (last_pointer_frame() - pf);
        } else {
-               frame_delta = (_current_pointer_frame - _last_pointer_frame);
+               frame_delta = (pf - last_pointer_frame());
        }
 
        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;
        }
 
        switch (_operation) {
        case StartTrim:
-               if ((left_direction == false) && (_current_pointer_frame <= rv->region()->first_frame()/speed)) {
+               if ((left_direction == false) && (pf <= rv->region()->first_frame()/speed)) {
                        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;
                }
 
        case EndTrim:
-               if ((left_direction == true) && (_current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
+               if ((left_direction == true) && (pf > (nframes64_t) (rv->region()->last_frame()/speed))) {
                        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,13 +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;
@@ -1643,11 +1861,9 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
                break;
        case ContentsTrim:
-               _editor->show_verbose_time_cursor(_current_pointer_frame, 10);
+               _editor->show_verbose_time_cursor (pf, 10);
                break;
        }
-
-       _last_pointer_frame = _current_pointer_frame;
 }
 
 
@@ -1661,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 ();
@@ -1681,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 ();
        }
 }
 
@@ -1719,25 +1950,19 @@ MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
        Drag::start_grab (event, cursor);
 
-       _pointer_frame_offset = _grab_frame - _marker->meter().frame();
+       _pointer_frame_offset = grab_frame() - _marker->meter().frame();
 
-       _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
+       _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
 }
 
 void
 MeterMarkerDrag::motion (GdkEvent* event, bool)
 {
-       nframes64_t const adjusted_frame = adjusted_current_frame (event);
-
-       if (adjusted_frame == _last_pointer_frame) {
-               return;
-       }
-
-       _marker->set_position (adjusted_frame);
+       nframes64_t const pf = adjusted_current_frame (event);
 
-       _last_pointer_frame = adjusted_frame;
-
-       _editor->show_verbose_time_cursor (adjusted_frame, 10);
+       _marker->set_position (pf);
+       
+       _editor->show_verbose_time_cursor (pf, 10);
 }
 
 void
@@ -1751,15 +1976,15 @@ MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
 
        BBT_Time when;
 
-       TempoMap& map (_editor->session->tempo_map());
-       map.bbt_time (_last_pointer_frame, when);
+       TempoMap& map (_editor->session()->tempo_map());
+       map.bbt_time (last_pointer_frame(), when);
 
        if (_copy == true) {
                _editor->begin_reversible_command (_("copy meter mark"));
                XMLNode &before = map.get_state();
                map.add_meter (_marker->meter(), when);
                XMLNode &after = map.get_state();
-               _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
+               _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
                _editor->commit_reversible_command ();
 
                // delete the dummy marker we used for visual representation of copying.
@@ -1770,11 +1995,17 @@ MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
                XMLNode &before = map.get_state();
                map.move_meter (_marker->meter(), when);
                XMLNode &after = map.get_state();
-               _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
+               _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
                _editor->commit_reversible_command ();
        }
 }
 
+void
+MeterMarkerDrag::aborted ()
+{
+       _marker->set_position (_marker->meter().frame ());
+}
+
 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
        : Drag (e, i),
          _copy (c)
@@ -1810,26 +2041,16 @@ TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
        Drag::start_grab (event, cursor);
 
-       _pointer_frame_offset = _grab_frame - _marker->tempo().frame();
-       _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
+       _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
+       _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
 }
 
 void
 TempoMarkerDrag::motion (GdkEvent* event, bool)
 {
-       nframes64_t const adjusted_frame = adjusted_current_frame (event);
-
-       if (adjusted_frame == _last_pointer_frame) {
-               return;
-       }
-
-       /* OK, we've moved far enough to make it worth actually move the thing. */
-
-       _marker->set_position (adjusted_frame);
-
-       _editor->show_verbose_time_cursor (adjusted_frame, 10);
-
-       _last_pointer_frame = adjusted_frame;
+       nframes64_t const pf = adjusted_current_frame (event);
+       _marker->set_position (pf);
+       _editor->show_verbose_time_cursor (pf, 10);
 }
 
 void
@@ -1843,15 +2064,15 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
 
        BBT_Time when;
 
-       TempoMap& map (_editor->session->tempo_map());
-       map.bbt_time (_last_pointer_frame, when);
+       TempoMap& map (_editor->session()->tempo_map());
+       map.bbt_time (last_pointer_frame(), when);
 
        if (_copy == true) {
                _editor->begin_reversible_command (_("copy tempo mark"));
                XMLNode &before = map.get_state();
                map.add_tempo (_marker->tempo(), when);
                XMLNode &after = map.get_state();
-               _editor->session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
+               _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
                _editor->commit_reversible_command ();
 
                // delete the dummy marker we used for visual representation of copying.
@@ -1862,11 +2083,16 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
                XMLNode &before = map.get_state();
                map.move_tempo (_marker->tempo(), when);
                XMLNode &after = map.get_state();
-               _editor->session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
+               _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
                _editor->commit_reversible_command ();
        }
 }
 
+void
+TempoMarkerDrag::aborted ()
+{
+       _marker->set_position (_marker->tempo().frame());
+}
 
 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
        : Drag (e, i),
@@ -1893,15 +2119,17 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
        if (_cursor == _editor->playhead_cursor) {
                _editor->_dragging_playhead = true;
 
-               if (_editor->session && _was_rolling && _stop) {
-                       _editor->session->request_stop ();
+               if (_editor->session() && _was_rolling && _stop) {
+                       _editor->session()->request_stop ();
                }
 
-               if (_editor->session && _editor->session->is_auditioning()) {
-                       _editor->session->cancel_audition ();
+               if (_editor->session() && _editor->session()->is_auditioning()) {
+                       _editor->session()->cancel_audition ();
                }
        }
 
+       _pointer_frame_offset = grab_frame() - _cursor->current_frame;
+
        _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
 }
 
@@ -1910,7 +2138,7 @@ CursorDrag::motion (GdkEvent* event, bool)
 {
        nframes64_t const adjusted_frame = adjusted_current_frame (event);
 
-       if (adjusted_frame == _last_pointer_frame) {
+       if (adjusted_frame == last_pointer_frame()) {
                return;
        }
 
@@ -1922,8 +2150,6 @@ CursorDrag::motion (GdkEvent* event, bool)
        _editor->update_canvas_now ();
 #endif
        _editor->UpdateAllTransportClocks (_cursor->current_frame);
-
-       _last_pointer_frame = adjusted_frame;
 }
 
 void
@@ -1938,13 +2164,20 @@ CursorDrag::finished (GdkEvent* event, bool movement_occurred)
        motion (event, false);
 
        if (_item == &_editor->playhead_cursor->canvas_item) {
-               if (_editor->session) {
-                       _editor->session->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
+               if (_editor->session()) {
+                       _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
                        _editor->_pending_locate_request = true;
                }
        }
 }
 
+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)
 {
@@ -1959,7 +2192,7 @@ FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
        boost::shared_ptr<AudioRegion> const r = a->audio_region ();
 
-       _pointer_frame_offset = _grab_frame - ((nframes64_t) r->fade_in()->back()->when + r->position());
+       _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
        _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
 }
 
@@ -1980,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;
@@ -2017,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;
@@ -2032,12 +2265,26 @@ FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
                tmp->audio_region()->set_fade_in_active (true);
 
                XMLNode &after = alist->get_state();
-               _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
+               _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
        }
 
        _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)
 {
@@ -2052,7 +2299,7 @@ FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
        boost::shared_ptr<AudioRegion> r = a->audio_region ();
 
-       _pointer_frame_offset = _grab_frame - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
+       _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
        _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
 }
 
@@ -2075,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;
@@ -2114,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;
@@ -2129,12 +2376,26 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
                tmp->audio_region()->set_fade_out_active (true);
 
                XMLNode &after = alist->get_state();
-               _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
+               _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
        }
 
        _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)
 {
@@ -2169,7 +2430,7 @@ MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        Location *location = _editor->find_location_from_marker (_marker, is_start);
        _editor->_dragging_edit_point = true;
 
-       _pointer_frame_offset = _grab_frame - (is_start ? location->start() : location->end());
+       _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
 
        update_item (location);
 
@@ -2206,7 +2467,7 @@ MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
                if (e < max_frames) {
                        ++e;
                }
-               _editor->session->locations()->find_all_between (s, e, ll, Location::Flags (0));
+               _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
                for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
                        Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
                        if (lm) {
@@ -2228,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));
        }
 }
@@ -2243,14 +2504,14 @@ 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);
 
        nframes64_t next = newframe;
 
-       if (_current_pointer_frame == _last_pointer_frame) {
+       if (newframe == last_pointer_frame()) {
                return;
        }
 
@@ -2329,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);
 
@@ -2375,8 +2636,6 @@ MarkerDrag::motion (GdkEvent* event, bool)
                }
        }
 
-       _last_pointer_frame = _current_pointer_frame;
-
        assert (!_copied_locations.empty());
 
        _editor->show_verbose_time_cursor (newframe, 10);
@@ -2416,7 +2675,7 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
        _editor->_dragging_edit_point = false;
 
        _editor->begin_reversible_command ( _("move marker") );
-       XMLNode &before = _editor->session->locations()->get_state();
+       XMLNode &before = _editor->session()->locations()->get_state();
 
        MarkerSelection::iterator i;
        list<Location*>::iterator x;
@@ -2442,13 +2701,19 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
                }
        }
 
-       XMLNode &after = _editor->session->locations()->get_state();
-       _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
+       XMLNode &after = _editor->session()->locations()->get_state();
+       _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
        _editor->commit_reversible_command ();
 
        _line->hide();
 }
 
+void
+MarkerDrag::aborted ()
+{
+       /* XXX: TODO */
+}
+
 void
 MarkerDrag::update_item (Location* location)
 {
@@ -2476,19 +2741,15 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 
        // start the grab at the center of the control point so
        // the point doesn't 'jump' to the mouse after the first drag
-       _grab_x = _point->get_x();
-       _grab_y = _point->get_y();
-
-       _point->line().parent_group().i2w (_grab_x, _grab_y);
-       _editor->track_canvas->w2c (_grab_x, _grab_y, _grab_x, _grab_y);
+       _time_axis_view_grab_x = _point->get_x();
+       _time_axis_view_grab_y = _point->get_y();
 
-       _grab_frame = _editor->pixel_to_frame (_grab_x);
+       float const fraction = 1 - (_point->get_y() / _point->line().height());
 
-       _point->line().start_drag (_point, _grab_frame, 0);
+       _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
 
-       float fraction = 1.0 - (_point->get_y() / _point->line().height());
        _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
-                                           _current_pointer_x + 10, _current_pointer_y + 10);
+                                           event->button.x + 10, event->button.y + 10);
 
        _editor->show_verbose_canvas_cursor ();
 }
@@ -2496,46 +2757,41 @@ 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;
                dy *= 0.1;
        }
 
-       double cx = _grab_x + _cumulative_x_drag + dx;
-       double cy = _grab_y + _cumulative_y_drag + dy;
+       /* coordinate in TimeAxisView's space */
+       double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
+       double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
 
        // calculate zero crossing point. back off by .01 to stay on the
        // positive side of zero
-       double _unused = 0;
-       double zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
-       _point->line().parent_group().i2w(_unused, zero_gain_y);
+       double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
 
        // make sure we hit zero when passing through
-       if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
-                       or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
+       if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
                cy = zero_gain_y;
        }
 
        if (_x_constrained) {
-               cx = _grab_x;
+               cx = _time_axis_view_grab_x;
        }
        if (_y_constrained) {
-               cy = _grab_y;
+               cy = _time_axis_view_grab_y;
        }
 
-       _cumulative_x_drag = cx - _grab_x;
-       _cumulative_y_drag = cy - _grab_y;
-
-       _point->line().parent_group().w2i (cx, cy);
+       _cumulative_x_drag = cx - _time_axis_view_grab_x;
+       _cumulative_y_drag = cy - _time_axis_view_grab_y;
 
        cx = max (0.0, cx);
        cy = max (0.0, cy);
        cy = min ((double) _point->line().height(), cy);
 
-       //translate cx to frames
        nframes64_t cx_frames = _editor->unit_to_frame (cx);
 
        if (!_x_constrained) {
@@ -2546,7 +2802,7 @@ ControlPointDrag::motion (GdkEvent* event, bool)
 
        bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
 
-       _point->line().point_drag (*_point, 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));
 }
@@ -2565,7 +2821,13 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
        } else {
                motion (event, false);
        }
-       _point->line().end_drag (_point);
+       _point->line().end_drag ();
+}
+
+void
+ControlPointDrag::aborted ()
+{
+       _point->line().reset ();
 }
 
 bool
@@ -2606,7 +2868,10 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 
        nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
 
-       if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
+       uint32_t before;
+       uint32_t after;
+       
+       if (!_line->control_points_adjacent (frame_within_region, before, after)) {
                /* no adjacent points */
                return;
        }
@@ -2615,15 +2880,15 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 
        /* store grab start in parent frame */
 
-       _grab_x = cx;
-       _grab_y = cy;
+       _time_axis_view_grab_x = cx;
+       _time_axis_view_grab_y = cy;
 
        double fraction = 1.0 - (cy / _line->height());
 
-       _line->start_drag (0, _grab_frame, fraction);
+       _line->start_drag_line (before, after, fraction);
 
        _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
-                                           _current_pointer_x + 10, _current_pointer_y + 10);
+                                           event->button.x + 10, event->button.y + 10);
 
        _editor->show_verbose_canvas_cursor ();
 }
@@ -2631,15 +2896,15 @@ 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;
        }
 
-       double cy = _grab_y + _cumulative_y_drag + dy;
+       double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
 
-       _cumulative_y_drag = cy - _grab_y;
+       _cumulative_y_drag = cy - _time_axis_view_grab_y;
 
        cy = max (0.0, cy);
        cy = min ((double) _line->height(), cy);
@@ -2654,7 +2919,8 @@ LineDrag::motion (GdkEvent* event, bool)
                push = true;
        }
 
-       _line->line_drag (_before, _after, 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));
 }
@@ -2663,53 +2929,53 @@ void
 LineDrag::finished (GdkEvent* event, bool)
 {
        motion (event, false);
-       _line->end_drag (0);
+       _line->end_drag ();
+}
+
+void
+LineDrag::aborted ()
+{
+       _line->reset ();
 }
 
 void
 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 {
        Drag::start_grab (event);
-       _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
+       _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
 }
 
 void
-RubberbandSelectDrag::motion (GdkEvent* event, bool first_move)
+RubberbandSelectDrag::motion (GdkEvent* event, bool)
 {
        nframes64_t start;
        nframes64_t end;
        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) (_current_pointer_frame - _grab_frame)) < 8) {
-               return;
+       nframes64_t grab = grab_frame ();
+       if (Config->get_rubberbanding_snaps_to_grid ()) {
+               _editor->snap_to_with_modifier (grab, event);
        }
 
-       if (Config->get_rubberbanding_snaps_to_grid()) {
-               if (first_move) {
-                       _editor->snap_to_with_modifier (_grab_frame, event);
-               }
-               _editor->snap_to_with_modifier (_current_pointer_frame, event);
-       }
-
        /* base start and end on initial click position */
 
-       if (_current_pointer_frame < _grab_frame) {
-               start = _current_pointer_frame;
-               end = _grab_frame;
+       if (pf < grab) {
+               start = pf;
+               end = grab;
        } else {
-               end = _current_pointer_frame;
-               start = _grab_frame;
+               end = pf;
+               start = grab;
        }
 
-       if (_current_pointer_y < _grab_y) {
-               y1 = _current_pointer_y;
-               y2 = _grab_y;
+       if (_drags->current_pointer_y() < grab_y()) {
+               y1 = _drags->current_pointer_y();
+               y2 = grab_y();
        } else {
-               y2 = _current_pointer_y;
-               y1 = _grab_y;
+               y2 = _drags->current_pointer_y();
+               y1 = grab_y();
        }
 
 
@@ -2726,9 +2992,7 @@ RubberbandSelectDrag::motion (GdkEvent* event, bool first_move)
                _editor->rubberband_rect->show();
                _editor->rubberband_rect->raise_to_top();
 
-               _last_pointer_frame = _current_pointer_frame;
-
-               _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
+               _editor->show_verbose_time_cursor (pf, 10);
        }
 }
 
@@ -2740,12 +3004,12 @@ RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
                motion (event, false);
 
                double y1,y2;
-               if (_current_pointer_y < _grab_y) {
-                       y1 = _current_pointer_y;
-                       y2 = _grab_y;
+               if (_drags->current_pointer_y() < grab_y()) {
+                       y1 = _drags->current_pointer_y();
+                       y2 = grab_y();
                } else {
-                       y2 = _current_pointer_y;
-                       y1 = _grab_y;
+                       y2 = _drags->current_pointer_y();
+                       y1 = grab_y();
                }
 
 
@@ -2754,10 +3018,10 @@ RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
 
                _editor->begin_reversible_command (_("rubberband selection"));
 
-               if (_grab_frame < _last_pointer_frame) {
-                       committed = _editor->select_all_within (_grab_frame, _last_pointer_frame - 1, y1, y2, _editor->track_views, op);
+               if (grab_frame() < last_pointer_frame()) {
+                       committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
                } else {
-                       committed = _editor->select_all_within (_last_pointer_frame, _grab_frame - 1, y1, y2, _editor->track_views, op);
+                       committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
                }
 
                if (!committed) {
@@ -2776,12 +3040,18 @@ 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 *)
 {
        Drag::start_grab (event);
 
-       _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
+       _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
 }
 
 void
@@ -2789,19 +3059,13 @@ TimeFXDrag::motion (GdkEvent* event, bool)
 {
        RegionView* rv = _primary;
 
-       _editor->snap_to_with_modifier (_current_pointer_frame, event);
-
-       if (_current_pointer_frame == _last_pointer_frame) {
-               return;
-       }
+       nframes64_t const pf = adjusted_current_frame (event);
 
-       if (_current_pointer_frame > rv->region()->position()) {
-               rv->get_time_axis_view().show_timestretch (rv->region()->position(), _current_pointer_frame);
+       if (pf > rv->region()->position()) {
+               rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
        }
 
-       _last_pointer_frame = _current_pointer_frame;
-
-       _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
+       _editor->show_verbose_time_cursor (pf, 10);
 }
 
 void
@@ -2813,12 +3077,12 @@ TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
                return;
        }
 
-       if (_last_pointer_frame < _primary->region()->position()) {
+       if (last_pointer_frame() < _primary->region()->position()) {
                /* backwards drag of the left edge - not usable */
                return;
        }
 
-       nframes64_t newlen = _last_pointer_frame - _primary->region()->position();
+       nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
 
        float percentage = (double) newlen / (double) _primary->region()->length();
 
@@ -2841,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 *)
 {
@@ -2850,22 +3121,30 @@ 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
 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
 {
-       if (movement_occurred && _editor->session) {
+       if (movement_occurred && _editor->session()) {
                /* make sure we stop */
-               _editor->session->request_transport_speed (0.0);
+               _editor->session()->request_transport_speed (0.0);
        }
 }
 
+void
+ScrubDrag::aborted ()
+{
+       /* XXX: TODO */
+}
+
 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
        : Drag (e, i)
        , _operation (o)
        , _copy (false)
+       , _original_pointer_time_axis (-1)
+       , _last_pointer_time_axis (-1)
 {
 
 }
@@ -2876,7 +3155,7 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
        nframes64_t start = 0;
        nframes64_t end = 0;
 
-       if (_editor->session == 0) {
+       if (_editor->session() == 0) {
                return;
        }
 
@@ -2897,34 +3176,34 @@ 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;
+               _pointer_frame_offset = grab_frame() - start;
                break;
 
        case SelectionEndTrim:
                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;
+               _pointer_frame_offset = grab_frame() - end;
                break;
 
        case SelectionMove:
                start = _editor->selection->time[_editor->clicked_selection].start;
                Drag::start_grab (event, cursor);
-               _pointer_frame_offset = _grab_frame - start;
+               _pointer_frame_offset = grab_frame() - start;
                break;
        }
 
        if (_operation == SelectionMove) {
                _editor->show_verbose_time_cursor (start, 10);
        } else {
-               _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
+               _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
        }
+
+       _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
 }
 
 void
@@ -2934,59 +3213,90 @@ 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 (_drags->current_pointer_y ());
+       if (pending_time_axis.first == 0) {
+               return;
+       }
+       
        nframes64_t const pending_position = adjusted_current_frame (event);
 
-       /* only alter selection if the current frame is
-          different from the last frame position (adjusted)
-        */
+       /* only alter selection if things have changed */
 
-       if (pending_position == _last_pointer_frame) {
+       if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
                return;
        }
 
        switch (_operation) {
        case CreateSelection:
+       {
+               nframes64_t grab = grab_frame ();
 
                if (first_move) {
-                       _editor->snap_to (_grab_frame);
+                       _editor->snap_to (grab);
                }
 
-               if (pending_position < _grab_frame) {
+               if (pending_position < grab_frame()) {
                        start = pending_position;
-                       end = _grab_frame;
+                       end = grab;
                } else {
                        end = pending_position;
-                       start = _grab_frame;
+                       start = grab;
                }
 
                /* first drag: Either add to the selection
-                  or create a new selection->
+                  or create a new selection
                */
 
                if (first_move) {
 
-                       _editor->begin_reversible_command (_("range selection"));
-                       _have_transaction = true;
-
                        if (_copy) {
                                /* adding to the selection */
+                               _editor->selection->add (_editor->clicked_axisview);
                                _editor->clicked_selection = _editor->selection->add (start, end);
                                _copy = false;
                        } else {
-                               /* new selection-> */
-                               _editor->clicked_selection = _editor->selection->set (_editor->clicked_axisview, start, end);
+                               /* new selection */
+
+                               if (!_editor->selection->selected (_editor->clicked_axisview)) {
+                                       _editor->selection->set (_editor->clicked_axisview);
+                               }
+                               
+                               _editor->clicked_selection = _editor->selection->set (start, end);
                        }
                }
-               break;
-
-       case SelectionStartTrim:
 
-               if (first_move) {
-                       _editor->begin_reversible_command (_("trim selection start"));
-                       _have_transaction = true;
+               /* select the track that we're in */
+               if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
+                       _editor->selection->add (pending_time_axis.first);
+                       _added_time_axes.push_back (pending_time_axis.first);
                }
+
+               /* deselect any tracks that this drag no longer includes, being careful to only deselect
+                  tracks that we selected in the first place.
+               */
                
+               int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
+               int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
+
+               list<TimeAxisView*>::iterator i = _added_time_axes.begin();
+               while (i != _added_time_axes.end()) {
+
+                       list<TimeAxisView*>::iterator tmp = i;
+                       ++tmp;
+                       
+                       if ((*i)->order() < min_order || (*i)->order() > max_order) {
+                               _editor->selection->remove (*i);
+                               _added_time_axes.remove (*i);
+                       }
+
+                       i = tmp;
+               }
+
+       }
+       break;
+
+       case SelectionStartTrim:
+
                start = _editor->selection->time[_editor->clicked_selection].start;
                end = _editor->selection->time[_editor->clicked_selection].end;
 
@@ -2999,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;
 
@@ -3017,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;
 
@@ -3043,8 +3343,6 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                _editor->selection->replace (_editor->clicked_selection, start, end);
        }
 
-       _last_pointer_frame = pending_position;
-
        if (_operation == SelectionMove) {
                _editor->show_verbose_time_cursor(start, 10);
        } else {
@@ -3055,7 +3353,7 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
 void
 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       Session* s = _editor->session;
+       Session* s = _editor->session();
 
        if (movement_occurred) {
                motion (event, false);
@@ -3064,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);
@@ -3078,9 +3372,11 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
                /* just a click, no pointer movement.*/
 
                if (Keyboard::no_modifier_keys_pressed (&event->button)) {
-
                        _editor->selection->clear_time();
+               }
 
+               if (!_editor->selection->selected (_editor->clicked_axisview)) {
+                       _editor->selection->set (_editor->clicked_axisview);
                }
                
                if (s && s->get_play_range () && s->transport_rolling()) {
@@ -3092,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),
@@ -3107,7 +3409,7 @@ RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operat
 void
 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 {
-       if (_editor->session == 0) {
+       if (_editor->session() == 0) {
                return;
        }
 
@@ -3133,7 +3435,7 @@ RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 
        Drag::start_grab (event, cursor);
 
-       _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
+       _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
 }
 
 void
@@ -3159,30 +3461,18 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
                break;
        }
 
-       _editor->snap_to_with_modifier (_current_pointer_frame, event);
+       nframes64_t const pf = adjusted_current_frame (event);
 
-       /* only alter selection if the current frame is
-          different from the last frame position.
-        */
-
-       if (_current_pointer_frame == _last_pointer_frame) {
-               return;
-       }
-
-       switch (_operation) {
-       case CreateRangeMarker:
-       case CreateTransportMarker:
-       case CreateCDMarker:
-               if (first_move) {
-                       _editor->snap_to (_grab_frame);
-               }
-
-               if (_current_pointer_frame < _grab_frame) {
-                       start = _current_pointer_frame;
-                       end = _grab_frame;
+       if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
+               nframes64_t grab = grab_frame ();
+               _editor->snap_to (grab);
+               
+               if (pf < grab_frame()) {
+                       start = pf;
+                       end = grab;
                } else {
-                       end = _current_pointer_frame;
-                       start = _grab_frame;
+                       end = pf;
+                       start = grab;
                }
 
                /* first drag: Either add to the selection
@@ -3200,7 +3490,6 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
                        //_drag_rect->raise_to_top();
 
                }
-               break;
        }
 
        if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
@@ -3218,9 +3507,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
                update_item (_editor->temp_location);
        }
 
-       _last_pointer_frame = _current_pointer_frame;
-
-       _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
+       _editor->show_verbose_time_cursor (pf, 10);
 
 }
 
@@ -3240,8 +3527,8 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
                case CreateCDMarker:
                    {
                        _editor->begin_reversible_command (_("new range marker"));
-                       XMLNode &before = _editor->session->locations()->get_state();
-                       _editor->session->locations()->next_available_name(rangename,"unnamed");
+                       XMLNode &before = _editor->session()->locations()->get_state();
+                       _editor->session()->locations()->next_available_name(rangename,"unnamed");
                        if (_operation == CreateCDMarker) {
                                flags = Location::IsRangeMarker | Location::IsCDMarker;
                                _editor->cd_marker_bar_drag_rect->hide();
@@ -3251,9 +3538,9 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
                                _editor->range_bar_drag_rect->hide();
                        }
                        newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
-                       _editor->session->locations()->add (newloc, true);
-                       XMLNode &after = _editor->session->locations()->get_state();
-                       _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
+                       _editor->session()->locations()->add (newloc, true);
+                       XMLNode &after = _editor->session()->locations()->get_state();
+                       _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
                        _editor->commit_reversible_command ();
                        break;
                    }
@@ -3271,14 +3558,14 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
                        nframes64_t start;
                        nframes64_t end;
 
-                       _editor->session->locations()->marks_either_side (_grab_frame, start, end);
+                       _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
 
                        if (end == max_frames) {
-                               end = _editor->session->current_end_frame ();
+                               end = _editor->session()->current_end_frame ();
                        }
 
                        if (start == max_frames) {
-                               start = _editor->session->current_start_frame ();
+                               start = _editor->session()->current_start_frame ();
                        }
 
                        switch (_editor->mouse_mode) {
@@ -3289,7 +3576,7 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
 
                        case MouseRange:
                                /* find the two markers on either side of the click and make the range out of it */
-                               _editor->selection->set (0, start, end);
+                               _editor->selection->set (start, end);
                                break;
 
                        default:
@@ -3301,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)
@@ -3317,7 +3608,7 @@ void
 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 {
        Drag::start_grab (event, _editor->zoom_cursor);
-       _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
+       _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
 }
 
 void
@@ -3326,23 +3617,18 @@ MouseZoomDrag::motion (GdkEvent* event, bool first_move)
        nframes64_t start;
        nframes64_t end;
 
-       _editor->snap_to_with_modifier (_current_pointer_frame, event);
+       nframes64_t const pf = adjusted_current_frame (event);
 
-       if (first_move) {
-               _editor->snap_to_with_modifier (_grab_frame, event);
-       }
-
-       if (_current_pointer_frame == _last_pointer_frame) {
-               return;
-       }
+       nframes64_t grab = grab_frame ();
+       _editor->snap_to_with_modifier (grab, event);
 
        /* base start and end on initial click position */
-       if (_current_pointer_frame < _grab_frame) {
-               start = _current_pointer_frame;
-               end = _grab_frame;
+       if (pf < grab) {
+               start = pf;
+               end = grab;
        } else {
-               end = _current_pointer_frame;
-               start = _grab_frame;
+               end = pf;
+               start = grab;
        }
 
        if (start != end) {
@@ -3354,9 +3640,7 @@ MouseZoomDrag::motion (GdkEvent* event, bool first_move)
 
                _editor->reposition_zoom_rect(start, end);
 
-               _last_pointer_frame = _current_pointer_frame;
-
-               _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
+               _editor->show_verbose_time_cursor (pf, 10);
        }
 }
 
@@ -3366,22 +3650,28 @@ MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
        if (movement_occurred) {
                motion (event, false);
 
-               if (_grab_frame < _last_pointer_frame) {
-                       _editor->temporal_zoom_by_frame (_grab_frame, _last_pointer_frame, "mouse zoom");
+               if (grab_frame() < last_pointer_frame()) {
+                       _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
                } else {
-                       _editor->temporal_zoom_by_frame (_last_pointer_frame, _grab_frame, "mouse zoom");
+                       _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
                }
        } else {
-               _editor->temporal_zoom_to_frame (false, _grab_frame);
+               _editor->temporal_zoom_to_frame (false, grab_frame());
                /*
                temporal_zoom_step (false);
-               center_screen (_grab_frame);
+               center_screen (grab_frame());
                */
        }
 
        _editor->zoom_rect->hide();
 }
 
+void
+MouseZoomDrag::aborted ()
+{
+       _editor->zoom_rect->hide ();
+}
+
 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
        : Drag (e, i)
 {
@@ -3400,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);
 
@@ -3438,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);
 
@@ -3508,3 +3798,126 @@ NoteDrag::finished (GdkEvent* ev, bool moved)
                region->note_dropped (cnote, drag_delta_x, drag_delta_note);
        }
 }
+
+void
+NoteDrag::aborted ()
+{
+       /* XXX: TODO */
+}
+
+AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
+       : Drag (e, i)
+       , _ranges (r)
+       , _nothing_to_drag (false)
+{
+       _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
+       assert (_atav);
+
+       _line = _atav->line ();
+}
+
+void
+AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
+{
+       Drag::start_grab (event, cursor);
+
+       list<ControlPoint*> points;
+
+       XMLNode* state = &_line->get_state ();
+       
+       if (_ranges.empty()) {
+               
+               uint32_t const N = _line->npoints ();
+               for (uint32_t i = 0; i < N; ++i) {
+                       points.push_back (_line->nth (i));
+               }
+               
+       } else {
+
+               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 (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 ();
+               for (uint32_t i = 0; i < N; ++i) {
+
+                       ControlPoint* p = _line->nth (i);
+
+                       list<AudioRange>::const_iterator j = _ranges.begin ();
+                       while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
+                               ++j;
+                       }
+
+                       if (j != _ranges.end()) {
+                               points.push_back (p);
+                       }
+               }
+       }
+
+       if (points.empty()) {
+               _nothing_to_drag = true;
+               return;
+       }
+
+       _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
+}
+
+void
+AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
+{
+       if (_nothing_to_drag) {
+               return;
+       }
+       
+       float const f = 1 - (_drags->current_pointer_y() / _line->height());
+
+       /* we are ignoring x position for this drag, so we can just pass in anything */
+       _line->drag_motion (0, f, true, false);
+}
+
+void
+AutomationRangeDrag::finished (GdkEvent* event, bool)
+{
+       if (_nothing_to_drag) {
+               return;
+       }
+       
+       motion (event, false);
+       _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 ();
+}