amend be62d335ee
[ardour.git] / gtk2_ardour / editor_drag.cc
index 815acd3e94ed85f3a6c37d98bd8027fe323f90a8..35e9d395c54bade152c75fa91c3e08dbabc76197 100644 (file)
@@ -40,6 +40,7 @@
 #include "ardour/profile.h"
 #include "ardour/region_factory.h"
 #include "ardour/session.h"
+#include "ardour/session_playlists.h"
 
 #include "canvas/canvas.h"
 #include "canvas/scroll_group.h"
@@ -662,6 +663,11 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, MusicFrame* pending_r
        /* compute the amount of pointer motion in frames, and where
           the region would be if we moved it by that much.
        */
+       if (_x_constrained) {
+               *pending_region_position = _last_position;
+               return 0.0;
+       }
+
        *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
 
        framecnt_t sync_offset;
@@ -1586,7 +1592,7 @@ RegionMoveDrag::finished_no_copy (
        PlaylistSet frozen_playlists;
        set<RouteTimeAxisView*> views_to_update;
        RouteTimeAxisView* new_time_axis_view = 0;
-       framecnt_t const drag_delta = _primary->region()->position() - _last_position.frame;
+       framecnt_t const drag_delta = _primary->region()->position() - last_position.frame;
 
        typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
        PlaylistMapping playlist_mapping;
@@ -2420,14 +2426,16 @@ RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisVi
 void
 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
 {
+
        if (first_move) {
                _editor->begin_reversible_command (_("create region"));
                _region = add_midi_region (_view, false);
                _view->playlist()->freeze ();
        } else {
+
                if (_region) {
                        framepos_t const f = adjusted_current_frame (event);
-                       if (f < grab_frame()) {
+                       if (f <= grab_frame()) {
                                _region->set_initial_position (f);
                        }
 
@@ -2437,9 +2445,10 @@ RegionCreateDrag::motion (GdkEvent* event, bool first_move)
                           a bit confusing as if a region starts 1 frame after a snap point, one cannot
                           place snapped notes at the start of the region.
                        */
-
-                       framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
-                       _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
+                       if (f != grab_frame()) {
+                               framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
+                               _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
+                       }
                }
        }
 }
@@ -2988,6 +2997,24 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                        if (insert_result.second) {
                                pl->freeze();
                        }
+
+                       MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (rv);
+                       /* a MRV start trim may change the source length. ensure we cover all playlists here */
+                       if (mrv && _operation == StartTrim) {
+                               vector<boost::shared_ptr<Playlist> > all_playlists;
+                               _editor->session()->playlists->get (all_playlists);
+                               for (vector<boost::shared_ptr<Playlist> >::iterator x = all_playlists.begin(); x != all_playlists.end(); ++x) {
+
+                                       if ((*x)->uses_source (rv->region()->source(0))) {
+                                               insert_result = _editor->motion_frozen_playlists.insert (*x);
+                                               if (insert_result.second) {
+                                                       (*x)->clear_owned_changes ();
+                                                       (*x)->freeze();
+                                               }
+
+                                       }
+                               }
+                       }
                }
        }
 
@@ -3136,29 +3163,22 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
                if (!_editor->selection->selected (_primary)) {
                        _primary->thaw_after_trim ();
                } else {
-
-                       set<boost::shared_ptr<Playlist> > diffed_playlists;
-
                        for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
                                i->view->thaw_after_trim ();
                                i->view->enable_display (true);
-
-                               /* Trimming one region may affect others on the playlist, so we need
-                                  to get undo Commands from the whole playlist rather than just the
-                                  region.  Use diffed_playlists to make sure we don't diff a given
-                                  playlist more than once.
-                               */
-                               boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
-                               if (diffed_playlists.find (p) == diffed_playlists.end()) {
-                                       vector<Command*> cmds;
-                                       p->rdiff (cmds);
-                                       _editor->session()->add_commands (cmds);
-                                       diffed_playlists.insert (p);
-                               }
                        }
                }
 
                for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
+                       /* Trimming one region may affect others on the playlist, so we need
+                          to get undo Commands from the whole playlist rather than just the
+                          region.  Use motion_frozen_playlists (a set) to make sure we don't
+                          diff a given playlist more than once.
+                       */
+
+                       vector<Command*> cmds;
+                       (*p)->rdiff (cmds);
+                       _editor->session()->add_commands (cmds);
                        (*p)->thaw ();
                }
 
@@ -3364,7 +3384,7 @@ TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
        , _copy (c)
        , _grab_bpm (120.0, 4.0)
        , _grab_qn (0.0)
-       , before_state (0)
+       , _before_state (0)
 {
        DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
 
@@ -3423,7 +3443,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
                _marker->hide();
 
                /* get current state */
-               before_state = &map.get_state();
+               _before_state = &map.get_state();
 
                if (!_copy) {
                        _editor->begin_reversible_command (_("move tempo mark"));
@@ -3451,7 +3471,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
        if (ArdourKeyboard::indicates_constraint (event->button.state) && ArdourKeyboard::indicates_copy (event->button.state)) {
                double new_bpm = max (1.5, _grab_bpm.end_note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
                stringstream strs;
-               _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()), true);
+               _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), new_bpm));
                strs << "end:" << fixed << setprecision(3) << new_bpm;
                show_verbose_cursor_text (strs.str());
 
@@ -3459,7 +3479,7 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
                /* use vertical movement to alter tempo .. should be log */
                double new_bpm = max (1.5, _grab_bpm.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
                stringstream strs;
-               _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()), false);
+               _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type(), _real_section->end_note_types_per_minute()));
                strs << "start:" << fixed << setprecision(3) << new_bpm;
                show_verbose_cursor_text (strs.str());
 
@@ -3501,7 +3521,7 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
        TempoMap& map (_editor->session()->tempo_map());
 
        XMLNode &after = map.get_state();
-       _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
+       _editor->session()->add_command (new MementoCommand<TempoMap>(map, _before_state, &after));
        _editor->commit_reversible_command ();
 
        // delete the dummy marker we used for visual representation while moving.
@@ -3515,7 +3535,7 @@ TempoMarkerDrag::aborted (bool moved)
        _marker->set_position (_marker->tempo().frame());
        if (moved) {
                TempoMap& map (_editor->session()->tempo_map());
-               map.set_state (*before_state, Stateful::current_state_version);
+               map.set_state (*_before_state, Stateful::current_state_version);
                // delete the dummy (hidden) marker we used for events while moving.
                delete _marker;
        }
@@ -3525,7 +3545,7 @@ BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
        : Drag (e, i)
        , _grab_qn (0.0)
        , _tempo (0)
-       , before_state (0)
+       , _before_state (0)
 {
        DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
 
@@ -3540,8 +3560,15 @@ BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        _editor->tempo_curve_selected (_tempo, true);
 
        ostringstream sstr;
-       sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
-       sstr << "mouse: " << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).note_types_per_minute();
+       if (_tempo->clamped()) {
+               TempoSection* prev = map.previous_tempo_section (_tempo);
+               if (prev) {
+                       _editor->tempo_curve_selected (prev, true);
+                       sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
+               }
+       }
+
+       sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
        show_verbose_cursor_text (sstr.str());
 }
 
@@ -3549,6 +3576,9 @@ void
 BBTRulerDrag::setup_pointer_frame_offset ()
 {
        TempoMap& map (_editor->session()->tempo_map());
+       /* get current state */
+       _before_state = &map.get_state();
+
        const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
        const uint32_t divisions = _editor->get_grid_beat_divisions (0);
        double beat = 0.0;
@@ -3575,8 +3605,6 @@ BBTRulerDrag::motion (GdkEvent* event, bool first_move)
        TempoMap& map (_editor->session()->tempo_map());
 
        if (first_move) {
-               /* get current state */
-               before_state = &map.get_state();
                _editor->begin_reversible_command (_("stretch tempo"));
        }
 
@@ -3592,9 +3620,16 @@ BBTRulerDrag::motion (GdkEvent* event, bool first_move)
                /* adjust previous tempo to match pointer frame */
                _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
        }
+
        ostringstream sstr;
-       sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
-       sstr << "mouse: " << fixed << setprecision(3) << map.tempo_at_frame (pf).note_types_per_minute();
+       if (_tempo->clamped()) {
+               TempoSection* prev = map.previous_tempo_section (_tempo);
+               if (prev) {
+                       _editor->tempo_curve_selected (prev, true);
+                       sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
+               }
+       }
+       sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
        show_verbose_cursor_text (sstr.str());
 }
 
@@ -3608,16 +3643,23 @@ BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
        TempoMap& map (_editor->session()->tempo_map());
 
        XMLNode &after = map.get_state();
-       _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
+       _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
        _editor->commit_reversible_command ();
        _editor->tempo_curve_selected (_tempo, false);
+
+       if (_tempo->clamped()) {
+               TempoSection* prev_tempo = map.previous_tempo_section (_tempo);
+               if (prev_tempo) {
+                       _editor->tempo_curve_selected (prev_tempo, false);
+               }
+       }
 }
 
 void
 BBTRulerDrag::aborted (bool moved)
 {
        if (moved) {
-               _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
+               _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
        }
 }
 
@@ -3626,7 +3668,9 @@ TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i)
        , _grab_qn (0.0)
        , _grab_tempo (0.0)
        , _tempo (0)
-       , before_state (0)
+       , _next_tempo (0)
+       , _drag_valid (true)
+       , _before_state (0)
 {
        DEBUG_TRACE (DEBUG::Drags, "New TempoTwistDrag\n");
 
@@ -3637,13 +3681,31 @@ TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 {
        Drag::start_grab (event, cursor);
        TempoMap& map (_editor->session()->tempo_map());
+       /* get current state */
+       _before_state = &map.get_state();
        _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
-       _grab_tempo = Tempo (_tempo->note_types_per_minute(), _tempo->note_type());
 
-       ostringstream sstr;
-       sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
-       sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute();
-       show_verbose_cursor_text (sstr.str());
+       _next_tempo = map.next_tempo_section (_tempo);
+       if (_next_tempo) {
+               if (!map.next_tempo_section (_next_tempo)) {
+                       _drag_valid = false;
+                       finished (event, false);
+
+                       return;
+               }
+               _editor->tempo_curve_selected (_tempo, true);
+               _editor->tempo_curve_selected (_next_tempo, true);
+
+               ostringstream sstr;
+               sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
+               sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
+               sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
+               show_verbose_cursor_text (sstr.str());
+       } else {
+               _drag_valid = false;
+       }
+
+       _grab_tempo = Tempo (_tempo->note_types_per_minute(), _tempo->note_type());
 }
 
 void
@@ -3673,11 +3735,14 @@ TempoTwistDrag::setup_pointer_frame_offset ()
 void
 TempoTwistDrag::motion (GdkEvent* event, bool first_move)
 {
+
+       if (!_next_tempo || !_drag_valid) {
+               return;
+       }
+
        TempoMap& map (_editor->session()->tempo_map());
 
        if (first_move) {
-               /* get current state */
-               before_state = &map.get_state();
                _editor->begin_reversible_command (_("twist tempo"));
        }
 
@@ -3694,22 +3759,26 @@ TempoTwistDrag::motion (GdkEvent* event, bool first_move)
        _editor->session()->tempo_map().gui_twist_tempi (_tempo, new_bpm, map.frame_at_quarter_note (_grab_qn), pf);
 
        ostringstream sstr;
-       sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
-       sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute();
+       sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
+       sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
+       sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
        show_verbose_cursor_text (sstr.str());
 }
 
 void
 TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
 {
-       if (!movement_occurred) {
+       TempoMap& map (_editor->session()->tempo_map());
+
+       if (!movement_occurred || !_drag_valid) {
                return;
        }
 
-       TempoMap& map (_editor->session()->tempo_map());
+       _editor->tempo_curve_selected (_tempo, false);
+       _editor->tempo_curve_selected (_next_tempo, false);
 
        XMLNode &after = map.get_state();
-       _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
+       _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
        _editor->commit_reversible_command ();
 }
 
@@ -3717,7 +3786,7 @@ void
 TempoTwistDrag::aborted (bool moved)
 {
        if (moved) {
-               _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
+               _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
        }
 }
 
@@ -3725,22 +3794,37 @@ TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i)
        : Drag (e, i)
        , _grab_qn (0.0)
        , _tempo (0)
-       , before_state (0)
+       , _before_state (0)
 {
        DEBUG_TRACE (DEBUG::Drags, "New TempoEndDrag\n");
-
+       TempoMarker* marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
+       _tempo = &marker->tempo();
+       _grab_qn = _tempo->pulse() * 4.0;
 }
 
 void
 TempoEndDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 {
        Drag::start_grab (event, cursor);
-       TempoMap& map (_editor->session()->tempo_map());
-       _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
-       _editor->tempo_curve_selected (&map.tempo_section_at_frame (_tempo->frame() - 1), true);
+       TempoMap& tmap (_editor->session()->tempo_map());
+
+       /* get current state */
+       _before_state = &tmap.get_state();
+
 
        ostringstream sstr;
-       sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
+
+       TempoSection* prev = 0;
+       if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
+               _editor->tempo_curve_selected (tmap.previous_tempo_section (_tempo), true);
+               sstr << "end: " << fixed << setprecision(3) << tmap.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
+       }
+
+       if (_tempo->clamped()) {
+               _editor->tempo_curve_selected (_tempo, true);
+               sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
+       }
+
        show_verbose_cursor_text (sstr.str());
 }
 
@@ -3748,21 +3832,6 @@ void
 TempoEndDrag::setup_pointer_frame_offset ()
 {
        TempoMap& map (_editor->session()->tempo_map());
-       const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
-       const uint32_t divisions = _editor->get_grid_beat_divisions (0);
-       double beat = 0.0;
-
-       if (divisions > 0) {
-               beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
-       } else {
-               /* while it makes some sense for the user to determine the division to 'grab',
-                  grabbing a bar often leads to confusing results wrt the actual tempo section being altered
-                  and the result over steep tempo curves. Use sixteenths.
-               */
-               beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
-       }
-
-       _grab_qn = map.quarter_note_at_beat (beat);
 
        _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
 
@@ -3774,8 +3843,6 @@ TempoEndDrag::motion (GdkEvent* event, bool first_move)
        TempoMap& map (_editor->session()->tempo_map());
 
        if (first_move) {
-               /* get current state */
-               before_state = &map.get_state();
                _editor->begin_reversible_command (_("stretch end tempo"));
        }
 
@@ -3785,7 +3852,12 @@ TempoEndDrag::motion (GdkEvent* event, bool first_move)
        map.gui_stretch_tempo_end (&map.tempo_section_at_frame (_tempo->frame() - 1), map.frame_at_quarter_note (_grab_qn), pf);
 
        ostringstream sstr;
-       sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute();
+       sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
+
+       if (_tempo->clamped()) {
+               sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
+       }
+
        show_verbose_cursor_text (sstr.str());
 }
 
@@ -3796,19 +3868,28 @@ TempoEndDrag::finished (GdkEvent* event, bool movement_occurred)
                return;
        }
 
-       TempoMap& map (_editor->session()->tempo_map());
+       TempoMap& tmap (_editor->session()->tempo_map());
 
-       XMLNode &after = map.get_state();
-       _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
+       XMLNode &after = tmap.get_state();
+       _editor->session()->add_command(new MementoCommand<TempoMap>(tmap, _before_state, &after));
        _editor->commit_reversible_command ();
-       _editor->tempo_curve_selected (&map.tempo_section_at_frame (_tempo->frame() - 1), false);
+
+       TempoSection* prev = 0;
+       if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
+               _editor->tempo_curve_selected (prev, false);
+       }
+
+       if (_tempo->clamped()) {
+               _editor->tempo_curve_selected (_tempo, false);
+
+       }
 }
 
 void
 TempoEndDrag::aborted (bool moved)
 {
        if (moved) {
-               _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
+               _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
        }
 }
 
@@ -3881,7 +3962,7 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
                }
 
 
-               if (AudioEngine::instance()->connected()) {
+               if (AudioEngine::instance()->running()) {
 
                        /* do this only if we're the engine is connected
                         * because otherwise this request will never be
@@ -3891,7 +3972,7 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
                         */
 
                        s->request_suspend_timecode_transmission ();
-                       while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
+                       while (AudioEngine::instance()->running() && !s->timecode_transmission_suspended ()) {
                                /* twiddle our thumbs */
                        }
                }
@@ -6619,7 +6700,10 @@ NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
        const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
        Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
 
+       _editor->begin_reversible_command (_("Create Note"));
+       _region_view->clear_editor_note_selection();
        _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
+       _editor->commit_reversible_command ();
 }
 
 double
@@ -6640,7 +6724,7 @@ HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView*
        : Drag (e, i)
        , _region_view (rv)
        , _last_pos (0)
-       , _last_y (0.0)
+       , _y (0.0)
 {
 }
 
@@ -6655,6 +6739,8 @@ HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
        TempoMap& map (_editor->session()->tempo_map());
 
+       _y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
+
        const framepos_t pf = _drags->current_pointer_frame ();
        const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
 
@@ -6667,14 +6753,13 @@ HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        }
 
        const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
-       const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
-
        Evoral::Beats length = _region_view->get_grid_beats (pf);
 
-       _region_view->create_note_at (start, y, length, event->button.state, false);
+       _editor->begin_reversible_command (_("Create Hit"));
+       _region_view->clear_editor_note_selection();
+       _region_view->create_note_at (start, _y, length, event->button.state, false);
 
        _last_pos = start;
-       _last_y = y;
 }
 
 void
@@ -6691,28 +6776,28 @@ HitCreateDrag::motion (GdkEvent* event, bool)
 
        const double eqaf = map.exact_qn_at_frame (pf, divisions);
        const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
-       const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
 
-       if (_last_pos == start && y == _last_y) {
+       if (_last_pos == start) {
                return;
        }
 
        Evoral::Beats length = _region_view->get_grid_beats (pf);
 
        boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
+
        if (eqaf >= mr->quarter_note() + mr->length_beats()) {
                return;
        }
 
-       _region_view->create_note_at (start, y, length, event->button.state, false);
+       _region_view->create_note_at (start, _y, length, event->button.state, false);
 
        _last_pos = start;
-       _last_y = y;
 }
 
 void
 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
 {
+       _editor->commit_reversible_command ();
 
 }