2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "verbose_cursor.h"
70 using namespace ARDOUR;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75 using namespace ArdourCanvas;
77 using Gtkmm2ext::Keyboard;
79 double ControlPointDrag::_zero_gain_fraction = -1.0;
81 DragManager::DragManager (Editor* e)
84 , _current_pointer_frame (0)
88 DragManager::~DragManager ()
93 /** Call abort for each active drag */
99 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
104 if (!_drags.empty ()) {
105 _editor->set_follow_playhead (_old_follow_playhead, false);
109 _editor->abort_reversible_command();
115 DragManager::add (Drag* d)
117 d->set_manager (this);
118 _drags.push_back (d);
122 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
124 d->set_manager (this);
125 _drags.push_back (d);
130 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
132 /* Prevent follow playhead during the drag to be nice to the user */
133 _old_follow_playhead = _editor->follow_playhead ();
134 _editor->set_follow_playhead (false);
136 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
138 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
139 (*i)->start_grab (e, c);
143 /** Call end_grab for each active drag.
144 * @return true if any drag reported movement having occurred.
147 DragManager::end_grab (GdkEvent* e)
152 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
153 bool const t = (*i)->end_grab (e);
164 _editor->set_follow_playhead (_old_follow_playhead, false);
170 DragManager::mark_double_click ()
172 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
173 (*i)->set_double_click (true);
178 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
182 /* calling this implies that we expect the event to have canvas
185 * Can we guarantee that this is true?
188 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
190 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
191 bool const t = (*i)->motion_handler (e, from_autoscroll);
192 /* run all handlers; return true if at least one of them
193 returns true (indicating that the event has been handled).
205 DragManager::have_item (ArdourCanvas::Item* i) const
207 list<Drag*>::const_iterator j = _drags.begin ();
208 while (j != _drags.end() && (*j)->item () != i) {
212 return j != _drags.end ();
215 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
218 , _pointer_frame_offset (0)
219 , _x_constrained (false)
220 , _y_constrained (false)
221 , _trackview_only (trackview_only)
222 , _move_threshold_passed (false)
223 , _starting_point_passed (false)
224 , _initially_vertical (false)
225 , _was_double_click (false)
226 , _raw_grab_frame (0)
228 , _last_pointer_frame (0)
235 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
241 _cursor_ctx = CursorContext::create (*_editor, cursor);
243 _cursor_ctx->change (cursor);
250 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
253 /* we set up x/y dragging constraints on first move */
255 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
257 setup_pointer_frame_offset ();
258 _grab_frame = adjusted_frame (_raw_grab_frame, event);
259 _last_pointer_frame = _grab_frame;
260 _last_pointer_x = _grab_x;
262 if (_trackview_only) {
263 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
266 _last_pointer_y = _grab_y;
270 if (!_editor->cursors()->is_invalid (cursor)) {
271 /* CAIROCANVAS need a variant here that passes *cursor */
272 _cursor_ctx = CursorContext::create (*_editor, cursor);
275 if (_editor->session() && _editor->session()->transport_rolling()) {
278 _was_rolling = false;
281 switch (_editor->snap_type()) {
282 case SnapToRegionStart:
283 case SnapToRegionEnd:
284 case SnapToRegionSync:
285 case SnapToRegionBoundary:
286 _editor->build_region_boundary_cache ();
293 /** Call to end a drag `successfully'. Ungrabs item and calls
294 * subclass' finished() method.
296 * @param event GDK event, or 0.
297 * @return true if some movement occurred, otherwise false.
300 Drag::end_grab (GdkEvent* event)
302 _editor->stop_canvas_autoscroll ();
306 finished (event, _move_threshold_passed);
308 _editor->verbose_cursor()->hide ();
311 return _move_threshold_passed;
315 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
319 if (f > _pointer_frame_offset) {
320 pos = f - _pointer_frame_offset;
324 _editor->snap_to_with_modifier (pos, event);
331 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
333 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
337 Drag::snap_delta (guint state) const
339 if (ArdourKeyboard::indicates_snap_delta (state)) {
347 Drag::current_pointer_x() const
349 return _drags->current_pointer_x ();
353 Drag::current_pointer_y () const
355 if (!_trackview_only) {
356 return _drags->current_pointer_y ();
359 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
363 Drag::setup_snap_delta (framepos_t pos)
365 framepos_t temp = pos;
366 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
367 _snap_delta = temp - pos;
371 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
373 /* check to see if we have moved in any way that matters since the last motion event */
374 if (_move_threshold_passed &&
375 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
376 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
380 pair<framecnt_t, int> const threshold = move_threshold ();
382 bool const old_move_threshold_passed = _move_threshold_passed;
384 if (!_move_threshold_passed) {
386 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
387 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
389 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
392 if (active (_editor->mouse_mode) && _move_threshold_passed) {
394 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
396 if (old_move_threshold_passed != _move_threshold_passed) {
400 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
401 _initially_vertical = true;
403 _initially_vertical = false;
405 /** check constraints for this drag.
406 * Note that the current convention is to use "contains" for
407 * key modifiers during motion and "equals" when initiating a drag.
408 * In this case we haven't moved yet, so "equals" applies here.
410 if (Config->get_edit_mode() != Lock) {
411 if (event->motion.state & Gdk::BUTTON2_MASK) {
412 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
413 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
414 _x_constrained = false;
415 _y_constrained = true;
417 _x_constrained = true;
418 _y_constrained = false;
420 } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
421 // if dragging normally, the motion is constrained to the first direction of movement.
422 if (_initially_vertical) {
423 _x_constrained = true;
424 _y_constrained = false;
426 _x_constrained = false;
427 _y_constrained = true;
431 if (event->button.state & Gdk::BUTTON2_MASK) {
432 _x_constrained = false;
434 _x_constrained = true;
436 _y_constrained = false;
440 if (!from_autoscroll) {
441 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
444 if (!_editor->autoscroll_active() || from_autoscroll) {
447 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
449 motion (event, first_move && !_starting_point_passed);
451 if (first_move && !_starting_point_passed) {
452 _starting_point_passed = true;
455 _last_pointer_x = _drags->current_pointer_x ();
456 _last_pointer_y = current_pointer_y ();
457 _last_pointer_frame = adjusted_current_frame (event);
467 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
475 aborted (_move_threshold_passed);
477 _editor->stop_canvas_autoscroll ();
478 _editor->verbose_cursor()->hide ();
482 Drag::show_verbose_cursor_time (framepos_t frame)
484 _editor->verbose_cursor()->set_time (frame);
485 _editor->verbose_cursor()->show ();
489 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
491 _editor->verbose_cursor()->set_duration (start, end);
492 _editor->verbose_cursor()->show ();
496 Drag::show_verbose_cursor_text (string const & text)
498 _editor->verbose_cursor()->set (text);
499 _editor->verbose_cursor()->show ();
502 boost::shared_ptr<Region>
503 Drag::add_midi_region (MidiTimeAxisView* view)
505 if (_editor->session()) {
506 const TempoMap& map (_editor->session()->tempo_map());
507 framecnt_t pos = grab_frame();
508 const Meter& m = map.meter_at (pos);
509 /* not that the frame rate used here can be affected by pull up/down which
512 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
513 return view->add_region (grab_frame(), len, true);
516 return boost::shared_ptr<Region>();
519 struct EditorOrderTimeAxisViewSorter {
520 bool operator() (TimeAxisView* a, TimeAxisView* b) {
521 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
522 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
524 return ra->route()->order_key () < rb->route()->order_key ();
528 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
533 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
535 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
536 as some of the regions we are dragging may be on such tracks.
539 TrackViewList track_views = _editor->track_views;
540 track_views.sort (EditorOrderTimeAxisViewSorter ());
542 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
543 _time_axis_views.push_back (*i);
545 TimeAxisView::Children children_list = (*i)->get_child_list ();
546 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
547 _time_axis_views.push_back (j->get());
551 /* the list of views can be empty at this point if this is a region list-insert drag
554 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
555 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
558 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
562 RegionDrag::region_going_away (RegionView* v)
564 list<DraggingView>::iterator i = _views.begin ();
565 while (i != _views.end() && i->view != v) {
569 if (i != _views.end()) {
574 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
575 * or -1 if it is not found.
578 RegionDrag::find_time_axis_view (TimeAxisView* t) const
581 int const N = _time_axis_views.size ();
582 while (i < N && _time_axis_views[i] != t) {
586 if (_time_axis_views[i] != t) {
593 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
594 : RegionDrag (e, i, p, v)
597 , _last_pointer_time_axis_view (0)
598 , _last_pointer_layer (0)
603 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
607 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
609 Drag::start_grab (event, cursor);
610 setup_snap_delta (_last_frame_position);
612 show_verbose_cursor_time (_last_frame_position);
614 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
616 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
617 assert(_last_pointer_time_axis_view >= 0);
618 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
623 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
625 /* compute the amount of pointer motion in frames, and where
626 the region would be if we moved it by that much.
628 *pending_region_position = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
630 framepos_t sync_frame;
631 framecnt_t sync_offset;
634 sync_offset = _primary->region()->sync_offset (sync_dir);
636 /* we don't handle a sync point that lies before zero.
638 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
640 sync_frame = *pending_region_position + (sync_dir * sync_offset);
642 _editor->snap_to_with_modifier (sync_frame, event);
644 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - snap_delta (event->button.state);
647 *pending_region_position = _last_frame_position;
650 if (*pending_region_position > max_framepos - _primary->region()->length()) {
651 *pending_region_position = _last_frame_position;
656 bool const x_move_allowed = !_x_constrained;
658 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
660 /* x movement since last time (in pixels) */
661 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
663 /* total x movement */
664 framecnt_t total_dx = *pending_region_position;
665 if (regions_came_from_canvas()) {
666 total_dx = total_dx - grab_frame ();
669 /* check that no regions have gone off the start of the session */
670 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
671 if ((i->view->region()->position() + total_dx) < 0) {
673 *pending_region_position = _last_frame_position;
684 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
690 const int tavsize = _time_axis_views.size();
691 const int dt = delta > 0 ? +1 : -1;
693 int target = start + delta - skip;
695 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
696 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
698 while (current >= 0 && current != target) {
700 if (current < 0 && dt < 0) {
703 if (current >= tavsize && dt > 0) {
706 if (current < 0 || current >= tavsize) {
710 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
711 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
715 if (distance_only && current == start + delta) {
723 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
725 if (_y_constrained) {
729 const int tavsize = _time_axis_views.size();
730 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
731 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
732 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
734 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
735 /* already in the drop zone */
736 if (delta_track >= 0) {
737 /* downward motion - OK if others are still not in the dropzone */
746 } else if (n >= tavsize) {
747 /* downward motion into drop zone. That's fine. */
751 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
752 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
753 /* not a track, or the wrong type */
757 double const l = i->layer + delta_layer;
759 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
760 mode to allow the user to place a region below another on layer 0.
762 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
763 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
764 If it has, the layers will be munged later anyway, so it's ok.
770 /* all regions being dragged are ok with this change */
774 struct DraggingViewSorter {
775 bool operator() (const DraggingView& a, const DraggingView& b) {
776 return a.time_axis_view < b.time_axis_view;
781 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
783 double delta_layer = 0;
784 int delta_time_axis_view = 0;
785 int current_pointer_time_axis_view = -1;
787 assert (!_views.empty ());
789 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
791 /* Find the TimeAxisView that the pointer is now over */
792 const double cur_y = current_pointer_y ();
793 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
794 TimeAxisView* tv = r.first;
796 if (!tv && cur_y < 0) {
797 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
801 /* find drop-zone y-position */
802 Coord last_track_bottom_edge;
803 last_track_bottom_edge = 0;
804 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
805 if (!(*t)->hidden()) {
806 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
811 if (tv && tv->view()) {
812 /* the mouse is over a track */
813 double layer = r.second;
815 if (first_move && tv->view()->layer_display() == Stacked) {
816 tv->view()->set_layer_display (Expanded);
819 /* Here's the current pointer position in terms of time axis view and layer */
820 current_pointer_time_axis_view = find_time_axis_view (tv);
821 assert(current_pointer_time_axis_view >= 0);
823 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
825 /* Work out the change in y */
827 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
828 if (!rtv || !rtv->is_track()) {
829 /* ignore busses early on. we can't move any regions on them */
830 } else if (_last_pointer_time_axis_view < 0) {
831 /* Was in the drop-zone, now over a track.
832 * Hence it must be an upward move (from the bottom)
834 * track_index is still -1, so delta must be set to
835 * move up the correct number of tracks from the bottom.
837 * This is necessary because steps may be skipped if
838 * the bottom-most track is not a valid target and/or
839 * if there are hidden tracks at the bottom.
840 * Hence the initial offset (_ddropzone) as well as the
841 * last valid pointer position (_pdropzone) need to be
842 * taken into account.
844 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
846 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
849 /* TODO needs adjustment per DraggingView,
851 * e.g. select one region on the top-layer of a track
852 * and one region which is at the bottom-layer of another track
855 * Indicated drop-zones and layering is wrong.
856 * and may infer additional layers on the target-track
857 * (depending how many layers the original track had).
859 * Or select two regions (different layers) on a same track,
860 * move across a non-layer track.. -> layering info is lost.
861 * on drop either of the regions may be on top.
863 * Proposed solution: screw it :) well,
864 * don't use delta_layer, use an absolute value
865 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
866 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
867 * 3) iterate over all DraggingView, find the one that is over the track with most layers
868 * 4) proportionally scale layer to layers available on target
870 delta_layer = current_pointer_layer - _last_pointer_layer;
873 /* for automation lanes, there is a TimeAxisView but no ->view()
874 * if (!tv) -> dropzone
876 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
877 /* Moving into the drop-zone.. */
878 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
879 /* delta_time_axis_view may not be sufficient to move into the DZ
880 * the mouse may enter it, but it may not be a valid move due to
883 * -> remember the delta needed to move into the dropzone
885 _ddropzone = delta_time_axis_view;
886 /* ..but subtract hidden tracks (or routes) at the bottom.
887 * we silently move mover them
889 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
890 - _time_axis_views.size();
892 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
893 /* move around inside the zone.
894 * This allows to move further down until all regions are in the zone.
896 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
897 assert(ptr_y >= last_track_bottom_edge);
898 assert(_ddropzone > 0);
900 /* calculate mouse position in 'tracks' below last track. */
901 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
902 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
904 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
906 delta_time_axis_view = dzpos - _pdropzone;
907 } else if (dzpos < _pdropzone && _ndropzone > 0) {
908 // move up inside the DZ
909 delta_time_axis_view = dzpos - _pdropzone;
913 /* Work out the change in x */
914 framepos_t pending_region_position;
915 double const x_delta = compute_x_delta (event, &pending_region_position);
916 _last_frame_position = pending_region_position;
918 /* calculate hidden tracks in current y-axis delta */
920 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
921 /* The mouse is more than one track below the dropzone.
922 * distance calculation is not needed (and would not work, either
923 * because the dropzone is "packed").
925 * Except when [partially] moving regions out of dropzone in a large step.
926 * (the mouse may or may not remain in the DZ)
927 * Hidden tracks at the bottom of the TAV need to be skipped.
929 * This also handles the case if the mouse entered the DZ
930 * in a large step (exessive delta), either due to fast-movement,
931 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
933 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
934 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
936 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
937 -_time_axis_views.size() - dt;
940 else if (_last_pointer_time_axis_view < 0) {
941 /* Moving out of the zone. Check for hidden tracks at the bottom. */
942 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
943 -_time_axis_views.size() - delta_time_axis_view;
945 /* calculate hidden tracks that are skipped by the pointer movement */
946 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
947 - _last_pointer_time_axis_view
948 - delta_time_axis_view;
951 /* Verify change in y */
952 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
953 /* this y movement is not allowed, so do no y movement this time */
954 delta_time_axis_view = 0;
959 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
960 /* haven't reached next snap point, and we're not switching
961 trackviews nor layers. nothing to do.
966 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
967 PlaylistDropzoneMap playlist_dropzone_map;
968 _ndropzone = 0; // number of elements currently in the dropzone
971 /* sort views by time_axis.
972 * This retains track order in the dropzone, regardless
973 * of actual selection order
975 _views.sort (DraggingViewSorter());
977 /* count number of distinct tracks of all regions
978 * being dragged, used for dropzone.
981 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
982 if (i->time_axis_view != prev_track) {
983 prev_track = i->time_axis_view;
989 _views.back().time_axis_view -
990 _views.front().time_axis_view;
992 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
993 - _views.back().time_axis_view;
995 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
999 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1001 RegionView* rv = i->view;
1006 if (rv->region()->locked() || rv->region()->video_locked()) {
1013 /* reparent the regionview into a group above all
1017 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1018 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1019 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1020 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1021 /* move the item so that it continues to appear at the
1022 same location now that its parent has changed.
1024 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1027 /* If we have moved tracks, we'll fudge the layer delta so that the
1028 region gets moved back onto layer 0 on its new track; this avoids
1029 confusion when dragging regions from non-zero layers onto different
1032 double this_delta_layer = delta_layer;
1033 if (delta_time_axis_view != 0) {
1034 this_delta_layer = - i->layer;
1037 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1039 int track_index = i->time_axis_view + this_delta_time_axis_view;
1040 assert(track_index >= 0);
1042 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1043 /* Track is in the Dropzone */
1045 i->time_axis_view = track_index;
1046 assert(i->time_axis_view >= (int) _time_axis_views.size());
1049 double yposition = 0;
1050 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1051 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1054 /* store index of each new playlist as a negative count, starting at -1 */
1056 if (pdz == playlist_dropzone_map.end()) {
1057 /* compute where this new track (which doesn't exist yet) will live
1060 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1062 /* How high is this region view ? */
1064 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1065 ArdourCanvas::Rect bbox;
1068 bbox = obbox.get ();
1071 last_track_bottom_edge += bbox.height();
1073 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1076 yposition = pdz->second;
1079 /* values are zero or negative, hence the use of min() */
1080 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1085 /* The TimeAxisView that this region is now over */
1086 TimeAxisView* current_tv = _time_axis_views[track_index];
1088 /* Ensure it is moved from stacked -> expanded if appropriate */
1089 if (current_tv->view()->layer_display() == Stacked) {
1090 current_tv->view()->set_layer_display (Expanded);
1093 /* We're only allowed to go -ve in layer on Expanded views */
1094 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1095 this_delta_layer = - i->layer;
1099 rv->set_height (current_tv->view()->child_height ());
1101 /* Update show/hidden status as the region view may have come from a hidden track,
1102 or have moved to one.
1104 if (current_tv->hidden ()) {
1105 rv->get_canvas_group()->hide ();
1107 rv->get_canvas_group()->show ();
1110 /* Update the DraggingView */
1111 i->time_axis_view = track_index;
1112 i->layer += this_delta_layer;
1115 _editor->mouse_brush_insert_region (rv, pending_region_position);
1119 /* Get the y coordinate of the top of the track that this region is now over */
1120 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1122 /* And adjust for the layer that it should be on */
1123 StreamView* cv = current_tv->view ();
1124 switch (cv->layer_display ()) {
1128 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1131 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1135 /* need to get the parent of the regionview
1136 * canvas group and get its position in
1137 * equivalent coordinate space as the trackview
1138 * we are now dragging over.
1141 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1146 /* Now move the region view */
1147 rv->move (x_delta, y_delta);
1149 } /* foreach region */
1151 _total_x_delta += x_delta;
1153 if (x_delta != 0 && !_brushing) {
1154 show_verbose_cursor_time (_last_frame_position);
1157 /* keep track of pointer movement */
1159 /* the pointer is currently over a time axis view */
1161 if (_last_pointer_time_axis_view < 0) {
1162 /* last motion event was not over a time axis view
1163 * or last y-movement out of the dropzone was not valid
1166 if (delta_time_axis_view < 0) {
1167 /* in the drop zone, moving up */
1169 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1170 * We do not use negative _last_pointer_time_axis_view because
1171 * the dropzone is "packed" (the actual track offset is ignored)
1173 * As opposed to the actual number
1174 * of elements in the dropzone (_ndropzone)
1175 * _pdropzone is not constrained. This is necessary
1176 * to allow moving multiple regions with y-distance
1179 * There can be 0 elements in the dropzone,
1180 * even though the drag-pointer is inside the DZ.
1183 * [ Audio-track, Midi-track, Audio-track, DZ ]
1184 * move regions from both audio tracks at the same time into the
1185 * DZ by grabbing the region in the bottom track.
1187 assert(current_pointer_time_axis_view >= 0);
1188 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1192 /* only move out of the zone if the movement is OK */
1193 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1194 assert(delta_time_axis_view < 0);
1195 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1196 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1197 * the current position can be calculated as follows:
1199 // a well placed oofus attack can still throw this off.
1200 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1201 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1204 /* last motion event was also over a time axis view */
1205 _last_pointer_time_axis_view += delta_time_axis_view;
1206 assert(_last_pointer_time_axis_view >= 0);
1211 /* the pointer is not over a time axis view */
1212 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1213 _pdropzone += delta_time_axis_view - delta_skip;
1214 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1217 _last_pointer_layer += delta_layer;
1221 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1223 if (_copy && first_move) {
1225 if (_x_constrained) {
1226 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1228 _editor->begin_reversible_command (Operations::region_copy);
1231 /* duplicate the regionview(s) and region(s) */
1233 list<DraggingView> new_regionviews;
1235 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1237 RegionView* rv = i->view;
1238 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1239 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1241 const boost::shared_ptr<const Region> original = rv->region();
1242 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1243 region_copy->set_position (original->position());
1244 /* need to set this so that the drop zone code can work. This doesn't
1245 actually put the region into the playlist, but just sets a weak pointer
1248 region_copy->set_playlist (original->playlist());
1252 boost::shared_ptr<AudioRegion> audioregion_copy
1253 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1255 nrv = new AudioRegionView (*arv, audioregion_copy);
1257 boost::shared_ptr<MidiRegion> midiregion_copy
1258 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1259 nrv = new MidiRegionView (*mrv, midiregion_copy);
1264 nrv->get_canvas_group()->show ();
1265 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1267 /* swap _primary to the copy */
1269 if (rv == _primary) {
1273 /* ..and deselect the one we copied */
1275 rv->set_selected (false);
1278 if (!new_regionviews.empty()) {
1280 /* reflect the fact that we are dragging the copies */
1282 _views = new_regionviews;
1284 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1287 } else if (!_copy && first_move) {
1289 if (_x_constrained) {
1290 _editor->begin_reversible_command (_("fixed time region drag"));
1292 _editor->begin_reversible_command (Operations::region_drag);
1296 RegionMotionDrag::motion (event, first_move);
1300 RegionMotionDrag::finished (GdkEvent *, bool)
1302 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1303 if (!(*i)->view()) {
1307 if ((*i)->view()->layer_display() == Expanded) {
1308 (*i)->view()->set_layer_display (Stacked);
1314 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1316 RegionMotionDrag::finished (ev, movement_occurred);
1318 if (!movement_occurred) {
1322 if (was_double_click() && !_views.empty()) {
1323 DraggingView dv = _views.front();
1324 dv.view->show_region_editor ();
1331 assert (!_views.empty ());
1333 /* We might have hidden region views so that they weren't visible during the drag
1334 (when they have been reparented). Now everything can be shown again, as region
1335 views are back in their track parent groups.
1337 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1338 i->view->get_canvas_group()->show ();
1341 bool const changed_position = (_last_frame_position != _primary->region()->position());
1342 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1343 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1363 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1367 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1369 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1374 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1375 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1376 uint32_t output_chan = region->n_channels();
1377 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1378 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1380 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1381 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1383 rtav->set_height (original->current_height());
1387 ChanCount one_midi_port (DataType::MIDI, 1);
1388 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1389 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1390 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1392 rtav->set_height (original->current_height());
1397 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1403 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1405 RegionSelection new_views;
1406 PlaylistSet modified_playlists;
1407 RouteTimeAxisView* new_time_axis_view = 0;
1410 /* all changes were made during motion event handlers */
1412 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1416 _editor->commit_reversible_command ();
1420 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1421 PlaylistMapping playlist_mapping;
1423 /* insert the regions into their new playlists */
1424 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1426 RouteTimeAxisView* dest_rtv = 0;
1428 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1434 if (changed_position && !_x_constrained) {
1435 where = i->view->region()->position() - drag_delta;
1437 where = i->view->region()->position();
1440 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1441 /* dragged to drop zone */
1443 PlaylistMapping::iterator pm;
1445 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1446 /* first region from this original playlist: create a new track */
1447 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1448 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1449 dest_rtv = new_time_axis_view;
1451 /* we already created a new track for regions from this playlist, use it */
1452 dest_rtv = pm->second;
1455 /* destination time axis view is the one we dragged to */
1456 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1459 if (dest_rtv != 0) {
1460 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1461 if (new_view != 0) {
1462 new_views.push_back (new_view);
1466 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1467 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1470 list<DraggingView>::const_iterator next = i;
1476 /* If we've created new regions either by copying or moving
1477 to a new track, we want to replace the old selection with the new ones
1480 if (new_views.size() > 0) {
1481 _editor->selection->set (new_views);
1484 /* write commands for the accumulated diffs for all our modified playlists */
1485 add_stateful_diff_commands_for_playlists (modified_playlists);
1487 _editor->commit_reversible_command ();
1491 RegionMoveDrag::finished_no_copy (
1492 bool const changed_position,
1493 bool const changed_tracks,
1494 framecnt_t const drag_delta
1497 RegionSelection new_views;
1498 PlaylistSet modified_playlists;
1499 PlaylistSet frozen_playlists;
1500 set<RouteTimeAxisView*> views_to_update;
1501 RouteTimeAxisView* new_time_axis_view = 0;
1504 /* all changes were made during motion event handlers */
1505 _editor->commit_reversible_command ();
1509 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1510 PlaylistMapping playlist_mapping;
1512 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1514 RegionView* rv = i->view;
1515 RouteTimeAxisView* dest_rtv = 0;
1517 if (rv->region()->locked() || rv->region()->video_locked()) {
1522 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1523 /* dragged to drop zone */
1525 PlaylistMapping::iterator pm;
1527 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1528 /* first region from this original playlist: create a new track */
1529 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1530 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1531 dest_rtv = new_time_axis_view;
1533 /* we already created a new track for regions from this playlist, use it */
1534 dest_rtv = pm->second;
1538 /* destination time axis view is the one we dragged to */
1539 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1544 double const dest_layer = i->layer;
1546 views_to_update.insert (dest_rtv);
1550 if (changed_position && !_x_constrained) {
1551 where = rv->region()->position() - drag_delta;
1553 where = rv->region()->position();
1556 if (changed_tracks) {
1558 /* insert into new playlist */
1560 RegionView* new_view = insert_region_into_playlist (
1561 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1564 if (new_view == 0) {
1569 new_views.push_back (new_view);
1571 /* remove from old playlist */
1573 /* the region that used to be in the old playlist is not
1574 moved to the new one - we use a copy of it. as a result,
1575 any existing editor for the region should no longer be
1578 rv->hide_region_editor();
1581 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1585 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1587 /* this movement may result in a crossfade being modified, or a layering change,
1588 so we need to get undo data from the playlist as well as the region.
1591 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1593 playlist->clear_changes ();
1596 rv->region()->clear_changes ();
1599 motion on the same track. plonk the previously reparented region
1600 back to its original canvas group (its streamview).
1601 No need to do anything for copies as they are fake regions which will be deleted.
1604 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1605 rv->get_canvas_group()->set_y_position (i->initial_y);
1608 /* just change the model */
1609 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1610 playlist->set_layer (rv->region(), dest_layer);
1613 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1615 r = frozen_playlists.insert (playlist);
1618 playlist->freeze ();
1621 rv->region()->set_position (where);
1623 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1626 if (changed_tracks) {
1628 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1629 was selected in all of them, then removing it from a playlist will have removed all
1630 trace of it from _views (i.e. there were N regions selected, we removed 1,
1631 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1632 corresponding regionview, and _views is now empty).
1634 This could have invalidated any and all iterators into _views.
1636 The heuristic we use here is: if the region selection is empty, break out of the loop
1637 here. if the region selection is not empty, then restart the loop because we know that
1638 we must have removed at least the region(view) we've just been working on as well as any
1639 that we processed on previous iterations.
1641 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1642 we can just iterate.
1646 if (_views.empty()) {
1657 /* If we've created new regions either by copying or moving
1658 to a new track, we want to replace the old selection with the new ones
1661 if (new_views.size() > 0) {
1662 _editor->selection->set (new_views);
1665 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1669 /* write commands for the accumulated diffs for all our modified playlists */
1670 add_stateful_diff_commands_for_playlists (modified_playlists);
1672 _editor->commit_reversible_command ();
1674 /* We have futzed with the layering of canvas items on our streamviews.
1675 If any region changed layer, this will have resulted in the stream
1676 views being asked to set up their region views, and all will be well.
1677 If not, we might now have badly-ordered region views. Ask the StreamViews
1678 involved to sort themselves out, just in case.
1681 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1682 (*i)->view()->playlist_layered ((*i)->track ());
1686 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1687 * @param region Region to remove.
1688 * @param playlist playlist To remove from.
1689 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1690 * that clear_changes () is only called once per playlist.
1693 RegionMoveDrag::remove_region_from_playlist (
1694 boost::shared_ptr<Region> region,
1695 boost::shared_ptr<Playlist> playlist,
1696 PlaylistSet& modified_playlists
1699 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1702 playlist->clear_changes ();
1705 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1709 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1710 * clearing the playlist's diff history first if necessary.
1711 * @param region Region to insert.
1712 * @param dest_rtv Destination RouteTimeAxisView.
1713 * @param dest_layer Destination layer.
1714 * @param where Destination position.
1715 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1716 * that clear_changes () is only called once per playlist.
1717 * @return New RegionView, or 0 if no insert was performed.
1720 RegionMoveDrag::insert_region_into_playlist (
1721 boost::shared_ptr<Region> region,
1722 RouteTimeAxisView* dest_rtv,
1725 PlaylistSet& modified_playlists
1728 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1729 if (!dest_playlist) {
1733 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1734 _new_region_view = 0;
1735 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1737 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1738 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1740 dest_playlist->clear_changes ();
1743 dest_playlist->add_region (region, where);
1745 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1746 dest_playlist->set_layer (region, dest_layer);
1751 assert (_new_region_view);
1753 return _new_region_view;
1757 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1759 _new_region_view = rv;
1763 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1765 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1766 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1768 _editor->session()->add_command (c);
1777 RegionMoveDrag::aborted (bool movement_occurred)
1781 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1782 list<DraggingView>::const_iterator next = i;
1791 RegionMotionDrag::aborted (movement_occurred);
1796 RegionMotionDrag::aborted (bool)
1798 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1800 StreamView* sview = (*i)->view();
1803 if (sview->layer_display() == Expanded) {
1804 sview->set_layer_display (Stacked);
1809 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1810 RegionView* rv = i->view;
1811 TimeAxisView* tv = &(rv->get_time_axis_view ());
1812 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1814 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1815 rv->get_canvas_group()->set_y_position (0);
1817 rv->move (-_total_x_delta, 0);
1818 rv->set_height (rtv->view()->child_height ());
1822 /** @param b true to brush, otherwise false.
1823 * @param c true to make copies of the regions being moved, otherwise false.
1825 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1826 : RegionMotionDrag (e, i, p, v, b)
1829 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1832 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1833 if (rtv && rtv->is_track()) {
1834 speed = rtv->track()->speed ();
1837 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1841 RegionMoveDrag::setup_pointer_frame_offset ()
1843 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1846 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1847 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1849 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1851 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1852 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1854 _primary = v->view()->create_region_view (r, false, false);
1856 _primary->get_canvas_group()->show ();
1857 _primary->set_position (pos, 0);
1858 _views.push_back (DraggingView (_primary, this, v));
1860 _last_frame_position = pos;
1862 _item = _primary->get_canvas_group ();
1866 RegionInsertDrag::finished (GdkEvent *, bool)
1868 int pos = _views.front().time_axis_view;
1869 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1871 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1873 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1874 _primary->get_canvas_group()->set_y_position (0);
1876 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1878 _editor->begin_reversible_command (Operations::insert_region);
1879 playlist->clear_changes ();
1880 playlist->add_region (_primary->region (), _last_frame_position);
1882 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1883 if (Config->get_edit_mode() == Ripple) {
1884 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1887 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1888 _editor->commit_reversible_command ();
1896 RegionInsertDrag::aborted (bool)
1903 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1904 : RegionMoveDrag (e, i, p, v, false, false)
1906 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1909 struct RegionSelectionByPosition {
1910 bool operator() (RegionView*a, RegionView* b) {
1911 return a->region()->position () < b->region()->position();
1916 RegionSpliceDrag::motion (GdkEvent* event, bool)
1918 /* Which trackview is this ? */
1920 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1921 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1923 /* The region motion is only processed if the pointer is over
1927 if (!tv || !tv->is_track()) {
1928 /* To make sure we hide the verbose canvas cursor when the mouse is
1929 not held over an audio track.
1931 _editor->verbose_cursor()->hide ();
1934 _editor->verbose_cursor()->show ();
1939 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1945 RegionSelection copy;
1946 _editor->selection->regions.by_position(copy);
1948 framepos_t const pf = adjusted_current_frame (event);
1950 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1952 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1958 boost::shared_ptr<Playlist> playlist;
1960 if ((playlist = atv->playlist()) == 0) {
1964 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1969 if (pf < (*i)->region()->last_frame() + 1) {
1973 if (pf > (*i)->region()->first_frame()) {
1979 playlist->shuffle ((*i)->region(), dir);
1984 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1986 RegionMoveDrag::finished (event, movement_occurred);
1990 RegionSpliceDrag::aborted (bool)
2000 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2003 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2005 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2006 RegionSelection to_ripple;
2007 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2008 if ((*i)->position() >= where) {
2009 to_ripple.push_back (rtv->view()->find_view(*i));
2013 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2014 if (!exclude.contains (*i)) {
2015 // the selection has already been added to _views
2017 if (drag_in_progress) {
2018 // do the same things that RegionMotionDrag::motion does when
2019 // first_move is true, for the region views that we're adding
2020 // to _views this time
2023 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2024 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2025 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2026 rvg->reparent (_editor->_drag_motion_group);
2028 // we only need to move in the y direction
2029 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2034 _views.push_back (DraggingView (*i, this, tav));
2040 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2043 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2044 // we added all the regions after the selection
2046 std::list<DraggingView>::iterator to_erase = i++;
2047 if (!_editor->selection->regions.contains (to_erase->view)) {
2048 // restore the non-selected regions to their original playlist & positions,
2049 // and then ripple them back by the length of the regions that were dragged away
2050 // do the same things as RegionMotionDrag::aborted
2052 RegionView *rv = to_erase->view;
2053 TimeAxisView* tv = &(rv->get_time_axis_view ());
2054 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2057 // plonk them back onto their own track
2058 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2059 rv->get_canvas_group()->set_y_position (0);
2063 // move the underlying region to match the view
2064 rv->region()->set_position (rv->region()->position() + amount);
2066 // restore the view to match the underlying region's original position
2067 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2070 rv->set_height (rtv->view()->child_height ());
2071 _views.erase (to_erase);
2077 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2079 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2081 return allow_moves_across_tracks;
2089 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2090 : RegionMoveDrag (e, i, p, v, false, false)
2092 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2093 // compute length of selection
2094 RegionSelection selected_regions = _editor->selection->regions;
2095 selection_length = selected_regions.end_frame() - selected_regions.start();
2097 // we'll only allow dragging to another track in ripple mode if all the regions
2098 // being dragged start off on the same track
2099 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2102 exclude = new RegionList;
2103 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2104 exclude->push_back((*i)->region());
2107 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2108 RegionSelection copy;
2109 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2111 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2112 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2114 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2115 // find ripple start point on each applicable playlist
2116 RegionView *first_selected_on_this_track = NULL;
2117 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2118 if ((*i)->region()->playlist() == (*pi)) {
2119 // region is on this playlist - it's the first, because they're sorted
2120 first_selected_on_this_track = *i;
2124 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2125 add_all_after_to_views (
2126 &first_selected_on_this_track->get_time_axis_view(),
2127 first_selected_on_this_track->region()->position(),
2128 selected_regions, false);
2131 if (allow_moves_across_tracks) {
2132 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2140 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2142 /* Which trackview is this ? */
2144 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2145 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2147 /* The region motion is only processed if the pointer is over
2151 if (!tv || !tv->is_track()) {
2152 /* To make sure we hide the verbose canvas cursor when the mouse is
2153 not held over an audiotrack.
2155 _editor->verbose_cursor()->hide ();
2159 framepos_t where = adjusted_current_frame (event);
2160 assert (where >= 0);
2162 double delta = compute_x_delta (event, &after);
2164 framecnt_t amount = _editor->pixel_to_sample (delta);
2166 if (allow_moves_across_tracks) {
2167 // all the originally selected regions were on the same track
2169 framecnt_t adjust = 0;
2170 if (prev_tav && tv != prev_tav) {
2171 // dragged onto a different track
2172 // remove the unselected regions from _views, restore them to their original positions
2173 // and add the regions after the drop point on the new playlist to _views instead.
2174 // undo the effect of rippling the previous playlist, and include the effect of removing
2175 // the dragged region(s) from this track
2177 remove_unselected_from_views (prev_amount, false);
2178 // ripple previous playlist according to the regions that have been removed onto the new playlist
2179 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2182 // move just the selected regions
2183 RegionMoveDrag::motion(event, first_move);
2185 // ensure that the ripple operation on the new playlist inserts selection_length time
2186 adjust = selection_length;
2187 // ripple the new current playlist
2188 tv->playlist()->ripple (where, amount+adjust, exclude);
2190 // add regions after point where drag entered this track to subsequent ripples
2191 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2194 // motion on same track
2195 RegionMoveDrag::motion(event, first_move);
2199 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2200 prev_position = where;
2202 // selection encompasses multiple tracks - just drag
2203 // cross-track drags are forbidden
2204 RegionMoveDrag::motion(event, first_move);
2207 if (!_x_constrained) {
2208 prev_amount += amount;
2211 _last_frame_position = after;
2215 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2217 if (!movement_occurred) {
2221 if (was_double_click() && !_views.empty()) {
2222 DraggingView dv = _views.front();
2223 dv.view->show_region_editor ();
2230 _editor->begin_reversible_command(_("Ripple drag"));
2232 // remove the regions being rippled from the dragging view, updating them to
2233 // their new positions
2234 remove_unselected_from_views (prev_amount, true);
2236 if (allow_moves_across_tracks) {
2238 // if regions were dragged across tracks, we've rippled any later
2239 // regions on the track the regions were dragged off, so we need
2240 // to add the original track to the undo record
2241 orig_tav->playlist()->clear_changes();
2242 vector<Command*> cmds;
2243 orig_tav->playlist()->rdiff (cmds);
2244 _editor->session()->add_commands (cmds);
2246 if (prev_tav && prev_tav != orig_tav) {
2247 prev_tav->playlist()->clear_changes();
2248 vector<Command*> cmds;
2249 prev_tav->playlist()->rdiff (cmds);
2250 _editor->session()->add_commands (cmds);
2253 // selection spanned multiple tracks - all will need adding to undo record
2255 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2256 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2258 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2259 (*pi)->clear_changes();
2260 vector<Command*> cmds;
2261 (*pi)->rdiff (cmds);
2262 _editor->session()->add_commands (cmds);
2266 // other modified playlists are added to undo by RegionMoveDrag::finished()
2267 RegionMoveDrag::finished (event, movement_occurred);
2268 _editor->commit_reversible_command();
2272 RegionRippleDrag::aborted (bool movement_occurred)
2274 RegionMoveDrag::aborted (movement_occurred);
2279 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2281 _view (dynamic_cast<MidiTimeAxisView*> (v))
2283 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2289 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2292 _region = add_midi_region (_view);
2293 _view->playlist()->freeze ();
2296 framepos_t const f = adjusted_current_frame (event);
2297 if (f < grab_frame()) {
2298 _region->set_position (f);
2301 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2302 so that if this region is duplicated, its duplicate starts on
2303 a snap point rather than 1 frame after a snap point. Otherwise things get
2304 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2305 place snapped notes at the start of the region.
2308 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2309 _region->set_length (len < 1 ? 1 : len);
2315 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2317 if (!movement_occurred) {
2318 add_midi_region (_view);
2320 _view->playlist()->thaw ();
2325 RegionCreateDrag::aborted (bool)
2328 _view->playlist()->thaw ();
2334 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2339 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2343 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2345 Gdk::Cursor* cursor;
2346 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2348 float x_fraction = cnote->mouse_x_fraction ();
2350 if (x_fraction > 0.0 && x_fraction < 0.25) {
2351 cursor = _editor->cursors()->left_side_trim;
2354 cursor = _editor->cursors()->right_side_trim;
2358 Drag::start_grab (event, cursor);
2360 region = &cnote->region_view();
2363 temp = region->snap_to_pixel (cnote->x0 (), true);
2364 _snap_delta = temp - cnote->x0 ();
2368 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2374 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2376 if (ms.size() > 1) {
2377 /* has to be relative, may make no sense otherwise */
2381 /* select this note; if it is already selected, preserve the existing selection,
2382 otherwise make this note the only one selected.
2384 region->note_selected (cnote, cnote->selected ());
2386 _editor->begin_reversible_command (_("resize notes"));
2388 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2389 MidiRegionSelection::iterator next;
2392 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2394 mrv->begin_resizing (at_front);
2401 NoteResizeDrag::motion (GdkEvent* event, bool /*first_move*/)
2403 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2404 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2405 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2407 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2411 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2413 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2414 if (_editor->snap_mode () != SnapOff) {
2418 if (_editor->snap_mode () == SnapOff) {
2420 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2421 if (apply_snap_delta) {
2427 if (apply_snap_delta) {
2431 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2437 NoteResizeDrag::finished (GdkEvent* event, bool /*movement_occurred*/)
2439 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2440 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2441 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2443 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2446 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2448 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2449 if (_editor->snap_mode () != SnapOff) {
2453 if (_editor->snap_mode () == SnapOff) {
2455 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2456 if (apply_snap_delta) {
2461 if (apply_snap_delta) {
2464 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2468 _editor->commit_reversible_command ();
2472 NoteResizeDrag::aborted (bool)
2474 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2475 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2476 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2478 mrv->abort_resizing ();
2483 AVDraggingView::AVDraggingView (RegionView* v)
2486 initial_position = v->region()->position ();
2489 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2492 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2495 TrackViewList empty;
2497 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2498 std::list<RegionView*> views = rs.by_layer();
2500 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2501 RegionView* rv = (*i);
2502 if (!rv->region()->video_locked()) {
2505 _views.push_back (AVDraggingView (rv));
2510 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2512 Drag::start_grab (event);
2513 if (_editor->session() == 0) {
2517 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2518 _max_backwards_drag = (
2519 ARDOUR_UI::instance()->video_timeline->get_duration()
2520 + ARDOUR_UI::instance()->video_timeline->get_offset()
2521 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2524 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2525 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2526 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2529 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2532 Timecode::Time timecode;
2533 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2534 snprintf (buf, sizeof (buf), "Video Start:\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, (_startdrag_video_offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2535 show_verbose_cursor_text (buf);
2539 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2541 if (_editor->session() == 0) {
2544 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2548 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2549 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2551 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2552 dt = - _max_backwards_drag;
2555 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2556 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2558 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2559 RegionView* rv = i->view;
2560 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2563 rv->region()->clear_changes ();
2564 rv->region()->suspend_property_changes();
2566 rv->region()->set_position(i->initial_position + dt);
2567 rv->region_changed(ARDOUR::Properties::position);
2570 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2571 Timecode::Time timecode;
2572 Timecode::Time timediff;
2574 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2575 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2576 snprintf (buf, sizeof (buf),
2577 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2578 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2579 , _("Video Start:"),
2580 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2582 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2584 show_verbose_cursor_text (buf);
2588 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2590 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2594 if (!movement_occurred || ! _editor->session()) {
2598 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2600 _editor->begin_reversible_command (_("Move Video"));
2602 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2603 ARDOUR_UI::instance()->video_timeline->save_undo();
2604 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2605 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2607 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2608 i->view->drag_end();
2609 i->view->region()->resume_property_changes ();
2611 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2614 _editor->session()->maybe_update_session_range(
2615 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2616 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2620 _editor->commit_reversible_command ();
2624 VideoTimeLineDrag::aborted (bool)
2626 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2629 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2630 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2632 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2633 i->view->region()->resume_property_changes ();
2634 i->view->region()->set_position(i->initial_position);
2638 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2639 : RegionDrag (e, i, p, v)
2640 , _preserve_fade_anchor (preserve_fade_anchor)
2641 , _jump_position_when_done (false)
2643 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2647 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2650 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2651 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2653 if (tv && tv->is_track()) {
2654 speed = tv->track()->speed();
2657 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2658 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2659 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2661 framepos_t const pf = adjusted_current_frame (event);
2662 setup_snap_delta (region_start);
2664 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2665 /* Move the contents of the region around without changing the region bounds */
2666 _operation = ContentsTrim;
2667 Drag::start_grab (event, _editor->cursors()->trimmer);
2669 /* These will get overridden for a point trim.*/
2670 if (pf < (region_start + region_length/2)) {
2671 /* closer to front */
2672 _operation = StartTrim;
2673 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2674 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2676 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2680 _operation = EndTrim;
2681 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2682 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2684 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2688 /* jump trim disabled for now
2689 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2690 _jump_position_when_done = true;
2694 switch (_operation) {
2696 show_verbose_cursor_time (region_start);
2697 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2698 i->view->trim_front_starting ();
2702 show_verbose_cursor_duration (region_start, region_end);
2705 show_verbose_cursor_time (pf);
2709 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2710 i->view->region()->suspend_property_changes ();
2715 TrimDrag::motion (GdkEvent* event, bool first_move)
2717 RegionView* rv = _primary;
2720 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2721 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2722 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2723 frameoffset_t frame_delta = 0;
2725 if (tv && tv->is_track()) {
2726 speed = tv->track()->speed();
2728 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2729 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2735 switch (_operation) {
2737 trim_type = "Region start trim";
2740 trim_type = "Region end trim";
2743 trim_type = "Region content trim";
2750 _editor->begin_reversible_command (trim_type);
2752 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2753 RegionView* rv = i->view;
2754 rv->enable_display (false);
2755 rv->region()->playlist()->clear_owned_changes ();
2757 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2760 arv->temporarily_hide_envelope ();
2764 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2765 insert_result = _editor->motion_frozen_playlists.insert (pl);
2767 if (insert_result.second) {
2773 bool non_overlap_trim = false;
2775 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2776 non_overlap_trim = true;
2779 /* contstrain trim to fade length */
2780 if (_preserve_fade_anchor) {
2781 switch (_operation) {
2783 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2784 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2786 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2787 if (ar->locked()) continue;
2788 framecnt_t len = ar->fade_in()->back()->when;
2789 if (len < dt) dt = min(dt, len);
2793 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2794 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2796 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2797 if (ar->locked()) continue;
2798 framecnt_t len = ar->fade_out()->back()->when;
2799 if (len < -dt) dt = max(dt, -len);
2808 switch (_operation) {
2810 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2811 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2812 if (changed && _preserve_fade_anchor) {
2813 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2815 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2816 framecnt_t len = ar->fade_in()->back()->when;
2817 framecnt_t diff = ar->first_frame() - i->initial_position;
2818 framepos_t new_length = len - diff;
2819 i->anchored_fade_length = min (ar->length(), new_length);
2820 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2821 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2828 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2829 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2830 if (changed && _preserve_fade_anchor) {
2831 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2833 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2834 framecnt_t len = ar->fade_out()->back()->when;
2835 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2836 framepos_t new_length = len + diff;
2837 i->anchored_fade_length = min (ar->length(), new_length);
2838 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2839 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2847 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2849 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2850 i->view->move_contents (frame_delta);
2856 switch (_operation) {
2858 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2861 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2864 // show_verbose_cursor_time (frame_delta);
2870 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2872 if (movement_occurred) {
2873 motion (event, false);
2875 if (_operation == StartTrim) {
2876 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2878 /* This must happen before the region's StatefulDiffCommand is created, as it may
2879 `correct' (ahem) the region's _start from being negative to being zero. It
2880 needs to be zero in the undo record.
2882 i->view->trim_front_ending ();
2884 if (_preserve_fade_anchor) {
2885 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2887 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2888 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2889 ar->set_fade_in_length(i->anchored_fade_length);
2890 ar->set_fade_in_active(true);
2893 if (_jump_position_when_done) {
2894 i->view->region()->set_position (i->initial_position);
2897 } else if (_operation == EndTrim) {
2898 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2899 if (_preserve_fade_anchor) {
2900 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2902 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2903 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2904 ar->set_fade_out_length(i->anchored_fade_length);
2905 ar->set_fade_out_active(true);
2908 if (_jump_position_when_done) {
2909 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2914 if (!_views.empty()) {
2915 if (_operation == StartTrim) {
2916 _editor->maybe_locate_with_edit_preroll(
2917 _views.begin()->view->region()->position());
2919 if (_operation == EndTrim) {
2920 _editor->maybe_locate_with_edit_preroll(
2921 _views.begin()->view->region()->position() +
2922 _views.begin()->view->region()->length());
2926 if (!_editor->selection->selected (_primary)) {
2927 _primary->thaw_after_trim ();
2930 set<boost::shared_ptr<Playlist> > diffed_playlists;
2932 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2933 i->view->thaw_after_trim ();
2934 i->view->enable_display (true);
2936 /* Trimming one region may affect others on the playlist, so we need
2937 to get undo Commands from the whole playlist rather than just the
2938 region. Use diffed_playlists to make sure we don't diff a given
2939 playlist more than once.
2941 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2942 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2943 vector<Command*> cmds;
2945 _editor->session()->add_commands (cmds);
2946 diffed_playlists.insert (p);
2951 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2955 _editor->motion_frozen_playlists.clear ();
2956 _editor->commit_reversible_command();
2959 /* no mouse movement */
2960 _editor->point_trim (event, adjusted_current_frame (event));
2963 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2964 if (_operation == StartTrim) {
2965 i->view->trim_front_ending ();
2968 i->view->region()->resume_property_changes ();
2973 TrimDrag::aborted (bool movement_occurred)
2975 /* Our motion method is changing model state, so use the Undo system
2976 to cancel. Perhaps not ideal, as this will leave an Undo point
2977 behind which may be slightly odd from the user's point of view.
2982 if (movement_occurred) {
2986 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2987 i->view->region()->resume_property_changes ();
2992 TrimDrag::setup_pointer_frame_offset ()
2994 list<DraggingView>::iterator i = _views.begin ();
2995 while (i != _views.end() && i->view != _primary) {
2999 if (i == _views.end()) {
3003 switch (_operation) {
3005 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3008 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3015 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3019 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3020 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3025 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3027 Drag::start_grab (event, cursor);
3028 show_verbose_cursor_time (adjusted_current_frame(event));
3032 MeterMarkerDrag::setup_pointer_frame_offset ()
3034 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3038 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3040 if (!_marker->meter().movable()) {
3046 // create a dummy marker for visual representation of moving the
3047 // section, because whether its a copy or not, we're going to
3048 // leave or lose the original marker (leave if its a copy; lose if its
3049 // not, because we'll remove it from the map).
3051 MeterSection section (_marker->meter());
3053 if (!section.movable()) {
3058 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3060 _marker = new MeterMarker (
3062 *_editor->meter_group,
3063 ARDOUR_UI::config()->color ("meter marker"),
3065 *new MeterSection (_marker->meter())
3068 /* use the new marker for the grab */
3069 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3072 TempoMap& map (_editor->session()->tempo_map());
3073 /* get current state */
3074 before_state = &map.get_state();
3075 /* remove the section while we drag it */
3076 map.remove_meter (section, true);
3080 framepos_t const pf = adjusted_current_frame (event);
3082 _marker->set_position (pf);
3083 show_verbose_cursor_time (pf);
3087 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3089 if (!movement_occurred) {
3090 if (was_double_click()) {
3091 _editor->edit_meter_marker (*_marker);
3096 if (!_marker->meter().movable()) {
3100 motion (event, false);
3102 Timecode::BBT_Time when;
3104 TempoMap& map (_editor->session()->tempo_map());
3105 map.bbt_time (last_pointer_frame(), when);
3107 if (_copy == true) {
3108 _editor->begin_reversible_command (_("copy meter mark"));
3109 XMLNode &before = map.get_state();
3110 map.add_meter (_marker->meter(), when);
3111 XMLNode &after = map.get_state();
3112 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3113 _editor->commit_reversible_command ();
3116 _editor->begin_reversible_command (_("move meter mark"));
3118 /* we removed it before, so add it back now */
3120 map.add_meter (_marker->meter(), when);
3121 XMLNode &after = map.get_state();
3122 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3123 _editor->commit_reversible_command ();
3126 // delete the dummy marker we used for visual representation while moving.
3127 // a new visual marker will show up automatically.
3132 MeterMarkerDrag::aborted (bool moved)
3134 _marker->set_position (_marker->meter().frame ());
3137 TempoMap& map (_editor->session()->tempo_map());
3138 /* we removed it before, so add it back now */
3139 map.add_meter (_marker->meter(), _marker->meter().frame());
3140 // delete the dummy marker we used for visual representation while moving.
3141 // a new visual marker will show up automatically.
3146 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3150 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3152 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3157 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3159 Drag::start_grab (event, cursor);
3160 show_verbose_cursor_time (adjusted_current_frame (event));
3164 TempoMarkerDrag::setup_pointer_frame_offset ()
3166 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3170 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3172 if (!_marker->tempo().movable()) {
3178 // create a dummy marker for visual representation of moving the
3179 // section, because whether its a copy or not, we're going to
3180 // leave or lose the original marker (leave if its a copy; lose if its
3181 // not, because we'll remove it from the map).
3183 // create a dummy marker for visual representation of moving the copy.
3184 // The actual copying is not done before we reach the finish callback.
3187 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3189 TempoSection section (_marker->tempo());
3191 _marker = new TempoMarker (
3193 *_editor->tempo_group,
3194 ARDOUR_UI::config()->color ("tempo marker"),
3196 *new TempoSection (_marker->tempo())
3199 /* use the new marker for the grab */
3200 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3203 TempoMap& map (_editor->session()->tempo_map());
3204 /* get current state */
3205 before_state = &map.get_state();
3206 /* remove the section while we drag it */
3207 map.remove_tempo (section, true);
3211 framepos_t const pf = adjusted_current_frame (event);
3212 _marker->set_position (pf);
3213 show_verbose_cursor_time (pf);
3217 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3219 if (!movement_occurred) {
3220 if (was_double_click()) {
3221 _editor->edit_tempo_marker (*_marker);
3226 if (!_marker->tempo().movable()) {
3230 motion (event, false);
3232 TempoMap& map (_editor->session()->tempo_map());
3233 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3234 Timecode::BBT_Time when;
3236 map.bbt_time (beat_time, when);
3238 if (_copy == true) {
3239 _editor->begin_reversible_command (_("copy tempo mark"));
3240 XMLNode &before = map.get_state();
3241 map.add_tempo (_marker->tempo(), when);
3242 XMLNode &after = map.get_state();
3243 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3244 _editor->commit_reversible_command ();
3247 _editor->begin_reversible_command (_("move tempo mark"));
3248 /* we removed it before, so add it back now */
3249 map.add_tempo (_marker->tempo(), when);
3250 XMLNode &after = map.get_state();
3251 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3252 _editor->commit_reversible_command ();
3255 // delete the dummy marker we used for visual representation while moving.
3256 // a new visual marker will show up automatically.
3261 TempoMarkerDrag::aborted (bool moved)
3263 _marker->set_position (_marker->tempo().frame());
3265 TempoMap& map (_editor->session()->tempo_map());
3266 /* we removed it before, so add it back now */
3267 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3268 // delete the dummy marker we used for visual representation while moving.
3269 // a new visual marker will show up automatically.
3274 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3275 : Drag (e, &c.track_canvas_item(), false)
3279 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3282 /** Do all the things we do when dragging the playhead to make it look as though
3283 * we have located, without actually doing the locate (because that would cause
3284 * the diskstream buffers to be refilled, which is too slow).
3287 CursorDrag::fake_locate (framepos_t t)
3289 _editor->playhead_cursor->set_position (t);
3291 Session* s = _editor->session ();
3292 if (s->timecode_transmission_suspended ()) {
3293 framepos_t const f = _editor->playhead_cursor->current_frame ();
3294 /* This is asynchronous so it will be sent "now"
3296 s->send_mmc_locate (f);
3297 /* These are synchronous and will be sent during the next
3300 s->queue_full_time_code ();
3301 s->queue_song_position_pointer ();
3304 show_verbose_cursor_time (t);
3305 _editor->UpdateAllTransportClocks (t);
3309 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3311 Drag::start_grab (event, c);
3312 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3314 _grab_zoom = _editor->samples_per_pixel;
3316 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3318 _editor->snap_to_with_modifier (where, event);
3320 _editor->_dragging_playhead = true;
3322 Session* s = _editor->session ();
3324 /* grab the track canvas item as well */
3326 _cursor.track_canvas_item().grab();
3329 if (_was_rolling && _stop) {
3333 if (s->is_auditioning()) {
3334 s->cancel_audition ();
3338 if (AudioEngine::instance()->connected()) {
3340 /* do this only if we're the engine is connected
3341 * because otherwise this request will never be
3342 * serviced and we'll busy wait forever. likewise,
3343 * notice if we are disconnected while waiting for the
3344 * request to be serviced.
3347 s->request_suspend_timecode_transmission ();
3348 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3349 /* twiddle our thumbs */
3354 fake_locate (where - snap_delta (event->button.state));
3358 CursorDrag::motion (GdkEvent* event, bool)
3360 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3361 _editor->snap_to_with_modifier (where, event);
3362 if (where != last_pointer_frame()) {
3363 fake_locate (where - snap_delta (event->button.state));
3368 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3370 _editor->_dragging_playhead = false;
3372 _cursor.track_canvas_item().ungrab();
3374 if (!movement_occurred && _stop) {
3378 motion (event, false);
3380 Session* s = _editor->session ();
3382 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3383 _editor->_pending_locate_request = true;
3384 s->request_resume_timecode_transmission ();
3389 CursorDrag::aborted (bool)
3391 _cursor.track_canvas_item().ungrab();
3393 if (_editor->_dragging_playhead) {
3394 _editor->session()->request_resume_timecode_transmission ();
3395 _editor->_dragging_playhead = false;
3398 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3401 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3402 : RegionDrag (e, i, p, v)
3404 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3408 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3410 Drag::start_grab (event, cursor);
3412 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3413 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3414 setup_snap_delta (r->position ());
3416 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3420 FadeInDrag::setup_pointer_frame_offset ()
3422 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3423 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3424 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3428 FadeInDrag::motion (GdkEvent* event, bool)
3430 framecnt_t fade_length;
3432 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3433 _editor->snap_to_with_modifier (pos, event);
3434 pos -= snap_delta (event->button.state);
3436 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3438 if (pos < (region->position() + 64)) {
3439 fade_length = 64; // this should be a minimum defined somewhere
3440 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3441 fade_length = region->length() - region->fade_out()->back()->when - 1;
3443 fade_length = pos - region->position();
3446 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3448 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3454 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3457 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3461 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3463 if (!movement_occurred) {
3467 framecnt_t fade_length;
3468 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3469 _editor->snap_to_with_modifier (pos, event);
3470 pos -= snap_delta (event->button.state);
3472 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3474 if (pos < (region->position() + 64)) {
3475 fade_length = 64; // this should be a minimum defined somewhere
3476 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3477 fade_length = region->length() - region->fade_out()->back()->when - 1;
3479 fade_length = pos - region->position();
3482 _editor->begin_reversible_command (_("change fade in length"));
3484 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3486 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3492 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3493 XMLNode &before = alist->get_state();
3495 tmp->audio_region()->set_fade_in_length (fade_length);
3496 tmp->audio_region()->set_fade_in_active (true);
3498 XMLNode &after = alist->get_state();
3499 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3502 _editor->commit_reversible_command ();
3506 FadeInDrag::aborted (bool)
3508 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3509 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3515 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3519 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3520 : RegionDrag (e, i, p, v)
3522 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3526 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3528 Drag::start_grab (event, cursor);
3530 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3531 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3532 setup_snap_delta (r->last_frame ());
3534 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3538 FadeOutDrag::setup_pointer_frame_offset ()
3540 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3541 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3542 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3546 FadeOutDrag::motion (GdkEvent* event, bool)
3548 framecnt_t fade_length;
3550 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3551 _editor->snap_to_with_modifier (pos, event);
3552 pos -= snap_delta (event->button.state);
3554 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3556 if (pos > (region->last_frame() - 64)) {
3557 fade_length = 64; // this should really be a minimum fade defined somewhere
3558 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3559 fade_length = region->length() - region->fade_in()->back()->when - 1;
3561 fade_length = region->last_frame() - pos;
3564 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3566 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3572 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3575 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3579 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3581 if (!movement_occurred) {
3585 framecnt_t fade_length;
3587 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3588 _editor->snap_to_with_modifier (pos, event);
3589 pos -= snap_delta (event->button.state);
3591 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3593 if (pos > (region->last_frame() - 64)) {
3594 fade_length = 64; // this should really be a minimum fade defined somewhere
3595 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3596 fade_length = region->length() - region->fade_in()->back()->when - 1;
3598 fade_length = region->last_frame() - pos;
3601 _editor->begin_reversible_command (_("change fade out length"));
3603 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3605 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3611 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3612 XMLNode &before = alist->get_state();
3614 tmp->audio_region()->set_fade_out_length (fade_length);
3615 tmp->audio_region()->set_fade_out_active (true);
3617 XMLNode &after = alist->get_state();
3618 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3621 _editor->commit_reversible_command ();
3625 FadeOutDrag::aborted (bool)
3627 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3628 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3634 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3638 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3641 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3643 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3646 _points.push_back (ArdourCanvas::Duple (0, 0));
3647 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3650 MarkerDrag::~MarkerDrag ()
3652 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3657 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3659 location = new Location (*l);
3660 markers.push_back (m);
3665 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3667 Drag::start_grab (event, cursor);
3671 Location *location = _editor->find_location_from_marker (_marker, is_start);
3672 _editor->_dragging_edit_point = true;
3674 update_item (location);
3676 // _drag_line->show();
3677 // _line->raise_to_top();
3680 show_verbose_cursor_time (location->start());
3682 show_verbose_cursor_time (location->end());
3685 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3688 case Selection::Toggle:
3689 /* we toggle on the button release */
3691 case Selection::Set:
3692 if (!_editor->selection->selected (_marker)) {
3693 _editor->selection->set (_marker);
3696 case Selection::Extend:
3698 Locations::LocationList ll;
3699 list<Marker*> to_add;
3701 _editor->selection->markers.range (s, e);
3702 s = min (_marker->position(), s);
3703 e = max (_marker->position(), e);
3706 if (e < max_framepos) {
3709 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3710 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3711 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3714 to_add.push_back (lm->start);
3717 to_add.push_back (lm->end);
3721 if (!to_add.empty()) {
3722 _editor->selection->add (to_add);
3726 case Selection::Add:
3727 _editor->selection->add (_marker);
3731 /* Set up copies for us to manipulate during the drag
3734 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3736 Location* l = _editor->find_location_from_marker (*i, is_start);
3743 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3745 /* range: check that the other end of the range isn't
3748 CopiedLocationInfo::iterator x;
3749 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3750 if (*(*x).location == *l) {
3754 if (x == _copied_locations.end()) {
3755 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3757 (*x).markers.push_back (*i);
3758 (*x).move_both = true;
3766 MarkerDrag::setup_pointer_frame_offset ()
3769 Location *location = _editor->find_location_from_marker (_marker, is_start);
3770 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3774 MarkerDrag::motion (GdkEvent* event, bool)
3776 framecnt_t f_delta = 0;
3778 bool move_both = false;
3779 Location *real_location;
3780 Location *copy_location = 0;
3782 framepos_t const newframe = adjusted_current_frame (event);
3783 framepos_t next = newframe;
3785 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3789 CopiedLocationInfo::iterator x;
3791 /* find the marker we're dragging, and compute the delta */
3793 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3795 copy_location = (*x).location;
3797 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3799 /* this marker is represented by this
3800 * CopiedLocationMarkerInfo
3803 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3808 if (real_location->is_mark()) {
3809 f_delta = newframe - copy_location->start();
3813 switch (_marker->type()) {
3814 case Marker::SessionStart:
3815 case Marker::RangeStart:
3816 case Marker::LoopStart:
3817 case Marker::PunchIn:
3818 f_delta = newframe - copy_location->start();
3821 case Marker::SessionEnd:
3822 case Marker::RangeEnd:
3823 case Marker::LoopEnd:
3824 case Marker::PunchOut:
3825 f_delta = newframe - copy_location->end();
3828 /* what kind of marker is this ? */
3837 if (x == _copied_locations.end()) {
3838 /* hmm, impossible - we didn't find the dragged marker */
3842 /* now move them all */
3844 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3846 copy_location = x->location;
3848 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3852 if (real_location->locked()) {
3856 if (copy_location->is_mark()) {
3860 copy_location->set_start (copy_location->start() + f_delta);
3864 framepos_t new_start = copy_location->start() + f_delta;
3865 framepos_t new_end = copy_location->end() + f_delta;
3867 if (is_start) { // start-of-range marker
3869 if (move_both || (*x).move_both) {
3870 copy_location->set_start (new_start);
3871 copy_location->set_end (new_end);
3872 } else if (new_start < copy_location->end()) {
3873 copy_location->set_start (new_start);
3874 } else if (newframe > 0) {
3875 _editor->snap_to (next, RoundUpAlways, true);
3876 copy_location->set_end (next);
3877 copy_location->set_start (newframe);
3880 } else { // end marker
3882 if (move_both || (*x).move_both) {
3883 copy_location->set_end (new_end);
3884 copy_location->set_start (new_start);
3885 } else if (new_end > copy_location->start()) {
3886 copy_location->set_end (new_end);
3887 } else if (newframe > 0) {
3888 _editor->snap_to (next, RoundDownAlways, true);
3889 copy_location->set_start (next);
3890 copy_location->set_end (newframe);
3895 update_item (copy_location);
3897 /* now lookup the actual GUI items used to display this
3898 * location and move them to wherever the copy of the location
3899 * is now. This means that the logic in ARDOUR::Location is
3900 * still enforced, even though we are not (yet) modifying
3901 * the real Location itself.
3904 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3907 lm->set_position (copy_location->start(), copy_location->end());
3912 assert (!_copied_locations.empty());
3914 show_verbose_cursor_time (newframe);
3918 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3920 if (!movement_occurred) {
3922 if (was_double_click()) {
3923 _editor->rename_marker (_marker);
3927 /* just a click, do nothing but finish
3928 off the selection process
3931 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3934 case Selection::Set:
3935 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3936 _editor->selection->set (_marker);
3940 case Selection::Toggle:
3941 /* we toggle on the button release, click only */
3942 _editor->selection->toggle (_marker);
3945 case Selection::Extend:
3946 case Selection::Add:
3953 _editor->_dragging_edit_point = false;
3955 _editor->begin_reversible_command ( _("move marker") );
3956 XMLNode &before = _editor->session()->locations()->get_state();
3958 MarkerSelection::iterator i;
3959 CopiedLocationInfo::iterator x;
3962 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3963 x != _copied_locations.end() && i != _editor->selection->markers.end();
3966 Location * location = _editor->find_location_from_marker (*i, is_start);
3970 if (location->locked()) {
3974 if (location->is_mark()) {
3975 location->set_start (((*x).location)->start());
3977 location->set (((*x).location)->start(), ((*x).location)->end());
3982 XMLNode &after = _editor->session()->locations()->get_state();
3983 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3984 _editor->commit_reversible_command ();
3988 MarkerDrag::aborted (bool movement_occured)
3990 if (!movement_occured) {
3994 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3996 /* move all markers to their original location */
3999 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4002 Location * location = _editor->find_location_from_marker (*m, is_start);
4005 (*m)->set_position (is_start ? location->start() : location->end());
4012 MarkerDrag::update_item (Location*)
4017 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4019 _cumulative_x_drag (0),
4020 _cumulative_y_drag (0)
4022 if (_zero_gain_fraction < 0.0) {
4023 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4026 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4028 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4034 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4036 Drag::start_grab (event, _editor->cursors()->fader);
4038 // start the grab at the center of the control point so
4039 // the point doesn't 'jump' to the mouse after the first drag
4040 _fixed_grab_x = _point->get_x();
4041 _fixed_grab_y = _point->get_y();
4043 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4044 setup_snap_delta (pos);
4046 float const fraction = 1 - (_point->get_y() / _point->line().height());
4048 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4050 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4052 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4054 if (!_point->can_slide ()) {
4055 _x_constrained = true;
4060 ControlPointDrag::motion (GdkEvent* event, bool)
4062 double dx = _drags->current_pointer_x() - last_pointer_x();
4063 double dy = current_pointer_y() - last_pointer_y();
4065 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4070 /* coordinate in pixels relative to the start of the region (for region-based automation)
4071 or track (for track-based automation) */
4072 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4073 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4075 // calculate zero crossing point. back off by .01 to stay on the
4076 // positive side of zero
4077 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4079 // make sure we hit zero when passing through
4080 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4084 if (_x_constrained) {
4087 if (_y_constrained) {
4091 _cumulative_x_drag = cx - _fixed_grab_x;
4092 _cumulative_y_drag = cy - _fixed_grab_y;
4096 cy = min ((double) _point->line().height(), cy);
4098 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4100 if (!_x_constrained) {
4101 _editor->snap_to_with_modifier (cx_frames, event);
4104 cx_frames -= snap_delta (event->button.state);
4105 cx_frames = min (cx_frames, _point->line().maximum_time());
4107 float const fraction = 1.0 - (cy / _point->line().height());
4109 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4111 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4115 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4117 if (!movement_occurred) {
4120 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4121 _editor->reset_point_selection ();
4125 motion (event, false);
4128 _point->line().end_drag (_pushing, _final_index);
4129 _editor->commit_reversible_command ();
4133 ControlPointDrag::aborted (bool)
4135 _point->line().reset ();
4139 ControlPointDrag::active (Editing::MouseMode m)
4141 if (m == Editing::MouseDraw) {
4142 /* always active in mouse draw */
4146 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4147 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4150 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4153 , _cumulative_y_drag (0)
4155 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4159 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4161 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4164 _item = &_line->grab_item ();
4166 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4167 origin, and ditto for y.
4170 double cx = event->button.x;
4171 double cy = event->button.y;
4173 _line->parent_group().canvas_to_item (cx, cy);
4175 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4180 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4181 /* no adjacent points */
4185 Drag::start_grab (event, _editor->cursors()->fader);
4187 /* store grab start in parent frame */
4192 double fraction = 1.0 - (cy / _line->height());
4194 _line->start_drag_line (before, after, fraction);
4196 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4200 LineDrag::motion (GdkEvent* event, bool)
4202 double dy = current_pointer_y() - last_pointer_y();
4204 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4208 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4210 _cumulative_y_drag = cy - _fixed_grab_y;
4213 cy = min ((double) _line->height(), cy);
4215 double const fraction = 1.0 - (cy / _line->height());
4218 /* we are ignoring x position for this drag, so we can just pass in anything */
4219 _line->drag_motion (0, fraction, true, false, ignored);
4221 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4225 LineDrag::finished (GdkEvent* event, bool movement_occured)
4227 if (movement_occured) {
4228 motion (event, false);
4229 _line->end_drag (false, 0);
4231 /* add a new control point on the line */
4233 AutomationTimeAxisView* atv;
4235 _line->end_drag (false, 0);
4237 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4238 framepos_t where = _editor->window_event_sample (event, 0, 0);
4239 atv->add_automation_event (event, where, event->button.y, false);
4243 _editor->commit_reversible_command ();
4247 LineDrag::aborted (bool)
4252 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4255 _cumulative_x_drag (0)
4257 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4261 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4263 Drag::start_grab (event);
4265 _line = reinterpret_cast<Line*> (_item);
4268 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4270 double cx = event->button.x;
4271 double cy = event->button.y;
4273 _item->parent()->canvas_to_item (cx, cy);
4275 /* store grab start in parent frame */
4276 _region_view_grab_x = cx;
4278 _before = *(float*) _item->get_data ("position");
4280 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4282 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4286 FeatureLineDrag::motion (GdkEvent*, bool)
4288 double dx = _drags->current_pointer_x() - last_pointer_x();
4290 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4292 _cumulative_x_drag += dx;
4294 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4303 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4305 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4307 float *pos = new float;
4310 _line->set_data ("position", pos);
4316 FeatureLineDrag::finished (GdkEvent*, bool)
4318 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4319 _arv->update_transient(_before, _before);
4323 FeatureLineDrag::aborted (bool)
4328 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4330 , _vertical_only (false)
4332 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4336 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4338 Drag::start_grab (event);
4339 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4343 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4350 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4352 framepos_t grab = grab_frame ();
4353 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4354 _editor->snap_to_with_modifier (grab, event);
4356 grab = raw_grab_frame ();
4359 /* base start and end on initial click position */
4369 if (current_pointer_y() < grab_y()) {
4370 y1 = current_pointer_y();
4373 y2 = current_pointer_y();
4377 if (start != end || y1 != y2) {
4379 double x1 = _editor->sample_to_pixel (start);
4380 double x2 = _editor->sample_to_pixel (end);
4381 const double min_dimension = 2.0;
4383 if (_vertical_only) {
4384 /* fixed 10 pixel width */
4388 x2 = min (x1 - min_dimension, x2);
4390 x2 = max (x1 + min_dimension, x2);
4395 y2 = min (y1 - min_dimension, y2);
4397 y2 = max (y1 + min_dimension, y2);
4400 /* translate rect into item space and set */
4402 ArdourCanvas::Rect r (x1, y1, x2, y2);
4404 /* this drag is a _trackview_only == true drag, so the y1 and
4405 * y2 (computed using current_pointer_y() and grab_y()) will be
4406 * relative to the top of the trackview group). The
4407 * rubberband rect has the same parent/scroll offset as the
4408 * the trackview group, so we can use the "r" rect directly
4409 * to set the shape of the rubberband.
4412 _editor->rubberband_rect->set (r);
4413 _editor->rubberband_rect->show();
4414 _editor->rubberband_rect->raise_to_top();
4416 show_verbose_cursor_time (pf);
4418 do_select_things (event, true);
4423 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4427 framepos_t grab = grab_frame ();
4428 framepos_t lpf = last_pointer_frame ();
4430 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4431 grab = raw_grab_frame ();
4432 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4446 if (current_pointer_y() < grab_y()) {
4447 y1 = current_pointer_y();
4450 y2 = current_pointer_y();
4454 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4458 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4460 if (movement_occurred) {
4462 motion (event, false);
4463 do_select_things (event, false);
4469 bool do_deselect = true;
4470 MidiTimeAxisView* mtv;
4472 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4474 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4475 /* nothing selected */
4476 add_midi_region (mtv);
4477 do_deselect = false;
4481 /* do not deselect if Primary or Tertiary (toggle-select or
4482 * extend-select are pressed.
4485 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4486 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4493 _editor->rubberband_rect->hide();
4497 RubberbandSelectDrag::aborted (bool)
4499 _editor->rubberband_rect->hide ();
4502 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4503 : RegionDrag (e, i, p, v)
4505 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4509 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4511 Drag::start_grab (event, cursor);
4513 _editor->get_selection().add (_primary);
4515 framepos_t where = _primary->region()->position();
4516 setup_snap_delta (where);
4518 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4522 TimeFXDrag::motion (GdkEvent* event, bool)
4524 RegionView* rv = _primary;
4525 StreamView* cv = rv->get_time_axis_view().view ();
4527 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4528 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4529 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4530 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4531 _editor->snap_to_with_modifier (pf, event);
4532 pf -= snap_delta (event->button.state);
4534 if (pf > rv->region()->position()) {
4535 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4538 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4542 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4544 _primary->get_time_axis_view().hide_timestretch ();
4546 if (!movement_occurred) {
4550 if (last_pointer_frame() < _primary->region()->position()) {
4551 /* backwards drag of the left edge - not usable */
4555 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4557 float percentage = (double) newlen / (double) _primary->region()->length();
4559 #ifndef USE_RUBBERBAND
4560 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4561 if (_primary->region()->data_type() == DataType::AUDIO) {
4562 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4566 if (!_editor->get_selection().regions.empty()) {
4567 /* primary will already be included in the selection, and edit
4568 group shared editing will propagate selection across
4569 equivalent regions, so just use the current region
4573 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4574 error << _("An error occurred while executing time stretch operation") << endmsg;
4580 TimeFXDrag::aborted (bool)
4582 _primary->get_time_axis_view().hide_timestretch ();
4585 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4588 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4592 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4594 Drag::start_grab (event);
4598 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4600 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4604 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4606 if (movement_occurred && _editor->session()) {
4607 /* make sure we stop */
4608 _editor->session()->request_transport_speed (0.0);
4613 ScrubDrag::aborted (bool)
4618 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4622 , _time_selection_at_start (!_editor->get_selection().time.empty())
4624 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4626 if (_time_selection_at_start) {
4627 start_at_start = _editor->get_selection().time.start();
4628 end_at_start = _editor->get_selection().time.end_frame();
4633 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4635 if (_editor->session() == 0) {
4639 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4641 switch (_operation) {
4642 case CreateSelection:
4643 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4648 cursor = _editor->cursors()->selector;
4649 Drag::start_grab (event, cursor);
4652 case SelectionStartTrim:
4653 if (_editor->clicked_axisview) {
4654 _editor->clicked_axisview->order_selection_trims (_item, true);
4656 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4659 case SelectionEndTrim:
4660 if (_editor->clicked_axisview) {
4661 _editor->clicked_axisview->order_selection_trims (_item, false);
4663 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4667 Drag::start_grab (event, cursor);
4670 case SelectionExtend:
4671 Drag::start_grab (event, cursor);
4675 if (_operation == SelectionMove) {
4676 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4678 show_verbose_cursor_time (adjusted_current_frame (event));
4683 SelectionDrag::setup_pointer_frame_offset ()
4685 switch (_operation) {
4686 case CreateSelection:
4687 _pointer_frame_offset = 0;
4690 case SelectionStartTrim:
4692 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4695 case SelectionEndTrim:
4696 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4699 case SelectionExtend:
4705 SelectionDrag::motion (GdkEvent* event, bool first_move)
4707 framepos_t start = 0;
4709 framecnt_t length = 0;
4710 framecnt_t distance = 0;
4712 framepos_t const pending_position = adjusted_current_frame (event);
4714 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4718 switch (_operation) {
4719 case CreateSelection:
4721 framepos_t grab = grab_frame ();
4724 grab = adjusted_current_frame (event, false);
4725 if (grab < pending_position) {
4726 _editor->snap_to (grab, RoundDownMaybe);
4728 _editor->snap_to (grab, RoundUpMaybe);
4732 if (pending_position < grab) {
4733 start = pending_position;
4736 end = pending_position;
4740 /* first drag: Either add to the selection
4741 or create a new selection
4748 /* adding to the selection */
4749 _editor->set_selected_track_as_side_effect (Selection::Add);
4750 _editor->clicked_selection = _editor->selection->add (start, end);
4757 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4758 _editor->set_selected_track_as_side_effect (Selection::Set);
4761 _editor->clicked_selection = _editor->selection->set (start, end);
4765 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4766 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4767 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4769 _editor->selection->add (atest);
4773 /* select all tracks within the rectangle that we've marked out so far */
4774 TrackViewList new_selection;
4775 TrackViewList& all_tracks (_editor->track_views);
4777 ArdourCanvas::Coord const top = grab_y();
4778 ArdourCanvas::Coord const bottom = current_pointer_y();
4780 if (top >= 0 && bottom >= 0) {
4782 //first, find the tracks that are covered in the y range selection
4783 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4784 if ((*i)->covered_by_y_range (top, bottom)) {
4785 new_selection.push_back (*i);
4789 //now find any tracks that are GROUPED with the tracks we selected
4790 TrackViewList grouped_add = new_selection;
4791 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4792 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4793 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4794 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4795 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4796 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4797 grouped_add.push_back (*j);
4802 //now compare our list with the current selection, and add or remove as necessary
4803 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4804 TrackViewList tracks_to_add;
4805 TrackViewList tracks_to_remove;
4806 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4807 if ( !_editor->selection->tracks.contains ( *i ) )
4808 tracks_to_add.push_back ( *i );
4809 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4810 if ( !grouped_add.contains ( *i ) )
4811 tracks_to_remove.push_back ( *i );
4812 _editor->selection->add(tracks_to_add);
4813 _editor->selection->remove(tracks_to_remove);
4819 case SelectionStartTrim:
4821 start = _editor->selection->time[_editor->clicked_selection].start;
4822 end = _editor->selection->time[_editor->clicked_selection].end;
4824 if (pending_position > end) {
4827 start = pending_position;
4831 case SelectionEndTrim:
4833 start = _editor->selection->time[_editor->clicked_selection].start;
4834 end = _editor->selection->time[_editor->clicked_selection].end;
4836 if (pending_position < start) {
4839 end = pending_position;
4846 start = _editor->selection->time[_editor->clicked_selection].start;
4847 end = _editor->selection->time[_editor->clicked_selection].end;
4849 length = end - start;
4850 distance = pending_position - start;
4851 start = pending_position;
4852 _editor->snap_to (start);
4854 end = start + length;
4858 case SelectionExtend:
4863 switch (_operation) {
4865 if (_time_selection_at_start) {
4866 _editor->selection->move_time (distance);
4870 _editor->selection->replace (_editor->clicked_selection, start, end);
4874 if (_operation == SelectionMove) {
4875 show_verbose_cursor_time(start);
4877 show_verbose_cursor_time(pending_position);
4882 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4884 Session* s = _editor->session();
4886 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4887 if (movement_occurred) {
4888 motion (event, false);
4889 /* XXX this is not object-oriented programming at all. ick */
4890 if (_editor->selection->time.consolidate()) {
4891 _editor->selection->TimeChanged ();
4894 /* XXX what if its a music time selection? */
4896 if (s->get_play_range() && s->transport_rolling()) {
4897 s->request_play_range (&_editor->selection->time, true);
4899 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4900 if (_operation == SelectionEndTrim)
4901 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4903 s->request_locate (_editor->get_selection().time.start());
4907 if (_editor->get_selection().time.length() != 0) {
4908 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
4910 s->clear_range_selection ();
4915 /* just a click, no pointer movement.
4918 if (_operation == SelectionExtend) {
4919 if (_time_selection_at_start) {
4920 framepos_t pos = adjusted_current_frame (event, false);
4921 framepos_t start = min (pos, start_at_start);
4922 framepos_t end = max (pos, end_at_start);
4923 _editor->selection->set (start, end);
4926 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4927 if (_editor->clicked_selection) {
4928 _editor->selection->remove (_editor->clicked_selection);
4931 if (!_editor->clicked_selection) {
4932 _editor->selection->clear_time();
4937 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4938 _editor->selection->set (_editor->clicked_axisview);
4941 if (s && s->get_play_range () && s->transport_rolling()) {
4942 s->request_stop (false, false);
4947 _editor->stop_canvas_autoscroll ();
4948 _editor->clicked_selection = 0;
4949 _editor->commit_reversible_selection_op ();
4953 SelectionDrag::aborted (bool)
4958 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4959 : Drag (e, i, false),
4963 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4965 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4966 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4967 physical_screen_height (_editor->get_window())));
4968 _drag_rect->hide ();
4970 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4971 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4974 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4976 /* normal canvas items will be cleaned up when their parent group is deleted. But
4977 this item is created as the child of a long-lived parent group, and so we
4978 need to explicitly delete it.
4984 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4986 if (_editor->session() == 0) {
4990 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4992 if (!_editor->temp_location) {
4993 _editor->temp_location = new Location (*_editor->session());
4996 switch (_operation) {
4997 case CreateSkipMarker:
4998 case CreateRangeMarker:
4999 case CreateTransportMarker:
5000 case CreateCDMarker:
5002 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5007 cursor = _editor->cursors()->selector;
5011 Drag::start_grab (event, cursor);
5013 show_verbose_cursor_time (adjusted_current_frame (event));
5017 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5019 framepos_t start = 0;
5021 ArdourCanvas::Rectangle *crect;
5023 switch (_operation) {
5024 case CreateSkipMarker:
5025 crect = _editor->range_bar_drag_rect;
5027 case CreateRangeMarker:
5028 crect = _editor->range_bar_drag_rect;
5030 case CreateTransportMarker:
5031 crect = _editor->transport_bar_drag_rect;
5033 case CreateCDMarker:
5034 crect = _editor->cd_marker_bar_drag_rect;
5037 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5042 framepos_t const pf = adjusted_current_frame (event);
5044 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5045 framepos_t grab = grab_frame ();
5046 _editor->snap_to (grab);
5048 if (pf < grab_frame()) {
5056 /* first drag: Either add to the selection
5057 or create a new selection.
5062 _editor->temp_location->set (start, end);
5066 update_item (_editor->temp_location);
5068 //_drag_rect->raise_to_top();
5074 _editor->temp_location->set (start, end);
5076 double x1 = _editor->sample_to_pixel (start);
5077 double x2 = _editor->sample_to_pixel (end);
5081 update_item (_editor->temp_location);
5084 show_verbose_cursor_time (pf);
5089 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5091 Location * newloc = 0;
5095 if (movement_occurred) {
5096 motion (event, false);
5099 switch (_operation) {
5100 case CreateSkipMarker:
5101 case CreateRangeMarker:
5102 case CreateCDMarker:
5104 XMLNode &before = _editor->session()->locations()->get_state();
5105 if (_operation == CreateSkipMarker) {
5106 _editor->begin_reversible_command (_("new skip marker"));
5107 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5108 flags = Location::IsRangeMarker | Location::IsSkip;
5109 _editor->range_bar_drag_rect->hide();
5110 } else if (_operation == CreateCDMarker) {
5111 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5112 _editor->begin_reversible_command (_("new CD marker"));
5113 flags = Location::IsRangeMarker | Location::IsCDMarker;
5114 _editor->cd_marker_bar_drag_rect->hide();
5116 _editor->begin_reversible_command (_("new skip marker"));
5117 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5118 flags = Location::IsRangeMarker;
5119 _editor->range_bar_drag_rect->hide();
5121 newloc = new Location (
5122 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5125 _editor->session()->locations()->add (newloc, true);
5126 XMLNode &after = _editor->session()->locations()->get_state();
5127 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5128 _editor->commit_reversible_command ();
5132 case CreateTransportMarker:
5133 // popup menu to pick loop or punch
5134 _editor->new_transport_marker_context_menu (&event->button, _item);
5140 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5142 if (_operation == CreateTransportMarker) {
5144 /* didn't drag, so just locate */
5146 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5148 } else if (_operation == CreateCDMarker) {
5150 /* didn't drag, but mark is already created so do
5153 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5158 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5160 if (end == max_framepos) {
5161 end = _editor->session()->current_end_frame ();
5164 if (start == max_framepos) {
5165 start = _editor->session()->current_start_frame ();
5168 switch (_editor->mouse_mode) {
5170 /* find the two markers on either side and then make the selection from it */
5171 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5175 /* find the two markers on either side of the click and make the range out of it */
5176 _editor->selection->set (start, end);
5185 _editor->stop_canvas_autoscroll ();
5189 RangeMarkerBarDrag::aborted (bool movement_occured)
5191 if (movement_occured) {
5192 _drag_rect->hide ();
5197 RangeMarkerBarDrag::update_item (Location* location)
5199 double const x1 = _editor->sample_to_pixel (location->start());
5200 double const x2 = _editor->sample_to_pixel (location->end());
5202 _drag_rect->set_x0 (x1);
5203 _drag_rect->set_x1 (x2);
5206 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5208 , _cumulative_dx (0)
5209 , _cumulative_dy (0)
5211 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5213 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5215 _region = &_primary->region_view ();
5216 _note_height = _region->midi_stream_view()->note_height ();
5220 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5222 Drag::start_grab (event);
5223 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5225 if (!(_was_selected = _primary->selected())) {
5227 /* tertiary-click means extend selection - we'll do that on button release,
5228 so don't add it here, because otherwise we make it hard to figure
5229 out the "extend-to" range.
5232 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5235 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5238 _region->note_selected (_primary, true);
5240 _region->unique_select (_primary);
5243 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5244 _editor->commit_reversible_selection_op();
5249 /** @return Current total drag x change in frames */
5251 NoteDrag::total_dx (const guint state) const
5254 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5256 /* primary note time */
5257 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5259 /* new time of the primary note in session frames */
5260 frameoffset_t st = n + dx + snap_delta (state);
5262 framepos_t const rp = _region->region()->position ();
5264 /* prevent the note being dragged earlier than the region's position */
5267 /* possibly snap and return corresponding delta */
5271 if (ArdourKeyboard::indicates_snap (state)) {
5272 if (_editor->snap_mode () != SnapOff) {
5276 if (_editor->snap_mode () == SnapOff) {
5278 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5279 if (ArdourKeyboard::indicates_snap_delta (state)) {
5287 ret = _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5289 ret = st - n - snap_delta (state);
5294 /** @return Current total drag y change in note number */
5296 NoteDrag::total_dy () const
5298 MidiStreamView* msv = _region->midi_stream_view ();
5299 double const y = _region->midi_view()->y_position ();
5300 /* new current note */
5301 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5303 n = max (msv->lowest_note(), n);
5304 n = min (msv->highest_note(), n);
5305 /* and work out delta */
5306 return n - msv->y_to_note (grab_y() - y);
5310 NoteDrag::motion (GdkEvent * event, bool)
5312 /* Total change in x and y since the start of the drag */
5313 frameoffset_t const dx = total_dx (event->button.state);
5314 int8_t const dy = total_dy ();
5316 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5317 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5318 double const tdy = -dy * _note_height - _cumulative_dy;
5321 _cumulative_dx += tdx;
5322 _cumulative_dy += tdy;
5324 int8_t note_delta = total_dy();
5326 _region->move_selection (tdx, tdy, note_delta);
5328 /* the new note value may be the same as the old one, but we
5329 * don't know what that means because the selection may have
5330 * involved more than one note and we might be doing something
5331 * odd with them. so show the note value anyway, always.
5335 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5337 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5338 (int) floor ((double)new_note));
5340 show_verbose_cursor_text (buf);
5345 NoteDrag::finished (GdkEvent* ev, bool moved)
5348 /* no motion - select note */
5350 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5351 _editor->current_mouse_mode() == Editing::MouseDraw) {
5353 bool changed = false;
5355 if (_was_selected) {
5356 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5358 _region->note_deselected (_primary);
5362 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5363 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5365 if (!extend && !add && _region->selection_size() > 1) {
5366 _region->unique_select (_primary);
5368 } else if (extend) {
5369 _region->note_selected (_primary, true, true);
5372 /* it was added during button press */
5377 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5378 _editor->commit_reversible_selection_op();
5382 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5387 NoteDrag::aborted (bool)
5392 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5393 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5394 : Drag (editor, atv->base_item ())
5396 , _y_origin (atv->y_position())
5397 , _nothing_to_drag (false)
5399 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5400 setup (atv->lines ());
5403 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5404 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5405 : Drag (editor, rv->get_canvas_group ())
5407 , _y_origin (rv->get_time_axis_view().y_position())
5408 , _nothing_to_drag (false)
5411 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5413 list<boost::shared_ptr<AutomationLine> > lines;
5415 AudioRegionView* audio_view;
5416 AutomationRegionView* automation_view;
5417 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5418 lines.push_back (audio_view->get_gain_line ());
5419 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5420 lines.push_back (automation_view->line ());
5423 error << _("Automation range drag created for invalid region type") << endmsg;
5429 /** @param lines AutomationLines to drag.
5430 * @param offset Offset from the session start to the points in the AutomationLines.
5433 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5435 /* find the lines that overlap the ranges being dragged */
5436 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5437 while (i != lines.end ()) {
5438 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5441 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5443 /* check this range against all the AudioRanges that we are using */
5444 list<AudioRange>::const_iterator k = _ranges.begin ();
5445 while (k != _ranges.end()) {
5446 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5452 /* add it to our list if it overlaps at all */
5453 if (k != _ranges.end()) {
5458 _lines.push_back (n);
5464 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5468 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5470 return 1.0 - ((global_y - _y_origin) / line->height());
5474 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5476 const double v = list->eval(x);
5477 return _integral ? rint(v) : v;
5481 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5483 Drag::start_grab (event, cursor);
5485 /* Get line states before we start changing things */
5486 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5487 i->state = &i->line->get_state ();
5488 i->original_fraction = y_fraction (i->line, current_pointer_y());
5491 if (_ranges.empty()) {
5493 /* No selected time ranges: drag all points */
5494 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5495 uint32_t const N = i->line->npoints ();
5496 for (uint32_t j = 0; j < N; ++j) {
5497 i->points.push_back (i->line->nth (j));
5503 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5505 framecnt_t const half = (i->start + i->end) / 2;
5507 /* find the line that this audio range starts in */
5508 list<Line>::iterator j = _lines.begin();
5509 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5513 if (j != _lines.end()) {
5514 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5516 /* j is the line that this audio range starts in; fade into it;
5517 64 samples length plucked out of thin air.
5520 framepos_t a = i->start + 64;
5525 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5526 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5528 the_list->editor_add (p, value (the_list, p));
5529 the_list->editor_add (q, value (the_list, q));
5532 /* same thing for the end */
5535 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5539 if (j != _lines.end()) {
5540 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5542 /* j is the line that this audio range starts in; fade out of it;
5543 64 samples length plucked out of thin air.
5546 framepos_t b = i->end - 64;
5551 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5552 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5554 the_list->editor_add (p, value (the_list, p));
5555 the_list->editor_add (q, value (the_list, q));
5559 _nothing_to_drag = true;
5561 /* Find all the points that should be dragged and put them in the relevant
5562 points lists in the Line structs.
5565 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5567 uint32_t const N = i->line->npoints ();
5568 for (uint32_t j = 0; j < N; ++j) {
5570 /* here's a control point on this line */
5571 ControlPoint* p = i->line->nth (j);
5572 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5574 /* see if it's inside a range */
5575 list<AudioRange>::const_iterator k = _ranges.begin ();
5576 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5580 if (k != _ranges.end()) {
5581 /* dragging this point */
5582 _nothing_to_drag = false;
5583 i->points.push_back (p);
5589 if (_nothing_to_drag) {
5593 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5594 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5599 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5601 if (_nothing_to_drag) {
5605 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5606 float const f = y_fraction (l->line, current_pointer_y());
5607 /* we are ignoring x position for this drag, so we can just pass in anything */
5609 l->line->drag_motion (0, f, true, false, ignored);
5610 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5615 AutomationRangeDrag::finished (GdkEvent* event, bool)
5617 if (_nothing_to_drag) {
5621 motion (event, false);
5622 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5623 i->line->end_drag (false, 0);
5626 _editor->commit_reversible_command ();
5630 AutomationRangeDrag::aborted (bool)
5632 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5637 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5639 , initial_time_axis_view (itav)
5641 /* note that time_axis_view may be null if the regionview was created
5642 * as part of a copy operation.
5644 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5645 layer = v->region()->layer ();
5646 initial_y = v->get_canvas_group()->position().y;
5647 initial_playlist = v->region()->playlist ();
5648 initial_position = v->region()->position ();
5649 initial_end = v->region()->position () + v->region()->length ();
5652 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5653 : Drag (e, i->canvas_item ())
5656 , _cumulative_dx (0)
5658 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5659 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5664 PatchChangeDrag::motion (GdkEvent* ev, bool)
5666 framepos_t f = adjusted_current_frame (ev);
5667 boost::shared_ptr<Region> r = _region_view->region ();
5668 f = max (f, r->position ());
5669 f = min (f, r->last_frame ());
5671 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5672 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5673 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5674 _cumulative_dx = dxu;
5678 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5680 if (!movement_occurred) {
5684 boost::shared_ptr<Region> r (_region_view->region ());
5685 framepos_t f = adjusted_current_frame (ev);
5686 f = max (f, r->position ());
5687 f = min (f, r->last_frame ());
5689 _region_view->move_patch_change (
5691 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5696 PatchChangeDrag::aborted (bool)
5698 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5702 PatchChangeDrag::setup_pointer_frame_offset ()
5704 boost::shared_ptr<Region> region = _region_view->region ();
5705 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5708 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5709 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5716 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5718 _region_view->update_drag_selection (
5720 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5724 MidiRubberbandSelectDrag::deselect_things ()
5729 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5730 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5733 _vertical_only = true;
5737 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5739 double const y = _region_view->midi_view()->y_position ();
5741 y1 = max (0.0, y1 - y);
5742 y2 = max (0.0, y2 - y);
5744 _region_view->update_vertical_drag_selection (
5747 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5752 MidiVerticalSelectDrag::deselect_things ()
5757 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5758 : RubberbandSelectDrag (e, i)
5764 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5766 if (drag_in_progress) {
5767 /* We just want to select things at the end of the drag, not during it */
5771 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5773 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5775 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5777 _editor->commit_reversible_selection_op ();
5781 EditorRubberbandSelectDrag::deselect_things ()
5783 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5785 _editor->selection->clear_tracks();
5786 _editor->selection->clear_regions();
5787 _editor->selection->clear_points ();
5788 _editor->selection->clear_lines ();
5789 _editor->selection->clear_midi_notes ();
5791 _editor->commit_reversible_selection_op();
5794 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5799 _note[0] = _note[1] = 0;
5802 NoteCreateDrag::~NoteCreateDrag ()
5808 NoteCreateDrag::grid_frames (framepos_t t) const
5811 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5813 grid_beats = Evoral::Beats(1);
5816 return _region_view->region_beats_to_region_frames (grid_beats);
5820 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5822 Drag::start_grab (event, cursor);
5824 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5826 framepos_t pf = _drags->current_pointer_frame ();
5827 framecnt_t const g = grid_frames (pf);
5829 /* Hack so that we always snap to the note that we are over, instead of snapping
5830 to the next one if we're more than halfway through the one we're over.
5832 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5836 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5837 _note[1] = _note[0];
5839 MidiStreamView* sv = _region_view->midi_stream_view ();
5840 double const x = _editor->sample_to_pixel (_note[0]);
5841 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5843 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5844 _drag_rect->set_outline_all ();
5845 _drag_rect->set_outline_color (0xffffff99);
5846 _drag_rect->set_fill_color (0xffffff66);
5850 NoteCreateDrag::motion (GdkEvent* event, bool)
5852 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5853 double const x0 = _editor->sample_to_pixel (_note[0]);
5854 double const x1 = _editor->sample_to_pixel (_note[1]);
5855 _drag_rect->set_x0 (std::min(x0, x1));
5856 _drag_rect->set_x1 (std::max(x0, x1));
5860 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5862 if (!had_movement) {
5866 framepos_t const start = min (_note[0], _note[1]);
5867 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5869 framecnt_t const g = grid_frames (start);
5870 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5872 if (_editor->snap_mode() == SnapNormal && length < g) {
5876 Evoral::Beats length_beats = max (
5877 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5879 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5883 NoteCreateDrag::y_to_region (double y) const
5886 _region_view->get_canvas_group()->canvas_to_item (x, y);
5891 NoteCreateDrag::aborted (bool)
5896 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5901 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5905 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5907 Drag::start_grab (event, cursor);
5911 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5917 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5920 distance = _drags->current_pointer_x() - grab_x();
5921 len = ar->fade_in()->back()->when;
5923 distance = grab_x() - _drags->current_pointer_x();
5924 len = ar->fade_out()->back()->when;
5927 /* how long should it be ? */
5929 new_length = len + _editor->pixel_to_sample (distance);
5931 /* now check with the region that this is legal */
5933 new_length = ar->verify_xfade_bounds (new_length, start);
5936 arv->reset_fade_in_shape_width (ar, new_length);
5938 arv->reset_fade_out_shape_width (ar, new_length);
5943 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5949 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5952 distance = _drags->current_pointer_x() - grab_x();
5953 len = ar->fade_in()->back()->when;
5955 distance = grab_x() - _drags->current_pointer_x();
5956 len = ar->fade_out()->back()->when;
5959 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5961 _editor->begin_reversible_command ("xfade trim");
5962 ar->playlist()->clear_owned_changes ();
5965 ar->set_fade_in_length (new_length);
5967 ar->set_fade_out_length (new_length);
5970 /* Adjusting the xfade may affect other regions in the playlist, so we need
5971 to get undo Commands from the whole playlist rather than just the
5975 vector<Command*> cmds;
5976 ar->playlist()->rdiff (cmds);
5977 _editor->session()->add_commands (cmds);
5978 _editor->commit_reversible_command ();
5983 CrossfadeEdgeDrag::aborted (bool)
5986 // arv->redraw_start_xfade ();
5988 // arv->redraw_end_xfade ();
5992 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5993 : Drag (e, item, true)
5994 , line (new EditorCursor (*e))
5996 line->set_position (pos);
6000 RegionCutDrag::~RegionCutDrag ()
6006 RegionCutDrag::motion (GdkEvent*, bool)
6008 framepos_t where = _drags->current_pointer_frame();
6009 _editor->snap_to (where);
6011 line->set_position (where);
6015 RegionCutDrag::finished (GdkEvent*, bool)
6017 _editor->get_track_canvas()->canvas()->re_enter();
6019 framepos_t pos = _drags->current_pointer_frame();
6023 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6029 _editor->split_regions_at (pos, rs);
6033 RegionCutDrag::aborted (bool)