Fix note resize double undo bug.
[ardour.git] / gtk2_ardour / editor_drag.cc
index ca41fa35cec572a52e7f07372f4f83b513402301..34e169b133c2de77e7e962609454c5fdbf80661c 100644 (file)
 #include "ardour/region_factory.h"
 #include "ardour/session.h"
 
+#include "canvas/canvas.h"
 #include "canvas/scroll_group.h"
 
 #include "editor.h"
 #include "i18n.h"
 #include "keyboard.h"
 #include "audio_region_view.h"
+#include "automation_region_view.h"
 #include "midi_region_view.h"
 #include "ardour_ui.h"
 #include "gui_thread.h"
@@ -94,6 +96,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;
@@ -271,7 +275,7 @@ Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
        } else {
                /* CAIROCANVAS need a variant here that passes *cursor */
                _item->grab ();
-               _editor->push_canvas_cursor (cursor);
+               _cursor_ctx = CursorContext::create(*_editor, cursor);
        }
 
        if (_editor->session() && _editor->session()->transport_rolling()) {
@@ -308,7 +312,7 @@ Drag::end_grab (GdkEvent* event)
        finished (event, _move_threshold_passed);
 
        _editor->verbose_cursor()->hide ();
-       _editor->pop_canvas_cursor ();
+       _cursor_ctx.reset();
 
        return _move_threshold_passed;
 }
@@ -335,6 +339,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
 {
@@ -350,7 +360,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;
        }
@@ -831,6 +841,12 @@ RegionMoveDrag::motion (GdkEvent* event, bool first_move)
 {
        if (_copy && first_move) {
 
+               if (_x_constrained) {
+                       _editor->begin_reversible_command (Operations::fixed_time_region_copy);
+               } else {
+                       _editor->begin_reversible_command (Operations::region_copy);
+               }
+
                /* duplicate the regionview(s) and region(s) */
 
                list<DraggingView> new_regionviews;
@@ -881,6 +897,14 @@ RegionMoveDrag::motion (GdkEvent* event, bool first_move)
 
                        swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
                }
+
+       } else if (!_copy && first_move) {
+
+               if (_x_constrained) {
+                       _editor->begin_reversible_command (_("fixed time region drag"));
+               } else {
+                       _editor->begin_reversible_command (Operations::region_drag);
+               }
        }
 
        RegionMotionDrag::motion (event, first_move);
@@ -1011,12 +1035,6 @@ RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed
                return;
        }
 
-       if (_x_constrained) {
-               _editor->begin_reversible_command (Operations::fixed_time_region_copy);
-       } else {
-               _editor->begin_reversible_command (Operations::region_copy);
-       }
-
        /* insert the regions into their new playlists */
        for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
 
@@ -1093,12 +1111,6 @@ RegionMoveDrag::finished_no_copy (
                return;
        }
 
-       if (_x_constrained) {
-               _editor->begin_reversible_command (_("fixed time region drag"));
-       } else {
-               _editor->begin_reversible_command (Operations::region_drag);
-       }
-
        for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
 
                RegionView* rv = i->view;
@@ -1788,6 +1800,15 @@ 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;
        }
 
@@ -1912,25 +1933,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) {
@@ -1951,11 +1963,16 @@ NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
        */
        region->note_selected (cnote, cnote->selected ());
 
+       _editor->begin_reversible_command (_("resize notes"));
+
        for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
                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;
        }
 }
@@ -1967,7 +1984,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);
+               }
        }
 }
 
@@ -1978,8 +1998,13 @@ 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);
+               }
        }
+
+       _editor->commit_reversible_command ();
 }
 
 void
@@ -1987,7 +2012,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 ();
+               }
        }
 }
 
@@ -2571,7 +2599,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())
                );
@@ -2589,6 +2617,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);
 }
@@ -2701,7 +2730,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())
                        );
@@ -2740,7 +2769,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);
@@ -3377,7 +3406,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);
                                }
@@ -3390,7 +3419,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);
                                }
@@ -3610,7 +3639,7 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
        }
 
        _point->line().end_drag (_pushing, _final_index);
-       _editor->session()->commit_reversible_command ();
+       _editor->commit_reversible_command ();
 }
 
 void
@@ -3622,8 +3651,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;
        }
 
@@ -3724,7 +3753,7 @@ LineDrag::finished (GdkEvent* event, bool movement_occured)
                }
        }
 
-       _editor->session()->commit_reversible_command ();
+       _editor->commit_reversible_command ();
 }
 
 void
@@ -3831,10 +3860,10 @@ RubberbandSelectDrag::motion (GdkEvent* event, bool)
        double y1;
        double y2;
 
-       framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
+       framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ());
 
        framepos_t grab = grab_frame ();
-       if (Config->get_rubberbanding_snaps_to_grid ()) {
+       if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
                _editor->snap_to_with_modifier (grab, event);
        }
 
@@ -3946,7 +3975,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;
@@ -4088,9 +4117,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");
@@ -4197,9 +4224,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);
                        }
                }
 
@@ -4236,9 +4263,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();
@@ -4246,26 +4280,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;
@@ -4337,6 +4384,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
 {
        Session* s = _editor->session();
 
+       _editor->begin_reversible_selection_op (_("Change Time Selection"));
        if (movement_occurred) {
                motion (event, false);
                /* XXX this is not object-oriented programming at all. ick */
@@ -4349,7 +4397,7 @@ 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_follow_edits() && !s->transport_rolling()) {
+                               if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
                                        if (_operation == SelectionEndTrim)
                                                _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
                                        else
@@ -4393,6 +4441,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
 
        _editor->stop_canvas_autoscroll ();
        _editor->clicked_selection = 0;
+       _editor->commit_reversible_selection_op ();
 }
 
 void
@@ -4413,8 +4462,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
@@ -4431,6 +4480,7 @@ RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
        }
 
        switch (_operation) {
+       case CreateSkipMarker:
        case CreateRangeMarker:
        case CreateTransportMarker:
        case CreateCDMarker:
@@ -4457,6 +4507,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;
@@ -4474,7 +4527,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);
 
@@ -4530,17 +4583,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();
                        }
@@ -4576,7 +4636,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;
@@ -4628,88 +4688,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)
@@ -4745,6 +4723,9 @@ NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
                        } else {
                                _region->unique_select (_primary);
                        }
+
+                       _editor->begin_reversible_selection_op(_("Select Note Press"));
+                       _editor->commit_reversible_selection_op();
                }
        }
 }
@@ -4826,14 +4807,17 @@ NoteDrag::finished (GdkEvent* ev, bool moved)
 {
        if (!moved) {
                /* no motion - select note */
-               
+
                if (_editor->current_mouse_mode() == Editing::MouseObject ||
                    _editor->current_mouse_mode() == Editing::MouseDraw) {
-                       
+
+                       bool changed = false;
+
                        if (_was_selected) {
                                bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
                                if (add) {
                                        _region->note_deselected (_primary);
+                                       changed = true;
                                }
                        } else {
                                bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
@@ -4841,12 +4825,19 @@ NoteDrag::finished (GdkEvent* ev, bool moved)
 
                                if (!extend && !add && _region->selection_size() > 1) {
                                        _region->unique_select (_primary);
+                                       changed = true;
                                } else if (extend) {
                                        _region->note_selected (_primary, true, true);
+                                       changed = true;
                                } else {
                                        /* it was added during button press */
                                }
                        }
+
+                       if (changed) {
+                               _editor->begin_reversible_selection_op(_("Select Note Release"));
+                               _editor->commit_reversible_selection_op();
+                       }
                }
        } else {
                _region->note_dropped (_primary, total_dx(), total_dy());
@@ -4863,24 +4854,36 @@ NoteDrag::aborted (bool)
 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
        : Drag (editor, atv->base_item ())
        , _ranges (r)
+       , _y_origin (atv->y_position())
        , _nothing_to_drag (false)
 {
        DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
-       y_origin = atv->y_position();
        setup (atv->lines ());
 }
 
-/** Make an AutomationRangeDrag for region gain lines */
-AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
+/** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
+AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
        : Drag (editor, rv->get_canvas_group ())
        , _ranges (r)
+       , _y_origin (rv->get_time_axis_view().y_position())
        , _nothing_to_drag (false)
+       , _integral (false)
 {
        DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
 
        list<boost::shared_ptr<AutomationLine> > lines;
-       lines.push_back (rv->get_gain_line ());
-       y_origin = rv->get_time_axis_view().y_position();
+
+       AudioRegionView*      audio_view;
+       AutomationRegionView* automation_view;
+       if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
+               lines.push_back (audio_view->get_gain_line ());
+       } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
+               lines.push_back (automation_view->line ());
+               _integral = true;
+       } else {
+               error << _("Automation range drag created for invalid region type") << endmsg;
+       }
+
        setup (lines);
 }
 
@@ -4925,7 +4928,14 @@ AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lin
 double
 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
 {
-       return 1.0 - ((global_y - y_origin) / line->height());
+       return 1.0 - ((global_y - _y_origin) / line->height());
+}
+
+double
+AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
+{
+       const double v = list->eval(x);
+       return _integral ? rint(v) : v;
 }
 
 void
@@ -4976,8 +4986,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, value (the_list, p));
+                               the_list->editor_add (q, value (the_list, q));
                        }
 
                        /* same thing for the end */
@@ -5002,8 +5012,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, value (the_list, p));
+                               the_list->editor_add (q, value (the_list, q));
                        }
                }
 
@@ -5074,7 +5084,7 @@ AutomationRangeDrag::finished (GdkEvent* event, bool)
                i->line->end_drag (false, 0);
        }
 
-       _editor->session()->commit_reversible_command ();
+       _editor->commit_reversible_command ();
 }
 
 void
@@ -5166,21 +5176,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
@@ -5232,21 +5230,26 @@ EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, fram
        }
        
        Selection::Operation op = ArdourKeyboard::selection_type (button_state);
-       
-       _editor->begin_reversible_command (_("rubberband selection"));
+
+       _editor->begin_reversible_selection_op (_("rubberband selection"));
+
        _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
-       _editor->commit_reversible_command ();
+
+       _editor->commit_reversible_selection_op ();
 }
 
 void
 EditorRubberbandSelectDrag::deselect_things ()
 {
-       if (!getenv("ARDOUR_SAE")) {
-               _editor->selection->clear_tracks();
-       }
+       _editor->begin_reversible_selection_op (_("Clear Selection (rubberband)"));
+
+       _editor->selection->clear_tracks();
        _editor->selection->clear_regions();
        _editor->selection->clear_points ();
        _editor->selection->clear_lines ();
+       _editor->selection->clear_midi_notes ();
+
+       _editor->commit_reversible_selection_op();
 }
 
 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
@@ -5254,7 +5257,7 @@ NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView
        , _region_view (rv)
        , _drag_rect (0)
 {
-       
+       _note[0] = _note[1] = 0;
 }
 
 NoteCreateDrag::~NoteCreateDrag ()
@@ -5266,9 +5269,9 @@ framecnt_t
 NoteCreateDrag::grid_frames (framepos_t t) const
 {
        bool success;
-       Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
+       Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
        if (!success) {
-               grid_beats = 1;
+               grid_beats = Evoral::Beats(1);
        }
 
        return _region_view->region_beats_to_region_frames (grid_beats);
@@ -5292,6 +5295,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]);
@@ -5307,12 +5311,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
@@ -5326,13 +5328,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::Beats const one_tick = Evoral::Beats::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::Beats 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);
 }
@@ -5447,21 +5450,44 @@ CrossfadeEdgeDrag::aborted (bool)
        }
 }
 
-RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item)
+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->split_region ();
+       _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