Snap to note start rather than mouse pointer when dragging MIDI notes. Fixes #3187.
authorCarl Hetherington <carl@carlh.net>
Thu, 19 Aug 2010 22:01:10 +0000 (22:01 +0000)
committerCarl Hetherington <carl@carlh.net>
Thu, 19 Aug 2010 22:01:10 +0000 (22:01 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@7654 d708f5d6-7413-0410-9779-e7cbd77b26cf

gtk2_ardour/editor_drag.cc
gtk2_ardour/editor_drag.h
gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_region_view.h

index ca85ae809e385b420e8883ba37b3b15043dc8025..0d8736b5fb28af77953b36bddce92612efb1caad 100644 (file)
@@ -3751,9 +3751,12 @@ MouseZoomDrag::aborted ()
 
 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
        : Drag (e, i)
+       , _cumulative_dx (0)
+       , _cumulative_dy (0)
 {
-       CanvasNoteEvent*     cnote = dynamic_cast<CanvasNoteEvent*>(_item);
-       region = &cnote->region_view();
+       _primary = dynamic_cast<CanvasNoteEvent*> (_item);
+       _region = &_primary->region_view ();
+       _note_height = _region->midi_stream_view()->note_height ();
 }
 
 void
@@ -3761,23 +3764,7 @@ NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 {
        Drag::start_grab (event);
 
-       drag_delta_x = 0;
-       drag_delta_note = 0;
-
-       double event_x;
-       double event_y;
-
-       event_x = _drags->current_pointer_x();
-       event_y = _drags->current_pointer_y();
-
-       _item->property_parent().get_value()->w2i(event_x, event_y);
-
-       last_x = region->snap_to_pixel(event_x);
-       last_y = event_y;
-
-       CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
-
-       if (!(was_selected = cnote->selected())) {
+       if (!(_was_selected = _primary->selected())) {
 
                /* tertiary-click means extend selection - we'll do that on button release,
                   so don't add it here, because otherwise we make it hard to figure
@@ -3790,79 +3777,73 @@ NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
                        bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
 
                        if (add) {
-                               region->note_selected (cnote, true);
+                               _region->note_selected (_primary, true);
                        } else {
-                               region->unique_select (cnote);
+                               _region->unique_select (_primary);
                        }
                }
        }
 }
 
-void
-NoteDrag::motion (GdkEvent*, bool)
+/** @return Current total drag x change in frames */
+frameoffset_t
+NoteDrag::total_dx () const
 {
-       MidiStreamView* streamview = region->midi_stream_view();
-       double event_x;
-       double event_y;
-
-       event_x = _drags->current_pointer_x();
-       event_y = _drags->current_pointer_y();
+       /* dx in frames */
+       frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
 
-       _item->property_parent().get_value()->w2i(event_x, event_y);
-
-       event_x = region->snap_to_pixel(event_x);
+       /* primary note time */
+       frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
+       
+       /* new time of the primary note relative to the region position */
+       frameoffset_t const st = n + dx;
 
-       double dx     = event_x - last_x;
-       double dy     = event_y - last_y;
-       last_x = event_x;
+       /* snap and return corresponding delta */
+       return _region->snap_frame_to_frame (st) - n;
+}
 
-       drag_delta_x += dx;
+/** @return Current total drag y change in notes */
+int8_t
+NoteDrag::total_dy () const
+{
+       /* this is `backwards' to make increasing note number go in the right direction */
+       double const dy = _drags->current_pointer_y() - grab_y();
 
-       // Snap to note rows
+       /* dy in notes */
+       int8_t ndy = 0;
 
-       if (abs (dy) < streamview->note_height()) {
-               dy = 0.0;
-       } else {
-               int8_t this_delta_note;
+       if (abs (dy) >= _note_height) {
                if (dy > 0) {
-                       this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
+                       ndy = (int8_t) ceil (dy / _note_height / 2.0);
                } else {
-                       this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
-               }
-               drag_delta_note -= this_delta_note;
-               dy = streamview->note_height() * this_delta_note;
-               last_y = last_y + dy;
-       }
-
-       if (dx || dy) {
-                
-               CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
-                Evoral::MusicalTime new_time;
-                
-                if (drag_delta_x) {
-                        nframes64_t start_frames = region->beats_to_frames(cnote->note()->time());
-                        if (drag_delta_x >= 0) {
-                                start_frames += region->snap_frame_to_frame(_editor->pixel_to_frame(drag_delta_x));
-                        } else {
-                                start_frames -= region->snap_frame_to_frame(_editor->pixel_to_frame(-drag_delta_x));
-                        }
-                        new_time = region->frames_to_beats(start_frames);
-                } else {
-                        new_time = cnote->note()->time();
-                }
-                
-                boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > check_note (
-                        new Evoral::Note<Evoral::MusicalTime> (cnote->note()->channel(), 
-                                                               new_time,
-                                                               cnote->note()->length(), 
-                                                               cnote->note()->note() + drag_delta_note,
-                                                               cnote->note()->velocity()));
+                       ndy = (int8_t) floor (dy / _note_height / 2.0);
+               }
+       }
 
-               region->move_selection (dx, dy);
+       return ndy;
+}
+       
+
+void
+NoteDrag::motion (GdkEvent *, bool)
+{
+       /* Total change in x and y since the start of the drag */
+       frameoffset_t const dx = total_dx ();
+       int8_t const dy = total_dy ();
+
+       /* Now work out what we have to do to the note canvas items to set this new drag delta */
+       double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
+       double const tdy = dy * _note_height - _cumulative_dy;
+
+       if (tdx || tdy) {
+               _region->move_selection (tdx, tdy);
+               _cumulative_dx += tdx;
+               _cumulative_dy += tdy;
 
                 char buf[12];
-                snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (cnote->note()->note() + drag_delta_note).c_str(),
-                          (int) floor ((cnote->note()->note() + drag_delta_note)));
+                snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + dy).c_str(),
+                          (int) floor (_primary->note()->note() + dy));
+               
                _editor->show_verbose_canvas_cursor_with (buf);
         }
 }
@@ -3870,31 +3851,29 @@ NoteDrag::motion (GdkEvent*, bool)
 void
 NoteDrag::finished (GdkEvent* ev, bool moved)
 {
-       ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
-
        if (!moved) {
                if (_editor->current_mouse_mode() == Editing::MouseObject) {
 
-                       if (was_selected) {
+                       if (_was_selected) {
                                bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
                                if (add) {
-                                       region->note_deselected (cnote);
+                                       _region->note_deselected (_primary);
                                }
                        } else {
                                bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
                                bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
 
-                               if (!extend && !add && region->selection_size() > 1) {
-                                       region->unique_select(cnote);
+                               if (!extend && !add && _region->selection_size() > 1) {
+                                       _region->unique_select (_primary);
                                } else if (extend) {
-                                       region->note_selected (cnote, true, true);
+                                       _region->note_selected (_primary, true, true);
                                } else {
                                        /* it was added during button press */
                                }
                        }
                }
        } else {
-               region->note_dropped (cnote, drag_delta_x, drag_delta_note);
+               _region->note_dropped (_primary, total_dx(), - total_dy());
        }
 }
 
index 647655e7fb459351ca1a342befbb3b3c12a6a3dd..b194366380c71a6589bb0c08dff55bd9b2331619 100644 (file)
@@ -35,6 +35,12 @@ namespace ARDOUR {
        class Location;
 }
 
+namespace Gnome {
+       namespace Canvas {
+               class CanvasNoteEvent;
+       }
+}
+
 class Editor;
 class EditorCursor;
 class TimeAxisView;
@@ -356,6 +362,7 @@ private:
        bool                at_front;
 };
 
+/** Drags to move MIDI notes */
 class NoteDrag : public Drag
 {
   public:
@@ -367,12 +374,16 @@ class NoteDrag : public Drag
        void aborted ();
 
   private:
-       MidiRegionView* region;
-       double last_x;
-       double last_y;
-       double drag_delta_x;
-       double drag_delta_note;
-       bool   was_selected;
+
+       ARDOUR::frameoffset_t total_dx () const;
+       int8_t total_dy () const;
+       
+       MidiRegionView* _region;
+       Gnome::Canvas::CanvasNoteEvent* _primary;
+       double _cumulative_dx;
+       double _cumulative_dy;
+       bool _was_selected;
+       double _note_height;
 };
 
 /** Drag of region gain */
index a520a22d50cbc1db37af544a22d6bf3127b2b6d9..1ca948f86075590a28475c283449e9753ee7ab35 100644 (file)
@@ -1956,7 +1956,7 @@ MidiRegionView::move_selection(double dx, double dy)
 }
 
 void
-MidiRegionView::note_dropped(CanvasNoteEvent *, double dt, int8_t dnote)
+MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
 {
        assert (!_selection.empty());
 
@@ -1991,15 +1991,7 @@ MidiRegionView::note_dropped(CanvasNoteEvent *, double dt, int8_t dnote)
 
        for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
 
-               nframes64_t start_frames = beats_to_frames((*i)->note()->time());
-
-               if (dt >= 0) {
-                       start_frames += snap_frame_to_frame(trackview.editor().pixel_to_frame(dt));
-               } else {
-                       start_frames -= snap_frame_to_frame(trackview.editor().pixel_to_frame(-dt));
-               }
-
-               Evoral::MusicalTime new_time = frames_to_beats(start_frames);
+               Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
 
                if (new_time < 0) {
                        continue;
index 75ee5f13d8dd817672c86e0e3e155c6ce76eef9e..952393756594f5518d35e079ffe387db197ba866 100644 (file)
@@ -200,7 +200,7 @@ class MidiRegionView : public RegionView
        size_t selection_size() { return _selection.size(); }
 
        void move_selection(double dx, double dy);
-       void note_dropped(ArdourCanvas::CanvasNoteEvent* ev, double d_pixels, int8_t d_note);
+       void note_dropped (ArdourCanvas::CanvasNoteEvent* ev, ARDOUR::frameoffset_t, int8_t d_note);
 
        void select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend);
        void toggle_matching_notes (uint8_t notenum, uint16_t channel_mask);