Merge branch 'master' into cairocanvas
[ardour.git] / gtk2_ardour / editor_drag.cc
index 8157011c5a9be9a30a7c711f38c4309d2e90df0b..e069d9f6c575b643d332e89baaca9436b4baadec 100644 (file)
@@ -22,6 +22,7 @@
 #endif
 
 #include <stdint.h>
+#include <algorithm>
 
 #include "pbd/memento_command.h"
 #include "pbd/basename.h"
 #include "editor_drag.h"
 #include "audio_time_axis.h"
 #include "midi_time_axis.h"
-#include "canvas-note.h"
 #include "selection.h"
 #include "midi_selection.h"
 #include "automation_time_axis.h"
 #include "debug.h"
 #include "editor_cursors.h"
 #include "mouse_cursors.h"
+#include "note_base.h"
+#include "patch_change.h"
 #include "verbose_cursor.h"
 
 using namespace std;
@@ -200,15 +202,15 @@ Drag::Drag (Editor* e, ArdourCanvas::Item* i)
 }
 
 void
-Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
+Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
 {
-       _item->ungrab (0);
+       _item->ungrab ();
        _item = new_item;
 
        if (cursor == 0) {
-               _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, time);
+               _item->grab ();
        } else {
-               _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
+               _item->grab ();
        }
 }
 
@@ -238,12 +240,12 @@ Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
        _last_pointer_y = _grab_y;
 
        if (cursor == 0) {
-               _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
-                            event->button.time);
+               _item->grab ();
+                            
        } else {
-               _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
-                            *cursor,
-                            event->button.time);
+               /* CAIROCANVAS need a variant here that passes *cursor */
+               _item->grab ();
+
        }
 
        if (_editor->session() && _editor->session()->transport_rolling()) {
@@ -275,7 +277,7 @@ Drag::end_grab (GdkEvent* event)
 {
        _editor->stop_canvas_autoscroll ();
 
-       _item->ungrab (event ? event->button.time : 0);
+       _item->ungrab ();
 
        finished (event, _move_threshold_passed);
 
@@ -354,7 +356,7 @@ void
 Drag::abort ()
 {
        if (_item) {
-               _item->ungrab (0);
+               _item->ungrab ();
        }
 
        aborted (_move_threshold_passed);
@@ -369,7 +371,7 @@ 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
+               _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value()
                );
 
        _editor->verbose_cursor()->show ();
@@ -383,7 +385,7 @@ Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xof
        _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
+               _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value()
                );
 }
 
@@ -395,7 +397,7 @@ Drag::show_verbose_cursor_text (string const & text)
        _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
+               _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value()
                );
 }
 
@@ -550,7 +552,7 @@ 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 (in pixels) */
-               dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
+               dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_pixel;
 
                /* total x movement */
                framecnt_t total_dx = *pending_region_position;
@@ -658,7 +660,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
 
                RegionView* rv = i->view;
 
-               if (rv->region()->locked()) {
+               if (rv->region()->locked() || rv->region()->video_locked()) {
                        continue;
                }
 
@@ -677,7 +679,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
                           later, as the two parent groups have different coordinates.
                        */
 
-                       rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
+                       rv->get_canvas_group()->reparent (_editor->_region_motion_group);
                        
                        rv->fake_set_opaque (true);
                }
@@ -728,7 +730,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
                        double y = 0;
 
                        /* Get the y coordinate of the top of the track that this region is now on */
-                       tv->canvas_display()->i2w (x, y);
+                       tv->canvas_display()->item_to_canvas (x, y);
                        y += _editor->get_trackview_group_vertical_offset();
                        
                        /* And adjust for the layer that it should be on */
@@ -745,17 +747,13 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
                        }
 
                        /* Now move the region view */
-                       rv->move (x_delta, y - rv->get_canvas_group()->property_y());
+                       rv->move (x_delta, y - rv->get_canvas_group()->position().y);
                }
 
        } /* foreach region */
 
        _total_x_delta += x_delta;
 
-       if (first_move) {
-               _editor->cursor_group->raise_to_top();
-       }
-
        if (x_delta != 0 && !_brushing) {
                show_verbose_cursor_time (_last_frame_position);
        }
@@ -899,9 +897,7 @@ RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
 
        }
 
-       if (_editor->session() && Config->get_always_play_range()) {
-               _editor->session()->request_locate (_editor->get_selection().regions.start());
-       }
+       _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
 }
 
 void
@@ -931,7 +927,7 @@ RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed
        /* insert the regions into their new playlists */
        for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
 
-               if (i->view->region()->locked()) {
+               if (i->view->region()->locked() || i->view->region()->video_locked()) {
                        continue;
                }
 
@@ -1010,7 +1006,7 @@ RegionMoveDrag::finished_no_copy (
                RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
                double const dest_layer = i->layer;
 
-               if (rv->region()->locked()) {
+               if (rv->region()->locked() || rv->region()->video_locked()) {
                        ++i;
                        continue;
                }
@@ -1062,8 +1058,8 @@ RegionMoveDrag::finished_no_copy (
                           No need to do anything for copies as they are fake regions which will be deleted.
                        */
 
-                       rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
-                       rv->get_canvas_group()->property_y() = i->initial_y;
+                       rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
+                       rv->get_canvas_group()->set_y_position (i->initial_y);
                        rv->drag_end ();
 
                        /* just change the model */
@@ -1276,8 +1272,8 @@ RegionMotionDrag::aborted (bool)
                TimeAxisView* tv = &(rv->get_time_axis_view ());
                RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
                assert (rtv);
-               rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
-               rv->get_canvas_group()->property_y() = 0;
+               rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
+               rv->get_canvas_group()->set_y_position (0);
                rv->drag_end ();
                rv->fake_set_opaque (false);
                rv->move (-_total_x_delta, 0);
@@ -1337,8 +1333,8 @@ RegionInsertDrag::finished (GdkEvent *, bool)
 
        RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
 
-       _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
-       _primary->get_canvas_group()->property_y() = 0;
+       _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
+       _primary->get_canvas_group()->set_y_position (0);
 
        boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
 
@@ -1482,7 +1478,7 @@ RegionCreateDrag::motion (GdkEvent* event, bool first_move)
                           place snapped notes at the start of the region.
                        */
 
-                       framecnt_t const len = abs (f - grab_frame () - 1);
+                       framecnt_t const len = (framecnt_t) fabs (f - grab_frame () - 1);
                        _region->set_length (len < 1 ? 1 : len);
                }
        }
@@ -1519,7 +1515,8 @@ void
 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
 {
        Gdk::Cursor* cursor;
-       ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
+       NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
+       assert (cnote);
        float x_fraction = cnote->mouse_x_fraction ();
 
        if (x_fraction > 0.0 && x_fraction < 0.25) {
@@ -1533,7 +1530,7 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
        region = &cnote->region_view();
 
        double const region_start = region->get_position_pixels();
-       double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
+       double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
 
        if (grab_x() <= middle_point) {
                cursor = _editor->cursors()->left_side_trim;
@@ -1543,7 +1540,7 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
                at_front = false;
        }
 
-       _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
+       _item->grab ();
 
        if (event->motion.state & Keyboard::PrimaryModifier) {
                relative = false;
@@ -1577,7 +1574,9 @@ 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::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
+               NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
+               assert (nb);
+               (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
        }
 }
 
@@ -1586,7 +1585,9 @@ 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::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
+               NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
+               assert (nb);
+               (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
        }
 }
 
@@ -1599,10 +1600,172 @@ NoteResizeDrag::aborted (bool)
        }
 }
 
-TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
+AVDraggingView::AVDraggingView (RegionView* v)
+       : view (v)
+{
+       initial_position = v->region()->position ();
+}
+
+VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
+       : Drag (e, i)
+{
+       DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
+
+       RegionSelection rs;
+       TrackViewList empty;
+       empty.clear();
+       _editor->get_regions_after(rs, (framepos_t) 0, empty);
+       std::list<RegionView*> views = rs.by_layer();
+
+       for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
+               RegionView* rv = (*i);
+               if (!rv->region()->video_locked()) {
+                       continue;
+               }
+               _views.push_back (AVDraggingView (rv));
+       }
+}
+
+void
+VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
+{
+       Drag::start_grab (event);
+       if (_editor->session() == 0) {
+               return;
+       }
+
+       _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
+       _max_backwards_drag = (
+                         ARDOUR_UI::instance()->video_timeline->get_duration()
+                       + ARDOUR_UI::instance()->video_timeline->get_offset()
+                       - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
+                       );
+
+       for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
+               if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
+                       _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
+               }
+       }
+       DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
+
+       char buf[128];
+       Timecode::Time timecode;
+       _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
+       snprintf (buf, sizeof (buf), "Video Start:\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, (_startdrag_video_offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
+       _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
+       _editor->verbose_cursor()->show ();
+}
+
+void
+VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
+{
+       if (_editor->session() == 0) {
+               return;
+       }
+       if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
+               return;
+       }
+
+       framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
+       dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(dt);
+
+       if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
+               dt = - _max_backwards_drag;
+       }
+
+       ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
+       ARDOUR_UI::instance()->flush_videotimeline_cache(true);
+
+       for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
+               RegionView* rv = i->view;
+               DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
+               if (first_move) {
+                       rv->drag_start ();
+                       _editor->update_canvas_now ();
+                       rv->fake_set_opaque (true);
+                       rv->region()->clear_changes ();
+                       rv->region()->suspend_property_changes();
+               }
+               rv->region()->set_position(i->initial_position + dt);
+               rv->region_changed(ARDOUR::Properties::position);
+       }
+
+       const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
+       Timecode::Time timecode;
+       Timecode::Time timediff;
+       char buf[128];
+       _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
+       _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
+       snprintf (buf, sizeof (buf),
+                       "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
+                       "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
+                       , _("Video Start:"),
+                               (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
+                       , _("Diff:"),
+                               (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
+                               );
+       _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
+       _editor->verbose_cursor()->show ();
+}
+
+void
+VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
+{
+       if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
+               return;
+       }
+
+       if (!movement_occurred || ! _editor->session()) {
+               return;
+       }
+
+       ARDOUR_UI::instance()->flush_videotimeline_cache(true);
+
+       _editor->begin_reversible_command (_("Move Video"));
+
+       XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
+       ARDOUR_UI::instance()->video_timeline->save_undo();
+       XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
+       _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
+
+       for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
+               i->view->drag_end();
+               i->view->fake_set_opaque (false);
+               i->view->region()->resume_property_changes ();
+
+               _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
+       }
+
+       _editor->session()->maybe_update_session_range(
+                       std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
+                       std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
+                       );
+
+
+       _editor->commit_reversible_command ();
+       _editor->update_canvas_now ();
+}
+
+void
+VideoTimeLineDrag::aborted (bool)
+{
+       if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
+               return;
+       }
+       ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
+       ARDOUR_UI::instance()->flush_videotimeline_cache(true);
+
+       for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
+               i->view->region()->resume_property_changes ();
+               i->view->region()->set_position(i->initial_position);
+       }
+}
+
+TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
        : RegionDrag (e, i, p, v)
 {
        DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
+       _preserve_fade_anchor = preserve_fade_anchor;
 }
 
 void
@@ -1724,13 +1887,41 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
        switch (_operation) {
        case StartTrim:
                for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                       i->view->trim_front (i->initial_position + dt, non_overlap_trim);
+                       bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
+                       if (changed && _preserve_fade_anchor) {
+                               AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
+                               if (arv) {
+                                       double distance;
+                                       double new_length;
+                                       framecnt_t len;
+                                       boost::shared_ptr<AudioRegion> ar (arv->audio_region());
+                                       distance = _drags->current_pointer_x() - grab_x();
+                                       len = ar->fade_in()->back()->when;
+                                       new_length = len - _editor->unit_to_frame (distance);
+                                       new_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
+                                       arv->reset_fade_in_shape_width (ar, new_length);  //the grey shape
+                               }
+                       }
                }
                break;
 
        case EndTrim:
                for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                       i->view->trim_end (i->initial_end + dt, non_overlap_trim);
+                       bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
+                       if (changed && _preserve_fade_anchor) {
+                               AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
+                               if (arv) {
+                                       double distance;
+                                       double new_length;
+                                       framecnt_t len;
+                                       boost::shared_ptr<AudioRegion> ar (arv->audio_region());
+                                       distance = grab_x() - _drags->current_pointer_x();
+                                       len = ar->fade_out()->back()->when;
+                                       new_length = len - _editor->unit_to_frame (distance);
+                                       new_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
+                                       arv->reset_fade_out_shape_width (ar, new_length);  //the grey shape
+                               }
+                       }
                }
                break;
 
@@ -1782,16 +1973,61 @@ 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 ();
+                               {
+                                       /* 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.
+                                       */
+                                       i->view->trim_front_ending ();
+                               }
+                               if (_preserve_fade_anchor) {
+                                       AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
+                                       if (arv) {
+                                               double distance;
+                                               double new_length;
+                                               framecnt_t len;
+                                               boost::shared_ptr<AudioRegion> ar (arv->audio_region());
+                                               distance = _drags->current_pointer_x() - grab_x();
+                                               len = ar->fade_in()->back()->when;
+                                               new_length = len - _editor->unit_to_frame (distance);
+                                               new_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
+                                               ar->set_fade_in_length(new_length);
+                                       }
+                               }
+                       }
+               } else if (_operation == EndTrim) {
+                       for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+                               if (_preserve_fade_anchor) {
+                                       AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
+                                       if (arv) {
+                                               double distance;
+                                               double new_length;
+                                               framecnt_t len;
+                                               boost::shared_ptr<AudioRegion> ar (arv->audio_region());
+                                               distance = _drags->current_pointer_x() - grab_x();
+                                               len = ar->fade_out()->back()->when;
+                                               new_length = len - _editor->unit_to_frame (distance);
+                                               new_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
+                                               ar->set_fade_out_length(new_length);
+                                       }
+                               }
                        }
                }
 
+               if (!_views.empty()) {
+                       if (_operation == StartTrim) {
+                               _editor->maybe_locate_with_edit_preroll(
+                                       _views.begin()->view->region()->position());
+                       }
+                       if (_operation == EndTrim) {
+                               _editor->maybe_locate_with_edit_preroll(
+                                       _views.begin()->view->region()->position() +
+                                       _views.begin()->view->region()->length());
+                       }
+               }
+       
                if (!_editor->selection->selected (_primary)) {
                        _primary->thaw_after_trim ();
                } else {
@@ -1817,6 +2053,7 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
                                }
                        }
                }
+
                for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
                        (*p)->thaw ();
                }
@@ -1824,10 +2061,6 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
                _editor->motion_frozen_playlists.clear ();
                _editor->commit_reversible_command();
 
-               for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                       i->view->drag_end ();
-               }
-
        } else {
                /* no mouse movement */
                _editor->point_trim (event, adjusted_current_frame (event));
@@ -2139,7 +2372,7 @@ CursorDrag::fake_locate (framepos_t t)
 
        Session* s = _editor->session ();
        if (s->timecode_transmission_suspended ()) {
-               framepos_t const f = _editor->playhead_cursor->current_frame;
+               framepos_t const f = _editor->playhead_cursor->current_frame ();
                s->send_mmc_locate (f);
                s->send_full_time_code (f);
        }
@@ -2153,7 +2386,7 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
 {
        Drag::start_grab (event, c);
 
-       _grab_zoom = _editor->frames_per_unit;
+       _grab_zoom = _editor->frames_per_pixel;
 
        framepos_t where = _editor->event_frame (event, 0, 0);
        _editor->snap_to_with_modifier (where, event);
@@ -2216,7 +2449,7 @@ CursorDrag::finished (GdkEvent* event, bool movement_occurred)
 
        Session* s = _editor->session ();
        if (s) {
-               s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
+               s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
                _editor->_pending_locate_request = true;
                s->request_resume_timecode_transmission ();
        }
@@ -2248,8 +2481,6 @@ FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
 
        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
@@ -2285,8 +2516,7 @@ FadeInDrag::motion (GdkEvent* event, bool)
                        continue;
                }
 
-               tmp->reset_fade_in_shape_width (fade_length);
-               tmp->show_fade_line((framecnt_t) fade_length);
+               tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
        }
 
        show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
@@ -2328,7 +2558,6 @@ FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
 
                tmp->audio_region()->set_fade_in_length (fade_length);
                tmp->audio_region()->set_fade_in_active (true);
-               tmp->hide_fade_line();
 
                XMLNode &after = alist->get_state();
                _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
@@ -2347,8 +2576,7 @@ FadeInDrag::aborted (bool)
                        continue;
                }
 
-               tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
-               tmp->hide_fade_line();
+               tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
        }
 }
 
@@ -2367,8 +2595,6 @@ FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        boost::shared_ptr<AudioRegion> r = arv->audio_region ();
 
        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
@@ -2406,8 +2632,7 @@ FadeOutDrag::motion (GdkEvent* event, bool)
                        continue;
                }
 
-               tmp->reset_fade_out_shape_width (fade_length);
-               tmp->show_fade_line(region->length() - fade_length);
+               tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
        }
 
        show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
@@ -2451,7 +2676,6 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
 
                tmp->audio_region()->set_fade_out_length (fade_length);
                tmp->audio_region()->set_fade_out_active (true);
-               tmp->hide_fade_line();
 
                XMLNode &after = alist->get_state();
                _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
@@ -2470,8 +2694,7 @@ FadeOutDrag::aborted (bool)
                        continue;
                }
 
-               tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
-               tmp->hide_fade_line();
+               tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
        }
 }
 
@@ -2483,17 +2706,24 @@ MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
        _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())));
+       _points.push_back (ArdourCanvas::Duple (0, 0));
+       _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
 }
 
 MarkerDrag::~MarkerDrag ()
 {
-       for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
-               delete *i;
+       for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
+               delete i->location;
        }
 }
 
+MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
+{
+       location = new Location (*l);
+       markers.push_back (m);
+       move_both = false;
+}
+
 void
 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 {
@@ -2519,7 +2749,7 @@ MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
        switch (op) {
        case Selection::Toggle:
-               _editor->selection->toggle (_marker);
+               /* we toggle on the button release */
                break;
        case Selection::Set:
                if (!_editor->selection->selected (_marker)) {
@@ -2561,11 +2791,37 @@ MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
                break;
        }
 
-       /* Set up copies for us to manipulate during the drag */
+       /* Set up copies for us to manipulate during the drag 
+        */
 
        for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
+
                Location* l = _editor->find_location_from_marker (*i, is_start);
-               _copied_locations.push_back (new Location (*l));
+
+               if (!l) {
+                       continue;
+               }
+
+               if (l->is_mark()) {
+                       _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
+               } else {
+                       /* range: check that the other end of the range isn't
+                          already there.
+                       */
+                       CopiedLocationInfo::iterator x;
+                       for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
+                               if (*(*x).location == *l) {
+                                       break;
+                               }
+                       }
+                       if (x == _copied_locations.end()) {
+                               _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
+                       } else {
+                               (*x).markers.push_back (*i);
+                               (*x).move_both = true;
+                       }
+               }
+                       
        }
 }
 
@@ -2583,33 +2839,31 @@ MarkerDrag::motion (GdkEvent* event, bool)
        framecnt_t f_delta = 0;
        bool is_start;
        bool move_both = false;
-       Marker* marker;
        Location *real_location;
        Location *copy_location = 0;
 
        framepos_t const newframe = adjusted_current_frame (event);
-
        framepos_t next = newframe;
 
        if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
                move_both = true;
        }
 
-       MarkerSelection::iterator i;
-       list<Location*>::iterator x;
+       CopiedLocationInfo::iterator x;
 
        /* find the marker we're dragging, and compute the delta */
 
-       for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
-            x != _copied_locations.end() && i != _editor->selection->markers.end();
-            ++i, ++x) {
+       for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
+
+               copy_location = (*x).location;
 
-               copy_location = *x;
-               marker = *i;
+               if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
 
-               if (marker == _marker) {
+                       /* this marker is represented by this
+                        * CopiedLocationMarkerInfo 
+                        */
 
-                       if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
+                       if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
                                /* que pasa ?? */
                                return;
                        }
@@ -2619,7 +2873,7 @@ MarkerDrag::motion (GdkEvent* event, bool)
                        } else {
 
 
-                               switch (marker->type()) {
+                               switch (_marker->type()) {
                                case Marker::SessionStart:
                                case Marker::RangeStart:
                                case Marker::LoopStart:
@@ -2638,27 +2892,25 @@ MarkerDrag::motion (GdkEvent* event, bool)
                                        return;
                                }
                        }
+
                        break;
                }
        }
 
-       if (i == _editor->selection->markers.end()) {
+       if (x == _copied_locations.end()) {
                /* hmm, impossible - we didn't find the dragged marker */
                return;
        }
 
        /* now move them all */
 
-       for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
-            x != _copied_locations.end() && i != _editor->selection->markers.end();
-            ++i, ++x) {
+       for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
 
-               copy_location = *x;
-               marker = *i;
+               copy_location = x->location;
 
                /* call this to find out if its the start or end */
 
-               if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
+               if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
                        continue;
                }
 
@@ -2673,13 +2925,13 @@ MarkerDrag::motion (GdkEvent* event, bool)
                        copy_location->set_start (copy_location->start() + f_delta);
 
                } else {
-
+                       
                        framepos_t new_start = copy_location->start() + f_delta;
                        framepos_t new_end = copy_location->end() + f_delta;
-
+                       
                        if (is_start) { // start-of-range marker
-
-                               if (move_both) {
+                               
+                               if (move_both || (*x).move_both) {
                                        copy_location->set_start (new_start);
                                        copy_location->set_end (new_end);
                                } else  if (new_start < copy_location->end()) {
@@ -2692,7 +2944,7 @@ MarkerDrag::motion (GdkEvent* event, bool)
 
                        } else { // end marker
 
-                               if (move_both) {
+                               if (move_both || (*x).move_both) {
                                        copy_location->set_end (new_end);
                                        copy_location->set_start (new_start);
                                } else if (new_end > copy_location->start()) {
@@ -2706,12 +2958,20 @@ MarkerDrag::motion (GdkEvent* event, bool)
                }
 
                update_item (copy_location);
-
+               
+               /* now lookup the actual GUI items used to display this
+                * location and move them to wherever the copy of the location
+                * is now. This means that the logic in ARDOUR::Location is
+                * still enforced, even though we are not (yet) modifying 
+                * the real Location itself.
+                */
+               
                Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
 
                if (lm) {
                        lm->set_position (copy_location->start(), copy_location->end());
                }
+
        }
 
        assert (!_copied_locations.empty());
@@ -2742,6 +3002,10 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
                        break;
 
                case Selection::Toggle:
+                       /* we toggle on the button release, click only */
+                       _editor->selection->toggle (_marker);
+                       break;
+
                case Selection::Extend:
                case Selection::Add:
                        break;
@@ -2756,7 +3020,7 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
        XMLNode &before = _editor->session()->locations()->get_state();
 
        MarkerSelection::iterator i;
-       list<Location*>::iterator x;
+       CopiedLocationInfo::iterator x;
        bool is_start;
 
        for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
@@ -2772,9 +3036,9 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
                        }
 
                        if (location->is_mark()) {
-                               location->set_start ((*x)->start());
+                               location->set_start (((*x).location)->start());
                        } else {
-                               location->set ((*x)->start(), (*x)->end());
+                               location->set (((*x).location)->start(), ((*x).location)->end());
                        }
                }
        }
@@ -2831,6 +3095,8 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 
        _editor->verbose_cursor()->show ();
 
+       _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
+
        if (!_point->can_slide ()) {
                _x_constrained = true;
        }
@@ -2885,9 +3151,7 @@ ControlPointDrag::motion (GdkEvent* event, bool)
 
        float const fraction = 1.0 - (cy / _point->line().height());
 
-       bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
-
-       _point->line().drag_motion (_editor->frame_to_unit_unrounded (cx_frames), fraction, false, push);
+       _point->line().drag_motion (_editor->frame_to_unit_unrounded (cx_frames), fraction, false, _pushing, _final_index);
 
        _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
 }
@@ -2907,7 +3171,7 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
                motion (event, false);
        }
 
-       _point->line().end_drag ();
+       _point->line().end_drag (_pushing, _final_index);
        _editor->session()->commit_reversible_command ();
 }
 
@@ -2952,9 +3216,9 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
        double cx = event->button.x;
        double cy = event->button.y;
 
-       _line->parent_group().w2i (cx, cy);
+       _line->parent_group().canvas_to_item (cx, cy);
 
-       framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
+       framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_pixel);
 
        uint32_t before;
        uint32_t after;
@@ -2998,10 +3262,10 @@ LineDrag::motion (GdkEvent* event, bool)
        cy = min ((double) _line->height(), cy);
 
        double const fraction = 1.0 - (cy / _line->height());
-       bool const push = !Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
+       uint32_t ignored;
 
        /* we are ignoring x position for this drag, so we can just pass in anything */
-       _line->drag_motion (0, fraction, true, push);
+       _line->drag_motion (0, fraction, true, false, ignored);
 
        _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
 }
@@ -3010,7 +3274,7 @@ void
 LineDrag::finished (GdkEvent* event, bool)
 {
        motion (event, false);
-       _line->end_drag ();
+       _line->end_drag (false, 0);
        _editor->session()->commit_reversible_command ();
 }
 
@@ -3041,7 +3305,7 @@ FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
        double cx = event->button.x;
        double cy = event->button.y;
 
-       _item->property_parent().get_value()->w2i(cx, cy);
+       _item->parent()->canvas_to_item (cx, cy);
 
        /* store grab start in parent frame */
        _region_view_grab_x = cx;
@@ -3071,16 +3335,9 @@ FeatureLineDrag::motion (GdkEvent*, bool)
                cx = 0;
        }
 
-       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;
+       boost::optional<Rect> bbox = _line->bounding_box ();
+       assert (bbox);
+       _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
 
        float *pos = new float;
        *pos = cx;
@@ -3156,16 +3413,16 @@ RubberbandSelectDrag::motion (GdkEvent* event, bool)
                double x1 = _editor->frame_to_pixel (start);
                double x2 = _editor->frame_to_pixel (end);
 
-               _editor->rubberband_rect->property_x1() = x1;
+               _editor->rubberband_rect->set_x0 (x1);
                if (_vertical_only) {
                        /* fixed 10 pixel width */
-                       _editor->rubberband_rect->property_x2() = x1 + 10;
+                       _editor->rubberband_rect->set_x1 (x1 + 10);
                } else {
-                       _editor->rubberband_rect->property_x2() = x2;
+                       _editor->rubberband_rect->set_x1 (x2);
                } 
 
-               _editor->rubberband_rect->property_y1() = y1;
-               _editor->rubberband_rect->property_y2() = y2;
+               _editor->rubberband_rect->set_y0 (y1);
+               _editor->rubberband_rect->set_y1 (y2);
 
                _editor->rubberband_rect->show();
                _editor->rubberband_rect->raise_to_top();
@@ -3228,7 +3485,13 @@ RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
                        }
                } 
 
-               if (do_deselect) {
+               /* do not deselect if Primary or Tertiary (toggle-select or
+                * extend-select are pressed.
+                */
+
+               if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) && 
+                   !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) && 
+                   do_deselect) {
                        deselect_things ();
                }
 
@@ -3356,12 +3619,18 @@ ScrubDrag::aborted (bool)
 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
        : Drag (e, i)
        , _operation (o)
-       , _copy (false)
+       , _add (false)
+       , _extend (false)
        , _original_pointer_time_axis (-1)
        , _last_pointer_time_axis (-1)
        , _time_selection_at_start (!_editor->get_selection().time.empty())
 {
        DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
+       
+       if (_time_selection_at_start) {
+               start_at_start = _editor->get_selection().time.start();
+               end_at_start = _editor->get_selection().time.end_frame();
+       }
 }
 
 void
@@ -3375,10 +3644,10 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
 
        switch (_operation) {
        case CreateSelection:
-               if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
-                       _copy = true;
+               if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
+                       _add = true;
                } else {
-                       _copy = false;
+                       _add = false;
                }
                cursor = _editor->cursors()->selector;
                Drag::start_grab (event, cursor);
@@ -3401,6 +3670,10 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
        case SelectionMove:
                Drag::start_grab (event, cursor);
                break;
+
+       case SelectionExtend:
+               Drag::start_grab (event, cursor);
+               break;
        }
 
        if (_operation == SelectionMove) {
@@ -3428,6 +3701,9 @@ SelectionDrag::setup_pointer_frame_offset ()
        case SelectionEndTrim:
                _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
                break;
+
+       case SelectionExtend:
+               break;
        }
 }
 
@@ -3436,7 +3712,8 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
 {
        framepos_t start = 0;
        framepos_t end = 0;
-       framecnt_t length;
+       framecnt_t length = 0;
+       framecnt_t distance = 0;
 
        pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
        if (pending_time_axis.first == 0) {
@@ -3457,10 +3734,15 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                framepos_t grab = grab_frame ();
 
                if (first_move) {
-                       _editor->snap_to (grab);
+                       grab = adjusted_current_frame (event, false);
+                       if (grab < pending_position) {
+                               _editor->snap_to (grab, -1);
+                       }  else {
+                               _editor->snap_to (grab, 1);
+                       }
                }
 
-               if (pending_position < grab_frame()) {
+               if (pending_position < grab) {
                        start = pending_position;
                        end = grab;
                } else {
@@ -3474,12 +3756,12 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
 
                if (first_move) {
 
-                       if (_copy) {
+                       if (_add) {
                                /* adding to the selection */
                                _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;
+                               _add = false;
                        } else {
                                /* new selection */
 
@@ -3547,28 +3829,39 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                }
 
                break;
-
+               
        case SelectionMove:
 
                start = _editor->selection->time[_editor->clicked_selection].start;
                end = _editor->selection->time[_editor->clicked_selection].end;
 
                length = end - start;
-
+               distance = pending_position - start;
                start = pending_position;
                _editor->snap_to (start);
 
                end = start + length;
 
                break;
+
+       case SelectionExtend:
+               break;
        }
 
-       if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
+       if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
                _editor->start_canvas_autoscroll (1, 0);
        }
 
        if (start != end) {
-               _editor->selection->replace (_editor->clicked_selection, start, end);
+               switch (_operation) {
+               case SelectionMove:     
+                       if (_time_selection_at_start) {
+                               _editor->selection->move_time (distance);
+                       }
+                       break;
+               default:
+                       _editor->selection->replace (_editor->clicked_selection, start, end);
+               }
        }
 
        if (_operation == SelectionMove) {
@@ -3592,13 +3885,11 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
 
                /* XXX what if its a music time selection? */
                if (s) {
-                       if ((s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
+                       if ( s->get_play_range() && s->transport_rolling() ) {
                                s->request_play_range (&_editor->selection->time, true);
                        } else {
-                               if (Config->get_always_play_range()) {
-                                       if (_editor->doing_range_stuff()) {
-                                               s->request_locate (_editor->get_selection().time.start());
-                                       } 
+                               if (Config->get_always_play_range() && !s->transport_rolling()) {
+                                       s->request_locate (_editor->get_selection().time.start());
                                }
                        }
                }
@@ -3607,44 +3898,37 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
                /* just a click, no pointer movement.
                 */
 
-               if (Keyboard::no_modifier_keys_pressed (&event->button)) {
-                       if (!_time_selection_at_start) {
-                               if (_editor->clicked_regionview) {
-                                       if (_editor->get_selection().selected (_editor->clicked_regionview)) {
-                                               /* range select the entire current
-                                                  region selection
-                                               */
-                                               _editor->select_range (_editor->get_selection().regions.start(), 
-                                                                      _editor->get_selection().regions.end_frame());
-                                       } else {
-                                               /* range select this (unselected)
-                                                * region
-                                                */
-                                               _editor->select_range (_editor->clicked_regionview->region()->position(), 
-                                                                      _editor->clicked_regionview->region()->last_frame());
-                                       }
+               if (_operation == SelectionExtend) {
+                       if (_time_selection_at_start) {
+                               framepos_t pos = adjusted_current_frame (event, false);
+                               framepos_t start = min (pos, start_at_start);
+                               framepos_t end = max (pos, end_at_start);
+                               _editor->selection->set (start, end);
+                       }
+               } else {
+                       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
+                               if (_editor->clicked_selection) {
+                                       _editor->selection->remove (_editor->clicked_selection);
                                }
                        } else {
-                               _editor->selection->clear_time();
+                               if (!_editor->clicked_selection) {
+                                       _editor->selection->clear_time();
+                               }
                        }
                }
 
                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);
                }
 
-               if (Config->get_always_play_range()) {
-                       if (_editor->doing_range_stuff()) {
-                               s->request_locate (_editor->get_selection().time.start());
-                       } 
-               }
        }
 
        _editor->stop_canvas_autoscroll ();
+       _editor->clicked_selection = 0;
 }
 
 void
@@ -3660,12 +3944,13 @@ RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operat
 {
        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::Rectangle (_editor->time_line_group, 
+                                                 ArdourCanvas::Rect (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();
-       _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
+       _drag_rect->set_fill_color (ARDOUR_UI::config()->canvasvar_RangeDragRect.get());
+       _drag_rect->set_outline_color (ARDOUR_UI::config()->canvasvar_RangeDragRect.get());
 }
 
 void
@@ -3705,7 +3990,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
 {
        framepos_t start = 0;
        framepos_t end = 0;
-       ArdourCanvas::SimpleRect *crect;
+       ArdourCanvas::Rectangle *crect;
 
        switch (_operation) {
        case CreateRangeMarker:
@@ -3718,7 +4003,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
                crect = _editor->cd_marker_bar_drag_rect;
                break;
        default:
-               cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
+               error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
                return;
                break;
        }
@@ -3754,7 +4039,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
                }
        }
 
-       if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
+       if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
                _editor->start_canvas_autoscroll (1, 0);
        }
 
@@ -3763,8 +4048,8 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
 
                double x1 = _editor->frame_to_pixel (start);
                double x2 = _editor->frame_to_pixel (end);
-               crect->property_x1() = x1;
-               crect->property_x2() = x2;
+               crect->set_x0 (x1);
+               crect->set_x1 (x2);
 
                update_item (_editor->temp_location);
        }
@@ -3865,8 +4150,8 @@ RangeMarkerBarDrag::update_item (Location* location)
        double const x1 = _editor->frame_to_pixel (location->start());
        double const x2 = _editor->frame_to_pixel (location->end());
 
-       _drag_rect->property_x1() = x1;
-       _drag_rect->property_x2() = x2;
+       _drag_rect->set_x0 (x1);
+       _drag_rect->set_x1 (x2);
 }
 
 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
@@ -3958,7 +4243,8 @@ NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
 {
        DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
 
-       _primary = dynamic_cast<CanvasNoteEvent*> (_item);
+       _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
+       assert (_primary);
        _region = &_primary->region_view ();
        _note_height = _region->midi_stream_view()->note_height ();
 }
@@ -4106,7 +4392,7 @@ AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView
        , _nothing_to_drag (false)
 {
        DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
-       track_view = atv;
+       y_origin = atv->y_position();
        setup (atv->lines ());
 }
 
@@ -4120,7 +4406,7 @@ AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, l
 
        list<boost::shared_ptr<AutomationLine> > lines;
        lines.push_back (rv->get_gain_line ());
-       track_view = &rv->get_time_axis_view();
+       y_origin = rv->get_time_axis_view().y_position();
        setup (lines);
 }
 
@@ -4162,6 +4448,12 @@ AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lin
        /* Now ::lines contains the AutomationLines that somehow overlap our drag */
 }
 
+double
+AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
+{
+       return 1.0 - ((global_y - y_origin) / line->height());
+}
+
 void
 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 {
@@ -4170,7 +4462,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        /* Get line states before we start changing things */
        for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
                i->state = &i->line->get_state ();
-               i->original_fraction = 1 - ((_drags->current_pointer_y() - track_view->y_position()) / i->line->height());
+               i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
        }
 
        if (_ranges.empty()) {
@@ -4276,7 +4568,7 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        }
 
        for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
-               i->line->start_drag_multiple (i->points, 1 - ((_drags->current_pointer_y() - track_view->y_position()) / i->line->height ()), i->state);
+               i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
        }
 }
 
@@ -4288,10 +4580,10 @@ AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
        }
 
        for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
-               float const f = 1 - ((_drags->current_pointer_y() - track_view->y_position()) / l->line->height());
-
+               float const f = y_fraction (l->line, _drags->current_pointer_y());
                /* we are ignoring x position for this drag, so we can just pass in anything */
-               l->line->drag_motion (0, f, true, false);
+               uint32_t ignored;
+               l->line->drag_motion (0, f, true, false, ignored);
                show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
        }
 }
@@ -4305,7 +4597,7 @@ AutomationRangeDrag::finished (GdkEvent* event, bool)
 
        motion (event, false);
        for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
-               i->line->end_drag ();
+               i->line->end_drag (false, 0);
        }
 
        _editor->session()->commit_reversible_command ();
@@ -4324,14 +4616,14 @@ DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
 {
        time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
        layer = v->region()->layer ();
-       initial_y = v->get_canvas_group()->property_y ();
+       initial_y = v->get_canvas_group()->position().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)
+PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
+       : Drag (e, i->canvas_item ())
        , _region_view (r)
        , _patch_change (i)
        , _cumulative_dx (0)
@@ -4351,7 +4643,7 @@ PatchChangeDrag::motion (GdkEvent* ev, bool)
 
        framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
        double const dxu = _editor->frame_to_unit (dxf); // permitted fx in units
-       _patch_change->move (dxu - _cumulative_dx, 0);
+       _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
        _cumulative_dx = dxu;
 }
 
@@ -4376,7 +4668,7 @@ PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
 void
 PatchChangeDrag::aborted (bool)
 {
-       _patch_change->move (-_cumulative_dx, 0);
+       _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
 }
 
 void
@@ -4509,7 +4801,7 @@ NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 {
        Drag::start_grab (event, cursor);
                                                 
-       _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
+       _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
 
        framepos_t pf = _drags->current_pointer_frame ();
        framecnt_t const g = grid_frames (pf);
@@ -4527,14 +4819,10 @@ NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        double const x = _editor->frame_to_pixel (_note[0]);
        double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
 
-       _drag_rect->property_x1() = x;
-       _drag_rect->property_y1() = y;
-       _drag_rect->property_x2() = x;
-       _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
-
-       _drag_rect->property_outline_what() = 0xff;
-       _drag_rect->property_outline_color_rgba() = 0xffffff99;
-       _drag_rect->property_fill_color_rgba()    = 0xffffff66;
+       _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
+       _drag_rect->set_outline_what (0xff);
+       _drag_rect->set_outline_color (0xffffff99);
+       _drag_rect->set_fill_color (0xffffff66);
 }
 
 void
@@ -4543,9 +4831,9 @@ NoteCreateDrag::motion (GdkEvent* event, bool)
        _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
        double const x = _editor->frame_to_pixel (_note[1]);
        if (_note[1] > _note[0]) {
-               _drag_rect->property_x2() = x;
+               _drag_rect->set_x1 (x);
        } else {
-               _drag_rect->property_x1() = x;
+               _drag_rect->set_x0 (x);
        }
 }
 
@@ -4557,7 +4845,7 @@ NoteCreateDrag::finished (GdkEvent*, bool had_movement)
        }
        
        framepos_t const start = min (_note[0], _note[1]);
-       framecnt_t length = abs (_note[0] - _note[1]);
+       framecnt_t length = (framecnt_t) fabs (_note[0] - _note[1]);
 
        framecnt_t const g = grid_frames (start);
        double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
@@ -4568,14 +4856,14 @@ NoteCreateDrag::finished (GdkEvent*, bool had_movement)
 
        double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
 
-       _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
+       _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
 }
 
 double
 NoteCreateDrag::y_to_region (double y) const
 {
        double x = 0;
-       _region_view->get_canvas_group()->w2i (x, y);
+       _region_view->get_canvas_group()->canvas_to_item (x, y);
        return y;
 }
 
@@ -4590,6 +4878,7 @@ CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanv
        , arv (rv)
        , start (start_yn)
 {
+       std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
 }
 
 void