13
0

Major rework of DnD.

Allow dragging multiple regions from multiple
tracks across any number of hidden tracks and 
automation lanes.

fixes #6176 and #6172
This commit is contained in:
Robin Gareus 2015-03-24 21:59:57 +01:00
parent 089c334d0c
commit b637c2223f
2 changed files with 276 additions and 243 deletions

View File

@ -640,46 +640,52 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r
return dx;
}
int
RegionDrag::apply_track_delta (const int start, const int delta, const int skip) const
{
if (delta == 0) {
return start;
}
const int dt = delta > 0 ? +1 : -1;
int current = start;
int target = start + delta - skip;
assert (current < 0 || current >= _time_axis_views.size() || !_time_axis_views[current]->hidden());
assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
#ifdef DEBUG_DROPZONEDRAG
if (current >= _time_axis_views.size() && target >= 0 && target < _time_axis_views.size()) {
printf("MOVE OUT OF THE ZONE cur: %d d: %d s: %d\n", start, delta, skip);
}
#endif
while (current >= 0 && current != target) {
current += dt;
if (current < 0 || current >= _time_axis_views.size()) {
continue;
}
if (_time_axis_views[current]->hidden()) {
target += dt;
}
}
return target;
}
bool
RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
{
if (_y_constrained) {
return false;
}
bool all_in_drop_zone = true;
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
int n = i->time_axis_view + delta_track;
/* TODO: for i > 0, account for hidden tracks
*
* example. top-to-bottom:
* 2 Audio Tracks [one region each] 1 Bus 1 Audio Track.
* Hide the Bus. (looks like 3 audio tracks)
*
* select both region, grab the upper and move down 1 track
* -> delta_track = 1;
*
* real_delta_track=1 (for region on track1)
* real_delta_track=2 (for region on track2, skip the hidden bus)
* -> fail
*
* assuming it worked..
* Audio Track Audio Track[with region] hidden-Bus Audio Track[with region]
*
* selected both regions, grab the lower region and
* move up one track -> given delta == -2 (skip the bus)
*
* real_delta_track=-1
* real_delta_track=-2
*
* It gets worse if both regions have to ignore differen numbers of hidden tracks
* in between.
*
*
* Proposed solution: if i == 0, count number of hidden tracks crossed
* subtract that number for i > 0 AND also subtract hidden tracks crossed
*/
int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
#ifdef DEBUG_DROPZONEDRAG
printf("Y MOVEMENT CHECK: from %d to %d skip: %d\n", i->time_axis_view, i->time_axis_view + delta_track, skip_invisible);
#endif
assert (n < 0 || n >= _time_axis_views.size() || !_time_axis_views[n]->hidden());
if (i->time_axis_view < 0) {
/* already in the drop zone */
if (delta_track >= 0) {
@ -700,9 +706,6 @@ RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
} else if (n >= int (_time_axis_views.size())) {
/* downward motion into drop zone. That's fine. */
continue;
} else {
/* target is not in the drop zone */
all_in_drop_zone = false;
}
RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
@ -725,7 +728,7 @@ RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
}
/* all regions being dragged are ok with this change */
return !all_in_drop_zone;
return true;
}
void
@ -749,6 +752,9 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
}
}
#ifdef DEBUG_DROPZONEDRAG
printf("--------- LAST AXIS: %d\n", _last_pointer_time_axis_view);
#endif
/* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
/* Find the TimeAxisView that the pointer is now over */
@ -786,6 +792,9 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
* This is necessary because steps may be skipped if
* the bottom-most track is not a valid target,
*/
#ifdef DEBUG_DROPZONEDRAG
printf("MOVE OUT OF THE ZONE...\n");
#endif
delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size ();
} else {
delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
@ -813,6 +822,22 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
* 4) proportionally scale layer to layers available on target
*/
delta_layer = current_pointer_layer - _last_pointer_layer;
} else if (current_pointer_y() >= 0 && _last_pointer_time_axis_view >= 0) {
/* Moving into the drop-zone..
*
* TODO allow moving further down in drop-zone:
* e.g. 2 Tracks, select a region on both of them.
*
* A) grab the upper, drag 2 down, both regions are in the dropzone: all fine (works)
*
* B) grab the lower, drag 1 down, region (and mouse) are in dropzone, The End.
* upper region is only down one track and cannot be moved into the zone.
*/
delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
#ifdef DEBUG_DROPZONEDRAG
printf("INTO THE ZONE DELTA: %d\n", delta_time_axis_view);
#endif
}
/* Work out the change in x */
@ -820,11 +845,37 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
double const x_delta = compute_x_delta (event, &pending_region_position);
_last_frame_position = pending_region_position;
/* calculate hidden tracks in current delta */
int delta_skip = 0;
if (_last_pointer_time_axis_view < 0) {
// Moving out of the zone, check for hidden tracks at the bottom.
delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0)
-_time_axis_views.size() - delta_time_axis_view;
#ifdef DEBUG_DROPZONEDRAG
printf("NOW WHAT?? last: %d delta %d || skip %d\n", _last_pointer_time_axis_view, delta_time_axis_view, delta_skip);
#endif
} else {
// calculate hidden tracks that are skipped by the pointer movement
delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0)
- _last_pointer_time_axis_view
- delta_time_axis_view;
#ifdef DEBUG_DROPZONEDRAG
printf("Drag from %d to %d || skip %d\n",
_last_pointer_time_axis_view,
_last_pointer_time_axis_view + delta_time_axis_view,
delta_skip);
#endif
}
/* Verify change in y */
if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
/* this y movement is not allowed, so do no y movement this time */
delta_time_axis_view = 0;
delta_layer = 0;
delta_skip = 0;
#ifdef DEBUG_DROPZONEDRAG
printf(" ** NOT ALLOWED\n");
#endif
}
if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
@ -839,13 +890,14 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
PlaylistDropzoneMap playlist_dropzone_map;
int biggest_drop_zone_offset = 0;
/* find drop-zone y-position */
Coord last_track_bottom_edge;
if (_time_axis_views.empty()) {
last_track_bottom_edge = 0;
} else {
TimeAxisView* last = _time_axis_views.back();
last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
if (!(*t)->hidden()) {
last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
break;
}
}
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
@ -886,40 +938,69 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
this_delta_layer = - i->layer;
}
if (tv) {
int this_delta_time_axis_view = delta_time_axis_view;
this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
int track_index;
if (i->time_axis_view >= 0) {
track_index = i->time_axis_view + delta_time_axis_view;
} else {
/* delta time axis view will be at least -1, because we can only
be moving up if some dragged views have i->time_axis_view negative.
the final real time axis view has index _time_axis_views.size() - 1.
so for DTAV = -1, the first drop zone track (i->tav == -1) should
move to the final TAV.
the second drop zone track should move to i->tav == -1 (i.e. becomes
the first drop zone track). and so on.
*/
int index_from_bottom = i->time_axis_view - delta_time_axis_view;
int const bottom = _time_axis_views.size() - 1;
if (index_from_bottom >= 0) {
track_index = bottom - index_from_bottom;
} else {
track_index = index_from_bottom;
}
}
int track_index = i->time_axis_view + this_delta_time_axis_view;
assert(track_index >= 0);
if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
// Velociraptor was here
goto dropzone;
i->time_axis_view = track_index;
#ifdef DEBUG_DROPZONEDRAG
printf("IN THE ZONE\n");
#endif
assert(i->time_axis_view >= _time_axis_views.size());
if (current_pointer_y() >= 0) {
int dzoffset;
NewTrackIndexAndPosition ip;
PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
/* store index of each new playlist as a negative count, starting at -1 */
if (pdz == playlist_dropzone_map.end()) {
/* TODO
* retain the ordering top -> bottom in the drop-zone
* this can be done by sorting the regions according to
* i->time_axis_view Y, prior to iterating over DraggingView
*/
int n = playlist_dropzone_map.size() + 1;
/* compute where this new track (which doesn't exist yet) will live
on the y-axis.
*/
ip.first = -n; /* in time axis units, negative to signify "in drop zone " */
ip.second = last_track_bottom_edge; /* where to place the top edge of the regionview */
/* How high is this region view ? */
boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
ArdourCanvas::Rect bbox;
if (obbox) {
bbox = obbox.get ();
}
last_track_bottom_edge += bbox.height();
playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), ip));
dzoffset = -n;
} else {
ip = pdz->second;
dzoffset = ip.first;
}
/* values are zero or negative, hence the use of min() */
biggest_drop_zone_offset = min (biggest_drop_zone_offset, dzoffset);
y_delta = ip.second - rv->get_canvas_group()->canvas_origin().y;
}
} else {
/* The TimeAxisView that this region is now over */
TimeAxisView* current_tv = _time_axis_views[track_index];
@ -979,54 +1060,6 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
}
} else {
dropzone:
if (current_pointer_y() >= 0) {
NewTrackIndexAndPosition ip;
PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
/* store index of each new playlist as a negative count, starting at -1 */
if (pdz == playlist_dropzone_map.end()) {
int n = playlist_dropzone_map.size() + 1;
/* compute where this new track (which doesn't exist yet) will live
on the y-axis.
*/
ip.first = -n; /* in time axis units, negative to signify "in drop zone " */
ip.second = last_track_bottom_edge; /* where to place the top edge of the regionview */
/* How high is this region view ? */
boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
ArdourCanvas::Rect bbox;
if (obbox) {
bbox = obbox.get ();
}
last_track_bottom_edge += bbox.height();
playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), ip));
i->time_axis_view = -n;
} else {
ip = pdz->second;
i->time_axis_view = ip.first;
}
/* values are zero or negative, hence the use of min() */
biggest_drop_zone_offset = min (biggest_drop_zone_offset, i->time_axis_view);
y_delta = ip.second - rv->get_canvas_group()->canvas_origin().y;
}
}
/* Now move the region view */
@ -1932,9 +1965,9 @@ RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regi
}
bool
RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
{
if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
if (delta_track) {
return allow_moves_across_tracks;
} else {
@ -4939,7 +4972,6 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
} else { /* operation == CreateRangeMarker || CreateSkipMarker */
framepos_t start;
framepos_t end;

View File

@ -292,6 +292,7 @@ protected:
/** a list of the non-hidden TimeAxisViews sorted by editor order key */
std::vector<TimeAxisView*> _time_axis_views;
int find_time_axis_view (TimeAxisView *) const;
int apply_track_delta (const int start, const int delta, const int skip) const;
int _visible_y_low;
int _visible_y_high;
@ -326,7 +327,7 @@ public:
protected:
double compute_x_delta (GdkEvent const *, ARDOUR::framepos_t *);
virtual bool y_movement_allowed (int, double) const;
virtual bool y_movement_allowed (int, double, int skip_invisible = 0) const;
bool _brushing;
ARDOUR::framepos_t _last_frame_position; ///< last position of the thing being dragged
@ -436,7 +437,7 @@ public:
void finished (GdkEvent *, bool);
void aborted (bool);
protected:
bool y_movement_allowed (int delta_track, double delta_layer) const;
bool y_movement_allowed (int delta_track, double delta_layer, int skip_invisible = 0) const;
private:
TimeAxisView *prev_tav; // where regions were most recently dragged from