new fade in/fade out colors from chrisg
[ardour.git] / gtk2_ardour / editor_drag.cc
index 0739e91bb1b756c8ec7b3e50340492e35eda1a6d..d49f60c9ca8ef302a6abec69b3c1e90853059d1f 100644 (file)
 #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"
@@ -68,24 +67,14 @@ DragManager::~DragManager ()
        abort ();
 }
 
+/** Call abort for each active drag */
 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 ();
+               (*i)->abort ();
                delete *i;
        }
 
@@ -120,6 +109,9 @@ DragManager::start_grab (GdkEvent* e)
        }
 }
 
+/** Call end_grab for each active drag.
+ *  @return true if any drag reported movement having occurred.
+ */
 bool
 DragManager::end_grab (GdkEvent* e)
 {
@@ -223,8 +215,8 @@ Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
        _last_pointer_y = _grab_y;
 
        _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
-                             *cursor,
-                             event->button.time);
+                     *cursor,
+                     event->button.time);
 
        if (_editor->session() && _editor->session()->transport_rolling()) {
                _was_rolling = true;
@@ -244,7 +236,10 @@ Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
        }
 }
 
-/** @param event GDK event, or 0.
+/** Call to end a drag `successfully'.  Ungrabs item and calls
+ *  subclass' finished() method.
+ *
+ *  @param event GDK event, or 0.
  *  @return true if some movement occurred, otherwise false.
  */
 bool
@@ -320,12 +315,12 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
                        return true;
                }
        }
-
        return false;
 }
 
+/** Call to abort a drag.  Ungrabs item and calls subclass's aborted () */
 void
-Drag::break_drag ()
+Drag::abort ()
 {
        if (_item) {
                _item->ungrab (0);
@@ -339,17 +334,25 @@ Drag::break_drag ()
 
 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
        : Drag (e, i),
-         _primary (p),
-         _views (v)
+         _primary (p)
 {
-       RegionView::RegionViewGoingAway.connect (death_connection, ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
+       for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
+               _views.push_back (DraggingView (*i));
+       }
+       
+       RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
 }
 
 void
 RegionDrag::region_going_away (RegionView* v)
 {
-       if (!_drags->ending ()) {
-               _views.remove (v);
+       list<DraggingView>::iterator i = _views.begin ();
+       while (i != _views.end() && i->view != v) {
+               ++i;
+       }
+
+       if (i != _views.end()) {
+               _views.erase (i);
        }
 }
 
@@ -455,9 +458,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;
@@ -546,9 +549,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.
 
@@ -556,7 +559,7 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_
                                rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
                                rv->get_canvas_frame()->i2w (ix1, iy1);
 
-                               if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
+                               if (-x_delta > ix1 + _editor->horizontal_position()) {
                                        x_delta = 0;
                                        *pending_region_position = _last_frame_position;
                                        break;
@@ -631,9 +634,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;
@@ -817,8 +820,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;
@@ -829,6 +833,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 */
@@ -839,8 +844,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);
                        }
                }
 
@@ -879,14 +884,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";
 
-               RegionView* rv = (*i);
-               RouteTimeAxisView* dest_rtv = final[*i].first;
-               layer_t dest_layer = final[*i].second;
+       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
+
+               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;
@@ -916,7 +926,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;
@@ -930,10 +940,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);
@@ -949,6 +967,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).
@@ -956,13 +976,13 @@ 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);
@@ -976,10 +996,11 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
                                playlist->freeze();
                        }
 
-                       XMLNode& before (rv->region()->get_state());
+                        cerr << "Moving region " << rv->region()->id() << endl;
+
                        rv->region()->set_position (where, (void*) this);
-                       _editor->session()->add_command (new MementoCommand<Region>(*rv->region(), &before, &playlist->get_state()));
 
+                       sdc.push_back (new StatefulDiffCommand (rv->region()));
                }
 
                if (changed_tracks && !_copy) {
@@ -989,11 +1010,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 */
@@ -1012,9 +1033,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
@@ -1035,7 +1062,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();
                        }
@@ -1047,11 +1083,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);
        }
@@ -1061,9 +1111,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 ();
 
@@ -1077,8 +1127,8 @@ RegionMoveDrag::aborted ()
 {
        if (_copy) {
 
-               for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                       delete *i;
+               for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                       delete i->view;
                }
 
                _views.clear ();
@@ -1091,16 +1141,17 @@ RegionMoveDrag::aborted ()
 void
 RegionMotionDrag::aborted ()
 {
-       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-               TimeAxisView* tv = &(*i)->get_time_axis_view ();
+       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+               RegionView* rv = i->view;
+               TimeAxisView* tv = &(rv->get_time_axis_view ());
                RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
                assert (rtv);
-               (*i)->get_canvas_group()->reparent (*rtv->view()->canvas_item());
-               (*i)->get_canvas_group()->property_y() = 0;
-               (*i)->get_time_axis_view().reveal_dependent_views (**i);
-               (*i)->fake_set_opaque (false);
-               (*i)->move (-_total_x_delta, 0);
-               (*i)->set_height (rtv->view()->child_height ());
+               rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
+               rv->get_canvas_group()->property_y() = 0;
+               rv->get_time_axis_view().reveal_dependent_views (*rv);
+               rv->fake_set_opaque (false);
+               rv->move (-_total_x_delta, 0);
+               rv->set_height (rtv->view()->child_height ());
        }
 
        _editor->update_canvas_now ();
@@ -1123,21 +1174,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
@@ -1148,7 +1201,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 */
 
@@ -1169,7 +1222,7 @@ RegionMotionDrag::copy_regions (GdkEvent* event)
 
        _views = new_regionviews;
 
-       swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
+       swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
 
        /*
           sync the canvas to what we think is its current state
@@ -1311,7 +1364,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);
@@ -1335,7 +1388,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;
 
@@ -1349,15 +1402,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;
@@ -1379,9 +1433,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;
@@ -1392,7 +1446,9 @@ RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
 void
 RegionInsertDrag::aborted ()
 {
-       /* XXX: TODO */
+       delete _primary;
+       _primary = 0;
+       _views.clear ();
 }
 
 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
@@ -1534,27 +1590,27 @@ NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
 }
 
 void
-NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
+NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
 {
-       Gdk::Cursor     cursor;
-       ArdourCanvas::CanvasNote*     cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
+       Gdk::Cursor* cursor;
+       ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
 
        Drag::start_grab (event);
 
        region = &cnote->region_view();
 
-       double region_start = region->get_position_pixels();
-       double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
+       double const region_start = region->get_position_pixels();
+       double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
 
        if (grab_x() <= middle_point) {
-               cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
+               cursor = _editor->left_side_trim_cursor;
                at_front = true;
        } else {
-               cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
+               cursor = _editor->right_side_trim_cursor;
                at_front = false;
        }
 
-       _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
+       _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
 
        if (event->motion.state & Keyboard::PrimaryModifier) {
                relative = false;
@@ -1569,7 +1625,10 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
                relative = true;
        }
 
-       region->note_selected (cnote, true);
+       /* select this note; if it is already selected, preserve the existing selection,
+          otherwise make this note the only one selected.
+       */
+       region->note_selected (cnote, cnote->selected ());
 
        for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
                MidiRegionSelection::iterator next;
@@ -1585,7 +1644,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, _drags->current_pointer_x() - grab_x(), relative);
+               (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
        }
 }
 
@@ -1594,7 +1653,7 @@ NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
 {
        MidiRegionSelection& ms (_editor->get_selection().midi_regions);
        for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
-               (*r)->commit_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
+               (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
        }
 }
 
@@ -1637,28 +1696,30 @@ 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);
        nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
        nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
 
-       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;
+                Drag::start_grab (event, _editor->trimmer_cursor);
        } else {
                /* These will get overridden for a point trim.*/
                if (pf < (region_start + region_length/2)) {
                        /* closer to start */
                        _operation = StartTrim;
-               } else if (pf > (region_end - region_length/2)) {
+                        Drag::start_grab (event, _editor->left_side_trim_cursor);
+               } else {
                        /* closer to end */
                        _operation = EndTrim;
-               }
+                        Drag::start_grab (event, _editor->right_side_trim_cursor);
+                }
        }
 
        switch (_operation) {
@@ -1678,10 +1739,6 @@ void
 TrimDrag::motion (GdkEvent* event, bool first_move)
 {
        RegionView* rv = _primary;
-       nframes64_t frame_delta = 0;
-
-       bool left_direction;
-       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
@@ -1694,17 +1751,11 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
        pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
 
        if (tv && tv->is_track()) {
-               speed = tv->get_diskstream()->speed();
+               speed = tv->track()->speed();
        }
 
        nframes64_t const pf = adjusted_current_frame (event);
 
-       if (last_pointer_frame() > pf) {
-               left_direction = true;
-       } else {
-               left_direction = false;
-       }
-
        if (first_move) {
 
                string trim_type;
@@ -1724,32 +1775,27 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                _editor->begin_reversible_command (trim_type);
                _have_transaction = true;
 
-               for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                       (*i)->fake_set_opaque(false);
-                       (*i)->region()->freeze ();
+               for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                       RegionView* rv = i->view;
+                       rv->fake_set_opaque(false);
+                        rv->region()->clear_history ();
+                       rv->region()->suspend_property_changes ();
 
-                       AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
+                       AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
 
                        if (arv){
                                arv->temporarily_hide_envelope ();
                        }
 
-                       boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
+                       boost::shared_ptr<Playlist> pl = rv->region()->playlist();
                        insert_result = _editor->motion_frozen_playlists.insert (pl);
 
                        if (insert_result.second) {
-                               _editor->session()->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
                                pl->freeze();
                        }
                }
        }
 
-       if (left_direction) {
-               frame_delta = (last_pointer_frame() - pf);
-       } else {
-               frame_delta = (pf - last_pointer_frame());
-       }
-
        bool non_overlap_trim = false;
 
        if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
@@ -1758,26 +1804,16 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
 
        switch (_operation) {
        case StartTrim:
-               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);
-                       }
-                       break;
+               for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                       _editor->single_start_trim (*i->view, pf, non_overlap_trim);
                }
+               break;
 
        case EndTrim:
-               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);
-                       }
-                       break;
+               for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                       _editor->single_end_trim (*i->view, pf, non_overlap_trim);
                }
+               break;
 
        case ContentsTrim:
                {
@@ -1787,8 +1823,21 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                                swap_direction = true;
                        }
 
-                       for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                               _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
+                       nframes64_t frame_delta = 0;
+                       
+                       bool left_direction = false;
+                       if (last_pointer_frame() > pf) {
+                               left_direction = true;
+                       }
+
+                       if (left_direction) {
+                               frame_delta = (last_pointer_frame() - pf);
+                       } else {
+                               frame_delta = (pf - last_pointer_frame());
+                       }
+
+                       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                               _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
                        }
                }
                break;
@@ -1818,16 +1867,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 ();
@@ -2135,6 +2184,7 @@ FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
        _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);
+       
 }
 
 void
@@ -2154,9 +2204,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;
@@ -2191,9 +2241,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;
@@ -2215,8 +2265,8 @@ FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
 void
 FadeInDrag::aborted ()
 {
-       for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
-               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
+       for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
 
                if (!tmp) {
                        continue;
@@ -2263,9 +2313,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;
@@ -2302,9 +2352,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;
@@ -2326,8 +2376,8 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
 void
 FadeOutDrag::aborted ()
 {
-       for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
-               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
+       for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
 
                if (!tmp) {
                        continue;
@@ -2430,10 +2480,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));
        }
 }
@@ -2445,7 +2495,7 @@ MarkerDrag::motion (GdkEvent* event, bool)
        bool is_start;
        bool move_both = false;
        Marker* marker;
-       Location  *real_location;
+       Location *real_location;
        Location *copy_location = 0;
 
        nframes64_t const newframe = adjusted_current_frame (event);
@@ -2531,7 +2581,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);
 
@@ -3041,7 +3091,7 @@ TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
        RegionSelection rs;
        rs.add (_primary);
 
-       if (!_editor->time_stretch (rs, percentage) == 0) {
+       if (_editor->time_stretch (rs, percentage) == -1) {
                error << _("An error occurred while executing time stretch operation") << endmsg;
        }
 }
@@ -3117,7 +3167,7 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
                if (_editor->clicked_axisview) {
                        _editor->clicked_axisview->order_selection_trims (_item, true);
                }
-               Drag::start_grab (event, _editor->trimmer_cursor);
+               Drag::start_grab (event, _editor->left_side_trim_cursor);
                start = _editor->selection->time[_editor->clicked_selection].start;
                _pointer_frame_offset = grab_frame() - start;
                break;
@@ -3126,7 +3176,7 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
                if (_editor->clicked_axisview) {
                        _editor->clicked_axisview->order_selection_trims (_item, false);
                }
-               Drag::start_grab (event, _editor->trimmer_cursor);
+               Drag::start_grab (event, _editor->right_side_trim_cursor);
                end = _editor->selection->time[_editor->clicked_selection].end;
                _pointer_frame_offset = grab_frame() - end;
                break;
@@ -3198,7 +3248,7 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                        } else {
                                /* new selection */
 
-                               if (!_editor->selection->selected (_editor->clicked_axisview)) {
+                               if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
                                        _editor->selection->set (_editor->clicked_axisview);
                                }
                                
@@ -3276,7 +3326,7 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                break;
        }
 
-       if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
+       if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
                _editor->start_canvas_autoscroll (1, 0);
        }
 
@@ -3316,7 +3366,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
                        _editor->selection->clear_time();
                }
 
-               if (!_editor->selection->selected (_editor->clicked_axisview)) {
+               if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
                        _editor->selection->set (_editor->clicked_axisview);
                }
                
@@ -3433,7 +3483,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
                }
        }
 
-       if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
+       if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
                _editor->start_canvas_autoscroll (1, 0);
        }
 
@@ -3699,12 +3749,34 @@ NoteDrag::motion (GdkEvent*, bool)
        }
 
        if (dx || dy) {
+                
+               CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
+                Evoral::MusicalTime new_time;
+                
+                if (drag_delta_x) {
+                        nframes64_t start_frames = region->beats_to_frames(cnote->note()->time());
+                        if (drag_delta_x >= 0) {
+                                start_frames += region->snap_frame_to_frame(_editor->pixel_to_frame(drag_delta_x));
+                        } else {
+                                start_frames -= region->snap_frame_to_frame(_editor->pixel_to_frame(-drag_delta_x));
+                        }
+                        new_time = region->frames_to_beats(start_frames);
+                } else {
+                        new_time = cnote->note()->time();
+                }
+                
+                boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > check_note (
+                        new Evoral::Note<Evoral::MusicalTime> (cnote->note()->channel(), 
+                                                               new_time,
+                                                               cnote->note()->length(), 
+                                                               cnote->note()->note() + drag_delta_note,
+                                                               cnote->note()->velocity()));
+
                region->move_selection (dx, dy);
 
-               CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
-                char buf[4];
-               snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
-               //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
+                char buf[12];
+                snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (cnote->note()->note()).c_str(),
+                          (int) floor ((cnote->note()->note() + drag_delta_note)));
                _editor->show_verbose_canvas_cursor_with (buf);
         }
 }
@@ -3856,3 +3928,9 @@ AutomationRangeDrag::aborted ()
        _line->clear_always_in_view ();
        _line->reset ();
 }
+
+DraggingView::DraggingView (RegionView* v)
+       : view (v)
+{
+       initial_y = v->get_canvas_group()->property_y ();
+}