Fix some set-but-not-used variable warnings from gcc 4.6
[ardour.git] / gtk2_ardour / editor_drag.cc
index 675a722509d11b7ca0a3fce5bd7ffff335572b2b..c889921db774b7ed5ac9f29edd9c1859d2f4e0c1 100644 (file)
 
 */
 
-#define __STDC_LIMIT_MACROS 1
+#ifdef WAF_BUILD
+#include "gtk2ardour-config.h"
+#endif
+
 #include <stdint.h>
+
 #include "pbd/memento_command.h"
 #include "pbd/basename.h"
 #include "pbd/stateful_diff_command.h"
+
+#include "gtkmm2ext/utils.h"
+
 #include "ardour/session.h"
 #include "ardour/dB.h"
 #include "ardour/region_factory.h"
+#include "ardour/operations.h"
+
 #include "editor.h"
 #include "i18n.h"
 #include "keyboard.h"
 #include "midi_selection.h"
 #include "automation_time_axis.h"
 #include "debug.h"
+#include "editor_cursors.h"
+#include "mouse_cursors.h"
+#include "verbose_cursor.h"
 
 using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 using namespace Gtk;
+using namespace Gtkmm2ext;
 using namespace Editing;
 using namespace ArdourCanvas;
 
 using Gtkmm2ext::Keyboard;
 
-double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
+double ControlPointDrag::_zero_gain_fraction = -1.0;
 
 DragManager::DragManager (Editor* e)
        : _editor (e)
        , _ending (false)
        , _current_pointer_frame (0)
 {
-
 }
 
 DragManager::~DragManager ()
@@ -73,12 +85,16 @@ void
 DragManager::abort ()
 {
        _ending = true;
-       
+
        for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
                (*i)->abort ();
                delete *i;
        }
 
+       if (!_drags.empty ()) {
+               _editor->set_follow_playhead (_old_follow_playhead, false);
+       }
+
        _drags.clear ();
 
        _ending = false;
@@ -94,7 +110,6 @@ DragManager::add (Drag* 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, c);
@@ -103,8 +118,12 @@ DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
 void
 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
 {
+       /* Prevent follow playhead during the drag to be nice to the user */
+       _old_follow_playhead = _editor->follow_playhead ();
+       _editor->set_follow_playhead (false);
+
        _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, c);
        }
@@ -117,7 +136,7 @@ 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);
@@ -130,7 +149,9 @@ DragManager::end_grab (GdkEvent* e)
        _drags.clear ();
 
        _ending = false;
-       
+
+       _editor->set_follow_playhead (_old_follow_playhead, false);
+
        return r;
 }
 
@@ -140,13 +161,13 @@ 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;
@@ -163,7 +184,7 @@ DragManager::have_item (ArdourCanvas::Item* i) const
        return j != _drags.end ();
 }
 
-Drag::Drag (Editor* e, ArdourCanvas::Item* i) 
+Drag::Drag (Editor* e, ArdourCanvas::Item* i)
        : _editor (e)
        , _item (i)
        , _pointer_frame_offset (0)
@@ -211,14 +232,15 @@ Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
        }
 
        _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
+       setup_pointer_frame_offset ();
        _grab_frame = adjusted_frame (_raw_grab_frame, event);
        _last_pointer_frame = _grab_frame;
        _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);
+                    *cursor,
+                    event->button.time);
 
        if (_editor->session() && _editor->session()->transport_rolling()) {
                _was_rolling = true;
@@ -253,7 +275,7 @@ Drag::end_grab (GdkEvent* event)
 
        finished (event, _move_threshold_passed);
 
-       _editor->hide_verbose_canvas_cursor();
+       _editor->verbose_cursor()->hide ();
 
        return _move_threshold_passed;
 }
@@ -284,8 +306,9 @@ bool
 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
 {
        /* 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 ()) ) {
+       if (_move_threshold_passed &&
+           (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
+           (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
                return false;
        }
 
@@ -295,7 +318,7 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
 
        if (!from_autoscroll && !_move_threshold_passed) {
 
-               bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
+               bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
                bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
 
                _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
@@ -313,7 +336,7 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
                        _last_pointer_x = _drags->current_pointer_x ();
                        _last_pointer_y = _drags->current_pointer_y ();
                        _last_pointer_frame = adjusted_current_frame (event);
-                       
+
                        return true;
                }
        }
@@ -328,12 +351,49 @@ Drag::abort ()
                _item->ungrab (0);
        }
 
-       aborted ();
+       aborted (_move_threshold_passed);
 
        _editor->stop_canvas_autoscroll ();
-       _editor->hide_verbose_canvas_cursor ();
+       _editor->verbose_cursor()->hide ();
+}
+
+void
+Drag::show_verbose_cursor_time (framepos_t frame)
+{
+       _editor->verbose_cursor()->set_time (
+               frame,
+               _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
+               _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
+               );
+
+       _editor->verbose_cursor()->show ();
+}
+
+void
+Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
+{
+       _editor->verbose_cursor()->show (xoffset);
+
+       _editor->verbose_cursor()->set_duration (
+               start, end,
+               _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
+               _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
+               );
+}
+
+void
+Drag::show_verbose_cursor_text (string const & text)
+{
+       _editor->verbose_cursor()->show ();
+
+       _editor->verbose_cursor()->set (
+               text,
+               _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
+               _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
+               );
 }
 
+
 struct EditorOrderTimeAxisViewSorter {
     bool operator() (TimeAxisView* a, TimeAxisView* b) {
            RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
@@ -356,7 +416,7 @@ RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<Re
 
        for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
                if (!(*i)->hidden()) {
-                       
+
                        _time_axis_views.push_back (*i);
 
                        TimeAxisView::Children children_list = (*i)->get_child_list ();
@@ -365,11 +425,14 @@ RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<Re
                        }
                }
        }
-       
+
+       /* the list of views can be empty at this point if this is a region list-insert drag
+        */
+
        for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
                _views.push_back (DraggingView (*i, this));
        }
-       
+
        RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
 }
 
@@ -399,7 +462,7 @@ RegionDrag::find_time_axis_view (TimeAxisView* t) const
        if (i == N) {
                return -1;
        }
-       
+
        return i;
 }
 
@@ -417,7 +480,7 @@ RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 {
        Drag::start_grab (event, cursor);
 
-       _editor->show_verbose_time_cursor (_last_frame_position, 10);
+       show_verbose_cursor_time (_last_frame_position);
 
        pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
        _last_pointer_time_axis_view = find_time_axis_view (tv.first);
@@ -435,19 +498,19 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r
        framepos_t sync_frame;
        framecnt_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;
        }
@@ -463,13 +526,13 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r
 
        if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
 
-               /* x movement since last time */
+               /* x movement since last time (in pixels) */
                dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
 
                /* total x movement */
                framecnt_t total_dx = *pending_region_position;
                if (regions_came_from_canvas()) {
-                       total_dx = total_dx - grab_frame () + _pointer_frame_offset;
+                       total_dx = total_dx - grab_frame ();
                }
 
                /* check that no regions have gone off the start of the session */
@@ -502,7 +565,7 @@ RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) cons
                        /* not a track, or the wrong type */
                        return false;
                }
-               
+
                int const l = i->layer + delta_layer;
                if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
                        /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
@@ -519,13 +582,15 @@ RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) cons
 void
 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
 {
+       assert (!_views.empty ());
+
        /* Find the TimeAxisView that the pointer is now over */
        pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
 
        /* Bail early if we're not over a track */
        RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
        if (!rtv || !rtv->is_track()) {
-               _editor->hide_verbose_canvas_cursor ();
+               _editor->verbose_cursor()->hide ();
                return;
        }
 
@@ -571,39 +636,39 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
                        /* here we are calculating the y distance from the
                           top of the first track view to the top of the region
                           area of the track view that we're working on */
-                       
+
                        /* this x value is just a dummy value so that we have something
                           to pass to i2w () */
-                       
+
                        double ix1 = 0;
-                       
+
                        /* distance from the top of this track view to the region area
                           of our track view is always 1 */
-                       
+
                        double iy1 = 1;
-                       
+
                        /* convert to world coordinates, ie distance from the top of
                           the ruler section */
-                       
+
                        rv->get_canvas_frame()->i2w (ix1, iy1);
-                       
+
                        /* compensate for the ruler section and the vertical scrollbar position */
                        iy1 += _editor->get_trackview_group_vertical_offset ();
-                       
+
                        // hide any dependent views
-                       
+
                        rv->get_time_axis_view().hide_dependent_views (*rv);
-                       
+
                        /*
                          reparent to a non scrolling group so that we can keep the
                          region selection above all time axis views.
                          reparenting means we have to move the rv as the two
                          parent groups have different coordinates.
                        */
-                       
+
                        rv->get_canvas_group()->property_y() = iy1 - 1;
                        rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
-                       
+
                        rv->fake_set_opaque (true);
                }
 
@@ -661,13 +726,13 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
        } /* foreach region */
 
        _total_x_delta += x_delta;
-       
+
        if (first_move) {
                _editor->cursor_group->raise_to_top();
        }
 
        if (x_delta != 0 && !_brushing) {
-               _editor->show_verbose_time_cursor (_last_frame_position, 10);
+               show_verbose_cursor_time (_last_frame_position);
        }
 
        _last_pointer_time_axis_view += delta_time_axis_view;
@@ -684,20 +749,20 @@ RegionMoveDrag::motion (GdkEvent* event, bool first_move)
                list<DraggingView> new_regionviews;
 
                for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++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);
-                       
+                       boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
+                       region_copy->set_position (original->position());
+
                        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
@@ -706,29 +771,29 @@ RegionMoveDrag::motion (GdkEvent* event, bool first_move)
                        } else {
                                continue;
                        }
-                       
+
                        nrv->get_canvas_group()->show ();
                        new_regionviews.push_back (DraggingView (nrv, this));
-                       
+
                        /* swap _primary to the copy */
-                       
+
                        if (rv == _primary) {
                                _primary = nrv;
                        }
-                       
+
                        /* ..and deselect the one we copied */
-                       
+
                        rv->set_selected (false);
                }
-               
+
                if (!new_regionviews.empty()) {
-                       
+
                        /* reflect the fact that we are dragging the copies */
-                       
+
                        _views = new_regionviews;
-                       
+
                        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
@@ -759,37 +824,35 @@ RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
                _x_constrained = !_x_constrained;
        }
 
+       assert (!_views.empty ());
+
        bool const changed_position = (_last_frame_position != _primary->region()->position());
        bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
        framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
 
        _editor->update_canvas_now ();
-       
+
        if (_copy) {
-               
+
                finished_copy (
                        changed_position,
                        changed_tracks,
                        drag_delta
                        );
-               
+
        } else {
-               
+
                finished_no_copy (
                        changed_position,
                        changed_tracks,
                        drag_delta
                        );
-               
+
        }
 }
 
 void
-RegionMoveDrag::finished_copy (
-       bool const changed_position,
-       bool const changed_tracks,
-       framecnt_t const drag_delta
-       )
+RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
 {
        RegionSelection new_views;
        PlaylistSet modified_playlists;
@@ -807,9 +870,9 @@ RegionMoveDrag::finished_copy (
        }
 
        if (_x_constrained) {
-               _editor->begin_reversible_command (_("fixed time region copy"));
+               _editor->begin_reversible_command (Operations::fixed_time_region_copy);
        } else {
-               _editor->begin_reversible_command (_("region copy"));
+               _editor->begin_reversible_command (Operations::region_copy);
        }
 
        /* insert the regions into their new playlists */
@@ -830,13 +893,13 @@ RegionMoveDrag::finished_copy (
                RegionView* new_view = insert_region_into_playlist (
                        i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
                        );
-               
+
                if (new_view == 0) {
                        continue;
                }
 
                new_views.push_back (new_view);
-               
+
                /* we don't need the copied RegionView any more */
                views_to_delete.push_back (i->view);
        }
@@ -849,8 +912,8 @@ RegionMoveDrag::finished_copy (
                delete *i;
        }
 
-       /* 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 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) {
@@ -883,7 +946,7 @@ RegionMoveDrag::finished_no_copy (
        if (_x_constrained) {
                _editor->begin_reversible_command (_("fixed time region drag"));
        } else {
-               _editor->begin_reversible_command (_("region drag"));
+               _editor->begin_reversible_command (Operations::region_drag);
        }
 
        for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
@@ -911,7 +974,7 @@ RegionMoveDrag::finished_no_copy (
                        /* insert into new playlist */
 
                        RegionView* new_view = insert_region_into_playlist (
-                               RegionFactory::create (rv->region ()), dest_rtv, dest_layer, where, modified_playlists
+                               RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
                                );
 
                        if (new_view == 0) {
@@ -934,7 +997,7 @@ RegionMoveDrag::finished_no_copy (
                        remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
 
                } else {
-                       
+
                        rv->region()->clear_changes ();
 
                        /*
@@ -950,12 +1013,12 @@ RegionMoveDrag::finished_no_copy (
                        /* 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 */
 
                        pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
@@ -967,19 +1030,19 @@ RegionMoveDrag::finished_no_copy (
                        /* this movement may result in a crossfade being modified, so we need to get undo
                           data from the playlist as well as the region.
                        */
-                       
+
                        r = modified_playlists.insert (playlist);
                        if (r.second) {
                                playlist->clear_changes ();
                        }
 
-                       rv->region()->set_position (where, (void*) this);
+                       rv->region()->set_position (where);
 
                        _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
                }
 
                if (changed_tracks) {
-                       
+
                        /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
                           was selected in all of them, then removing it from a playlist will have removed all
                           trace of it from _views (i.e. there were N regions selected, we removed 1,
@@ -997,9 +1060,9 @@ RegionMoveDrag::finished_no_copy (
                           we can just iterate.
                        */
 
-                       
+
                        if (_views.empty()) {
-                                break;
+                               break;
                        } else {
                                i = _views.begin();
                        }
@@ -1009,8 +1072,8 @@ RegionMoveDrag::finished_no_copy (
                }
        }
 
-       /* 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 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) {
@@ -1048,7 +1111,7 @@ RegionMoveDrag::remove_region_from_playlist (
 
        playlist->remove_region (region);
 }
-       
+
 
 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
  *  clearing the playlist's diff history first if necessary.
@@ -1057,7 +1120,7 @@ RegionMoveDrag::remove_region_from_playlist (
  *  @param dest_layer Destination layer.
  *  @param where Destination position.
  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
- *  that clear_changes () is only called once per playlist. 
+ *  that clear_changes () is only called once per playlist.
  *  @return New RegionView, or 0 if no insert was performed.
  */
 RegionView *
@@ -1078,7 +1141,7 @@ RegionMoveDrag::insert_region_into_playlist (
        _new_region_view = 0;
        sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
 
-       /* clear history for the playlist we are about to insert to, provided we haven't already done so */     
+       /* clear history for the playlist we are about to insert to, provided we haven't already done so */
        pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
        if (r.second) {
                dest_playlist->clear_changes ();
@@ -1110,7 +1173,7 @@ RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & pl
        for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
                StatefulDiffCommand* c = new StatefulDiffCommand (*i);
                if (!c->empty()) {
-                       _editor->session()->add_command (new StatefulDiffCommand (*i));
+                       _editor->session()->add_command (c);
                } else {
                        delete c;
                }
@@ -1119,7 +1182,7 @@ RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & pl
 
 
 void
-RegionMoveDrag::aborted ()
+RegionMoveDrag::aborted (bool movement_occurred)
 {
        if (_copy) {
 
@@ -1130,12 +1193,12 @@ RegionMoveDrag::aborted ()
                _views.clear ();
 
        } else {
-               RegionMotionDrag::aborted ();
+               RegionMotionDrag::aborted (movement_occurred);
        }
 }
 
 void
-RegionMotionDrag::aborted ()
+RegionMotionDrag::aborted (bool)
 {
        for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
                RegionView* rv = i->view;
@@ -1152,13 +1215,16 @@ RegionMotionDrag::aborted ()
 
        _editor->update_canvas_now ();
 }
-                                     
+
+/** @param b true to brush, otherwise false.
+ *  @param c true to make copies of the regions being moved, otherwise false.
+ */
 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
        : RegionMotionDrag (e, i, p, v, b),
          _copy (c)
 {
        DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
-       
+
        double speed = 1;
        RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
        if (rtv && rtv->is_track()) {
@@ -1169,10 +1235,8 @@ RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p,
 }
 
 void
-RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
+RegionMoveDrag::setup_pointer_frame_offset ()
 {
-       RegionMotionDrag::start_grab (event, c);
-
        _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
 }
 
@@ -1180,7 +1244,7 @@ RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, Rout
        : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
 {
        DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
-       
+
        assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
                (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
 
@@ -1207,8 +1271,8 @@ RegionInsertDrag::finished (GdkEvent *, bool)
 
        boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
 
-       _editor->begin_reversible_command (_("insert region"));
-        playlist->clear_changes ();
+       _editor->begin_reversible_command (Operations::insert_region);
+       playlist->clear_changes ();
        playlist->add_region (_primary->region (), _last_frame_position);
        _editor->session()->add_command (new StatefulDiffCommand (playlist));
        _editor->commit_reversible_command ();
@@ -1219,7 +1283,7 @@ RegionInsertDrag::finished (GdkEvent *, bool)
 }
 
 void
-RegionInsertDrag::aborted ()
+RegionInsertDrag::aborted (bool)
 {
        delete _primary;
        _primary = 0;
@@ -1245,11 +1309,6 @@ RegionSpliceDrag::motion (GdkEvent* event, bool)
 
        pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
        RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
-       layer_t layer = tvp.second;
-
-       if (tv && tv->layer_display() == Overlaid) {
-               layer = 0;
-       }
 
        /* The region motion is only processed if the pointer is over
           an audio track.
@@ -1259,7 +1318,7 @@ RegionSpliceDrag::motion (GdkEvent* event, bool)
                /* To make sure we hide the verbose canvas cursor when the mouse is
                   not held over and audiotrack.
                */
-               _editor->hide_verbose_canvas_cursor ();
+               _editor->verbose_cursor()->hide ();
                return;
        }
 
@@ -1318,7 +1377,7 @@ RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-RegionSpliceDrag::aborted ()
+RegionSpliceDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -1328,7 +1387,7 @@ RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisVi
          _view (dynamic_cast<MidiTimeAxisView*> (v))
 {
        DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
-       
+
        assert (_view);
 }
 
@@ -1336,51 +1395,64 @@ void
 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
 {
        if (first_move) {
-                add_region();
+               add_region();
+               _view->playlist()->freeze ();
        } else {
-                if (_region) {
-                        framepos_t const f = adjusted_current_frame (event);
-                        if (f < grab_frame()) {
-                                _region->set_position (f, this);
-                        }
-                        
-                        /* again, don't use a zero-length region (see above) */
-                        framecnt_t const len = abs (f - grab_frame ());
-                        _region->set_length (len < 1 ? 1 : len, this);
-                }
-        }
+               if (_region) {
+                       framepos_t const f = adjusted_current_frame (event);
+                       if (f < grab_frame()) {
+                               _region->set_position (f);
+                       }
+
+                       /* Don't use a zero-length region, and subtract 1 frame from the snapped length
+                          so that if this region is duplicated, its duplicate starts on
+                          a snap point rather than 1 frame after a snap point.  Otherwise things get
+                          a bit confusing as if a region starts 1 frame after a snap point, one cannot
+                          place snapped notes at the start of the region.
+                       */
+
+                       framecnt_t const len = abs (f - grab_frame () - 1);
+                       _region->set_length (len < 1 ? 1 : len);
+               }
+       }
 }
 
 void
-RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
+RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
 {
        if (!movement_occurred) {
-                add_region ();
+               add_region ();
+       } else {
+               _view->playlist()->thaw ();
        }
 
-        if (_region) {
-                _editor->commit_reversible_command ();
-        }
+       if (_region) {
+               _editor->commit_reversible_command ();
+       }
 }
 
 void
 RegionCreateDrag::add_region ()
 {
-        if (_editor->session()) {
-                const TempoMap& map (_editor->session()->tempo_map());
-                framecnt_t pos = grab_frame();
-                const Meter& m = map.meter_at (pos);
-                /* not that the frame rate used here can be affected by pull up/down which
-                   might be wrong.
-                */
-                framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
-                _region = _view->add_region (grab_frame(), len, false);
-        }
+       if (_editor->session()) {
+               const TempoMap& map (_editor->session()->tempo_map());
+               framecnt_t pos = grab_frame();
+               const Meter& m = map.meter_at (pos);
+               /* not that the frame rate used here can be affected by pull up/down which
+                  might be wrong.
+               */
+               framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
+               _region = _view->add_region (grab_frame(), len, false);
+       }
 }
 
 void
-RegionCreateDrag::aborted ()
+RegionCreateDrag::aborted (bool)
 {
+       if (_region) {
+               _view->playlist()->thaw ();
+       }
+
        /* XXX */
 }
 
@@ -1395,9 +1467,16 @@ void
 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
 {
        Gdk::Cursor* cursor;
-       ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
+       ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
+       float x_fraction = cnote->mouse_x_fraction ();
 
-       Drag::start_grab (event);
+       if (x_fraction > 0.0 && x_fraction < 0.25) {
+               cursor = _editor->cursors()->left_side_trim;
+       } else  {
+               cursor = _editor->cursors()->right_side_trim;
+       }
+
+       Drag::start_grab (event, cursor);
 
        region = &cnote->region_view();
 
@@ -1405,10 +1484,10 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
        double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
 
        if (grab_x() <= middle_point) {
-               cursor = _editor->left_side_trim_cursor;
+               cursor = _editor->cursors()->left_side_trim;
                at_front = true;
        } else {
-               cursor = _editor->right_side_trim_cursor;
+               cursor = _editor->cursors()->right_side_trim;
                at_front = false;
        }
 
@@ -1446,7 +1525,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 (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
+               (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
        }
 }
 
@@ -1455,12 +1534,12 @@ 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 (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
+               (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
        }
 }
 
 void
-NoteResizeDrag::aborted ()
+NoteResizeDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -1484,20 +1563,19 @@ RegionGainDrag::finished (GdkEvent *, bool)
 }
 
 void
-RegionGainDrag::aborted ()
+RegionGainDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
 
 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
        : RegionDrag (e, i, p, v)
-       , _have_transaction (false)
 {
        DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
 }
 
 void
-TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
+TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
 {
        double speed = 1.0;
        TimeAxisView* tvp = &_primary->get_time_axis_view ();
@@ -1514,32 +1592,40 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        framepos_t const pf = adjusted_current_frame (event);
 
        if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
+               /* Move the contents of the region around without changing the region bounds */
                _operation = ContentsTrim;
-                Drag::start_grab (event, _editor->trimmer_cursor);
+               Drag::start_grab (event, _editor->cursors()->trimmer);
        } else {
                /* These will get overridden for a point trim.*/
                if (pf < (region_start + region_length/2)) {
-                       /* closer to start */
+                       /* closer to front */
                        _operation = StartTrim;
-                        Drag::start_grab (event, _editor->left_side_trim_cursor);
+                       Drag::start_grab (event, _editor->cursors()->left_side_trim);
                } else {
                        /* closer to end */
                        _operation = EndTrim;
-                        Drag::start_grab (event, _editor->right_side_trim_cursor);
-                }
+                       Drag::start_grab (event, _editor->cursors()->right_side_trim);
+               }
        }
 
        switch (_operation) {
        case StartTrim:
-               _editor->show_verbose_time_cursor (region_start, 10);
+               show_verbose_cursor_time (region_start);
+               for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
+                       i->view->trim_front_starting ();
+               }
                break;
        case EndTrim:
-               _editor->show_verbose_time_cursor (region_end, 10);
+               show_verbose_cursor_time (region_end);
                break;
        case ContentsTrim:
-               _editor->show_verbose_time_cursor (pf, 10);
+               show_verbose_cursor_time (pf);
                break;
        }
+
+       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+               i->view->region()->suspend_property_changes ();
+       }
 }
 
 void
@@ -1547,11 +1633,6 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
 {
        RegionView* rv = _primary;
 
-       /* snap modifier works differently here..
-          its current state has to be passed to the
-          various trim functions in order to work properly
-       */
-
        double speed = 1.0;
        TimeAxisView* tvp = &_primary->get_time_axis_view ();
        RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
@@ -1561,7 +1642,7 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                speed = tv->track()->speed();
        }
 
-       framepos_t const pf = adjusted_current_frame (event);
+       framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
 
        if (first_move) {
 
@@ -1580,18 +1661,16 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                }
 
                _editor->begin_reversible_command (trim_type);
-               _have_transaction = true;
 
                for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
                        RegionView* rv = i->view;
-                       rv->fake_set_opaque(false);
+                       rv->fake_set_opaque (false);
                        rv->enable_display (false);
-                        rv->region()->clear_changes ();
-                       rv->region()->suspend_property_changes ();
+                       rv->region()->playlist()->clear_owned_changes ();
 
                        AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
 
-                       if (arv){
+                       if (arv) {
                                arv->temporarily_hide_envelope ();
                        }
 
@@ -1613,13 +1692,13 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
        switch (_operation) {
        case StartTrim:
                for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                       _editor->single_start_trim (*i->view, pf, non_overlap_trim);
+                       i->view->trim_front (i->initial_position + dt, non_overlap_trim);
                }
                break;
 
        case EndTrim:
                for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                       _editor->single_end_trim (*i->view, pf, non_overlap_trim);
+                       i->view->trim_end (i->initial_end + dt, non_overlap_trim);
                }
                break;
 
@@ -1632,20 +1711,20 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                        }
 
                        framecnt_t frame_delta = 0;
-                       
+
                        bool left_direction = false;
-                       if (last_pointer_frame() > pf) {
+                       if (last_pointer_frame() > adjusted_current_frame(event)) {
                                left_direction = true;
                        }
 
                        if (left_direction) {
-                               frame_delta = (last_pointer_frame() - pf);
+                               frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
                        } else {
-                               frame_delta = (pf - last_pointer_frame());
+                               frame_delta = (adjusted_current_frame(event) - 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);
+                               i->view->trim_contents (frame_delta, left_direction, swap_direction);
                        }
                }
                break;
@@ -1653,13 +1732,13 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
 
        switch (_operation) {
        case StartTrim:
-               _editor->show_verbose_time_cursor((framepos_t) (rv->region()->position()/speed), 10);
+               show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
                break;
        case EndTrim:
-               _editor->show_verbose_time_cursor((framepos_t) (rv->region()->last_frame()/speed), 10);
+               show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
                break;
        case ContentsTrim:
-               _editor->show_verbose_time_cursor (pf, 10);
+               show_verbose_cursor_time (adjusted_current_frame (event));
                break;
        }
 }
@@ -1671,17 +1750,39 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
        if (movement_occurred) {
                motion (event, false);
 
+               /* This must happen before the region's StatefulDiffCommand is created, as it may
+                  `correct' (ahem) the region's _start from being negative to being zero.  It
+                  needs to be zero in the undo record.
+               */
+               if (_operation == StartTrim) {
+                       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                               i->view->trim_front_ending ();
+                       }
+               }
+
                if (!_editor->selection->selected (_primary)) {
-                       _editor->thaw_region_after_trim (*_primary);
+                       _primary->thaw_after_trim ();
                } else {
 
+                       set<boost::shared_ptr<Playlist> > diffed_playlists;
+
                        for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                               _editor->thaw_region_after_trim (*i->view);
+                                i->view->thaw_after_trim ();
                                i->view->enable_display (true);
                                i->view->fake_set_opaque (true);
-                                if (_have_transaction) {
-                                        _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
-                                }
+
+                               /* Trimming one region may affect others on the playlist, so we need
+                                  to get undo Commands from the whole playlist rather than just the
+                                  region.  Use diffed_playlists to make sure we don't diff a given
+                                  playlist more than once.
+                               */
+                               boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
+                               if (diffed_playlists.find (p) == diffed_playlists.end()) {
+                                       vector<Command*> cmds;
+                                       p->rdiff (cmds);
+                                       _editor->session()->add_commands (cmds);
+                                       diffed_playlists.insert (p);
+                               }
                        }
                }
                for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
@@ -1689,19 +1790,24 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
                }
 
                _editor->motion_frozen_playlists.clear ();
-
-               if (_have_transaction) {
-                       _editor->commit_reversible_command();
-               }
+               _editor->commit_reversible_command();
 
        } else {
                /* no mouse movement */
                _editor->point_trim (event, adjusted_current_frame (event));
        }
+
+       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+               if (_operation == StartTrim) {
+                       i->view->trim_front_ending ();
+               }
+
+               i->view->region()->resume_property_changes ();
+       }
 }
 
 void
-TrimDrag::aborted ()
+TrimDrag::aborted (bool movement_occurred)
 {
        /* Our motion method is changing model state, so use the Undo system
           to cancel.  Perhaps not ideal, as this will leave an Undo point
@@ -1709,10 +1815,38 @@ TrimDrag::aborted ()
        */
 
        finished (0, true);
-       
-       if (_have_transaction) {
+
+       if (movement_occurred) {
                _editor->undo ();
        }
+
+       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+               i->view->region()->resume_property_changes ();
+       }
+}
+
+void
+TrimDrag::setup_pointer_frame_offset ()
+{
+       list<DraggingView>::iterator i = _views.begin ();
+       while (i != _views.end() && i->view != _primary) {
+               ++i;
+       }
+
+       if (i == _views.end()) {
+               return;
+       }
+
+       switch (_operation) {
+       case StartTrim:
+               _pointer_frame_offset = raw_grab_frame() - i->initial_position;
+               break;
+       case EndTrim:
+               _pointer_frame_offset = raw_grab_frame() - i->initial_end;
+               break;
+       case ContentsTrim:
+               break;
+       }
 }
 
 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
@@ -1720,7 +1854,7 @@ MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
          _copy (c)
 {
        DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
-       
+
        _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
        assert (_marker);
 }
@@ -1733,8 +1867,14 @@ MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
                // The actual copying is not done before we reach the finish callback.
                char name[64];
                snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
-               MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
-                                                         *new MeterSection (_marker->meter()));
+
+               MeterMarker* new_marker = new MeterMarker (
+                       *_editor,
+                       *_editor->meter_group,
+                       ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
+                       name,
+                       *new MeterSection (_marker->meter())
+                       );
 
                _item = &new_marker->the_item ();
                _marker = new_marker;
@@ -1751,9 +1891,13 @@ MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
        Drag::start_grab (event, cursor);
 
-       _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
+       show_verbose_cursor_time (adjusted_current_frame(event));
+}
 
-       _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
+void
+MeterMarkerDrag::setup_pointer_frame_offset ()
+{
+       _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
 }
 
 void
@@ -1762,8 +1906,8 @@ MeterMarkerDrag::motion (GdkEvent* event, bool)
        framepos_t const pf = adjusted_current_frame (event);
 
        _marker->set_position (pf);
-       
-       _editor->show_verbose_time_cursor (pf, 10);
+
+       show_verbose_cursor_time (pf);
 }
 
 void
@@ -1775,7 +1919,7 @@ MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
 
        motion (event, false);
 
-       BBT_Time when;
+       Timecode::BBT_Time when;
 
        TempoMap& map (_editor->session()->tempo_map());
        map.bbt_time (last_pointer_frame(), when);
@@ -1802,7 +1946,7 @@ MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-MeterMarkerDrag::aborted ()
+MeterMarkerDrag::aborted (bool)
 {
        _marker->set_position (_marker->meter().frame ());
 }
@@ -1812,7 +1956,7 @@ TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
          _copy (c)
 {
        DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
-       
+
        _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
        assert (_marker);
 }
@@ -1820,32 +1964,35 @@ TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
 void
 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 {
-
        if (_copy) {
 
                // create a dummy marker for visual representation of moving the copy.
                // The actual copying is not done before we reach the finish callback.
                char name[64];
                snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
-               TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
-                                                         *new TempoSection (_marker->tempo()));
+
+               TempoMarker* new_marker = new TempoMarker (
+                       *_editor,
+                       *_editor->tempo_group,
+                       ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
+                       name,
+                       *new TempoSection (_marker->tempo())
+                       );
 
                _item = &new_marker->the_item ();
                _marker = new_marker;
 
-       } else {
-
-               MetricSection& section (_marker->tempo());
-
-               if (!section.movable()) {
-                       return;
-               }
        }
 
        Drag::start_grab (event, cursor);
 
+       show_verbose_cursor_time (adjusted_current_frame (event));
+}
+
+void
+TempoMarkerDrag::setup_pointer_frame_offset ()
+{
        _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
-       _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
 }
 
 void
@@ -1853,7 +2000,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool)
 {
        framepos_t const pf = adjusted_current_frame (event);
        _marker->set_position (pf);
-       _editor->show_verbose_time_cursor (pf, 10);
+       show_verbose_cursor_time (pf);
 }
 
 void
@@ -1865,7 +2012,7 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
 
        motion (event, false);
 
-       BBT_Time when;
+       Timecode::BBT_Time when;
 
        TempoMap& map (_editor->session()->tempo_map());
        map.bbt_time (last_pointer_frame(), when);
@@ -1892,7 +2039,7 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-TempoMarkerDrag::aborted ()
+TempoMarkerDrag::aborted (bool)
 {
        _marker->set_position (_marker->tempo().frame());
 }
@@ -1902,79 +2049,103 @@ CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
          _stop (s)
 {
        DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
-       
-       _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
-       assert (_cursor);
 }
 
+/** Do all the things we do when dragging the playhead to make it look as though
+ *  we have located, without actually doing the locate (because that would cause
+ *  the diskstream buffers to be refilled, which is too slow).
+ */
 void
-CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
+CursorDrag::fake_locate (framepos_t t)
 {
-       Drag::start_grab (event, c);
+       _editor->playhead_cursor->set_position (t);
 
-       if (!_stop) {
+       Session* s = _editor->session ();
+       if (s->timecode_transmission_suspended ()) {
+               framepos_t const f = _editor->playhead_cursor->current_frame;
+               s->send_mmc_locate (f);
+               s->send_full_time_code (f);
+       }
 
-               framepos_t where = _editor->event_frame (event, 0, 0);
+       show_verbose_cursor_time (t);
+       _editor->UpdateAllTransportClocks (t);
+}
 
-               _editor->snap_to_with_modifier (where, event);
-               _editor->playhead_cursor->set_position (where);
+void
+CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
+{
+       Drag::start_grab (event, c);
 
-       }
+       _grab_zoom = _editor->frames_per_unit;
 
-       if (_cursor == _editor->playhead_cursor) {
-               _editor->_dragging_playhead = true;
+       framepos_t where = _editor->event_frame (event, 0, 0);
+       _editor->snap_to_with_modifier (where, event);
 
-               Session* s = _editor->session ();
+       _editor->_dragging_playhead = true;
 
-               if (s) {
-                       if (_was_rolling && _stop) {
-                               s->request_stop ();
-                       }
+       Session* s = _editor->session ();
 
-                       if (s->is_auditioning()) {
-                               s->cancel_audition ();
-                       }
+       if (s) {
+               if (_was_rolling && _stop) {
+                       s->request_stop ();
+               }
 
-                       s->request_suspend_timecode_transmission ();
+               if (s->is_auditioning()) {
+                       s->cancel_audition ();
+               }
 
-                       if (s->timecode_transmission_suspended ()) {
-                               framepos_t const f = _editor->playhead_cursor->current_frame;
-                               s->send_mmc_locate (f);
-                               s->send_full_time_code (f);
-                       }
+               s->request_suspend_timecode_transmission ();
+               while (!s->timecode_transmission_suspended ()) {
+                       /* twiddle our thumbs */
                }
        }
 
-       _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
-
-       _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
+       fake_locate (where);
 }
 
 void
 CursorDrag::motion (GdkEvent* event, bool)
 {
-       framepos_t const adjusted_frame = adjusted_current_frame (event);
+       if (_drags->current_pointer_y() != last_pointer_y()) {
 
-       if (adjusted_frame == last_pointer_frame()) {
-               return;
-       }
+               /* zoom when we move the pointer up and down */
 
-       _cursor->set_position (adjusted_frame);
+               /* y range to operate over (pixels) */
+               double const y_range = 512;
+               /* we will multiply the grab zoom by a factor between scale_range and scale_range^-1 */
+               double const scale_range = 4;
+               /* dead zone around the grab point in which to do no zooming (pixels) */
+               double const dead_zone = 100;
 
-       _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
+               /* current dy */
+               double dy = _drags->current_pointer_y() - grab_y();
 
-       Session* s = _editor->session ();
-       if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
-               framepos_t const f = _editor->playhead_cursor->current_frame;
-               s->send_mmc_locate (f);
-               s->send_full_time_code (f);
+               if (dy < -dead_zone || dy > dead_zone) {
+                       /* we are outside the dead zone; remove it from our calculation */
+                       if (dy < 0) {
+                               dy += dead_zone;
+                       } else {
+                               dy -= dead_zone;
+                       }
+
+                       /* get a number from -1 to 1 as dy ranges from -y_range to y_range */
+                       double udy = max (min (dy / y_range, 1.0), -1.0);
+
+                       /* and zoom, using playhead focus temporarily */
+                       Editing::ZoomFocus const zf = _editor->get_zoom_focus ();
+                       _editor->set_zoom_focus (Editing::ZoomFocusPlayhead);
+                       _editor->temporal_zoom (_grab_zoom * pow (scale_range, -udy));
+                       _editor->set_zoom_focus (zf);
+               }
        }
-       
 
+       framepos_t const adjusted_frame = adjusted_current_frame (event);
+       if (adjusted_frame != last_pointer_frame()) {
+               fake_locate (adjusted_frame);
 #ifdef GTKOSX
-       _editor->update_canvas_now ();
+               _editor->update_canvas_now ();
 #endif
-       _editor->UpdateAllTransportClocks (_cursor->current_frame);
+       }
 }
 
 void
@@ -1988,25 +2159,23 @@ CursorDrag::finished (GdkEvent* event, bool movement_occurred)
 
        motion (event, false);
 
-       if (_item == &_editor->playhead_cursor->canvas_item) {
-               Session* s = _editor->session ();
-               if (s) {
-                       s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
-                       _editor->_pending_locate_request = true;
-                       s->request_resume_timecode_transmission ();
-               }
+       Session* s = _editor->session ();
+       if (s) {
+               s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
+               _editor->_pending_locate_request = true;
+               s->request_resume_timecode_transmission ();
        }
 }
 
 void
-CursorDrag::aborted ()
+CursorDrag::aborted (bool)
 {
        if (_editor->_dragging_playhead) {
                _editor->session()->request_resume_timecode_transmission ();
                _editor->_dragging_playhead = false;
        }
-       
-       _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
+
+       _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
 }
 
 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
@@ -2023,12 +2192,19 @@ FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
        boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
 
-       _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
-       _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
-       
+       show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
+
        arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
 }
 
+void
+FadeInDrag::setup_pointer_frame_offset ()
+{
+       AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
+       boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
+       _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
+}
+
 void
 FadeInDrag::motion (GdkEvent* event, bool)
 {
@@ -2058,7 +2234,7 @@ FadeInDrag::motion (GdkEvent* event, bool)
                tmp->show_fade_line((framecnt_t) fade_length);
        }
 
-       _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
+       show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
 }
 
 void
@@ -2107,7 +2283,7 @@ FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-FadeInDrag::aborted ()
+FadeInDrag::aborted (bool)
 {
        for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
                AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
@@ -2135,12 +2311,19 @@ FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
        boost::shared_ptr<AudioRegion> r = arv->audio_region ();
 
-       _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_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);
-       
+       show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
+
        arv->show_fade_line(r->length() - r->fade_out()->back()->when);
 }
 
+void
+FadeOutDrag::setup_pointer_frame_offset ()
+{
+       AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
+       boost::shared_ptr<AudioRegion> r = arv->audio_region ();
+       _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
+}
+
 void
 FadeOutDrag::motion (GdkEvent* event, bool)
 {
@@ -2172,7 +2355,7 @@ FadeOutDrag::motion (GdkEvent* event, bool)
                tmp->show_fade_line(region->length() - fade_length);
        }
 
-       _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
+       show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
 }
 
 void
@@ -2223,7 +2406,7 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-FadeOutDrag::aborted ()
+FadeOutDrag::aborted (bool)
 {
        for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
                AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
@@ -2241,19 +2424,12 @@ MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
        : Drag (e, i)
 {
        DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
-       
+
        _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
        assert (_marker);
 
        _points.push_back (Gnome::Art::Point (0, 0));
        _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
-
-       _line = new ArdourCanvas::Line (*_editor->timebar_group);
-       _line->property_width_pixels() = 1;
-       _line->property_points () = _points;
-       _line->hide ();
-
-       _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
 }
 
 MarkerDrag::~MarkerDrag ()
@@ -2273,17 +2449,15 @@ 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 = raw_grab_frame() - (is_start ? location->start() : location->end());
-
        update_item (location);
 
        // _drag_line->show();
        // _line->raise_to_top();
 
        if (is_start) {
-               _editor->show_verbose_time_cursor (location->start(), 10);
+               show_verbose_cursor_time (location->start());
        } else {
-               _editor->show_verbose_time_cursor (location->end(), 10);
+               show_verbose_cursor_time (location->end());
        }
 
        Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
@@ -2340,6 +2514,14 @@ MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        }
 }
 
+void
+MarkerDrag::setup_pointer_frame_offset ()
+{
+       bool is_start;
+       Location *location = _editor->find_location_from_marker (_marker, is_start);
+       _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
+}
+
 void
 MarkerDrag::motion (GdkEvent* event, bool)
 {
@@ -2354,10 +2536,6 @@ MarkerDrag::motion (GdkEvent* event, bool)
 
        framepos_t next = newframe;
 
-       if (newframe == last_pointer_frame()) {
-               return;
-       }
-
        if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
                move_both = true;
        }
@@ -2387,13 +2565,15 @@ MarkerDrag::motion (GdkEvent* event, bool)
 
 
                                switch (marker->type()) {
-                               case Marker::Start:
+                               case Marker::SessionStart:
+                               case Marker::RangeStart:
                                case Marker::LoopStart:
                                case Marker::PunchIn:
                                        f_delta = newframe - copy_location->start();
                                        break;
 
-                               case Marker::End:
+                               case Marker::SessionEnd:
+                               case Marker::RangeEnd:
                                case Marker::LoopEnd:
                                case Marker::PunchOut:
                                        f_delta = newframe - copy_location->end();
@@ -2449,7 +2629,7 @@ MarkerDrag::motion (GdkEvent* event, bool)
                                        copy_location->set_end (new_end);
                                } else  if (new_start < copy_location->end()) {
                                        copy_location->set_start (new_start);
-                               } else {
+                               } else if (newframe > 0) {
                                        _editor->snap_to (next, 1, true);
                                        copy_location->set_end (next);
                                        copy_location->set_start (newframe);
@@ -2481,7 +2661,7 @@ MarkerDrag::motion (GdkEvent* event, bool)
 
        assert (!_copied_locations.empty());
 
-       _editor->show_verbose_time_cursor (newframe, 10);
+       show_verbose_cursor_time (newframe);
 
 #ifdef GTKOSX
        _editor->update_canvas_now ();
@@ -2547,12 +2727,10 @@ 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));
        _editor->commit_reversible_command ();
-
-       _line->hide();
 }
 
 void
-MarkerDrag::aborted ()
+MarkerDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -2560,11 +2738,7 @@ MarkerDrag::aborted ()
 void
 MarkerDrag::update_item (Location* location)
 {
-       double const x1 = _editor->frame_to_pixel (location->start());
-
-       _points.front().set_x(x1);
-       _points.back().set_x(x1);
-       _line->property_points() = _points;
+        /* noop */
 }
 
 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
@@ -2572,8 +2746,12 @@ ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
          _cumulative_x_drag (0),
          _cumulative_y_drag (0)
 {
+       if (_zero_gain_fraction < 0.0) {
+               _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
+       }
+
        DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
-       
+
        _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
        assert (_point);
 }
@@ -2582,7 +2760,7 @@ ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
 void
 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 {
-       Drag::start_grab (event, _editor->fader_cursor);
+       Drag::start_grab (event, _editor->cursors()->fader);
 
        // start the grab at the center of the control point so
        // the point doesn't 'jump' to the mouse after the first drag
@@ -2593,10 +2771,10 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 
        _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
 
-       _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
-                                           event->button.x + 10, event->button.y + 10);
+       _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
+                                       event->button.x + 10, event->button.y + 10);
 
-       _editor->show_verbose_canvas_cursor ();
+       _editor->verbose_cursor()->show ();
 }
 
 void
@@ -2639,7 +2817,7 @@ ControlPointDrag::motion (GdkEvent* event, bool)
        cy = min ((double) _point->line().height(), cy);
 
        framepos_t cx_frames = _editor->unit_to_frame (cx);
-       
+
        if (!_x_constrained) {
                _editor->snap_to_with_modifier (cx_frames, event);
        }
@@ -2652,7 +2830,7 @@ ControlPointDrag::motion (GdkEvent* event, bool)
 
        _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));
+       _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
 }
 
 void
@@ -2669,13 +2847,13 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
        } else {
                motion (event, false);
        }
-       
+
        _point->line().end_drag ();
        _editor->session()->commit_reversible_command ();
 }
 
 void
-ControlPointDrag::aborted ()
+ControlPointDrag::aborted (bool)
 {
        _point->line().reset ();
 }
@@ -2721,13 +2899,13 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 
        uint32_t before;
        uint32_t after;
-       
+
        if (!_line->control_points_adjacent (frame_within_region, before, after)) {
                /* no adjacent points */
                return;
        }
 
-       Drag::start_grab (event, _editor->fader_cursor);
+       Drag::start_grab (event, _editor->cursors()->fader);
 
        /* store grab start in parent frame */
 
@@ -2738,10 +2916,10 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 
        _line->start_drag_line (before, after, fraction);
 
-       _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
-                                           event->button.x + 10, event->button.y + 10);
+       _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
+                                       event->button.x + 10, event->button.y + 10);
 
-       _editor->show_verbose_canvas_cursor ();
+       _editor->verbose_cursor()->show ();
 }
 
 void
@@ -2773,7 +2951,7 @@ LineDrag::motion (GdkEvent* event, bool)
        /* 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));
+       _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
 }
 
 void
@@ -2785,7 +2963,7 @@ LineDrag::finished (GdkEvent* event, bool)
 }
 
 void
-LineDrag::aborted ()
+LineDrag::aborted (bool)
 {
        _line->reset ();
 }
@@ -2802,8 +2980,8 @@ void
 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 {
        Drag::start_grab (event);
-       
-       _line = reinterpret_cast<SimpleLine*> (_item);
+
+       _line = reinterpret_cast<Line*> (_item);
        assert (_line);
 
        /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
@@ -2815,47 +2993,60 @@ FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 
        /* store grab start in parent frame */
        _region_view_grab_x = cx;
-       
-       _before = _line->property_x1();
-       
+
+       _before = *(float*) _item->get_data ("position");
+
        _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
-               
+
        _max_x = _editor->frame_to_pixel(_arv->get_duration());
 }
 
 void
-FeatureLineDrag::motion (GdkEvent* event, bool)
+FeatureLineDrag::motion (GdkEvent*, bool)
 {
        double dx = _drags->current_pointer_x() - last_pointer_x();
-       
+
        double cx = _region_view_grab_x + _cumulative_x_drag + dx;
-       
+
        _cumulative_x_drag += dx;
-               
+
        /* Clamp the min and max extent of the drag to keep it within the region view bounds */
-       
+
        if (cx > _max_x){
                cx = _max_x;
        }
        else if(cx < 0){
                cx = 0;
        }
-       
-       _line->property_x1() = cx; 
-       _line->property_x2() = cx;
 
-       _before = _line->property_x1();
+       ArdourCanvas::Points points;
+
+       double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
+
+       _line->get_bounds(x1, y2, x2, y2);
+
+       points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
+       points.push_back(Gnome::Art::Point(cx, y2 - y1));
+
+       _line->property_points() = points;
+
+       float *pos = new float;
+       *pos = cx;
+
+       _line->set_data ("position", pos);
+
+       _before = cx;
 }
 
 void
-FeatureLineDrag::finished (GdkEvent* event, bool)
+FeatureLineDrag::finished (GdkEvent*, bool)
 {
        _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
-       _arv->update_transient(_before, _line->property_x1());
+       _arv->update_transient(_before, _before);
 }
 
 void
-FeatureLineDrag::aborted ()
+FeatureLineDrag::aborted (bool)
 {
        //_line->reset ();
 }
@@ -2870,7 +3061,7 @@ void
 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 {
        Drag::start_grab (event);
-       _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
+       show_verbose_cursor_time (adjusted_current_frame (event));
 }
 
 void
@@ -2920,7 +3111,7 @@ RubberbandSelectDrag::motion (GdkEvent* event, bool)
                _editor->rubberband_rect->show();
                _editor->rubberband_rect->raise_to_top();
 
-               _editor->show_verbose_time_cursor (pf, 10);
+               show_verbose_cursor_time (pf);
        }
 }
 
@@ -2942,19 +3133,16 @@ RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
 
 
                Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
-               bool committed;
 
                _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, false);
+                       _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
                } else {
-                       committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
+                       _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
                }
 
-               if (!committed) {
-                       _editor->commit_reversible_command ();
-               }
+               _editor->commit_reversible_command ();
 
        } else {
                if (!getenv("ARDOUR_SAE")) {
@@ -2969,7 +3157,7 @@ RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-RubberbandSelectDrag::aborted ()
+RubberbandSelectDrag::aborted (bool)
 {
        _editor->rubberband_rect->hide ();
 }
@@ -2985,7 +3173,7 @@ TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 {
        Drag::start_grab (event, cursor);
 
-       _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
+       show_verbose_cursor_time (adjusted_current_frame (event));
 }
 
 void
@@ -2999,7 +3187,7 @@ TimeFXDrag::motion (GdkEvent* event, bool)
                rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
        }
 
-       _editor->show_verbose_time_cursor (pf, 10);
+       show_verbose_cursor_time (pf);
 }
 
 void
@@ -3027,8 +3215,6 @@ TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
        }
 #endif
 
-       _editor->begin_reversible_command (_("timestretch"));
-
        // XXX how do timeFX on multiple regions ?
 
        RegionSelection rs;
@@ -3040,7 +3226,7 @@ TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
 }
 
 void
-TimeFXDrag::aborted ()
+TimeFXDrag::aborted (bool)
 {
        _primary->get_time_axis_view().hide_timestretch ();
 }
@@ -3073,7 +3259,7 @@ ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
 }
 
 void
-ScrubDrag::aborted ()
+ScrubDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -3091,9 +3277,6 @@ SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
 void
 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
 {
-       framepos_t start = 0;
-       framepos_t end = 0;
-
        if (_editor->session() == 0) {
                return;
        }
@@ -3107,7 +3290,7 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
                } else {
                        _copy = false;
                }
-               cursor = _editor->selector_cursor;
+               cursor = _editor->cursors()->selector;
                Drag::start_grab (event, cursor);
                break;
 
@@ -3115,36 +3298,49 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
                if (_editor->clicked_axisview) {
                        _editor->clicked_axisview->order_selection_trims (_item, true);
                }
-               Drag::start_grab (event, _editor->left_side_trim_cursor);
-               start = _editor->selection->time[_editor->clicked_selection].start;
-               _pointer_frame_offset = raw_grab_frame() - start;
+               Drag::start_grab (event, _editor->cursors()->left_side_trim);
                break;
 
        case SelectionEndTrim:
                if (_editor->clicked_axisview) {
                        _editor->clicked_axisview->order_selection_trims (_item, false);
                }
-               Drag::start_grab (event, _editor->right_side_trim_cursor);
-               end = _editor->selection->time[_editor->clicked_selection].end;
-               _pointer_frame_offset = raw_grab_frame() - end;
+               Drag::start_grab (event, _editor->cursors()->right_side_trim);
                break;
 
        case SelectionMove:
-               start = _editor->selection->time[_editor->clicked_selection].start;
                Drag::start_grab (event, cursor);
-               _pointer_frame_offset = raw_grab_frame() - start;
                break;
        }
 
        if (_operation == SelectionMove) {
-               _editor->show_verbose_time_cursor (start, 10);
+               show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
        } else {
-               _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
+               show_verbose_cursor_time (adjusted_current_frame (event));
        }
 
        _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
 }
 
+void
+SelectionDrag::setup_pointer_frame_offset ()
+{
+       switch (_operation) {
+       case CreateSelection:
+               _pointer_frame_offset = 0;
+               break;
+
+       case SelectionStartTrim:
+       case SelectionMove:
+               _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
+               break;
+
+       case SelectionEndTrim:
+               _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
+               break;
+       }
+}
+
 void
 SelectionDrag::motion (GdkEvent* event, bool first_move)
 {
@@ -3156,7 +3352,7 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
        if (pending_time_axis.first == 0) {
                return;
        }
-       
+
        framepos_t const pending_position = adjusted_current_frame (event);
 
        /* only alter selection if things have changed */
@@ -3190,7 +3386,7 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
 
                        if (_copy) {
                                /* adding to the selection */
-                                _editor->set_selected_track_as_side_effect (Selection::Add);
+                               _editor->set_selected_track_as_side_effect (Selection::Add);
                                //_editor->selection->add (_editor->clicked_axisview);
                                _editor->clicked_selection = _editor->selection->add (start, end);
                                _copy = false;
@@ -3198,17 +3394,17 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                                /* new selection */
 
                                if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
-                                        //_editor->selection->set (_editor->clicked_axisview);
-                                        _editor->set_selected_track_as_side_effect (Selection::Set);
+                                       //_editor->selection->set (_editor->clicked_axisview);
+                                       _editor->set_selected_track_as_side_effect (Selection::Set);
                                }
-                               
+
                                _editor->clicked_selection = _editor->selection->set (start, end);
                        }
                }
 
                /* 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->set_selected_track_as_side_effect (Selection::Add);
+                       // _editor->set_selected_track_as_side_effect (Selection::Add);
                        _editor->selection->add (pending_time_axis.first);
                        _added_time_axes.push_back (pending_time_axis.first);
                }
@@ -3216,7 +3412,7 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                /* 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());
 
@@ -3225,7 +3421,7 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
 
                        list<TimeAxisView*>::iterator tmp = i;
                        ++tmp;
-                       
+
                        if ((*i)->order() < min_order || (*i)->order() > max_order) {
                                _editor->selection->remove (*i);
                                _added_time_axes.remove (*i);
@@ -3286,9 +3482,9 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
        }
 
        if (_operation == SelectionMove) {
-               _editor->show_verbose_time_cursor(start, 10);
+               show_verbose_cursor_time(start);
        } else {
-               _editor->show_verbose_time_cursor(pending_position, 10);
+               show_verbose_cursor_time(pending_position);
        }
 }
 
@@ -3320,7 +3516,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
                if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
                        _editor->selection->set (_editor->clicked_axisview);
                }
-               
+
                if (s && s->get_play_range () && s->transport_rolling()) {
                        s->request_stop (false, false);
                }
@@ -3331,7 +3527,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-SelectionDrag::aborted ()
+SelectionDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -3342,9 +3538,9 @@ RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operat
          _copy (false)
 {
        DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
-       
-       _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, 
-                                                   physical_screen_height (_editor->get_window()));
+
+       _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
+                                                  physical_screen_height (_editor->get_window()));
        _drag_rect->hide ();
 
        _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
@@ -3374,13 +3570,13 @@ RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
                } else {
                        _copy = false;
                }
-               cursor = _editor->selector_cursor;
+               cursor = _editor->cursors()->selector;
                break;
        }
 
        Drag::start_grab (event, cursor);
 
-       _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
+       show_verbose_cursor_time (adjusted_current_frame (event));
 }
 
 void
@@ -3411,7 +3607,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
        if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
                framepos_t grab = grab_frame ();
                _editor->snap_to (grab);
-               
+
                if (pf < grab_frame()) {
                        start = pf;
                        end = grab;
@@ -3452,7 +3648,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
                update_item (_editor->temp_location);
        }
 
-       _editor->show_verbose_time_cursor (pf, 10);
+       show_verbose_cursor_time (pf);
 
 }
 
@@ -3485,7 +3681,7 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
                        newloc = new Location (
                                *_editor->session(), _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));
@@ -3537,7 +3733,7 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-RangeMarkerBarDrag::aborted ()
+RangeMarkerBarDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -3554,6 +3750,7 @@ RangeMarkerBarDrag::update_item (Location* location)
 
 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
        : Drag (e, i)
+       , _zoom_out (false)
 {
        DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
 }
@@ -3561,8 +3758,15 @@ MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
 void
 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 {
-       Drag::start_grab (event, _editor->zoom_cursor);
-       _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
+       if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
+               Drag::start_grab (event, _editor->cursors()->zoom_out);
+               _zoom_out = true;
+       } else {
+               Drag::start_grab (event, _editor->cursors()->zoom_in);
+               _zoom_out = false;
+       }
+
+       show_verbose_cursor_time (adjusted_current_frame (event));
 }
 
 void
@@ -3594,7 +3798,7 @@ MouseZoomDrag::motion (GdkEvent* event, bool first_move)
 
                _editor->reposition_zoom_rect(start, end);
 
-               _editor->show_verbose_time_cursor (pf, 10);
+               show_verbose_cursor_time (pf);
        }
 }
 
@@ -3610,18 +3814,18 @@ MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
                        _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
                }
        } else {
-               _editor->temporal_zoom_to_frame (false, grab_frame());
-               /*
-               temporal_zoom_step (false);
-               center_screen (grab_frame());
-               */
+               if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
+                       _editor->tav_zoom_step (_zoom_out);
+               } else {
+                       _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
+               }
        }
 
        _editor->zoom_rect->hide();
 }
 
 void
-MouseZoomDrag::aborted ()
+MouseZoomDrag::aborted (bool)
 {
        _editor->zoom_rect->hide ();
 }
@@ -3673,9 +3877,14 @@ NoteDrag::total_dx () const
 
        /* primary note time */
        frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
-       
+
        /* new time of the primary note relative to the region position */
-       frameoffset_t const st = n + dx;
+       frameoffset_t st = n + dx;
+
+       /* prevent the note being dragged earlier than the region's position */
+       if (st < 0) {
+               st = 0;
+       }
 
        /* snap and return corresponding delta */
        return _region->snap_frame_to_frame (st) - n;
@@ -3699,16 +3908,19 @@ NoteDrag::total_dy () const
                }
        }
 
-       return ndy;
+       /* more positive value = higher pitch and higher y-axis position on track,
+          which is the inverse of the X-centric geometric universe
+       */
+
+       return -ndy;
 }
-       
 
 void
 NoteDrag::motion (GdkEvent *, bool)
 {
        /* Total change in x and y since the start of the drag */
        frameoffset_t const dx = total_dx ();
-       int8_t const dy = total_dy ();
+       int8_t const dy = -total_dy ();
 
        /* Now work out what we have to do to the note canvas items to set this new drag delta */
        double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
@@ -3718,16 +3930,16 @@ NoteDrag::motion (GdkEvent *, bool)
                _cumulative_dx += tdx;
                _cumulative_dy += tdy;
 
-                int8_t note_delta = total_dy();
+               int8_t note_delta = total_dy();
 
                _region->move_selection (tdx, tdy, note_delta);
 
-                char buf[12];
-                snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
-                          (int) floor (_primary->note()->note() + note_delta));
-                
-               _editor->show_verbose_canvas_cursor_with (buf);
-        }
+               char buf[12];
+               snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
+                         (int) floor (_primary->note()->note() + note_delta));
+
+               show_verbose_cursor_text (buf);
+       }
 }
 
 void
@@ -3755,12 +3967,12 @@ NoteDrag::finished (GdkEvent* ev, bool moved)
                        }
                }
        } else {
-               _region->note_dropped (_primary, total_dx(), total_dy());
+               _region->note_dropped (_primary, total_dx(), total_dy());
        }
 }
 
 void
-NoteDrag::aborted ()
+NoteDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -3771,7 +3983,7 @@ AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* it
        , _nothing_to_drag (false)
 {
        DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
-       
+
        _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
        assert (_atav);
 
@@ -3829,13 +4041,13 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
                                i->points.push_back (i->line->nth (j));
                        }
                }
-               
+
        } else {
 
                for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
 
                        framecnt_t const half = (i->start + i->end) / 2;
-                       
+
                        /* find the line that this audio range starts in */
                        list<Line>::iterator j = _lines.begin();
                        while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
@@ -3844,7 +4056,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
                        if (j != _lines.end()) {
                                boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
-                               
+
                                /* j is the line that this audio range starts in; fade into it;
                                   64 samples length plucked out of thin air.
                                */
@@ -3864,7 +4076,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
                        }
 
                        /* same thing for the end */
-                       
+
                        j = _lines.begin();
                        while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
                                ++j;
@@ -3872,11 +4084,11 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
                        if (j != _lines.end()) {
                                boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
-                               
+
                                /* j is the line that this audio range starts in; fade out of it;
                                   64 samples length plucked out of thin air.
                                */
-                               
+
                                framepos_t b = i->end - 64;
                                if (b < half) {
                                        b = half;
@@ -3884,7 +4096,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
                                double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
                                double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
-                               
+
                                the_list->add (p, the_list->eval (p));
                                j->line->add_always_in_view (p);
                                the_list->add (q, the_list->eval (q));
@@ -3932,7 +4144,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 }
 
 void
-AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
+AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
 {
        if (_nothing_to_drag) {
                return;
@@ -3952,7 +4164,7 @@ AutomationRangeDrag::finished (GdkEvent* event, bool)
        if (_nothing_to_drag) {
                return;
        }
-       
+
        motion (event, false);
        for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
                i->line->end_drag ();
@@ -3963,7 +4175,7 @@ AutomationRangeDrag::finished (GdkEvent* event, bool)
 }
 
 void
-AutomationRangeDrag::aborted ()
+AutomationRangeDrag::aborted (bool)
 {
        for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
                i->line->clear_always_in_view ();
@@ -3978,4 +4190,62 @@ DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
        layer = v->region()->layer ();
        initial_y = v->get_canvas_group()->property_y ();
        initial_playlist = v->region()->playlist ();
+       initial_position = v->region()->position ();
+       initial_end = v->region()->position () + v->region()->length ();
 }
+
+PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
+       : Drag (e, i)
+       , _region_view (r)
+       , _patch_change (i)
+       , _cumulative_dx (0)
+{
+       DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
+}
+
+void
+PatchChangeDrag::motion (GdkEvent* ev, bool)
+{
+       framepos_t f = adjusted_current_frame (ev);
+       boost::shared_ptr<Region> r = _region_view->region ();
+       f = max (f, r->position ());
+       f = min (f, r->last_frame ());
+
+       framecnt_t const dxf = f - grab_frame();
+       double const dxu = _editor->frame_to_unit (dxf);
+       _patch_change->move (dxu - _cumulative_dx, 0);
+       _cumulative_dx = dxu;
+}
+
+void
+PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
+{
+       if (!movement_occurred) {
+               return;
+       }
+
+       boost::shared_ptr<Region> r (_region_view->region ());
+
+       framepos_t f = adjusted_current_frame (ev);
+       f = max (f, r->position ());
+       f = min (f, r->last_frame ());
+
+       _region_view->move_patch_change (
+               *_patch_change,
+               _region_view->frames_to_beats (f - r->position() - r->start())
+               );
+}
+
+void
+PatchChangeDrag::aborted (bool)
+{
+       _patch_change->move (-_cumulative_dx, 0);
+}
+
+void
+PatchChangeDrag::setup_pointer_frame_offset ()
+{
+       boost::shared_ptr<Region> region = _region_view->region ();
+       _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();
+}
+