avoid doing needless duplicate work in Selection::add (TimeAxisView*)
[ardour.git] / gtk2_ardour / editor_drag.cc
index ac761dcb6cb13190ab9971d4933298677a961e2a..ff3b03221037f36055fd152c76b31960ef0cf7d8 100644 (file)
@@ -238,6 +238,7 @@ Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
        , _grab_frame (0)
        , _last_pointer_frame (0)
        , _snap_delta (0)
+       , _snap_delta_music (0.0)
        , _constraint_pressed (false)
 {
 
@@ -355,6 +356,15 @@ Drag::snap_delta (guint state) const
 
        return 0;
 }
+double
+Drag::snap_delta_music (guint state) const
+{
+       if (ArdourKeyboard::indicates_snap_delta (state)) {
+               return _snap_delta_music;
+       }
+
+       return 0.0;
+}
 
 double
 Drag::current_pointer_x() const
@@ -373,11 +383,18 @@ Drag::current_pointer_y () const
 }
 
 void
-Drag::setup_snap_delta (framepos_t pos)
+Drag::setup_snap_delta (MusicFrame pos)
 {
-       MusicFrame snap (pos, 0);
+       TempoMap& map (_editor->session()->tempo_map());
+       MusicFrame snap (pos);
        _editor->snap_to (snap, ARDOUR::RoundNearest, false, true);
-       _snap_delta = snap.frame - pos;
+       _snap_delta = snap.frame - pos.frame;
+
+       _snap_delta_music = 0.0;
+
+       if (_snap_delta != 0) {
+               _snap_delta_music = map.exact_qn_at_frame (snap.frame, snap.division) - map.exact_qn_at_frame (pos.frame, pos.division);
+       }
 }
 
 bool
@@ -618,7 +635,7 @@ void
 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 {
        Drag::start_grab (event, cursor);
-       setup_snap_delta (_last_position.frame);
+       setup_snap_delta (_last_position);
 
        show_verbose_cursor_time (_last_position.frame);
 
@@ -928,8 +945,13 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
        }
 
        /* Work out the change in x */
+       TempoMap& tmap = _editor->session()->tempo_map();
        MusicFrame pending_region_position (0, 0);
        double const x_delta = compute_x_delta (event, &pending_region_position);
+
+       double const last_pos_qn = tmap.exact_qn_at_frame (_last_position.frame, _last_position.division);
+       double const qn_delta = tmap.exact_qn_at_frame (pending_region_position.frame, pending_region_position.division) - last_pos_qn;
+
        _last_position = pending_region_position;
 
        /* calculate hidden tracks in current y-axis delta */
@@ -1173,7 +1195,15 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
                }
 
                /* Now move the region view */
-               rv->move (x_delta, y_delta);
+               if (rv->region()->position_lock_style() == MusicTime) {
+                       double const last_qn = tmap.quarter_note_at_frame (rv->get_position());
+                       framepos_t const x_pos_music = tmap.frame_at_quarter_note (last_qn + qn_delta);
+
+                       rv->set_position (x_pos_music, 0);
+                       rv->move (0, y_delta);
+               } else {
+                       rv->move (x_delta, y_delta);
+               }
 
        } /* foreach region */
 
@@ -1628,7 +1658,7 @@ RegionMoveDrag::finished_no_copy (
 
                        /* insert into new playlist */
                        RegionView* new_view;
-                       if (rv == _primary) {
+                       if (rv == _primary && !_x_constrained) {
                                new_view = insert_region_into_playlist (
                                        RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, last_position, last_pos_qn,
                                        modified_playlists, true
@@ -2850,7 +2880,7 @@ TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
        framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
 
        framepos_t const pf = adjusted_current_frame (event);
-       setup_snap_delta (region_start);
+       setup_snap_delta (MusicFrame(region_start, 0));
 
        if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
                /* Move the contents of the region around without changing the region bounds */
@@ -3629,7 +3659,7 @@ void
 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
 {
        Drag::start_grab (event, c);
-       setup_snap_delta (_editor->playhead_cursor->current_frame());
+       setup_snap_delta (MusicFrame (_editor->playhead_cursor->current_frame(), 0));
 
        _grab_zoom = _editor->samples_per_pixel;
 
@@ -3637,6 +3667,7 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
 
        _editor->snap_to_with_modifier (where, event);
        _editor->_dragging_playhead = true;
+       _editor->_control_scroll_target = where.frame;
 
        Session* s = _editor->session ();
 
@@ -3732,7 +3763,7 @@ FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
        AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
        boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
-       setup_snap_delta (r->position());
+       setup_snap_delta (MusicFrame (r->position(), 0));
 
        show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
 }
@@ -3858,7 +3889,7 @@ FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
        AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
        boost::shared_ptr<AudioRegion> r = arv->audio_region ();
-       setup_snap_delta (r->last_frame());
+       setup_snap_delta (MusicFrame (r->last_frame(), 0));
 
        show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
 }
@@ -4019,7 +4050,7 @@ MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
        } else {
                show_verbose_cursor_time (location->end());
        }
-       setup_snap_delta (is_start ? location->start() : location->end());
+       setup_snap_delta (MusicFrame (is_start ? location->start() : location->end(), 0));
 
        Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
 
@@ -4406,7 +4437,7 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
        _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
        _fixed_grab_y = _point->get_y();
 
-       setup_snap_delta (_editor->pixel_to_sample (_fixed_grab_x));
+       setup_snap_delta (MusicFrame (_editor->pixel_to_sample (_fixed_grab_x), 0));
 
        float const fraction = 1 - (_point->get_y() / _point->line().height());
        show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
@@ -4904,10 +4935,10 @@ TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
 
        _editor->get_selection().add (_primary);
 
-       framepos_t where = _primary->region()->position();
+       MusicFrame where (_primary->region()->position(), 0);
        setup_snap_delta (where);
 
-       show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
+       show_verbose_cursor_duration (where.frame, adjusted_current_frame (event), 0);
 }
 
 void
@@ -5196,11 +5227,17 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                        //( 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;
+                       vector<RouteGroup*> selected_route_groups;
 
                        if (!first_move) {
                                for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i) {
                                        if (!new_selection.contains (*i) && !_track_selection_at_start.contains (*i)) {
                                                tracks_to_remove.push_back (*i);
+                                       } else {
+                                               RouteGroup* rg = (*i)->route_group();
+                                               if (rg && rg->is_active() && rg->is_select()) {
+                                                       selected_route_groups.push_back (rg);
+                                               }
                                        }
                                }
                        }
@@ -5208,12 +5245,35 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
                        for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
                                if (!_editor->selection->tracks.contains (*i)) {
                                        tracks_to_add.push_back (*i);
+                                       RouteGroup* rg = (*i)->route_group();
+
+                                       if (rg && rg->is_active() && rg->is_select()) {
+                                               selected_route_groups.push_back (rg);
+                                       }
                                }
                        }
 
                        _editor->selection->add (tracks_to_add);
 
                        if (!tracks_to_remove.empty()) {
+
+                               /* check all these to-be-removed tracks against the
+                                * possibility that they are selected by being
+                                * in the same group as an approved track.
+                                */
+
+                               for (TrackViewList::iterator i = tracks_to_remove.begin(); i != tracks_to_remove.end(); ) {
+                                       RouteGroup* rg = (*i)->route_group();
+
+                                       if (rg && find (selected_route_groups.begin(), selected_route_groups.end(), rg) != selected_route_groups.end()) {
+                                               i = tracks_to_remove.erase (i);
+                                       } else {
+                                               ++i;
+                                       }
+                               }
+
+                               /* remove whatever is left */
+
                                _editor->selection->remove (tracks_to_remove);
                        }
                }
@@ -5615,6 +5675,7 @@ NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
        : Drag (e, i)
        , _cumulative_dx (0)
        , _cumulative_dy (0)
+       , _earliest (0.0)
        , _was_selected (false)
        , _copy (false)
 {
@@ -5626,18 +5687,25 @@ NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
        _note_height = _region->midi_stream_view()->note_height ();
 }
 
+void
+NoteDrag::setup_pointer_frame_offset ()
+{
+       _pointer_frame_offset = raw_grab_frame()
+               - _editor->session()->tempo_map().frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
+}
+
 void
 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
 {
        Drag::start_grab (event);
 
-       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
+       if (ArdourKeyboard::indicates_copy (event->button.state)) {
                _copy = true;
        } else {
                _copy = false;
        }
 
-       setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
+       setup_snap_delta (MusicFrame (_region->source_beats_to_absolute_frames (_primary->note()->time ()), 0));
 
        if (!(_was_selected = _primary->selected())) {
 
@@ -5661,55 +5729,38 @@ NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
        }
 }
 
-/** @return Current total drag x change in frames */
-frameoffset_t
-NoteDrag::total_dx (const guint state) const
+/** @return Current total drag x change in quarter notes */
+double
+NoteDrag::total_dx (GdkEvent * event) const
 {
        if (_x_constrained) {
                return 0;
        }
+
        TempoMap& map (_editor->session()->tempo_map());
 
        /* dx in frames */
        frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
 
        /* primary note time */
-       double const quarter_note_start = _region->region()->quarter_note() - _region->midi_region()->start_beats();
-       frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double());
-
-       /* new time of the primary note in session frames */
-       frameoffset_t st = n + dx + snap_delta (state);
+       frameoffset_t const n = map.frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
 
-       framepos_t const rp = _region->region()->position ();
+       /* primary note time in quarter notes */
+       double const n_qn = _region->session_relative_qn (_primary->note()->time().to_double());
 
-       /* prevent the note being dragged earlier than the region's position */
-       st = max (st, rp);
-
-       /* possibly snap and return corresponding delta */
+       /* new time of the primary note in session frames */
+       frameoffset_t st = n + dx + snap_delta (event->button.state);
 
-       bool snap = true;
+       /* possibly snap and return corresponding delta in quarter notes */
+       MusicFrame snap (st, 0);
+       _editor->snap_to_with_modifier (snap, event);
+       double ret = map.exact_qn_at_frame (snap.frame, snap.division) - n_qn - snap_delta_music (event->button.state);
 
-       if (ArdourKeyboard::indicates_snap (state)) {
-               if (_editor->snap_mode () != SnapOff) {
-                       snap = false;
-               }
-       } else {
-               if (_editor->snap_mode () == SnapOff) {
-                       snap = false;
-                       /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
-                       if (ArdourKeyboard::indicates_snap_delta (state)) {
-                               snap = true;
-                       }
-               }
+       /* prevent the earliest note being dragged earlier than the region's start position */
+       if (_earliest + ret < _region->midi_region()->start_beats()) {
+               ret -= (_earliest + ret) - _region->midi_region()->start_beats();
        }
 
-       frameoffset_t ret;
-       if (snap) {
-               bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
-               ret =  _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
-       } else {
-               ret = st - n - snap_delta (state);
-       }
        return ret;
 }
 
@@ -5735,30 +5786,33 @@ NoteDrag::total_dy () const
 void
 NoteDrag::motion (GdkEvent * event, bool first_move)
 {
-       if (_copy && first_move) {
-               /* make copies of all the selected notes */
-               _primary = _region->copy_selection ();
+       if (first_move) {
+               _earliest = _region->earliest_in_selection().to_double();
+               if (_copy) {
+                       /* make copies of all the selected notes */
+                       _primary = _region->copy_selection (_primary);
+               }
        }
 
        /* Total change in x and y since the start of the drag */
-       frameoffset_t const dx = total_dx (event->button.state);
+       double const dx_qn = total_dx (event);
        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 = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx;
+       double const tdx = _x_constrained ? 0 : dx_qn - _cumulative_dx;
        double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
 
        if (tdx || tdy) {
-               _cumulative_dx += tdx;
+               _cumulative_dx = dx_qn;
                _cumulative_dy += tdy;
 
                int8_t note_delta = total_dy();
 
                if (tdx || tdy) {
                        if (_copy) {
-                               _region->move_copies (tdx, tdy, note_delta);
+                               _region->move_copies (dx_qn, tdy, note_delta);
                        } else {
-                               _region->move_selection (tdx, tdy, note_delta);
+                               _region->move_selection (dx_qn, tdy, note_delta);
                        }
 
                        /* the new note value may be the same as the old one, but we
@@ -5819,7 +5873,7 @@ NoteDrag::finished (GdkEvent* ev, bool moved)
                        }
                }
        } else {
-               _region->note_dropped (_primary, total_dx (ev->button.state), total_dy(), _copy);
+               _region->note_dropped (_primary, total_dx (ev), total_dy(), _copy);
        }
 }