#include "ardour/audioengine.h"
#include "ardour/audioregion.h"
+#include "ardour/audio_track.h"
#include "ardour/dB.h"
#include "ardour/midi_region.h"
+#include "ardour/midi_track.h"
#include "ardour/operations.h"
#include "ardour/region_factory.h"
#include "ardour/session.h"
+#include "canvas/scroll_group.h"
+
#include "editor.h"
#include "i18n.h"
#include "keyboard.h"
void
RegionMotionDrag::motion (GdkEvent* event, bool first_move)
{
+ double delta_layer = 0;
+ int delta_time_axis_view = 0;
+
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<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+ pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+ TimeAxisView* tv = r.first;
- /* Bail early if we're not over a track */
- RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
+ if (tv && tv->view()) {
+ double layer = r.second;
+
+ if (first_move && tv->view()->layer_display() == Stacked) {
+ tv->view()->set_layer_display (Expanded);
+ }
- if (!rtv || !rtv->is_track()) {
- _editor->verbose_cursor()->hide ();
- return;
- }
+ /* 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);
+ double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
+
+ /* Work out the change in y */
- if (first_move && tv.first->view()->layer_display() == Stacked) {
- tv.first->view()->set_layer_display (Expanded);
+ delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
+ delta_layer = current_pointer_layer - _last_pointer_layer;
}
-
- /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
-
- /* 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.first);
- double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
-
+
/* Work out the change in x */
framepos_t pending_region_position;
double const x_delta = compute_x_delta (event, &pending_region_position);
- /* Work out the change in y */
-
- int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
- double delta_layer = current_pointer_layer - _last_pointer_layer;
-
+ /* Verify change in y */
if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
/* this y movement is not allowed, so do no y movement this time */
delta_time_axis_view = 0;
}
if (first_move) {
-
rv->drag_start ();
-
- /* Reparent to a non scrolling group so that we can keep the
- region selection above all time axis views.
- Reparenting means that we will have to move the region view
- within its new parent, as the two parent groups have different coordinates.
- */
-
- ArdourCanvas::Group* rvg = rv->get_canvas_group();
- Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
-
- rv->get_canvas_group()->reparent (_editor->_region_motion_group);
-
rv->fake_set_opaque (true);
- rvg->set_position (rv_canvas_offset);
+ rv->raise_to_top ();
}
/* If we have moved tracks, we'll fudge the layer delta so that the
this_delta_layer = - i->layer;
}
- /* The TimeAxisView that this region is now on */
- TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
+ if (tv) {
- /* Ensure it is moved from stacked -> expanded if appropriate */
- if (tv->view()->layer_display() == Stacked) {
- tv->view()->set_layer_display (Expanded);
- }
+ int track_index = i->time_axis_view + delta_time_axis_view;
+
+ if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
+ continue;
+ }
+
+ /* The TimeAxisView that this region is now over */
+ TimeAxisView* current_tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
+
+ /* Ensure it is moved from stacked -> expanded if appropriate */
+ if (current_tv->view()->layer_display() == Stacked) {
+ current_tv->view()->set_layer_display (Expanded);
+ }
- /* We're only allowed to go -ve in layer on Expanded views */
- if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
- this_delta_layer = - i->layer;
- }
+ /* We're only allowed to go -ve in layer on Expanded views */
+ if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
+ this_delta_layer = - i->layer;
+ }
- /* Set height */
- rv->set_height (tv->view()->child_height ());
+ /* Set height */
+ rv->set_height (current_tv->view()->child_height ());
- /* Update show/hidden status as the region view may have come from a hidden track,
- or have moved to one.
- */
- if (tv->hidden ()) {
- rv->get_canvas_group()->hide ();
- } else {
- rv->get_canvas_group()->show ();
- }
+ /* Update show/hidden status as the region view may have come from a hidden track,
+ or have moved to one.
+ */
+ if (current_tv->hidden ()) {
+ rv->get_canvas_group()->hide ();
+ } else {
+ rv->get_canvas_group()->show ();
+ }
- /* Update the DraggingView */
- i->time_axis_view += delta_time_axis_view;
- i->layer += this_delta_layer;
+ /* Update the DraggingView */
+ i->time_axis_view += delta_time_axis_view;
+ i->layer += this_delta_layer;
- if (_brushing) {
- _editor->mouse_brush_insert_region (rv, pending_region_position);
- } else {
- double x = 0;
- double y = 0;
+ if (_brushing) {
+ _editor->mouse_brush_insert_region (rv, pending_region_position);
+ } else {
+ Duple track_origin;
- /* Get the y coordinate of the top of the track that this region is now on */
- tv->canvas_display()->item_to_canvas (x, y);
+ /* Get the y coordinate of the top of the track that this region is now over */
+ track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
+
+ /* And adjust for the layer that it should be on */
+ StreamView* cv = current_tv->view ();
+ switch (cv->layer_display ()) {
+ case Overlaid:
+ break;
+ case Stacked:
+ track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
+ break;
+ case Expanded:
+ track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
+ break;
+ }
- /* And adjust for the layer that it should be on */
- StreamView* cv = tv->view ();
- switch (cv->layer_display ()) {
- case Overlaid:
- break;
- case Stacked:
- y += (cv->layers() - i->layer - 1) * cv->child_height ();
- break;
- case Expanded:
- y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
- break;
+ /* need to get the parent of the regionview
+ * canvas group and get its position in
+ * equivalent coordinate space as the trackview
+ * we are now dragging over.
+ */
+
+ /* Now move the region view */
+ rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0, 0)).y);
}
+ } else {
- /* Now move the region view */
- rv->move (x_delta, y - rv->get_canvas_group()->position().y);
- }
+ /* Only move the region into the empty dropzone at the bottom if the pointer
+ * is down there.
+ */
+ if (_drags->current_pointer_y() >= _editor->get_trackview_group()->item_to_canvas (Duple (0,0)).y) {
+ Duple track_origin;
+
+ TimeAxisView* last = _time_axis_views.back();
+ track_origin = last->canvas_display()->item_to_canvas (track_origin);
+ track_origin.y += last->effective_height();
+ rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0,0)).y);
+ i->time_axis_view = -1;
+ }
+ }
+
} /* foreach region */
_total_x_delta += x_delta;
_editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
}
+RouteTimeAxisView*
+RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region)
+{
+ /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
+ new track.
+ */
+
+ try {
+ if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
+ list<boost::shared_ptr<AudioTrack> > audio_tracks;
+ audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
+ return _editor->axis_view_from_route (audio_tracks.front());
+ } else {
+ ChanCount one_midi_port (DataType::MIDI, 1);
+ list<boost::shared_ptr<MidiTrack> > midi_tracks;
+ midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
+ return _editor->axis_view_from_route (midi_tracks.front());
+ }
+ } catch (...) {
+ error << _("Could not create new track after region placed in the drop zone") << endmsg;
+ return 0;
+ }
+}
+
void
RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
{
RegionSelection new_views;
PlaylistSet modified_playlists;
- list<RegionView*> views_to_delete;
+ RouteTimeAxisView* new_time_axis_view = 0;
if (_brushing) {
/* all changes were made during motion event handlers */
}
/* insert the regions into their new playlists */
- for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
+
+ RouteTimeAxisView* dest_rtv = 0;
if (i->view->region()->locked() || i->view->region()->video_locked()) {
continue;
} else {
where = i->view->region()->position();
}
-
- RegionView* new_view = insert_region_into_playlist (
- i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
- );
-
- if (new_view == 0) {
- continue;
+
+ if (i->time_axis_view < 0) {
+ if (!new_time_axis_view) {
+ new_time_axis_view = create_destination_time_axis (i->view->region());
+ }
+ dest_rtv = new_time_axis_view;
+ } else {
+ dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
+ }
+
+ if (dest_rtv != 0) {
+ RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
+ if (new_view != 0) {
+ new_views.push_back (new_view);
+ }
}
+
+ /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
+ since deletion will automagically remove it from _views, thus invalidating i as an iterator.
+ */
- new_views.push_back (new_view);
-
- /* we don't need the copied RegionView any more */
- views_to_delete.push_back (i->view);
- }
-
- /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
- because when views are deleted they are automagically removed from _views, which messes
- up the iteration.
- */
- for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
- delete *i;
+ list<DraggingView>::const_iterator next = i;
+ ++next;
+ delete i->view;
+ i = next;
}
/* If we've created new regions either by copying or moving
PlaylistSet modified_playlists;
PlaylistSet frozen_playlists;
set<RouteTimeAxisView*> views_to_update;
+ RouteTimeAxisView* new_time_axis_view = 0;
if (_brushing) {
/* all changes were made during motion event handlers */
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
RegionView* rv = i->view;
-
- RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
- double const dest_layer = i->layer;
+ RouteTimeAxisView* dest_rtv = 0;
if (rv->region()->locked() || rv->region()->video_locked()) {
++i;
continue;
}
+
+ if (i->time_axis_view < 0) {
+ if (!new_time_axis_view) {
+ new_time_axis_view = create_destination_time_axis (rv->region());
+ }
+ dest_rtv = new_time_axis_view;
+ } else {
+ dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
+ }
+
+ assert (dest_rtv);
+ double const dest_layer = i->layer;
+
views_to_update.insert (dest_rtv);
framepos_t where;
speed = tv->track()->speed();
}
- framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
+ framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
if (first_move) {
case ContentsTrim:
trim_type = "Region content trim";
break;
+ default:
+ assert(0);
+ break;
}
_editor->begin_reversible_command (trim_type);
non_overlap_trim = true;
}
+ /* contstrain trim to fade length */
+ if (_preserve_fade_anchor) {
+ switch (_operation) {
+ case StartTrim:
+ for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
+ if (!arv) continue;
+ boost::shared_ptr<AudioRegion> ar (arv->audio_region());
+ if (ar->locked()) continue;
+ framecnt_t len = ar->fade_in()->back()->when;
+ if (len < dt) dt = min(dt, len);
+ }
+ break;
+ case EndTrim:
+ for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
+ if (!arv) continue;
+ boost::shared_ptr<AudioRegion> ar (arv->audio_region());
+ if (ar->locked()) continue;
+ framecnt_t len = ar->fade_out()->back()->when;
+ if (len < -dt) dt = max(dt, -len);
+ }
+ break;
+ case ContentsTrim:
+ break;
+ }
+ }
+
+
switch (_operation) {
case StartTrim:
- for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
if (changed && _preserve_fade_anchor) {
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
if (arv) {
- double distance;
- double new_length;
- framecnt_t len;
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
- distance = _drags->current_pointer_x() - grab_x();
- len = ar->fade_in()->back()->when;
- new_length = len - _editor->pixel_to_sample (distance);
- new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
- arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
+ framecnt_t len = ar->fade_in()->back()->when;
+ framecnt_t diff = ar->first_frame() - i->initial_position;
+ framepos_t new_length = len - diff;
+ i->anchored_fade_length = min (ar->length(), new_length);
+ //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
+ arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
}
}
}
break;
case EndTrim:
- for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
if (changed && _preserve_fade_anchor) {
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
if (arv) {
- double distance;
- double new_length;
- framecnt_t len;
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
- distance = grab_x() - _drags->current_pointer_x();
- len = ar->fade_out()->back()->when;
- new_length = len - _editor->pixel_to_sample (distance);
- new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
- arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
+ framecnt_t len = ar->fade_out()->back()->when;
+ framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
+ framepos_t new_length = len + diff;
+ i->anchored_fade_length = min (ar->length(), new_length);
+ //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
+ arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
}
}
}
case ContentsTrim:
{
- frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
- // frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
+ frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
i->view->move_contents (frame_delta);
if (_preserve_fade_anchor) {
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
if (arv) {
- double distance;
- double new_length;
- framecnt_t len;
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
- distance = _drags->current_pointer_x() - grab_x();
- len = ar->fade_in()->back()->when;
- new_length = len - _editor->pixel_to_sample (distance);
- new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
- ar->set_fade_in_length(new_length);
+ arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
+ ar->set_fade_in_length(i->anchored_fade_length);
+ ar->set_fade_in_active(true);
}
}
if (_jump_position_when_done) {
if (_preserve_fade_anchor) {
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
if (arv) {
- double distance;
- double new_length;
- framecnt_t len;
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
- distance = _drags->current_pointer_x() - grab_x();
- len = ar->fade_out()->back()->when;
- new_length = len - _editor->pixel_to_sample (distance);
- new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
- ar->set_fade_out_length(new_length);
+ arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
+ ar->set_fade_out_length(i->anchored_fade_length);
+ ar->set_fade_out_active(true);
}
}
if (_jump_position_when_done) {
}
CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
- : Drag (e, &c.time_bar_canvas_item())
+ : Drag (e, &c.track_canvas_item())
, _cursor (c)
, _stop (s)
{
break;
}
- _editor->maybe_autoscroll (true, false, false);
-
if (start != end) {
switch (_operation) {
case SelectionMove:
}
}
- _editor->maybe_autoscroll (true, false, false);
-
if (start != end) {
_editor->temp_location->set (start, end);