#include "i18n.h"
#include "keyboard.h"
#include "audio_region_view.h"
+#include "automation_region_view.h"
#include "midi_region_view.h"
#include "ardour_ui.h"
#include "gui_thread.h"
}
_drags.clear ();
+ _editor->abort_reversible_command();
_ending = false;
}
bool r = false;
/* calling this implies that we expect the event to have canvas
- * coordinates
+ * coordinates
*
* Can we guarantee that this is true?
*/
, _pointer_frame_offset (0)
, _trackview_only (trackview_only)
, _move_threshold_passed (false)
+ , _starting_point_passed (false)
+ , _initially_vertical (false)
, _was_double_click (false)
, _raw_grab_frame (0)
, _grab_frame (0)
_item->ungrab ();
_item = new_item;
- if (cursor == 0) {
- _item->grab ();
+ if (!_cursor_ctx) {
+ _cursor_ctx = CursorContext::create (*_editor, cursor);
} else {
- _item->grab ();
+ _cursor_ctx->change (cursor);
}
+
+ _item->grab ();
}
void
_last_pointer_y = _grab_y;
- if (cursor == 0) {
- _item->grab ();
- } else {
+ _item->grab ();
+
+ if (!_editor->cursors()->is_invalid (cursor)) {
/* CAIROCANVAS need a variant here that passes *cursor */
- _item->grab ();
- _editor->push_canvas_cursor (cursor);
+ _cursor_ctx = CursorContext::create (*_editor, cursor);
}
if (_editor->session() && _editor->session()->transport_rolling()) {
finished (event, _move_threshold_passed);
_editor->verbose_cursor()->hide ();
- _editor->pop_canvas_cursor ();
+ _cursor_ctx.reset();
return _move_threshold_passed;
}
double
Drag::current_pointer_x() const
{
- return _drags->current_pointer_x ();
+ return _drags->current_pointer_x ();
}
double
if (!_trackview_only) {
return _drags->current_pointer_y ();
}
-
+
return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
}
bool const old_move_threshold_passed = _move_threshold_passed;
- if (!from_autoscroll && !_move_threshold_passed) {
+ if (!_move_threshold_passed) {
bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
if (active (_editor->mouse_mode) && _move_threshold_passed) {
if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
+
+ if (old_move_threshold_passed != _move_threshold_passed) {
+
+ /* just changed */
+
+ if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
+ _initially_vertical = true;
+ } else {
+ _initially_vertical = false;
+ }
+ }
+
if (!from_autoscroll) {
_editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
}
if (!_editor->autoscroll_active() || from_autoscroll) {
- motion (event, _move_threshold_passed != old_move_threshold_passed);
-
+
+
+ bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
+
+ motion (event, first_move && !_starting_point_passed);
+
+ if (first_move && !_starting_point_passed) {
+ _starting_point_passed = true;
+ }
+
_last_pointer_x = _drags->current_pointer_x ();
_last_pointer_y = current_pointer_y ();
_last_pointer_frame = adjusted_current_frame (event);
}
struct EditorOrderTimeAxisViewSorter {
- bool operator() (TimeAxisView* a, TimeAxisView* b) {
- RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
- RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
- assert (ra && rb);
- return ra->route()->order_key () < rb->route()->order_key ();
- }
+ bool operator() (TimeAxisView* a, TimeAxisView* b) {
+ RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
+ RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
+ assert (ra && rb);
+ return ra->route()->order_key () < rb->route()->order_key ();
+ }
};
RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
- : Drag (e, i),
- _primary (p)
+ : Drag (e, i)
+ , _primary (p)
+ , _ntracks (0)
{
_editor->visible_order_range (&_visible_y_low, &_visible_y_high);
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
_time_axis_views.push_back (*i);
-
+
TimeAxisView::Children children_list = (*i)->get_child_list ();
for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
_time_axis_views.push_back (j->get());
++i;
}
- if (i == N) {
+ if (_time_axis_views[i] != t) {
return -1;
}
, _total_x_delta (0)
, _last_pointer_time_axis_view (0)
, _last_pointer_layer (0)
+ , _single_axis (false)
+ , _ndropzone (0)
+ , _pdropzone (0)
+ , _ddropzone (0)
{
DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
}
{
Drag::start_grab (event, cursor);
+ if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
+ _single_axis = true;
+ }
+
show_verbose_cursor_time (_last_frame_position);
pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
if (tv.first) {
_last_pointer_time_axis_view = find_time_axis_view (tv.first);
+ assert(_last_pointer_time_axis_view >= 0);
_last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
}
}
return dx;
}
+int
+RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
+{
+ if (delta == 0) {
+ return start;
+ }
+
+ const int tavsize = _time_axis_views.size();
+ const int dt = delta > 0 ? +1 : -1;
+ int current = start;
+ int target = start + delta - skip;
+
+ assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
+ assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
+
+ while (current >= 0 && current != target) {
+ current += dt;
+ if (current < 0 && dt < 0) {
+ break;
+ }
+ if (current >= tavsize && dt > 0) {
+ break;
+ }
+ if (current < 0 || current >= tavsize) {
+ continue;
+ }
+
+ RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
+ if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
+ target += dt;
+ }
+
+ if (distance_only && current == start + delta) {
+ break;
+ }
+ }
+ 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;
+ }
+
+ const int tavsize = _time_axis_views.size();
for (list<DraggingView>::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 */
+ int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
+ assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
+
+ if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
+ /* already in the drop zone */
+ if (delta_track >= 0) {
+ /* downward motion - OK if others are still not in the dropzone */
+ continue;
+ }
+
+ }
+
+ if (n < 0) {
+ /* off the top */
return false;
+ } else if (n >= tavsize) {
+ /* downward motion into drop zone. That's fine. */
+ continue;
}
RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
- if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
+ if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
/* not a track, or the wrong type */
return false;
}
return true;
}
+struct DraggingViewSorter {
+ bool operator() (const DraggingView& a, const DraggingView& b) {
+ return a.time_axis_view < b.time_axis_view;
+ }
+};
+
void
RegionMotionDrag::motion (GdkEvent* event, bool first_move)
{
double delta_layer = 0;
int delta_time_axis_view = 0;
+ int current_pointer_time_axis_view = -1;
assert (!_views.empty ());
+ if (first_move) {
+ if (_single_axis) {
+ if (initially_vertical()) {
+ _y_constrained = false;
+ _x_constrained = true;
+ } else {
+ _y_constrained = true;
+ _x_constrained = false;
+ }
+ }
+ }
+
/* 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 r = _editor->trackview_by_y_position (current_pointer_y ());
+ const double cur_y = current_pointer_y ();
+ pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
TimeAxisView* tv = r.first;
+ if (!tv && cur_y < 0) {
+ /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
+ return;
+ }
+
+ /* find drop-zone y-position */
+ Coord last_track_bottom_edge;
+ last_track_bottom_edge = 0;
+ 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;
+ }
+ }
+
if (tv && tv->view()) {
+ /* the mouse is over a track */
double layer = r.second;
if (first_move && tv->view()->layer_display() == Stacked) {
}
/* 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);
+ assert(current_pointer_time_axis_view >= 0);
+
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;
+ RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
+ if (!rtv || !rtv->is_track()) {
+ /* ignore busses early on. we can't move any regions on them */
+ } else if (_last_pointer_time_axis_view < 0) {
+ /* Was in the drop-zone, now over a track.
+ * Hence it must be an upward move (from the bottom)
+ *
+ * track_index is still -1, so delta must be set to
+ * move up the correct number of tracks from the bottom.
+ *
+ * This is necessary because steps may be skipped if
+ * the bottom-most track is not a valid target and/or
+ * if there are hidden tracks at the bottom.
+ * Hence the initial offset (_ddropzone) as well as the
+ * last valid pointer position (_pdropzone) need to be
+ * taken into account.
+ */
+ delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
+ } else {
+ delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
+ }
+
+ /* TODO needs adjustment per DraggingView,
+ *
+ * e.g. select one region on the top-layer of a track
+ * and one region which is at the bottom-layer of another track
+ * drag both.
+ *
+ * Indicated drop-zones and layering is wrong.
+ * and may infer additional layers on the target-track
+ * (depending how many layers the original track had).
+ *
+ * Or select two regions (different layers) on a same track,
+ * move across a non-layer track.. -> layering info is lost.
+ * on drop either of the regions may be on top.
+ *
+ * Proposed solution: screw it :) well,
+ * don't use delta_layer, use an absolute value
+ * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
+ * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
+ * 3) iterate over all DraggingView, find the one that is over the track with most layers
+ * 4) proportionally scale layer to layers available on target
+ */
delta_layer = current_pointer_layer - _last_pointer_layer;
- }
-
+
+ }
+ /* for automation lanes, there is a TimeAxisView but no ->view()
+ * if (!tv) -> dropzone
+ */
+ else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
+ /* Moving into the drop-zone.. */
+ delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
+ /* delta_time_axis_view may not be sufficient to move into the DZ
+ * the mouse may enter it, but it may not be a valid move due to
+ * constraints.
+ *
+ * -> remember the delta needed to move into the dropzone
+ */
+ _ddropzone = delta_time_axis_view;
+ /* ..but subtract hidden tracks (or routes) at the bottom.
+ * we silently move mover them
+ */
+ _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
+ - _time_axis_views.size();
+ }
+ else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
+ /* move around inside the zone.
+ * This allows to move further down until all regions are in the zone.
+ */
+ const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
+ assert(ptr_y >= last_track_bottom_edge);
+ assert(_ddropzone > 0);
+
+ /* calculate mouse position in 'tracks' below last track. */
+ const double dzi_h = TimeAxisView::preset_height (HeightNormal);
+ uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
+
+ if (dzpos > _pdropzone && _ndropzone < _ntracks) {
+ // move further down
+ delta_time_axis_view = dzpos - _pdropzone;
+ } else if (dzpos < _pdropzone && _ndropzone > 0) {
+ // move up inside the DZ
+ delta_time_axis_view = dzpos - _pdropzone;
+ }
+ }
+
/* Work out the change in x */
framepos_t pending_region_position;
double const x_delta = compute_x_delta (event, &pending_region_position);
_last_frame_position = pending_region_position;
+ /* calculate hidden tracks in current y-axis delta */
+ int delta_skip = 0;
+ if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
+ /* The mouse is more than one track below the dropzone.
+ * distance calculation is not needed (and would not work, either
+ * because the dropzone is "packed").
+ *
+ * Except when [partially] moving regions out of dropzone in a large step.
+ * (the mouse may or may not remain in the DZ)
+ * Hidden tracks at the bottom of the TAV need to be skipped.
+ *
+ * This also handles the case if the mouse entered the DZ
+ * in a large step (exessive delta), either due to fast-movement,
+ * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
+ */
+ if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
+ const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
+ assert(dt <= 0);
+ delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
+ -_time_axis_views.size() - dt;
+ }
+ }
+ else 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, true)
+ -_time_axis_views.size() - delta_time_axis_view;
+ } 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, true)
+ - _last_pointer_time_axis_view
+ - delta_time_axis_view;
+ }
+
/* 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;
}
if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
return;
}
- pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
+ typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
+ PlaylistDropzoneMap playlist_dropzone_map;
+ _ndropzone = 0; // number of elements currently in the dropzone
+
+ if (first_move) {
+ /* sort views by time_axis.
+ * This retains track order in the dropzone, regardless
+ * of actual selection order
+ */
+ _views.sort (DraggingViewSorter());
+
+ /* count number of distinct tracks of all regions
+ * being dragged, used for dropzone.
+ */
+ int prev_track = -1;
+ for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ if (i->time_axis_view != prev_track) {
+ prev_track = i->time_axis_view;
+ ++_ntracks;
+ }
+ }
+#ifndef NDEBUG
+ int spread =
+ _views.back().time_axis_view -
+ _views.front().time_axis_view;
+
+ spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
+ - _views.back().time_axis_view;
+
+ printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
+#endif
+ }
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
RegionView* rv = i->view;
+ double y_delta;
+
+ y_delta = 0;
if (rv->region()->locked() || rv->region()->video_locked()) {
continue;
}
if (first_move) {
- rv->drag_start ();
+ rv->drag_start ();
/* reparent the regionview into a group above all
* others
*/
-
+
ArdourCanvas::Item* rvg = rv->get_canvas_group();
- Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
- Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
- rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
+ Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
+ Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
+ rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
/* move the item so that it continues to appear at the
same location now that its parent has changed.
- */
+ */
rvg->move (rv_canvas_offset - dmg_canvas_offset);
}
this_delta_layer = - i->layer;
}
- if (tv) {
+ int 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 {
- track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
- }
-
- if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
- continue;
+ 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()) {
+ /* Track is in the Dropzone */
+
+ i->time_axis_view = track_index;
+ assert(i->time_axis_view >= (int) _time_axis_views.size());
+ if (cur_y >= 0) {
+
+ double yposition = 0;
+ PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
+ rv->set_height (TimeAxisView::preset_height (HeightNormal));
+ ++_ndropzone;
+
+ /* store index of each new playlist as a negative count, starting at -1 */
+
+ if (pdz == playlist_dropzone_map.end()) {
+ /* compute where this new track (which doesn't exist yet) will live
+ on the y-axis.
+ */
+ yposition = 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(), yposition));
+
+ } else {
+ yposition = pdz->second;
+ }
+
+ /* values are zero or negative, hence the use of min() */
+ y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
}
+ } else {
+
/* The TimeAxisView that this region is now over */
TimeAxisView* current_tv = _time_axis_views[track_index];
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 (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
this_delta_layer = - i->layer;
}
-
+
/* 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.
*/
/* 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 ()) {
* 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()->canvas_origin().y);
- }
- } else {
- /* Only move the region into the empty dropzone at the bottom if the pointer
- * is down there.
- */
+ y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
- 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();
- } else {
- last_track_bottom_edge = 0;
- }
-
- rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
- i->time_axis_view = -1;
}
}
-
+
+ /* Now move the region view */
+ rv->move (x_delta, y_delta);
+
} /* foreach region */
_total_x_delta += x_delta;
show_verbose_cursor_time (_last_frame_position);
}
- _last_pointer_time_axis_view += delta_time_axis_view;
+ /* keep track of pointer movement */
+ 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
+ * or last y-movement out of the dropzone was not valid
+ */
+ int dtz = 0;
+ if (delta_time_axis_view < 0) {
+ /* in the drop zone, moving up */
+
+ /* _pdropzone is the last known pointer y-axis position inside the DZ.
+ * We do not use negative _last_pointer_time_axis_view because
+ * the dropzone is "packed" (the actual track offset is ignored)
+ *
+ * As opposed to the actual number
+ * of elements in the dropzone (_ndropzone)
+ * _pdropzone is not constrained. This is necessary
+ * to allow moving multiple regions with y-distance
+ * into the DZ.
+ *
+ * There can be 0 elements in the dropzone,
+ * even though the drag-pointer is inside the DZ.
+ *
+ * example:
+ * [ Audio-track, Midi-track, Audio-track, DZ ]
+ * move regions from both audio tracks at the same time into the
+ * DZ by grabbing the region in the bottom track.
+ */
+ assert(current_pointer_time_axis_view >= 0);
+ dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
+ _pdropzone -= dtz;
+ }
+
+ /* only move out of the zone if the movement is OK */
+ if (_pdropzone == 0 && delta_time_axis_view != 0) {
+ assert(delta_time_axis_view < 0);
+ _last_pointer_time_axis_view = current_pointer_time_axis_view;
+ /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
+ * the current position can be calculated as follows:
+ */
+ // a well placed oofus attack can still throw this off.
+ // likley auto-scroll related, printf() debugging may tell, commented out for now.
+ //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
+ }
+ } else {
+ /* last motion event was also over a time axis view */
+ _last_pointer_time_axis_view += delta_time_axis_view;
+ assert(_last_pointer_time_axis_view >= 0);
+ }
+
+ } else {
+
+ /* the pointer is not over a time axis view */
+ assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
+ _pdropzone += delta_time_axis_view - delta_skip;
+ _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
+ }
+
_last_pointer_layer += delta_layer;
}
{
if (_copy && first_move) {
+ if (_x_constrained) {
+ _editor->begin_reversible_command (Operations::fixed_time_region_copy);
+ } else {
+ _editor->begin_reversible_command (Operations::region_copy);
+ }
+
/* duplicate the regionview(s) and region(s) */
list<DraggingView> new_regionviews;
const boost::shared_ptr<const Region> original = rv->region();
boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
region_copy->set_position (original->position());
+ /* need to set this so that the drop zone code can work. This doesn't
+ actually put the region into the playlist, but just sets a weak pointer
+ to it.
+ */
+ region_copy->set_playlist (original->playlist());
RegionView* nrv;
if (arv) {
swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
}
+
+ } else if (!_copy && first_move) {
+
+ if (_x_constrained) {
+ _editor->begin_reversible_command (_("fixed time region drag"));
+ } else {
+ _editor->begin_reversible_command (Operations::region_drag);
+ }
}
RegionMotionDrag::motion (event, first_move);
RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
{
RegionMotionDrag::finished (ev, movement_occurred);
-
+
if (!movement_occurred) {
-
+
/* just a click */
if (was_double_click() && !_views.empty()) {
DraggingView dv = _views.front();
dv.view->show_region_editor ();
-
+
}
return;
for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
i->view->get_canvas_group()->show ();
}
-
+
bool const changed_position = (_last_frame_position != _primary->region()->position());
bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
-
+
if (_copy) {
finished_copy (
RouteTimeAxisView*
RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
-{
+{
/* 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());
+ uint32_t output_chan = region->n_channels();
+ if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
+ output_chan = _editor->session()->master_out()->n_inputs().n_audio();
+ }
+ audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
if (rtav) {
rtav->set_height (original->current_height());
rtav->set_height (original->current_height());
}
return rtav;
- }
+ }
} catch (...) {
error << _("Could not create new track after region placed in the drop zone") << endmsg;
return 0;
{
RegionSelection new_views;
PlaylistSet modified_playlists;
- RouteTimeAxisView* new_time_axis_view = 0;
+ RouteTimeAxisView* new_time_axis_view = 0;
if (_brushing) {
/* all changes were made during motion event handlers */
return;
}
- if (_x_constrained) {
- _editor->begin_reversible_command (Operations::fixed_time_region_copy);
- } else {
- _editor->begin_reversible_command (Operations::region_copy);
- }
+ typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
+ PlaylistMapping playlist_mapping;
/* insert the regions into their new playlists */
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
} else {
where = i->view->region()->position();
}
-
- if (i->time_axis_view < 0) {
- if (!new_time_axis_view) {
+
+ if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
+ /* dragged to drop zone */
+
+ PlaylistMapping::iterator pm;
+
+ if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
+ /* first region from this original playlist: create a new track */
new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
+ playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
+ dest_rtv = new_time_axis_view;
+ } else {
+ /* we already created a new track for regions from this playlist, use it */
+ dest_rtv = pm->second;
}
- dest_rtv = new_time_axis_view;
} else {
+ /* destination time axis view is the one we dragged to */
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.
*/
return;
}
- if (_x_constrained) {
- _editor->begin_reversible_command (_("fixed time region drag"));
- } else {
- _editor->begin_reversible_command (Operations::region_drag);
- }
+ typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
+ PlaylistMapping playlist_mapping;
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
++i;
continue;
}
-
- if (i->time_axis_view < 0) {
- if (!new_time_axis_view) {
- new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
+
+ if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
+ /* dragged to drop zone */
+
+ PlaylistMapping::iterator pm;
+
+ if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
+ /* first region from this original playlist: create a new track */
+ new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
+ playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
+ dest_rtv = new_time_axis_view;
+ } else {
+ /* we already created a new track for regions from this playlist, use it */
+ dest_rtv = pm->second;
}
- dest_rtv = new_time_axis_view;
+
} else {
+ /* destination time axis view is the one we dragged to */
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;
{
if (_copy) {
- for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
+ list<DraggingView>::const_iterator next = i;
+ ++next;
delete i->view;
+ i = next;
}
_views.clear ();
}
}
}
-
+
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
RegionView* rv = i->view;
TimeAxisView* tv = &(rv->get_time_axis_view ());
* @param c true to make copies of the regions being moved, otherwise false.
*/
RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
- : RegionMotionDrag (e, i, p, v, b),
- _copy (c)
+ : RegionMotionDrag (e, i, p, v, b)
+ , _copy (c)
{
DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
void
RegionInsertDrag::finished (GdkEvent *, bool)
{
- RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
+ int pos = _views.front().time_axis_view;
+ assert(pos >= 0 && pos < (int)_time_axis_views.size());
+
+ RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
_primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
_primary->get_canvas_group()->set_y_position (0);
}
struct RegionSelectionByPosition {
- bool operator() (RegionView*a, RegionView* b) {
- return a->region()->position () < b->region()->position();
- }
+ bool operator() (RegionView*a, RegionView* b) {
+ return a->region()->position () < b->region()->position();
+ }
};
void
{
for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
- // we added all the regions after the selection
+ // we added all the regions after the selection
std::list<DraggingView>::iterator to_erase = i++;
if (!_editor->selection->regions.contains (to_erase->view)) {
}
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 {
framecnt_t adjust = 0;
if (prev_tav && tv != prev_tav) {
- // dragged onto a different track
+ // dragged onto a different track
// remove the unselected regions from _views, restore them to their original positions
// and add the regions after the drop point on the new playlist to _views instead.
// undo the effect of rippling the previous playlist, and include the effect of removing
prev_amount = 0;
// move just the selected regions
- RegionMoveDrag::motion(event, first_move);
+ RegionMoveDrag::motion(event, first_move);
- // ensure that the ripple operation on the new playlist inserts selection_length time
+ // ensure that the ripple operation on the new playlist inserts selection_length time
adjust = selection_length;
// ripple the new current playlist
tv->playlist()->ripple (where, amount+adjust, exclude);
} else {
// motion on same track
- RegionMoveDrag::motion(event, first_move);
+ RegionMoveDrag::motion(event, first_move);
}
prev_tav = tv;
} else {
// selection encompasses multiple tracks - just drag
// cross-track drags are forbidden
- RegionMoveDrag::motion(event, first_move);
+ RegionMoveDrag::motion(event, first_move);
}
if (!_x_constrained) {
RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
{
if (!movement_occurred) {
-
+
/* just a click */
if (was_double_click() && !_views.empty()) {
DraggingView dv = _views.front();
dv.view->show_region_editor ();
-
+
}
return;
}
_editor->begin_reversible_command(_("Ripple drag"));
-
- // remove the regions being rippled from the dragging view, updating them to
+
+ // remove the regions being rippled from the dragging view, updating them to
// their new positions
remove_unselected_from_views (prev_amount, true);
*/
region->note_selected (cnote, cnote->selected ());
+ _editor->begin_reversible_command (_("resize notes"));
+
for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
MidiRegionSelection::iterator next;
next = r;
mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
}
}
+
+ _editor->commit_reversible_command ();
}
void
_views.begin()->view->region()->length());
}
}
-
+
if (!_editor->selection->selected (_primary)) {
_primary->thaw_after_trim ();
} else {
set<boost::shared_ptr<Playlist> > diffed_playlists;
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
- i->view->thaw_after_trim ();
+ i->view->thaw_after_trim ();
i->view->enable_display (true);
/* Trimming one region may affect others on the playlist, so we need
if (first_move) {
// create a dummy marker for visual representation of moving the
- // section, because whether its a copy or not, we're going to
+ // section, because whether its a copy or not, we're going to
// leave or lose the original marker (leave if its a copy; lose if its
// not, because we'll remove it from the map).
-
+
MeterSection section (_marker->meter());
if (!section.movable()) {
return;
}
-
+
char name[64];
snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
-
+
_marker = new MeterMarker (
*_editor,
*_editor->meter_group,
- ARDOUR_UI::config()->get_MeterMarker(),
+ ARDOUR_UI::config()->color ("meter marker"),
name,
*new MeterSection (_marker->meter())
);
-
+
/* use the new marker for the grab */
swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
TempoMap& map (_editor->session()->tempo_map());
map.bbt_time (last_pointer_frame(), when);
-
+
if (_copy == true) {
_editor->begin_reversible_command (_("copy meter mark"));
XMLNode &before = map.get_state();
_editor->begin_reversible_command (_("move meter mark"));
/* we removed it before, so add it back now */
-
+
map.add_meter (_marker->meter(), when);
XMLNode &after = map.get_state();
_editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
if (first_move) {
// create a dummy marker for visual representation of moving the
- // section, because whether its a copy or not, we're going to
+ // section, because whether its a copy or not, we're going to
// leave or lose the original marker (leave if its a copy; lose if its
// not, because we'll remove it from the map).
-
+
// create a dummy marker for visual representation of moving the copy.
// The actual copying is not done before we reach the finish callback.
_marker = new TempoMarker (
*_editor,
*_editor->tempo_group,
- ARDOUR_UI::config()->get_TempoMarker(),
+ ARDOUR_UI::config()->color ("tempo marker"),
name,
*new TempoSection (_marker->tempo())
);
if (AudioEngine::instance()->connected()) {
-
+
/* do this only if we're the engine is connected
* because otherwise this request will never be
* serviced and we'll busy wait forever. likewise,
FadeInDrag::motion (GdkEvent* event, bool)
{
framecnt_t fade_length;
-
framepos_t const pos = adjusted_current_frame (event);
-
- boost::shared_ptr<Region> region = _primary->region ();
+ boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
if (pos < (region->position() + 64)) {
fade_length = 64; // this should be a minimum defined somewhere
- } else if (pos > region->last_frame()) {
- fade_length = region->length();
+ } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
+ fade_length = region->length() - region->fade_out()->back()->when - 1;
} else {
fade_length = pos - region->position();
}
framepos_t const pos = adjusted_current_frame (event);
- boost::shared_ptr<Region> region = _primary->region ();
+ boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
if (pos < (region->position() + 64)) {
fade_length = 64; // this should be a minimum defined somewhere
- } else if (pos > region->last_frame()) {
- fade_length = region->length();
+ } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
+ fade_length = region->length() - region->fade_out()->back()->when - 1;
} else {
fade_length = pos - region->position();
}
framepos_t const pos = adjusted_current_frame (event);
- boost::shared_ptr<Region> region = _primary->region ();
+ boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
if (pos > (region->last_frame() - 64)) {
fade_length = 64; // this should really be a minimum fade defined somewhere
- }
- else if (pos < region->position()) {
- fade_length = region->length();
- }
- else {
+ } else if (pos <= region->position() + region->fade_in()->back()->when) {
+ fade_length = region->length() - region->fade_in()->back()->when - 1;
+ } else {
fade_length = region->last_frame() - pos;
}
framepos_t const pos = adjusted_current_frame (event);
- boost::shared_ptr<Region> region = _primary->region ();
+ boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
if (pos > (region->last_frame() - 64)) {
fade_length = 64; // this should really be a minimum fade defined somewhere
- }
- else if (pos < region->position()) {
- fade_length = region->length();
- }
- else {
+ } else if (pos <= region->position() + region->fade_in()->back()->when) {
+ fade_length = region->length() - region->fade_in()->back()->when - 1;
+ } else {
fade_length = region->last_frame() - pos;
}
break;
}
- /* Set up copies for us to manipulate during the drag
+ /* Set up copies for us to manipulate during the drag
*/
for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
(*x).move_both = true;
}
}
-
+
}
}
if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
/* this marker is represented by this
- * CopiedLocationMarkerInfo
+ * CopiedLocationMarkerInfo
*/
if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
copy_location = x->location;
- /* call this to find out if its the start or end */
-
if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
continue;
}
copy_location->set_start (copy_location->start() + f_delta);
} else {
-
+
framepos_t new_start = copy_location->start() + f_delta;
framepos_t new_end = copy_location->end() + f_delta;
-
+
if (is_start) { // start-of-range marker
-
+
if (move_both || (*x).move_both) {
copy_location->set_start (new_start);
copy_location->set_end (new_end);
}
update_item (copy_location);
-
+
/* now lookup the actual GUI items used to display this
* location and move them to wherever the copy of the location
* is now. This means that the logic in ARDOUR::Location is
- * still enforced, even though we are not (yet) modifying
+ * still enforced, even though we are not (yet) modifying
* the real Location itself.
*/
-
+
Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
if (lm) {
MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
{
if (!movement_occurred) {
-
+
if (was_double_click()) {
_editor->rename_marker (_marker);
return;
}
void
-MarkerDrag::aborted (bool)
+MarkerDrag::aborted (bool movement_occured)
{
- /* XXX: TODO */
+ if (!movement_occured) {
+ return;
+ }
+
+ for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
+
+ /* move all markers to their original location */
+
+
+ for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
+
+ bool is_start;
+ Location * location = _editor->find_location_from_marker (*m, is_start);
+
+ if (location) {
+ (*m)->set_position (is_start ? location->start() : location->end());
+ }
+ }
+ }
}
void
}
_point->line().end_drag (_pushing, _final_index);
- _editor->session()->commit_reversible_command ();
+ _editor->commit_reversible_command ();
}
void
}
}
- _editor->session()->commit_reversible_command ();
+ _editor->commit_reversible_command ();
}
void
RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
{
Drag::start_grab (event);
- show_verbose_cursor_time (adjusted_current_frame (event));
+ show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
}
void
double y1;
double y2;
- framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
+ framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
framepos_t grab = grab_frame ();
- if (Config->get_rubberbanding_snaps_to_grid ()) {
+ if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
_editor->snap_to_with_modifier (grab, event);
+ } else {
+ grab = raw_grab_frame ();
}
/* base start and end on initial click position */
} else {
x2 = max (x1 + min_dimension, x2);
}
- }
+ }
if (y2 < y1) {
y2 = min (y1 - min_dimension, y2);
/* translate rect into item space and set */
- ArdourCanvas::Rect r (x1, y1, x2, y2);
+ ArdourCanvas::Rect r (x1, y1, x2, y2);
/* this drag is a _trackview_only == true drag, so the y1 and
* y2 (computed using current_pointer_y() and grab_y()) will be
{
framepos_t x1;
framepos_t x2;
-
- if (grab_frame() < last_pointer_frame()) {
- x1 = grab_frame ();
- x2 = last_pointer_frame ();
+ framepos_t grab = grab_frame ();
+ framepos_t lpf = last_pointer_frame ();
+
+ if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
+ grab = raw_grab_frame ();
+ lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
+ }
+
+ if (grab < lpf) {
+ x1 = grab;
+ x2 = lpf;
} else {
- x2 = grab_frame ();
- x1 = last_pointer_frame ();
+ x2 = grab;
+ x1 = lpf;
}
double y1;
double y2;
-
+
if (current_pointer_y() < grab_y()) {
y1 = current_pointer_y();
y2 = grab_y();
add_midi_region (mtv);
do_deselect = false;
}
- }
+ }
/* do not deselect if Primary or Tertiary (toggle-select or
* extend-select are pressed.
*/
- if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
- !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
+ if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
+ !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
do_deselect) {
deselect_things ();
}
: Drag (e, i)
, _operation (o)
, _add (false)
- , _original_pointer_time_axis (-1)
, _time_selection_at_start (!_editor->get_selection().time.empty())
{
DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
-
+
if (_time_selection_at_start) {
start_at_start = _editor->get_selection().time.start();
end_at_start = _editor->get_selection().time.end_frame();
return;
}
- Gdk::Cursor* cursor = 0;
+ Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
switch (_operation) {
case CreateSelection:
} else {
show_verbose_cursor_time (adjusted_current_frame (event));
}
-
- _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
}
void
_editor->set_selected_track_as_side_effect (Selection::Add);
_editor->clicked_selection = _editor->selection->add (start, end);
_add = false;
-
+
} else {
/* new selection */
_editor->clicked_selection = _editor->selection->set (start, end);
}
}
-
- //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
- // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
+
+ //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
+ // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
if (atest) {
_editor->selection->add (atest);
- break;
+ break;
}
-
+
/* select all tracks within the rectangle that we've marked out so far */
TrackViewList new_selection;
TrackViewList& all_tracks (_editor->track_views);
}
break;
-
+
case SelectionMove:
start = _editor->selection->time[_editor->clicked_selection].start;
if (start != end) {
switch (_operation) {
- case SelectionMove:
+ case SelectionMove:
if (_time_selection_at_start) {
_editor->selection->move_time (distance);
}
{
Session* s = _editor->session();
+ _editor->begin_reversible_selection_op (X_("Change Time Selection"));
if (movement_occurred) {
motion (event, false);
/* XXX this is not object-oriented programming at all. ick */
if ( s->get_play_range() && s->transport_rolling() ) {
s->request_play_range (&_editor->selection->time, true);
} else {
- if (Config->get_follow_edits() && !s->transport_rolling()) {
+ if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
if (_operation == SelectionEndTrim)
_editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
else
if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
_editor->selection->set (_editor->clicked_axisview);
}
-
+
if (s && s->get_play_range () && s->transport_rolling()) {
s->request_stop (false, false);
}
_editor->stop_canvas_autoscroll ();
_editor->clicked_selection = 0;
+ _editor->commit_reversible_selection_op ();
}
void
{
DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
- _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
+ _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
ArdourCanvas::Rect (0.0, 0.0, 0.0,
physical_screen_height (_editor->get_window())));
_drag_rect->hide ();
- _drag_rect->set_fill_color (ARDOUR_UI::config()->get_RangeDragRect());
- _drag_rect->set_outline_color (ARDOUR_UI::config()->get_RangeDragRect());
+ _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
+ _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
+}
+
+RangeMarkerBarDrag::~RangeMarkerBarDrag()
+{
+ /* normal canvas items will be cleaned up when their parent group is deleted. But
+ this item is created as the child of a long-lived parent group, and so we
+ need to explicitly delete it.
+ */
+ delete _drag_rect;
}
void
return;
}
- Gdk::Cursor* cursor = 0;
+ Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
if (!_editor->temp_location) {
_editor->temp_location = new Location (*_editor->session());
* nothing */
} else { /* operation == CreateRangeMarker || CreateSkipMarker */
-
framepos_t start;
framepos_t end;
}
void
-RangeMarkerBarDrag::aborted (bool)
+RangeMarkerBarDrag::aborted (bool movement_occured)
{
- /* XXX: TODO */
+ if (movement_occured) {
+ _drag_rect->hide ();
+ }
}
void
} else {
_region->unique_select (_primary);
}
+
+ _editor->begin_reversible_selection_op(X_("Select Note Press"));
+ _editor->commit_reversible_selection_op();
}
}
}
char buf[12];
uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
-
+
snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
(int) floor ((double)new_note));
{
if (!moved) {
/* no motion - select note */
-
+
if (_editor->current_mouse_mode() == Editing::MouseObject ||
_editor->current_mouse_mode() == Editing::MouseDraw) {
-
+
+ bool changed = false;
+
if (_was_selected) {
bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
if (add) {
_region->note_deselected (_primary);
+ changed = true;
}
} else {
bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
if (!extend && !add && _region->selection_size() > 1) {
_region->unique_select (_primary);
+ changed = true;
} else if (extend) {
_region->note_selected (_primary, true, true);
+ changed = true;
} else {
/* it was added during button press */
}
}
+
+ if (changed) {
+ _editor->begin_reversible_selection_op(X_("Select Note Release"));
+ _editor->commit_reversible_selection_op();
+ }
}
} else {
_region->note_dropped (_primary, total_dx(), total_dy());
AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
: Drag (editor, atv->base_item ())
, _ranges (r)
+ , _y_origin (atv->y_position())
, _nothing_to_drag (false)
{
DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
- y_origin = atv->y_position();
setup (atv->lines ());
}
-/** Make an AutomationRangeDrag for region gain lines */
-AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
+/** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
+AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
: Drag (editor, rv->get_canvas_group ())
, _ranges (r)
+ , _y_origin (rv->get_time_axis_view().y_position())
, _nothing_to_drag (false)
+ , _integral (false)
{
DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
list<boost::shared_ptr<AutomationLine> > lines;
- lines.push_back (rv->get_gain_line ());
- y_origin = rv->get_time_axis_view().y_position();
+
+ AudioRegionView* audio_view;
+ AutomationRegionView* automation_view;
+ if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
+ lines.push_back (audio_view->get_gain_line ());
+ } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
+ lines.push_back (automation_view->line ());
+ _integral = true;
+ } else {
+ error << _("Automation range drag created for invalid region type") << endmsg;
+ }
+
setup (lines);
}
double
AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
{
- return 1.0 - ((global_y - y_origin) / line->height());
+ return 1.0 - ((global_y - _y_origin) / line->height());
+}
+
+double
+AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
+{
+ const double v = list->eval(x);
+ return _integral ? rint(v) : v;
}
void
double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
- the_list->editor_add (p, the_list->eval (p));
- the_list->editor_add (q, the_list->eval (q));
+ the_list->editor_add (p, value (the_list, p));
+ the_list->editor_add (q, value (the_list, q));
}
/* same thing for the end */
double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
- the_list->editor_add (p, the_list->eval (p));
- the_list->editor_add (q, the_list->eval (q));
+ the_list->editor_add (p, value (the_list, p));
+ the_list->editor_add (q, value (the_list, q));
}
}
i->line->end_drag (false, 0);
}
- _editor->session()->commit_reversible_command ();
+ _editor->commit_reversible_command ();
}
void
y1 = max (0.0, y1 - y);
y2 = max (0.0, y2 - y);
-
+
_region_view->update_vertical_drag_selection (
y1,
y2,
/* We just want to select things at the end of the drag, not during it */
return;
}
-
+
Selection::Operation op = ArdourKeyboard::selection_type (button_state);
-
- _editor->begin_reversible_command (_("rubberband selection"));
+
+ _editor->begin_reversible_selection_op (X_("rubberband selection"));
+
_editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
- _editor->commit_reversible_command ();
+
+ _editor->commit_reversible_selection_op ();
}
void
EditorRubberbandSelectDrag::deselect_things ()
{
- if (!getenv("ARDOUR_SAE")) {
- _editor->selection->clear_tracks();
- }
+ _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
+
+ _editor->selection->clear_tracks();
_editor->selection->clear_regions();
_editor->selection->clear_points ();
_editor->selection->clear_lines ();
+ _editor->selection->clear_midi_notes ();
+
+ _editor->commit_reversible_selection_op();
}
NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
, _region_view (rv)
, _drag_rect (0)
{
-
+ _note[0] = _note[1] = 0;
}
NoteCreateDrag::~NoteCreateDrag ()
NoteCreateDrag::grid_frames (framepos_t t) const
{
bool success;
- Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
+ Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
if (!success) {
- grid_beats = Evoral::MusicalTime(1);
+ grid_beats = Evoral::Beats(1);
}
return _region_view->region_beats_to_region_frames (grid_beats);
NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
{
Drag::start_grab (event, cursor);
-
+
_drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
framepos_t pf = _drags->current_pointer_frame ();
}
_note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
+ _note[1] = _note[0];
MidiStreamView* sv = _region_view->midi_stream_view ();
double const x = _editor->sample_to_pixel (_note[0]);
if (!had_movement) {
return;
}
-
+
framepos_t const start = min (_note[0], _note[1]);
framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
framecnt_t const g = grid_frames (start);
- Evoral::MusicalTime const one_tick = Evoral::MusicalTime::ticks(1);
-
+ Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
+
if (_editor->snap_mode() == SnapNormal && length < g) {
length = g;
}
- Evoral::MusicalTime length_beats = max (
+ Evoral::Beats length_beats = max (
one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
_region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
void
NoteCreateDrag::aborted (bool)
{
-
+
}
CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
}
new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
-
+
_editor->begin_reversible_command ("xfade trim");
- ar->playlist()->clear_owned_changes ();
+ ar->playlist()->clear_owned_changes ();
if (start) {
ar->set_fade_in_length (new_length);
CrossfadeEdgeDrag::aborted (bool)
{
if (start) {
- arv->redraw_start_xfade ();
+ // arv->redraw_start_xfade ();
} else {
- arv->redraw_end_xfade ();
+ // arv->redraw_end_xfade ();
}
}
_editor->get_track_canvas()->canvas()->re_enter();
framepos_t pos = _drags->current_pointer_frame();
-
+
line->hide ();
RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);