add editor to Theme Manager for modifiers
[ardour.git] / gtk2_ardour / editor_drag.cc
index f6f05925deab0c4394274cb3dfd90b9abf4f5566..370a6936b16864beb94858bf89e82a7b7a90c0c4 100644 (file)
@@ -40,6 +40,7 @@
 #include "ardour/region_factory.h"
 #include "ardour/session.h"
 
+#include "canvas/canvas.h"
 #include "canvas/scroll_group.h"
 
 #include "editor.h"
@@ -50,7 +51,6 @@
 #include "ardour_ui.h"
 #include "gui_thread.h"
 #include "control_point.h"
-#include "utils.h"
 #include "region_gain_line.h"
 #include "editor_drag.h"
 #include "audio_time_axis.h"
@@ -95,6 +95,8 @@ DragManager::abort ()
 {
        _ending = true;
 
+       cerr << "Aborting drag\n";
+
        for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
                (*i)->abort ();
                delete *i;
@@ -177,6 +179,12 @@ DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
 {
        bool r = false;
 
+       /* calling this implies that we expect the event to have canvas
+        * coordinates 
+        *
+        * Can we guarantee that this is true?
+        */
+
        _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
 
        for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
@@ -193,24 +201,6 @@ DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
        return r;
 }
 
-bool
-DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
-{
-       bool r = false;
-
-       _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
-
-       for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
-               bool const t = (*i)->motion_handler (e, from_autoscroll);
-               if (t) {
-                       r = true;
-               }
-
-       }
-
-       return r;
-}
-
 bool
 DragManager::have_item (ArdourCanvas::Item* i) const
 {
@@ -348,6 +338,12 @@ Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
        return adjusted_frame (_drags->current_pointer_frame (), event, snap);
 }
 
+double
+Drag::current_pointer_x() const
+{
+        return _drags->current_pointer_x ();
+}
+
 double
 Drag::current_pointer_y () const
 {
@@ -363,7 +359,7 @@ 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 (_move_threshold_passed &&
-           (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
+           (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
            (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
                return false;
        }
@@ -398,6 +394,7 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
                        return true;
                }
        }
+
        return false;
 }
 
@@ -418,58 +415,22 @@ Drag::abort ()
 void
 Drag::show_verbose_cursor_time (framepos_t frame)
 {
-       /* We use DragManager::current_pointer_y() here 
-          because we need to position the verbose canvas
-          cursor within the overall canvas, regardless 
-          of this particular drag's _trackview_only
-          setting.
-       */
-          
-       _editor->verbose_cursor()->set_time (
-               frame,
-               _drags->current_pointer_x() + 10,
-               _drags->current_pointer_y() + 10
-               );
-
+       _editor->verbose_cursor()->set_time (frame);
        _editor->verbose_cursor()->show ();
 }
 
 void
-Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
+Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
 {
-       _editor->verbose_cursor()->show (xoffset);
-
-       /* We use DragManager::current_pointer_y() here 
-          because we need to position the verbose canvas
-          cursor within the overall canvas, regardless 
-          of this particular drag's _trackview_only
-          setting.
-       */
-
-       _editor->verbose_cursor()->set_duration (
-               start, end,
-               _drags->current_pointer_x() + 10,
-               _drags->current_pointer_y() + 10
-               );
+       _editor->verbose_cursor()->set_duration (start, end);
+       _editor->verbose_cursor()->show ();
 }
 
 void
 Drag::show_verbose_cursor_text (string const & text)
 {
+       _editor->verbose_cursor()->set (text);
        _editor->verbose_cursor()->show ();
-
-       /* We use DragManager::current_pointer_y() here 
-          because we need to position the verbose canvas
-          cursor within the overall canvas, regardless 
-          of this particular drag's _trackview_only
-          setting.
-       */
-
-       _editor->verbose_cursor()->set (
-               text,
-               _drags->current_pointer_x() + 10,
-               _drags->current_pointer_y() + 10
-               );
 }
 
 boost::shared_ptr<Region>
@@ -524,7 +485,7 @@ RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<Re
         */
 
        for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
-               _views.push_back (DraggingView (*i, this));
+               _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
        }
 
        RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
@@ -643,7 +604,6 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r
                        }
                }
 
-               _last_frame_position = *pending_region_position;
        }
 
        return dx;
@@ -711,11 +671,12 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
 
                delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
                delta_layer = current_pointer_layer - _last_pointer_layer;
-       }
+       } 
        
        /* Work out the change in x */
        framepos_t pending_region_position;
        double const x_delta = compute_x_delta (event, &pending_region_position);
+       _last_frame_position = pending_region_position;
 
        /* Verify change in y */
        if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
@@ -724,7 +685,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
                delta_layer = 0;
        }
 
-       if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
+       if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
                /* haven't reached next snap point, and we're not switching
                   trackviews nor layers. nothing to do.
                */
@@ -747,8 +708,8 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
                        /* reparent the regionview into a group above all
                         * others
                         */
-
-                       ArdourCanvas::Group* rvg = rv->get_canvas_group();                                                                                                                                     
+                       
+                       ArdourCanvas::Item* rvg = rv->get_canvas_group();
                         Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
                         Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
                         rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
@@ -908,7 +869,7 @@ RegionMoveDrag::motion (GdkEvent* event, bool first_move)
                        }
 
                        nrv->get_canvas_group()->show ();
-                       new_regionviews.push_back (DraggingView (nrv, this));
+                       new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
 
                        /* swap _primary to the copy */
 
@@ -1010,7 +971,7 @@ RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
 }
 
 RouteTimeAxisView*
-RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region)
+RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
 {                      
        /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
           new track.
@@ -1020,12 +981,20 @@ RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region)
                if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
                        list<boost::shared_ptr<AudioTrack> > audio_tracks;
                        audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
-                       return _editor->axis_view_from_route (audio_tracks.front());
+                       RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
+                       if (rtav) {
+                               rtav->set_height (original->current_height());
+                       }
+                       return rtav;
                } else {
                        ChanCount one_midi_port (DataType::MIDI, 1);
                        list<boost::shared_ptr<MidiTrack> > midi_tracks;
                        midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
-                       return _editor->axis_view_from_route (midi_tracks.front());
+                       RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
+                       if (rtav) {
+                               rtav->set_height (original->current_height());
+                       }
+                       return rtav;
                }                                               
        } catch (...) {
                error << _("Could not create new track after region placed in the drop zone") << endmsg;
@@ -1076,7 +1045,7 @@ RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed
                
                if (i->time_axis_view < 0) {
                        if (!new_time_axis_view) {
-                               new_time_axis_view = create_destination_time_axis (i->view->region());
+                               new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
                        }
                        dest_rtv = new_time_axis_view;
                } else {
@@ -1151,7 +1120,7 @@ RegionMoveDrag::finished_no_copy (
                
                if (i->time_axis_view < 0) {
                        if (!new_time_axis_view) {
-                               new_time_axis_view = create_destination_time_axis (rv->region());
+                               new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
                        }
                        dest_rtv = new_time_axis_view;
                } else {
@@ -1321,7 +1290,7 @@ RegionMoveDrag::remove_region_from_playlist (
                playlist->clear_changes ();
        }
 
-       playlist->remove_region (region);
+       playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
 }
 
 
@@ -1471,7 +1440,7 @@ RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, Rout
 
        _primary->get_canvas_group()->show ();
        _primary->set_position (pos, 0);
-       _views.push_back (DraggingView (_primary, this));
+       _views.push_back (DraggingView (_primary, this, v));
 
        _last_frame_position = pos;
 
@@ -1491,6 +1460,12 @@ RegionInsertDrag::finished (GdkEvent *, bool)
        _editor->begin_reversible_command (Operations::insert_region);
        playlist->clear_changes ();
        playlist->add_region (_primary->region (), _last_frame_position);
+
+       // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
+       if (Config->get_edit_mode() == Ripple) {
+               playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
+       }
+
        _editor->session()->add_command (new StatefulDiffCommand (playlist));
        _editor->commit_reversible_command ();
 
@@ -1533,10 +1508,12 @@ RegionSpliceDrag::motion (GdkEvent* event, bool)
 
        if (!tv || !tv->is_track()) {
                /* To make sure we hide the verbose canvas cursor when the mouse is
-                  not held over and audiotrack.
+                  not held over an audio track.
                */
                _editor->verbose_cursor()->hide ();
                return;
+       } else {
+               _editor->verbose_cursor()->show ();
        }
 
        int dir;
@@ -1547,10 +1524,8 @@ RegionSpliceDrag::motion (GdkEvent* event, bool)
                dir = -1;
        }
 
-       RegionSelection copy (_editor->selection->regions);
-
-       RegionSelectionByPosition cmp;
-       copy.sort (cmp);
+       RegionSelection copy;
+       _editor->selection->regions.by_position(copy);
 
        framepos_t const pf = adjusted_current_frame (event);
 
@@ -1599,6 +1574,290 @@ RegionSpliceDrag::aborted (bool)
        /* XXX: TODO */
 }
 
+/***
+ * ripple mode...
+ */
+
+void
+RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
+{
+
+       boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
+
+       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
+       RegionSelection to_ripple;
+       for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
+               if ((*i)->position() >= where) {
+                       to_ripple.push_back (rtv->view()->find_view(*i));
+               }
+       }
+
+       for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
+               if (!exclude.contains (*i)) {
+                       // the selection has already been added to _views
+
+                       if (drag_in_progress) {
+                               // do the same things that RegionMotionDrag::motion does when
+                               // first_move is true, for the region views that we're adding
+                               // to _views this time
+
+                               (*i)->drag_start();
+                               ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
+                               Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
+                               Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
+                               rvg->reparent (_editor->_drag_motion_group);
+
+                               // we only need to move in the y direction
+                               Duple fudge = rv_canvas_offset - dmg_canvas_offset;
+                               fudge.x = 0;
+                               rvg->move (fudge);
+
+                       }
+                       _views.push_back (DraggingView (*i, this, tav));
+               }
+       }
+}
+
+void
+RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
+{
+
+       for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
+               // we added all the regions after the selection 
+
+               std::list<DraggingView>::iterator to_erase = i++;
+               if (!_editor->selection->regions.contains (to_erase->view)) {
+                       // restore the non-selected regions to their original playlist & positions,
+                       // and then ripple them back by the length of the regions that were dragged away
+                       // do the same things as RegionMotionDrag::aborted
+
+                       RegionView *rv = to_erase->view;
+                       TimeAxisView* tv = &(rv->get_time_axis_view ());
+                       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
+                       assert (rtv);
+
+                       // plonk them back onto their own track
+                       rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
+                       rv->get_canvas_group()->set_y_position (0);
+                       rv->drag_end ();
+
+                       if (move_regions) {
+                               // move the underlying region to match the view
+                               rv->region()->set_position (rv->region()->position() + amount);
+                       } else {
+                               // restore the view to match the underlying region's original position
+                               rv->move(-amount, 0);   // second parameter is y delta - seems 0 is OK
+                       }
+
+                       rv->set_height (rtv->view()->child_height ());
+                       _views.erase (to_erase);
+               }
+       }
+}
+
+bool
+RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
+{
+       if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
+               if (delta_track) {
+                       return allow_moves_across_tracks;
+               } else {
+                       return true;
+               }
+       }
+       return false;
+}
+
+RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
+       : RegionMoveDrag (e, i, p, v, false, false)
+{
+       DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
+       // compute length of selection
+       RegionSelection selected_regions = _editor->selection->regions;
+       selection_length = selected_regions.end_frame() - selected_regions.start();
+
+       // we'll only allow dragging to another track in ripple mode if all the regions
+       // being dragged start off on the same track
+       allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
+       prev_tav = NULL;
+       prev_amount = 0;
+       exclude = new RegionList;
+       for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
+               exclude->push_back((*i)->region());
+       }
+
+       // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
+       RegionSelection copy;
+       selected_regions.by_position(copy); // get selected regions sorted by position into copy
+
+       std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
+       std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
+
+       for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
+               // find ripple start point on each applicable playlist
+               RegionView *first_selected_on_this_track = NULL;
+               for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
+                       if ((*i)->region()->playlist() == (*pi)) {
+                               // region is on this playlist - it's the first, because they're sorted
+                               first_selected_on_this_track = *i;
+                               break;
+                       }
+               }
+               assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
+               add_all_after_to_views (
+                               &first_selected_on_this_track->get_time_axis_view(),
+                               first_selected_on_this_track->region()->position(),
+                               selected_regions, false);
+       }
+
+       if (allow_moves_across_tracks) {
+               orig_tav = &(*selected_regions.begin())->get_time_axis_view();
+       } else {
+               orig_tav = NULL;
+       }
+
+}
+
+void
+RegionRippleDrag::motion (GdkEvent* event, bool first_move)
+{
+       /* Which trackview is this ? */
+
+       pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
+       RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
+
+       /* The region motion is only processed if the pointer is over
+          an audio track.
+        */
+
+       if (!tv || !tv->is_track()) {
+               /* To make sure we hide the verbose canvas cursor when the mouse is
+                  not held over an audiotrack.
+                */
+               _editor->verbose_cursor()->hide ();
+               return;
+       }
+
+       framepos_t where = adjusted_current_frame (event);
+       assert (where >= 0);
+       framepos_t after;
+       double delta = compute_x_delta (event, &after);
+
+       framecnt_t amount = _editor->pixel_to_sample (delta);
+
+       if (allow_moves_across_tracks) {
+               // all the originally selected regions were on the same track
+
+               framecnt_t adjust = 0;
+               if (prev_tav && tv != prev_tav) {
+                       // dragged onto a different track 
+                       // remove the unselected regions from _views, restore them to their original positions
+                       // and add the regions after the drop point on the new playlist to _views instead.
+                       // undo the effect of rippling the previous playlist, and include the effect of removing
+                       // the dragged region(s) from this track
+
+                       remove_unselected_from_views (prev_amount, false);
+                       // ripple previous playlist according to the regions that have been removed onto the new playlist
+                       prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
+                       prev_amount = 0;
+
+                       // move just the selected regions
+                       RegionMoveDrag::motion(event, first_move); 
+
+                       // ensure that the ripple operation on the new playlist inserts selection_length time 
+                       adjust = selection_length;
+                       // ripple the new current playlist
+                       tv->playlist()->ripple (where, amount+adjust, exclude);
+
+                       // add regions after point where drag entered this track to subsequent ripples
+                       add_all_after_to_views (tv, where, _editor->selection->regions, true);
+
+               } else {
+                       // motion on same track
+                       RegionMoveDrag::motion(event, first_move); 
+               }
+               prev_tav = tv;
+
+               // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
+               prev_position = where;
+       } else {
+               // selection encompasses multiple tracks - just drag
+               // cross-track drags are forbidden
+               RegionMoveDrag::motion(event, first_move); 
+       }
+
+       if (!_x_constrained) {
+               prev_amount += amount;
+       }
+
+       _last_frame_position = after;
+}
+
+void
+RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
+{
+       if (!movement_occurred) {
+               
+               /* just a click */
+
+               if (was_double_click() && !_views.empty()) {
+                       DraggingView dv = _views.front();
+                       dv.view->show_region_editor ();
+                       
+               }
+
+               return;
+       }
+
+       _editor->begin_reversible_command(_("Ripple drag"));
+       
+       // remove the regions being rippled from the dragging view, updating them to 
+       // their new positions
+       remove_unselected_from_views (prev_amount, true);
+
+       if (allow_moves_across_tracks) {
+               if (orig_tav) {
+                       // if regions were dragged across tracks, we've rippled any later
+                       // regions on the track the regions were dragged off, so we need
+                       // to add the original track to the undo record
+                       orig_tav->playlist()->clear_changes();
+                       vector<Command*> cmds;
+                       orig_tav->playlist()->rdiff (cmds);
+                       _editor->session()->add_commands (cmds);
+               }
+               if (prev_tav && prev_tav != orig_tav) {
+                       prev_tav->playlist()->clear_changes();
+                       vector<Command*> cmds;
+                       prev_tav->playlist()->rdiff (cmds);
+                       _editor->session()->add_commands (cmds);
+               }
+       } else {
+               // selection spanned multiple tracks - all will need adding to undo record
+
+               std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
+               std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
+
+               for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
+                       (*pi)->clear_changes();
+                       vector<Command*> cmds;
+                       (*pi)->rdiff (cmds);
+                       _editor->session()->add_commands (cmds);
+               }
+       }
+
+       // other modified playlists are added to undo by RegionMoveDrag::finished()
+       RegionMoveDrag::finished (event, movement_occurred);
+       _editor->commit_reversible_command();
+}
+
+void
+RegionRippleDrag::aborted (bool movement_occurred)
+{
+       RegionMoveDrag::aborted (movement_occurred);
+       _views.clear ();
+}
+
+
 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
        : Drag (e, i),
          _view (dynamic_cast<MidiTimeAxisView*> (v))
@@ -1671,25 +1930,16 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
 
        if (x_fraction > 0.0 && x_fraction < 0.25) {
                cursor = _editor->cursors()->left_side_trim;
+               at_front = true;
        } else  {
                cursor = _editor->cursors()->right_side_trim;
+               at_front = false;
        }
 
        Drag::start_grab (event, cursor);
 
        region = &cnote->region_view();
 
-       double const region_start = region->get_position_pixels();
-       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;
-               at_front = true;
-       } else {
-               cursor = _editor->cursors()->right_side_trim;
-               at_front = false;
-       }
-
        _item->grab ();
 
        if (event->motion.state & Keyboard::PrimaryModifier) {
@@ -1714,7 +1964,10 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
                MidiRegionSelection::iterator next;
                next = r;
                ++next;
-               (*r)->begin_resizing (at_front);
+               MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
+               if (mrv) {
+                       mrv->begin_resizing (at_front);
+               }
                r = next;
        }
 }
@@ -1726,7 +1979,10 @@ NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
        for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
                NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
                assert (nb);
-               (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
+               MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
+               if (mrv) {
+                       mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
+               }
        }
 }
 
@@ -1737,7 +1993,10 @@ NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
        for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
                NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
                assert (nb);
-               (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
+               MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
+               if (mrv) {
+                       mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
+               }
        }
 }
 
@@ -1746,7 +2005,10 @@ NoteResizeDrag::aborted (bool)
 {
        MidiRegionSelection& ms (_editor->get_selection().midi_regions);
        for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
-               (*r)->abort_resizing ();
+               MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
+               if (mrv) {
+                       mrv->abort_resizing ();
+               }
        }
 }
 
@@ -1802,8 +2064,7 @@ VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
        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 ();
+       show_verbose_cursor_text (buf);
 }
 
 void
@@ -1852,8 +2113,7 @@ VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
                        , _("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 ();
+       show_verbose_cursor_text (buf);
 }
 
 void
@@ -1932,8 +2192,6 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
 
        framepos_t const pf = adjusted_current_frame (event);
 
-       cerr << "Button state = " << hex << event->button.state << dec << endl;
-
        if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
                /* Move the contents of the region around without changing the region bounds */
                _operation = ContentsTrim;
@@ -1945,7 +2203,6 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
                        _operation = StartTrim;
 
                        if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
-                               cerr << "start anchored leftdrag\n";
                                Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
                        } else {
                                Drag::start_grab (event, _editor->cursors()->left_side_trim);
@@ -1955,7 +2212,6 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
                        _operation = EndTrim;
                        if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
                                Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
-                               cerr << "start anchored right drag\n";
                        } else {
                                Drag::start_grab (event, _editor->cursors()->right_side_trim);
                        }
@@ -1997,8 +2253,6 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
        pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
        frameoffset_t frame_delta = 0;
 
-       cerr << "trim drag @ " << this << " motion\n";
-
        if (tv && tv->is_track()) {
                speed = tv->track()->speed();
        }
@@ -2338,7 +2592,7 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
                _marker = new MeterMarker (
                        *_editor,
                        *_editor->meter_group,
-                       ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
+                       ARDOUR_UI::config()->color ("meter marker"),
                        name,
                        *new MeterSection (_marker->meter())
                );
@@ -2356,6 +2610,7 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
        }
 
        framepos_t const pf = adjusted_current_frame (event);
+
        _marker->set_position (pf);
        show_verbose_cursor_time (pf);
 }
@@ -2468,7 +2723,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
                _marker = new TempoMarker (
                        *_editor,
                        *_editor->tempo_group,
-                       ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
+                       ARDOUR_UI::config()->color ("tempo marker"),
                        name,
                        *new TempoSection (_marker->tempo())
                        );
@@ -2507,7 +2762,7 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
        motion (event, false);
 
        TempoMap& map (_editor->session()->tempo_map());
-       framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
+       framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
        Timecode::BBT_Time when;
 
        map.bbt_time (beat_time, when);
@@ -3144,7 +3399,7 @@ MarkerDrag::motion (GdkEvent* event, bool)
                                } else  if (new_start < copy_location->end()) {
                                        copy_location->set_start (new_start);
                                } else if (newframe > 0) {
-                                       _editor->snap_to (next, 1, true);
+                                       _editor->snap_to (next, RoundUpAlways, true);
                                        copy_location->set_end (next);
                                        copy_location->set_start (newframe);
                                }
@@ -3157,7 +3412,7 @@ MarkerDrag::motion (GdkEvent* event, bool)
                                } else if (new_end > copy_location->start()) {
                                        copy_location->set_end (new_end);
                                } else if (newframe > 0) {
-                                       _editor->snap_to (next, -1, true);
+                                       _editor->snap_to (next, RoundDownAlways, true);
                                        copy_location->set_start (next);
                                        copy_location->set_end (newframe);
                                }
@@ -3298,10 +3553,7 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 
        _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
 
-       _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
-                                       event->button.x + 10, event->button.y + 10);
-
-       _editor->verbose_cursor()->show ();
+       show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
 
        _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
 
@@ -3361,7 +3613,7 @@ ControlPointDrag::motion (GdkEvent* event, bool)
 
        _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
 
-       _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
+       show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
 }
 
 void
@@ -3392,8 +3644,8 @@ ControlPointDrag::aborted (bool)
 bool
 ControlPointDrag::active (Editing::MouseMode m)
 {
-       if (m == Editing::MouseGain) {
-               /* always active in mouse gain */
+       if (m == Editing::MouseDraw) {
+               /* always active in mouse draw */
                return true;
        }
 
@@ -3447,10 +3699,7 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 
        _line->start_drag_line (before, after, fraction);
 
-       _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
-                                       event->button.x + 10, event->button.y + 10);
-
-       _editor->verbose_cursor()->show ();
+       show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
 }
 
 void
@@ -3475,7 +3724,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, false, ignored);
 
-       _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
+       show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
 }
 
 void
@@ -3719,7 +3968,7 @@ RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
 
                if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
                        /* MIDI track */
-                       if (_editor->selection->empty()) {
+                       if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
                                /* nothing selected */
                                add_midi_region (mtv);
                                do_deselect = false;
@@ -3861,9 +4110,7 @@ SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
        : Drag (e, i)
        , _operation (o)
        , _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");
@@ -3970,9 +4217,9 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                if (first_move) {
                        grab = adjusted_current_frame (event, false);
                        if (grab < pending_position) {
-                               _editor->snap_to (grab, -1);
+                               _editor->snap_to (grab, RoundDownMaybe);
                        }  else {
-                               _editor->snap_to (grab, 1);
+                               _editor->snap_to (grab, RoundUpMaybe);
                        }
                }
 
@@ -4009,9 +4256,16 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                        }
                }
                
+               //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff, 
+               // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this 
+               AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
+               if (atest) {
+                       _editor->selection->add (atest);
+                       break; 
+               }
+               
                /* select all tracks within the rectangle that we've marked out so far */
-               TrackViewList to_be_added_to_selection;
-               TrackViewList to_be_removed_from_selection;
+               TrackViewList new_selection;
                TrackViewList& all_tracks (_editor->track_views);
 
                ArdourCanvas::Coord const top = grab_y();
@@ -4019,26 +4273,39 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
 
                if (top >= 0 && bottom >= 0) {
 
+                       //first, find the tracks that are covered in the y range selection
                        for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
-                       
                                if ((*i)->covered_by_y_range (top, bottom)) {
-                                       if (!(*i)->get_selected()) {
-                                               to_be_added_to_selection.push_back (*i);
-                                       }
-                               } else {
-                                       if ((*i)->get_selected()) {
-                                               to_be_removed_from_selection.push_back (*i);
-                                       }
+                                       new_selection.push_back (*i);
                                }
                        }
 
-                       if (!to_be_added_to_selection.empty()) {
-                               _editor->selection->add (to_be_added_to_selection);
-                       }
-                       
-                       if (!to_be_removed_from_selection.empty()) {
-                               _editor->selection->remove (to_be_removed_from_selection);
+                       //now find any tracks that are GROUPED with the tracks we selected
+                       TrackViewList grouped_add = new_selection;
+                       for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
+                               RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
+                               if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
+                                       for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
+                                               RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
+                                               if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
+                                                       grouped_add.push_back (*j);
+                                       }
+                               }
                        }
+
+                       //now compare our list with the current selection, and add or remove as necessary
+                       //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
+                       TrackViewList tracks_to_add;
+                       TrackViewList tracks_to_remove;
+                       for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
+                               if ( !_editor->selection->tracks.contains ( *i ) )
+                                       tracks_to_add.push_back ( *i );
+                       for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
+                               if ( !grouped_add.contains ( *i ) )
+                                       tracks_to_remove.push_back ( *i );
+                       _editor->selection->add(tracks_to_add);
+                       _editor->selection->remove(tracks_to_remove);
+
                }
        }
        break;
@@ -4122,8 +4389,11 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
                        if ( s->get_play_range() && s->transport_rolling() ) {
                                s->request_play_range (&_editor->selection->time, true);
                        } else {
-                               if (Config->get_always_play_range() && !s->transport_rolling()) {
-                                       s->request_locate (_editor->get_selection().time.start());
+                               if (Config->get_follow_edits() && !s->transport_rolling()) {
+                                       if (_operation == SelectionEndTrim)
+                                               _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
+                                       else
+                                               s->request_locate (_editor->get_selection().time.start());
                                }
                        }
                }
@@ -4183,8 +4453,8 @@ RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operat
                                                                      physical_screen_height (_editor->get_window())));
        _drag_rect->hide ();
 
-       _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
-       _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
+       _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
+       _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
 }
 
 void
@@ -4201,6 +4471,7 @@ RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
        }
 
        switch (_operation) {
+       case CreateSkipMarker:
        case CreateRangeMarker:
        case CreateTransportMarker:
        case CreateCDMarker:
@@ -4227,6 +4498,9 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
        ArdourCanvas::Rectangle *crect;
 
        switch (_operation) {
+       case CreateSkipMarker:
+               crect = _editor->range_bar_drag_rect;
+               break;
        case CreateRangeMarker:
                crect = _editor->range_bar_drag_rect;
                break;
@@ -4244,7 +4518,7 @@ RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
 
        framepos_t const pf = adjusted_current_frame (event);
 
-       if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
+       if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
                framepos_t grab = grab_frame ();
                _editor->snap_to (grab);
 
@@ -4300,17 +4574,24 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
                _drag_rect->hide();
 
                switch (_operation) {
+               case CreateSkipMarker:
                case CreateRangeMarker:
                case CreateCDMarker:
                    {
-                       _editor->begin_reversible_command (_("new range marker"));
                        XMLNode &before = _editor->session()->locations()->get_state();
-                       _editor->session()->locations()->next_available_name(rangename,"unnamed");
-                       if (_operation == CreateCDMarker) {
+                       if (_operation == CreateSkipMarker) {
+                               _editor->begin_reversible_command (_("new skip marker"));
+                               _editor->session()->locations()->next_available_name(rangename,_("skip"));
+                               flags = Location::IsRangeMarker | Location::IsSkip;
+                               _editor->range_bar_drag_rect->hide();
+                       } else if (_operation == CreateCDMarker) {
+                               _editor->session()->locations()->next_available_name(rangename, _("CD"));
+                               _editor->begin_reversible_command (_("new CD marker"));
                                flags = Location::IsRangeMarker | Location::IsCDMarker;
                                _editor->cd_marker_bar_drag_rect->hide();
-                       }
-                       else {
+                       } else {
+                               _editor->begin_reversible_command (_("new skip marker"));
+                               _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
                                flags = Location::IsRangeMarker;
                                _editor->range_bar_drag_rect->hide();
                        }
@@ -4346,7 +4627,7 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
                        /* didn't drag, but mark is already created so do
                         * nothing */
 
-               } else { /* operation == CreateRangeMarker */
+               } else { /* operation == CreateRangeMarker || CreateSkipMarker */
                        
 
                        framepos_t start;
@@ -4398,88 +4679,6 @@ RangeMarkerBarDrag::update_item (Location* location)
        _drag_rect->set_x1 (x2);
 }
 
-MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
-       : Drag (e, i)
-       , _zoom_out (false)
-{
-       DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
-}
-
-void
-MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
-{
-       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
-MouseZoomDrag::motion (GdkEvent* event, bool first_move)
-{
-       framepos_t start;
-       framepos_t end;
-
-       framepos_t const pf = adjusted_current_frame (event);
-
-       framepos_t grab = grab_frame ();
-       _editor->snap_to_with_modifier (grab, event);
-
-       /* base start and end on initial click position */
-       if (pf < grab) {
-               start = pf;
-               end = grab;
-       } else {
-               end = pf;
-               start = grab;
-       }
-
-       if (start != end) {
-
-               if (first_move) {
-                       _editor->zoom_rect->show();
-                       _editor->zoom_rect->raise_to_top();
-               }
-
-               _editor->reposition_zoom_rect(start, end);
-
-               show_verbose_cursor_time (pf);
-       }
-}
-
-void
-MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
-{
-       if (movement_occurred) {
-               motion (event, false);
-
-               if (grab_frame() < last_pointer_frame()) {
-                       _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
-               } else {
-                       _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
-               }
-       } else {
-               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 (bool)
-{
-       _editor->zoom_rect->hide ();
-}
-
 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
        : Drag (e, i)
        , _cumulative_dx (0)
@@ -4746,8 +4945,8 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
                                double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
                                double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
 
-                               the_list->add (p, the_list->eval (p));
-                               the_list->add (q, the_list->eval (q));
+                               the_list->editor_add (p, the_list->eval (p));
+                               the_list->editor_add (q, the_list->eval (q));
                        }
 
                        /* same thing for the end */
@@ -4772,8 +4971,8 @@ 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));
-                               the_list->add (q, the_list->eval (q));
+                               the_list->editor_add (p, the_list->eval (p));
+                               the_list->editor_add (q, the_list->eval (q));
                        }
                }
 
@@ -4855,9 +5054,13 @@ AutomationRangeDrag::aborted (bool)
        }
 }
 
-DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
+DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
        : view (v)
+       , initial_time_axis_view (itav)
 {
+       /* note that time_axis_view may be null if the regionview was created
+        * as part of a copy operation.
+        */
        time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
        layer = v->region()->layer ();
        initial_y = v->get_canvas_group()->position().y;
@@ -4932,21 +5135,9 @@ MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* r
 void
 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
 {
-       framepos_t const p = _region_view->region()->position ();
-       double const y = _region_view->midi_view()->y_position ();
-
-       x1 = max ((framepos_t) 0, x1 - p);
-       x2 = max ((framepos_t) 0, x2 - p);
-       y1 = max (0.0, y1 - y);
-       y2 = max (0.0, y2 - y);
-       
        _region_view->update_drag_selection (
-               _editor->sample_to_pixel (x1),
-               _editor->sample_to_pixel (x2),
-               y1,
-               y2,
-               Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
-               );
+               x1, x2, y1, y2,
+               Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
 }
 
 void
@@ -5020,7 +5211,7 @@ NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView
        , _region_view (rv)
        , _drag_rect (0)
 {
-       
+       _note[0] = _note[1] = 0;
 }
 
 NoteCreateDrag::~NoteCreateDrag ()
@@ -5034,7 +5225,7 @@ NoteCreateDrag::grid_frames (framepos_t t) const
        bool success;
        Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
        if (!success) {
-               grid_beats = 1;
+               grid_beats = Evoral::MusicalTime(1);
        }
 
        return _region_view->region_beats_to_region_frames (grid_beats);
@@ -5058,6 +5249,7 @@ NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        }
 
        _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
+       _note[1] = _note[0];
 
        MidiStreamView* sv = _region_view->midi_stream_view ();
        double const x = _editor->sample_to_pixel (_note[0]);
@@ -5073,12 +5265,10 @@ void
 NoteCreateDrag::motion (GdkEvent* event, bool)
 {
        _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
-       double const x = _editor->sample_to_pixel (_note[1]);
-       if (_note[1] > _note[0]) {
-               _drag_rect->set_x1 (x);
-       } else {
-               _drag_rect->set_x0 (x);
-       }
+       double const x0 = _editor->sample_to_pixel (_note[0]);
+       double const x1 = _editor->sample_to_pixel (_note[1]);
+       _drag_rect->set_x0 (std::min(x0, x1));
+       _drag_rect->set_x1 (std::max(x0, x1));
 }
 
 void
@@ -5092,13 +5282,14 @@ NoteCreateDrag::finished (GdkEvent*, bool had_movement)
        framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
 
        framecnt_t const g = grid_frames (start);
-       double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
+       Evoral::MusicalTime const one_tick = Evoral::MusicalTime::ticks(1);
        
        if (_editor->snap_mode() == SnapNormal && length < g) {
-               length = g - one_tick;
+               length = g;
        }
 
-       double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
+       Evoral::MusicalTime length_beats = max (
+               one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
 
        _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
 }
@@ -5213,3 +5404,47 @@ CrossfadeEdgeDrag::aborted (bool)
        }
 }
 
+RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
+       : Drag (e, item, true)
+       , line (new EditorCursor (*e))
+{
+       line->set_position (pos);
+       line->show ();
+}
+
+RegionCutDrag::~RegionCutDrag ()
+{
+       delete line;
+}
+
+void
+RegionCutDrag::motion (GdkEvent*, bool)
+{
+       framepos_t where = _drags->current_pointer_frame();
+       _editor->snap_to (where);
+
+       line->set_position (where);
+}
+
+void
+RegionCutDrag::finished (GdkEvent*, bool)
+{
+       _editor->get_track_canvas()->canvas()->re_enter();
+
+       framepos_t pos = _drags->current_pointer_frame();
+       
+       line->hide ();
+
+       RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
+
+       if (rs.empty()) {
+               return;
+       }
+
+       _editor->split_regions_at (pos, rs);
+}
+
+void
+RegionCutDrag::aborted (bool)
+{
+}