diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 2fd642fb29..c09d92b6f9 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -645,14 +645,39 @@ RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const if (_y_constrained) { return false; } + + bool all_in_drop_zone = true; for (list::const_iterator i = _views.begin(); i != _views.end(); ++i) { - int const n = i->time_axis_view + delta_track; - if (n < 0 || n >= int (_time_axis_views.size())) { - /* off the top or bottom track */ - return false; + int n = i->time_axis_view + delta_track; + if (i->time_axis_view < 0) { + /* already in the drop zone */ + if (delta_track > 0) { + /* downward motion - might be OK if others are still not in the dropzone, + so check at the end of the loop if that is the case. + */ + continue; + } + /* upward motion, and this view is currently in the drop zone. That's fine, so + we have to set all_in_drop_zone correctly to avoid blocking upward motion. + */ + all_in_drop_zone = false; + continue; + } else { + + all_in_drop_zone = false; + + if (n < 0) { + /* off the top */ + return false; + } } + if (n >= int (_time_axis_views.size())) { + /* downward motion into drop zone. That's fine. */ + continue; + } + RouteTimeAxisView const * to = dynamic_cast (_time_axis_views[n]); if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) { /* not a track, or the wrong type */ @@ -673,7 +698,7 @@ RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const } /* all regions being dragged are ok with this change */ - return true; + return !all_in_drop_zone; } void @@ -681,15 +706,10 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) { double delta_layer = 0; int delta_time_axis_view = 0; + int current_pointer_time_axis_view; assert (!_views.empty ()); - /* 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 */ - pair const r = _editor->trackview_by_y_position (current_pointer_y ()); - TimeAxisView* tv = r.first; - if (first_move) { if (_single_axis) { if (initially_vertical()) { @@ -702,6 +722,13 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) } } + /* 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 */ + + pair const r = _editor->trackview_by_y_position (current_pointer_y ()); + TimeAxisView* tv = r.first; + if (tv && tv->view()) { double layer = r.second; @@ -710,15 +737,22 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) } /* Here's the current pointer position in terms of time axis view and layer */ - int const current_pointer_time_axis_view = find_time_axis_view (tv); + current_pointer_time_axis_view = find_time_axis_view (tv); + double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer; /* Work out the change in y */ - delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view; + if (_last_pointer_time_axis_view < 0) { + /* we can only move up */ + delta_time_axis_view = -1; + } else { + delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view; + } + delta_layer = current_pointer_layer - _last_pointer_layer; } - + /* Work out the change in x */ framepos_t pending_region_position; double const x_delta = compute_x_delta (event, &pending_region_position); @@ -738,8 +772,20 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) return; } - pair >::iterator,bool> insert_result; + typedef pair NewTrackIndexAndPosition; + typedef map,NewTrackIndexAndPosition> PlaylistDropzoneMap; + PlaylistDropzoneMap playlist_dropzone_map; + int biggest_drop_zone_offset = 0; + 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 (list::iterator i = _views.begin(); i != _views.end(); ++i) { RegionView* rv = i->view; @@ -785,11 +831,30 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) if (i->time_axis_view >= 0) { track_index = i->time_axis_view + delta_time_axis_view; } else { - track_index = _time_axis_views.size() - 1 + delta_time_axis_view; + /* 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; + } } - + if (track_index < 0 || track_index >= (int) _time_axis_views.size()) { - continue; + goto dropzone; } /* The TimeAxisView that this region is now over */ @@ -855,22 +920,49 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) } else { - /* Only move the region into the empty dropzone at the bottom if the pointer - * is down there. - */ - + dropzone: + if (current_pointer_y() >= 0) { - Coord last_track_bottom_edge; - if (!_time_axis_views.empty()) { - TimeAxisView* last = _time_axis_views.back(); - last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height(); + 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 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 { - last_track_bottom_edge = 0; + ip = pdz->second; + i->time_axis_view = ip.first; } - y_delta = last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y; - i->time_axis_view = -1; + /* 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; } } @@ -885,7 +977,35 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) show_verbose_cursor_time (_last_frame_position); } - _last_pointer_time_axis_view += delta_time_axis_view; + if (tv) { + + /* the pointer is currently over a time axis view */ + + if (_last_pointer_time_axis_view < 0) { + + /* last motion event was not over a time axis view */ + + if (delta_time_axis_view < 0) { + /* was in the drop zone, moving up */ + _last_pointer_time_axis_view = current_pointer_time_axis_view; + } else { + /* was in the drop zone, moving down ... not possible */ + } + + } else { + + /* last motion event was also over a time axis view */ + + _last_pointer_time_axis_view = current_pointer_time_axis_view; + } + + } else { + + /* the pointer is not over a time axis view */ + + _last_pointer_time_axis_view = biggest_drop_zone_offset; + } + _last_pointer_layer += delta_layer; } diff --git a/gtk2_ardour/editor_routes.cc b/gtk2_ardour/editor_routes.cc index c7ad7c54ff..30082adec4 100644 --- a/gtk2_ardour/editor_routes.cc +++ b/gtk2_ardour/editor_routes.cc @@ -515,8 +515,17 @@ EditorRoutes::redisplay_real () continue; } + if (route->is_master()) { + + tv->hide(); // never show master bus + if (_editor->master_bus_ui ()) { + _editor->master_bus_ui ()->set_route (route); + } + continue; + } + bool visible = tv->marked_for_display (); - + /* show or hide the TimeAxisView */ if (visible) { position += tv->show_at (position, n, &_editor->edit_controls_vbox);