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 cerr << "Aborting drag\n";
101 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
106 if (!_drags.empty ()) {
107 _editor->set_follow_playhead (_old_follow_playhead, false);
111 _editor->abort_reversible_command();
117 DragManager::add (Drag* d)
119 d->set_manager (this);
120 _drags.push_back (d);
124 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
126 d->set_manager (this);
127 _drags.push_back (d);
132 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
134 /* Prevent follow playhead during the drag to be nice to the user */
135 _old_follow_playhead = _editor->follow_playhead ();
136 _editor->set_follow_playhead (false);
138 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
140 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
141 (*i)->start_grab (e, c);
145 /** Call end_grab for each active drag.
146 * @return true if any drag reported movement having occurred.
149 DragManager::end_grab (GdkEvent* e)
154 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
155 bool const t = (*i)->end_grab (e);
166 _editor->set_follow_playhead (_old_follow_playhead, false);
172 DragManager::mark_double_click ()
174 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
175 (*i)->set_double_click (true);
180 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
184 /* calling this implies that we expect the event to have canvas
187 * Can we guarantee that this is true?
190 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
192 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
193 bool const t = (*i)->motion_handler (e, from_autoscroll);
194 /* run all handlers; return true if at least one of them
195 returns true (indicating that the event has been handled).
207 DragManager::have_item (ArdourCanvas::Item* i) const
209 list<Drag*>::const_iterator j = _drags.begin ();
210 while (j != _drags.end() && (*j)->item () != i) {
214 return j != _drags.end ();
217 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
220 , _pointer_frame_offset (0)
221 , _x_constrained (false)
222 , _y_constrained (false)
223 , _trackview_only (trackview_only)
224 , _move_threshold_passed (false)
225 , _starting_point_passed (false)
226 , _initially_vertical (false)
227 , _was_double_click (false)
228 , _raw_grab_frame (0)
230 , _last_pointer_frame (0)
237 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
243 _cursor_ctx = CursorContext::create (*_editor, cursor);
245 _cursor_ctx->change (cursor);
252 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
255 /* we set up x/y dragging constraints on first move */
257 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
259 setup_pointer_frame_offset ();
260 _grab_frame = adjusted_frame (_raw_grab_frame, event);
261 _last_pointer_frame = _grab_frame;
262 _last_pointer_x = _grab_x;
264 if (_trackview_only) {
265 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
268 _last_pointer_y = _grab_y;
272 if (!_editor->cursors()->is_invalid (cursor)) {
273 /* CAIROCANVAS need a variant here that passes *cursor */
274 _cursor_ctx = CursorContext::create (*_editor, cursor);
277 if (_editor->session() && _editor->session()->transport_rolling()) {
280 _was_rolling = false;
283 switch (_editor->snap_type()) {
284 case SnapToRegionStart:
285 case SnapToRegionEnd:
286 case SnapToRegionSync:
287 case SnapToRegionBoundary:
288 _editor->build_region_boundary_cache ();
295 /** Call to end a drag `successfully'. Ungrabs item and calls
296 * subclass' finished() method.
298 * @param event GDK event, or 0.
299 * @return true if some movement occurred, otherwise false.
302 Drag::end_grab (GdkEvent* event)
304 _editor->stop_canvas_autoscroll ();
308 finished (event, _move_threshold_passed);
310 _editor->verbose_cursor()->hide ();
313 return _move_threshold_passed;
317 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
321 if (f > _pointer_frame_offset) {
322 pos = f - _pointer_frame_offset;
326 _editor->snap_to_with_modifier (pos, event);
333 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
335 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
339 Drag::snap_delta (guint state) const
341 if (ArdourKeyboard::indicates_snap_delta (state)) {
349 Drag::current_pointer_x() const
351 return _drags->current_pointer_x ();
355 Drag::current_pointer_y () const
357 if (!_trackview_only) {
358 return _drags->current_pointer_y ();
361 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
365 Drag::setup_snap_delta (framepos_t pos)
367 framepos_t temp = pos;
368 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
369 _snap_delta = temp - pos;
373 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
375 /* check to see if we have moved in any way that matters since the last motion event */
376 if (_move_threshold_passed &&
377 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
378 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
382 pair<framecnt_t, int> const threshold = move_threshold ();
384 bool const old_move_threshold_passed = _move_threshold_passed;
386 if (!_move_threshold_passed) {
388 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
389 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
391 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
394 if (active (_editor->mouse_mode) && _move_threshold_passed) {
396 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
398 if (old_move_threshold_passed != _move_threshold_passed) {
402 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
403 _initially_vertical = true;
405 _initially_vertical = false;
407 /** check constraints for this drag.
408 * Note that the current convention is to use "contains" for
409 * key modifiers during motion and "equals" when initiating a drag.
410 * In this case we haven't moved yet, so "equals" applies here.
412 if (Config->get_edit_mode() != Lock) {
413 if (event->motion.state & Gdk::BUTTON2_MASK) {
414 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
415 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
416 _x_constrained = false;
417 _y_constrained = true;
419 _x_constrained = true;
420 _y_constrained = false;
422 } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
423 // if dragging normally, the motion is constrained to the first direction of movement.
424 if (_initially_vertical) {
425 _x_constrained = true;
426 _y_constrained = false;
428 _x_constrained = false;
429 _y_constrained = true;
433 if (event->button.state & Gdk::BUTTON2_MASK) {
434 _x_constrained = false;
436 _x_constrained = true;
438 _y_constrained = false;
442 if (!from_autoscroll) {
443 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
446 if (!_editor->autoscroll_active() || from_autoscroll) {
449 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
451 motion (event, first_move && !_starting_point_passed);
453 if (first_move && !_starting_point_passed) {
454 _starting_point_passed = true;
457 _last_pointer_x = _drags->current_pointer_x ();
458 _last_pointer_y = current_pointer_y ();
459 _last_pointer_frame = adjusted_current_frame (event);
469 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
477 aborted (_move_threshold_passed);
479 _editor->stop_canvas_autoscroll ();
480 _editor->verbose_cursor()->hide ();
484 Drag::show_verbose_cursor_time (framepos_t frame)
486 _editor->verbose_cursor()->set_time (frame);
487 _editor->verbose_cursor()->show ();
491 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
493 _editor->verbose_cursor()->set_duration (start, end);
494 _editor->verbose_cursor()->show ();
498 Drag::show_verbose_cursor_text (string const & text)
500 _editor->verbose_cursor()->set (text);
501 _editor->verbose_cursor()->show ();
504 boost::shared_ptr<Region>
505 Drag::add_midi_region (MidiTimeAxisView* view)
507 if (_editor->session()) {
508 const TempoMap& map (_editor->session()->tempo_map());
509 framecnt_t pos = grab_frame();
510 const Meter& m = map.meter_at (pos);
511 /* not that the frame rate used here can be affected by pull up/down which
514 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
515 return view->add_region (grab_frame(), len, true);
518 return boost::shared_ptr<Region>();
521 struct EditorOrderTimeAxisViewSorter {
522 bool operator() (TimeAxisView* a, TimeAxisView* b) {
523 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
524 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
526 return ra->route()->order_key () < rb->route()->order_key ();
530 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
535 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
537 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
538 as some of the regions we are dragging may be on such tracks.
541 TrackViewList track_views = _editor->track_views;
542 track_views.sort (EditorOrderTimeAxisViewSorter ());
544 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
545 _time_axis_views.push_back (*i);
547 TimeAxisView::Children children_list = (*i)->get_child_list ();
548 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
549 _time_axis_views.push_back (j->get());
553 /* the list of views can be empty at this point if this is a region list-insert drag
556 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
557 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
560 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
564 RegionDrag::region_going_away (RegionView* v)
566 list<DraggingView>::iterator i = _views.begin ();
567 while (i != _views.end() && i->view != v) {
571 if (i != _views.end()) {
576 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
577 * or -1 if it is not found.
580 RegionDrag::find_time_axis_view (TimeAxisView* t) const
583 int const N = _time_axis_views.size ();
584 while (i < N && _time_axis_views[i] != t) {
588 if (_time_axis_views[i] != t) {
595 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
596 : RegionDrag (e, i, p, v)
599 , _last_pointer_time_axis_view (0)
600 , _last_pointer_layer (0)
605 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
609 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
611 Drag::start_grab (event, cursor);
612 setup_snap_delta (_last_frame_position);
614 show_verbose_cursor_time (_last_frame_position);
616 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
618 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
619 assert(_last_pointer_time_axis_view >= 0);
620 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
625 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
627 /* compute the amount of pointer motion in frames, and where
628 the region would be if we moved it by that much.
630 *pending_region_position = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
632 framepos_t sync_frame;
633 framecnt_t sync_offset;
636 sync_offset = _primary->region()->sync_offset (sync_dir);
638 /* we don't handle a sync point that lies before zero.
640 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
642 sync_frame = *pending_region_position + (sync_dir * sync_offset);
644 _editor->snap_to_with_modifier (sync_frame, event);
646 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - snap_delta (event->button.state);
649 *pending_region_position = _last_frame_position;
652 if (*pending_region_position > max_framepos - _primary->region()->length()) {
653 *pending_region_position = _last_frame_position;
658 bool const x_move_allowed = !_x_constrained;
660 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
662 /* x movement since last time (in pixels) */
663 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
665 /* total x movement */
666 framecnt_t total_dx = *pending_region_position;
667 if (regions_came_from_canvas()) {
668 total_dx = total_dx - grab_frame ();
671 /* check that no regions have gone off the start of the session */
672 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
673 if ((i->view->region()->position() + total_dx) < 0) {
675 *pending_region_position = _last_frame_position;
686 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
692 const int tavsize = _time_axis_views.size();
693 const int dt = delta > 0 ? +1 : -1;
695 int target = start + delta - skip;
697 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
698 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
700 while (current >= 0 && current != target) {
702 if (current < 0 && dt < 0) {
705 if (current >= tavsize && dt > 0) {
708 if (current < 0 || current >= tavsize) {
712 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
713 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
717 if (distance_only && current == start + delta) {
725 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
727 if (_y_constrained) {
731 const int tavsize = _time_axis_views.size();
732 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
733 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
734 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
736 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
737 /* already in the drop zone */
738 if (delta_track >= 0) {
739 /* downward motion - OK if others are still not in the dropzone */
748 } else if (n >= tavsize) {
749 /* downward motion into drop zone. That's fine. */
753 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
754 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
755 /* not a track, or the wrong type */
759 double const l = i->layer + delta_layer;
761 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
762 mode to allow the user to place a region below another on layer 0.
764 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
765 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
766 If it has, the layers will be munged later anyway, so it's ok.
772 /* all regions being dragged are ok with this change */
776 struct DraggingViewSorter {
777 bool operator() (const DraggingView& a, const DraggingView& b) {
778 return a.time_axis_view < b.time_axis_view;
783 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
785 double delta_layer = 0;
786 int delta_time_axis_view = 0;
787 int current_pointer_time_axis_view = -1;
789 assert (!_views.empty ());
791 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
793 /* Find the TimeAxisView that the pointer is now over */
794 const double cur_y = current_pointer_y ();
795 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
796 TimeAxisView* tv = r.first;
798 if (!tv && cur_y < 0) {
799 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
803 /* find drop-zone y-position */
804 Coord last_track_bottom_edge;
805 last_track_bottom_edge = 0;
806 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
807 if (!(*t)->hidden()) {
808 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
813 if (tv && tv->view()) {
814 /* the mouse is over a track */
815 double layer = r.second;
817 if (first_move && tv->view()->layer_display() == Stacked) {
818 tv->view()->set_layer_display (Expanded);
821 /* Here's the current pointer position in terms of time axis view and layer */
822 current_pointer_time_axis_view = find_time_axis_view (tv);
823 assert(current_pointer_time_axis_view >= 0);
825 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
827 /* Work out the change in y */
829 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
830 if (!rtv || !rtv->is_track()) {
831 /* ignore busses early on. we can't move any regions on them */
832 } else if (_last_pointer_time_axis_view < 0) {
833 /* Was in the drop-zone, now over a track.
834 * Hence it must be an upward move (from the bottom)
836 * track_index is still -1, so delta must be set to
837 * move up the correct number of tracks from the bottom.
839 * This is necessary because steps may be skipped if
840 * the bottom-most track is not a valid target and/or
841 * if there are hidden tracks at the bottom.
842 * Hence the initial offset (_ddropzone) as well as the
843 * last valid pointer position (_pdropzone) need to be
844 * taken into account.
846 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
848 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
851 /* TODO needs adjustment per DraggingView,
853 * e.g. select one region on the top-layer of a track
854 * and one region which is at the bottom-layer of another track
857 * Indicated drop-zones and layering is wrong.
858 * and may infer additional layers on the target-track
859 * (depending how many layers the original track had).
861 * Or select two regions (different layers) on a same track,
862 * move across a non-layer track.. -> layering info is lost.
863 * on drop either of the regions may be on top.
865 * Proposed solution: screw it :) well,
866 * don't use delta_layer, use an absolute value
867 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
868 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
869 * 3) iterate over all DraggingView, find the one that is over the track with most layers
870 * 4) proportionally scale layer to layers available on target
872 delta_layer = current_pointer_layer - _last_pointer_layer;
875 /* for automation lanes, there is a TimeAxisView but no ->view()
876 * if (!tv) -> dropzone
878 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
879 /* Moving into the drop-zone.. */
880 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
881 /* delta_time_axis_view may not be sufficient to move into the DZ
882 * the mouse may enter it, but it may not be a valid move due to
885 * -> remember the delta needed to move into the dropzone
887 _ddropzone = delta_time_axis_view;
888 /* ..but subtract hidden tracks (or routes) at the bottom.
889 * we silently move mover them
891 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
892 - _time_axis_views.size();
894 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
895 /* move around inside the zone.
896 * This allows to move further down until all regions are in the zone.
898 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
899 assert(ptr_y >= last_track_bottom_edge);
900 assert(_ddropzone > 0);
902 /* calculate mouse position in 'tracks' below last track. */
903 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
904 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
906 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
908 delta_time_axis_view = dzpos - _pdropzone;
909 } else if (dzpos < _pdropzone && _ndropzone > 0) {
910 // move up inside the DZ
911 delta_time_axis_view = dzpos - _pdropzone;
915 /* Work out the change in x */
916 framepos_t pending_region_position;
917 double const x_delta = compute_x_delta (event, &pending_region_position);
918 _last_frame_position = pending_region_position;
920 /* calculate hidden tracks in current y-axis delta */
922 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
923 /* The mouse is more than one track below the dropzone.
924 * distance calculation is not needed (and would not work, either
925 * because the dropzone is "packed").
927 * Except when [partially] moving regions out of dropzone in a large step.
928 * (the mouse may or may not remain in the DZ)
929 * Hidden tracks at the bottom of the TAV need to be skipped.
931 * This also handles the case if the mouse entered the DZ
932 * in a large step (exessive delta), either due to fast-movement,
933 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
935 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
936 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
938 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
939 -_time_axis_views.size() - dt;
942 else if (_last_pointer_time_axis_view < 0) {
943 /* Moving out of the zone. Check for hidden tracks at the bottom. */
944 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
945 -_time_axis_views.size() - delta_time_axis_view;
947 /* calculate hidden tracks that are skipped by the pointer movement */
948 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
949 - _last_pointer_time_axis_view
950 - delta_time_axis_view;
953 /* Verify change in y */
954 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
955 /* this y movement is not allowed, so do no y movement this time */
956 delta_time_axis_view = 0;
961 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
962 /* haven't reached next snap point, and we're not switching
963 trackviews nor layers. nothing to do.
968 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
969 PlaylistDropzoneMap playlist_dropzone_map;
970 _ndropzone = 0; // number of elements currently in the dropzone
973 /* sort views by time_axis.
974 * This retains track order in the dropzone, regardless
975 * of actual selection order
977 _views.sort (DraggingViewSorter());
979 /* count number of distinct tracks of all regions
980 * being dragged, used for dropzone.
983 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
984 if (i->time_axis_view != prev_track) {
985 prev_track = i->time_axis_view;
991 _views.back().time_axis_view -
992 _views.front().time_axis_view;
994 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
995 - _views.back().time_axis_view;
997 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1001 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1003 RegionView* rv = i->view;
1008 if (rv->region()->locked() || rv->region()->video_locked()) {
1015 /* reparent the regionview into a group above all
1019 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1020 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1021 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1022 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1023 /* move the item so that it continues to appear at the
1024 same location now that its parent has changed.
1026 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1029 /* If we have moved tracks, we'll fudge the layer delta so that the
1030 region gets moved back onto layer 0 on its new track; this avoids
1031 confusion when dragging regions from non-zero layers onto different
1034 double this_delta_layer = delta_layer;
1035 if (delta_time_axis_view != 0) {
1036 this_delta_layer = - i->layer;
1039 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1041 int track_index = i->time_axis_view + this_delta_time_axis_view;
1042 assert(track_index >= 0);
1044 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1045 /* Track is in the Dropzone */
1047 i->time_axis_view = track_index;
1048 assert(i->time_axis_view >= (int) _time_axis_views.size());
1051 double yposition = 0;
1052 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1053 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1056 /* store index of each new playlist as a negative count, starting at -1 */
1058 if (pdz == playlist_dropzone_map.end()) {
1059 /* compute where this new track (which doesn't exist yet) will live
1062 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1064 /* How high is this region view ? */
1066 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1067 ArdourCanvas::Rect bbox;
1070 bbox = obbox.get ();
1073 last_track_bottom_edge += bbox.height();
1075 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1078 yposition = pdz->second;
1081 /* values are zero or negative, hence the use of min() */
1082 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1087 /* The TimeAxisView that this region is now over */
1088 TimeAxisView* current_tv = _time_axis_views[track_index];
1090 /* Ensure it is moved from stacked -> expanded if appropriate */
1091 if (current_tv->view()->layer_display() == Stacked) {
1092 current_tv->view()->set_layer_display (Expanded);
1095 /* We're only allowed to go -ve in layer on Expanded views */
1096 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1097 this_delta_layer = - i->layer;
1101 rv->set_height (current_tv->view()->child_height ());
1103 /* Update show/hidden status as the region view may have come from a hidden track,
1104 or have moved to one.
1106 if (current_tv->hidden ()) {
1107 rv->get_canvas_group()->hide ();
1109 rv->get_canvas_group()->show ();
1112 /* Update the DraggingView */
1113 i->time_axis_view = track_index;
1114 i->layer += this_delta_layer;
1117 _editor->mouse_brush_insert_region (rv, pending_region_position);
1121 /* Get the y coordinate of the top of the track that this region is now over */
1122 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1124 /* And adjust for the layer that it should be on */
1125 StreamView* cv = current_tv->view ();
1126 switch (cv->layer_display ()) {
1130 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1133 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1137 /* need to get the parent of the regionview
1138 * canvas group and get its position in
1139 * equivalent coordinate space as the trackview
1140 * we are now dragging over.
1143 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1148 /* Now move the region view */
1149 rv->move (x_delta, y_delta);
1151 } /* foreach region */
1153 _total_x_delta += x_delta;
1155 if (x_delta != 0 && !_brushing) {
1156 show_verbose_cursor_time (_last_frame_position);
1159 /* keep track of pointer movement */
1161 /* the pointer is currently over a time axis view */
1163 if (_last_pointer_time_axis_view < 0) {
1164 /* last motion event was not over a time axis view
1165 * or last y-movement out of the dropzone was not valid
1168 if (delta_time_axis_view < 0) {
1169 /* in the drop zone, moving up */
1171 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1172 * We do not use negative _last_pointer_time_axis_view because
1173 * the dropzone is "packed" (the actual track offset is ignored)
1175 * As opposed to the actual number
1176 * of elements in the dropzone (_ndropzone)
1177 * _pdropzone is not constrained. This is necessary
1178 * to allow moving multiple regions with y-distance
1181 * There can be 0 elements in the dropzone,
1182 * even though the drag-pointer is inside the DZ.
1185 * [ Audio-track, Midi-track, Audio-track, DZ ]
1186 * move regions from both audio tracks at the same time into the
1187 * DZ by grabbing the region in the bottom track.
1189 assert(current_pointer_time_axis_view >= 0);
1190 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1194 /* only move out of the zone if the movement is OK */
1195 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1196 assert(delta_time_axis_view < 0);
1197 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1198 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1199 * the current position can be calculated as follows:
1201 // a well placed oofus attack can still throw this off.
1202 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1203 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1206 /* last motion event was also over a time axis view */
1207 _last_pointer_time_axis_view += delta_time_axis_view;
1208 assert(_last_pointer_time_axis_view >= 0);
1213 /* the pointer is not over a time axis view */
1214 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1215 _pdropzone += delta_time_axis_view - delta_skip;
1216 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1219 _last_pointer_layer += delta_layer;
1223 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1225 if (_copy && first_move) {
1227 if (_x_constrained) {
1228 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1230 _editor->begin_reversible_command (Operations::region_copy);
1233 /* duplicate the regionview(s) and region(s) */
1235 list<DraggingView> new_regionviews;
1237 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1239 RegionView* rv = i->view;
1240 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1241 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1243 const boost::shared_ptr<const Region> original = rv->region();
1244 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1245 region_copy->set_position (original->position());
1246 /* need to set this so that the drop zone code can work. This doesn't
1247 actually put the region into the playlist, but just sets a weak pointer
1250 region_copy->set_playlist (original->playlist());
1254 boost::shared_ptr<AudioRegion> audioregion_copy
1255 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1257 nrv = new AudioRegionView (*arv, audioregion_copy);
1259 boost::shared_ptr<MidiRegion> midiregion_copy
1260 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1261 nrv = new MidiRegionView (*mrv, midiregion_copy);
1266 nrv->get_canvas_group()->show ();
1267 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1269 /* swap _primary to the copy */
1271 if (rv == _primary) {
1275 /* ..and deselect the one we copied */
1277 rv->set_selected (false);
1280 if (!new_regionviews.empty()) {
1282 /* reflect the fact that we are dragging the copies */
1284 _views = new_regionviews;
1286 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1289 } else if (!_copy && first_move) {
1291 if (_x_constrained) {
1292 _editor->begin_reversible_command (_("fixed time region drag"));
1294 _editor->begin_reversible_command (Operations::region_drag);
1298 RegionMotionDrag::motion (event, first_move);
1302 RegionMotionDrag::finished (GdkEvent *, bool)
1304 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1305 if (!(*i)->view()) {
1309 if ((*i)->view()->layer_display() == Expanded) {
1310 (*i)->view()->set_layer_display (Stacked);
1316 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1318 RegionMotionDrag::finished (ev, movement_occurred);
1320 if (!movement_occurred) {
1324 if (was_double_click() && !_views.empty()) {
1325 DraggingView dv = _views.front();
1326 dv.view->show_region_editor ();
1333 assert (!_views.empty ());
1335 /* We might have hidden region views so that they weren't visible during the drag
1336 (when they have been reparented). Now everything can be shown again, as region
1337 views are back in their track parent groups.
1339 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1340 i->view->get_canvas_group()->show ();
1343 bool const changed_position = (_last_frame_position != _primary->region()->position());
1344 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1345 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1365 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1369 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1371 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1376 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1377 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1378 uint32_t output_chan = region->n_channels();
1379 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1380 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1382 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1383 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1385 rtav->set_height (original->current_height());
1389 ChanCount one_midi_port (DataType::MIDI, 1);
1390 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1391 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1392 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1394 rtav->set_height (original->current_height());
1399 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1405 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1407 RegionSelection new_views;
1408 PlaylistSet modified_playlists;
1409 RouteTimeAxisView* new_time_axis_view = 0;
1412 /* all changes were made during motion event handlers */
1414 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1418 _editor->commit_reversible_command ();
1422 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1423 PlaylistMapping playlist_mapping;
1425 /* insert the regions into their new playlists */
1426 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1428 RouteTimeAxisView* dest_rtv = 0;
1430 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1436 if (changed_position && !_x_constrained) {
1437 where = i->view->region()->position() - drag_delta;
1439 where = i->view->region()->position();
1442 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1443 /* dragged to drop zone */
1445 PlaylistMapping::iterator pm;
1447 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1448 /* first region from this original playlist: create a new track */
1449 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1450 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1451 dest_rtv = new_time_axis_view;
1453 /* we already created a new track for regions from this playlist, use it */
1454 dest_rtv = pm->second;
1457 /* destination time axis view is the one we dragged to */
1458 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1461 if (dest_rtv != 0) {
1462 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1463 if (new_view != 0) {
1464 new_views.push_back (new_view);
1468 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1469 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1472 list<DraggingView>::const_iterator next = i;
1478 /* If we've created new regions either by copying or moving
1479 to a new track, we want to replace the old selection with the new ones
1482 if (new_views.size() > 0) {
1483 _editor->selection->set (new_views);
1486 /* write commands for the accumulated diffs for all our modified playlists */
1487 add_stateful_diff_commands_for_playlists (modified_playlists);
1489 _editor->commit_reversible_command ();
1493 RegionMoveDrag::finished_no_copy (
1494 bool const changed_position,
1495 bool const changed_tracks,
1496 framecnt_t const drag_delta
1499 RegionSelection new_views;
1500 PlaylistSet modified_playlists;
1501 PlaylistSet frozen_playlists;
1502 set<RouteTimeAxisView*> views_to_update;
1503 RouteTimeAxisView* new_time_axis_view = 0;
1506 /* all changes were made during motion event handlers */
1507 _editor->commit_reversible_command ();
1511 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1512 PlaylistMapping playlist_mapping;
1514 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1516 RegionView* rv = i->view;
1517 RouteTimeAxisView* dest_rtv = 0;
1519 if (rv->region()->locked() || rv->region()->video_locked()) {
1524 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1525 /* dragged to drop zone */
1527 PlaylistMapping::iterator pm;
1529 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1530 /* first region from this original playlist: create a new track */
1531 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1532 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1533 dest_rtv = new_time_axis_view;
1535 /* we already created a new track for regions from this playlist, use it */
1536 dest_rtv = pm->second;
1540 /* destination time axis view is the one we dragged to */
1541 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1546 double const dest_layer = i->layer;
1548 views_to_update.insert (dest_rtv);
1552 if (changed_position && !_x_constrained) {
1553 where = rv->region()->position() - drag_delta;
1555 where = rv->region()->position();
1558 if (changed_tracks) {
1560 /* insert into new playlist */
1562 RegionView* new_view = insert_region_into_playlist (
1563 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1566 if (new_view == 0) {
1571 new_views.push_back (new_view);
1573 /* remove from old playlist */
1575 /* the region that used to be in the old playlist is not
1576 moved to the new one - we use a copy of it. as a result,
1577 any existing editor for the region should no longer be
1580 rv->hide_region_editor();
1583 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1587 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1589 /* this movement may result in a crossfade being modified, or a layering change,
1590 so we need to get undo data from the playlist as well as the region.
1593 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1595 playlist->clear_changes ();
1598 rv->region()->clear_changes ();
1601 motion on the same track. plonk the previously reparented region
1602 back to its original canvas group (its streamview).
1603 No need to do anything for copies as they are fake regions which will be deleted.
1606 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1607 rv->get_canvas_group()->set_y_position (i->initial_y);
1610 /* just change the model */
1611 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1612 playlist->set_layer (rv->region(), dest_layer);
1615 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1617 r = frozen_playlists.insert (playlist);
1620 playlist->freeze ();
1623 rv->region()->set_position (where);
1625 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1628 if (changed_tracks) {
1630 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1631 was selected in all of them, then removing it from a playlist will have removed all
1632 trace of it from _views (i.e. there were N regions selected, we removed 1,
1633 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1634 corresponding regionview, and _views is now empty).
1636 This could have invalidated any and all iterators into _views.
1638 The heuristic we use here is: if the region selection is empty, break out of the loop
1639 here. if the region selection is not empty, then restart the loop because we know that
1640 we must have removed at least the region(view) we've just been working on as well as any
1641 that we processed on previous iterations.
1643 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1644 we can just iterate.
1648 if (_views.empty()) {
1659 /* If we've created new regions either by copying or moving
1660 to a new track, we want to replace the old selection with the new ones
1663 if (new_views.size() > 0) {
1664 _editor->selection->set (new_views);
1667 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1671 /* write commands for the accumulated diffs for all our modified playlists */
1672 add_stateful_diff_commands_for_playlists (modified_playlists);
1674 _editor->commit_reversible_command ();
1676 /* We have futzed with the layering of canvas items on our streamviews.
1677 If any region changed layer, this will have resulted in the stream
1678 views being asked to set up their region views, and all will be well.
1679 If not, we might now have badly-ordered region views. Ask the StreamViews
1680 involved to sort themselves out, just in case.
1683 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1684 (*i)->view()->playlist_layered ((*i)->track ());
1688 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1689 * @param region Region to remove.
1690 * @param playlist playlist To remove from.
1691 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1692 * that clear_changes () is only called once per playlist.
1695 RegionMoveDrag::remove_region_from_playlist (
1696 boost::shared_ptr<Region> region,
1697 boost::shared_ptr<Playlist> playlist,
1698 PlaylistSet& modified_playlists
1701 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1704 playlist->clear_changes ();
1707 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1711 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1712 * clearing the playlist's diff history first if necessary.
1713 * @param region Region to insert.
1714 * @param dest_rtv Destination RouteTimeAxisView.
1715 * @param dest_layer Destination layer.
1716 * @param where Destination position.
1717 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1718 * that clear_changes () is only called once per playlist.
1719 * @return New RegionView, or 0 if no insert was performed.
1722 RegionMoveDrag::insert_region_into_playlist (
1723 boost::shared_ptr<Region> region,
1724 RouteTimeAxisView* dest_rtv,
1727 PlaylistSet& modified_playlists
1730 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1731 if (!dest_playlist) {
1735 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1736 _new_region_view = 0;
1737 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1739 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1740 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1742 dest_playlist->clear_changes ();
1745 dest_playlist->add_region (region, where);
1747 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1748 dest_playlist->set_layer (region, dest_layer);
1753 assert (_new_region_view);
1755 return _new_region_view;
1759 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1761 _new_region_view = rv;
1765 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1767 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1768 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1770 _editor->session()->add_command (c);
1779 RegionMoveDrag::aborted (bool movement_occurred)
1783 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1784 list<DraggingView>::const_iterator next = i;
1793 RegionMotionDrag::aborted (movement_occurred);
1798 RegionMotionDrag::aborted (bool)
1800 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1802 StreamView* sview = (*i)->view();
1805 if (sview->layer_display() == Expanded) {
1806 sview->set_layer_display (Stacked);
1811 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1812 RegionView* rv = i->view;
1813 TimeAxisView* tv = &(rv->get_time_axis_view ());
1814 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1816 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1817 rv->get_canvas_group()->set_y_position (0);
1819 rv->move (-_total_x_delta, 0);
1820 rv->set_height (rtv->view()->child_height ());
1824 /** @param b true to brush, otherwise false.
1825 * @param c true to make copies of the regions being moved, otherwise false.
1827 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1828 : RegionMotionDrag (e, i, p, v, b)
1831 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1834 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1835 if (rtv && rtv->is_track()) {
1836 speed = rtv->track()->speed ();
1839 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1843 RegionMoveDrag::setup_pointer_frame_offset ()
1845 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1848 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1849 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1851 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1853 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1854 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1856 _primary = v->view()->create_region_view (r, false, false);
1858 _primary->get_canvas_group()->show ();
1859 _primary->set_position (pos, 0);
1860 _views.push_back (DraggingView (_primary, this, v));
1862 _last_frame_position = pos;
1864 _item = _primary->get_canvas_group ();
1868 RegionInsertDrag::finished (GdkEvent *, bool)
1870 int pos = _views.front().time_axis_view;
1871 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1873 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1875 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1876 _primary->get_canvas_group()->set_y_position (0);
1878 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1880 _editor->begin_reversible_command (Operations::insert_region);
1881 playlist->clear_changes ();
1882 playlist->add_region (_primary->region (), _last_frame_position);
1884 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1885 if (Config->get_edit_mode() == Ripple) {
1886 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1889 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1890 _editor->commit_reversible_command ();
1898 RegionInsertDrag::aborted (bool)
1905 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1906 : RegionMoveDrag (e, i, p, v, false, false)
1908 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1911 struct RegionSelectionByPosition {
1912 bool operator() (RegionView*a, RegionView* b) {
1913 return a->region()->position () < b->region()->position();
1918 RegionSpliceDrag::motion (GdkEvent* event, bool)
1920 /* Which trackview is this ? */
1922 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1923 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1925 /* The region motion is only processed if the pointer is over
1929 if (!tv || !tv->is_track()) {
1930 /* To make sure we hide the verbose canvas cursor when the mouse is
1931 not held over an audio track.
1933 _editor->verbose_cursor()->hide ();
1936 _editor->verbose_cursor()->show ();
1941 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1947 RegionSelection copy;
1948 _editor->selection->regions.by_position(copy);
1950 framepos_t const pf = adjusted_current_frame (event);
1952 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1954 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1960 boost::shared_ptr<Playlist> playlist;
1962 if ((playlist = atv->playlist()) == 0) {
1966 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1971 if (pf < (*i)->region()->last_frame() + 1) {
1975 if (pf > (*i)->region()->first_frame()) {
1981 playlist->shuffle ((*i)->region(), dir);
1986 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1988 RegionMoveDrag::finished (event, movement_occurred);
1992 RegionSpliceDrag::aborted (bool)
2002 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2005 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2007 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2008 RegionSelection to_ripple;
2009 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2010 if ((*i)->position() >= where) {
2011 to_ripple.push_back (rtv->view()->find_view(*i));
2015 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2016 if (!exclude.contains (*i)) {
2017 // the selection has already been added to _views
2019 if (drag_in_progress) {
2020 // do the same things that RegionMotionDrag::motion does when
2021 // first_move is true, for the region views that we're adding
2022 // to _views this time
2025 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2026 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2027 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2028 rvg->reparent (_editor->_drag_motion_group);
2030 // we only need to move in the y direction
2031 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2036 _views.push_back (DraggingView (*i, this, tav));
2042 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2045 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2046 // we added all the regions after the selection
2048 std::list<DraggingView>::iterator to_erase = i++;
2049 if (!_editor->selection->regions.contains (to_erase->view)) {
2050 // restore the non-selected regions to their original playlist & positions,
2051 // and then ripple them back by the length of the regions that were dragged away
2052 // do the same things as RegionMotionDrag::aborted
2054 RegionView *rv = to_erase->view;
2055 TimeAxisView* tv = &(rv->get_time_axis_view ());
2056 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2059 // plonk them back onto their own track
2060 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2061 rv->get_canvas_group()->set_y_position (0);
2065 // move the underlying region to match the view
2066 rv->region()->set_position (rv->region()->position() + amount);
2068 // restore the view to match the underlying region's original position
2069 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2072 rv->set_height (rtv->view()->child_height ());
2073 _views.erase (to_erase);
2079 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2081 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2083 return allow_moves_across_tracks;
2091 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2092 : RegionMoveDrag (e, i, p, v, false, false)
2094 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2095 // compute length of selection
2096 RegionSelection selected_regions = _editor->selection->regions;
2097 selection_length = selected_regions.end_frame() - selected_regions.start();
2099 // we'll only allow dragging to another track in ripple mode if all the regions
2100 // being dragged start off on the same track
2101 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2104 exclude = new RegionList;
2105 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2106 exclude->push_back((*i)->region());
2109 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2110 RegionSelection copy;
2111 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2113 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2114 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2116 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2117 // find ripple start point on each applicable playlist
2118 RegionView *first_selected_on_this_track = NULL;
2119 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2120 if ((*i)->region()->playlist() == (*pi)) {
2121 // region is on this playlist - it's the first, because they're sorted
2122 first_selected_on_this_track = *i;
2126 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2127 add_all_after_to_views (
2128 &first_selected_on_this_track->get_time_axis_view(),
2129 first_selected_on_this_track->region()->position(),
2130 selected_regions, false);
2133 if (allow_moves_across_tracks) {
2134 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2142 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2144 /* Which trackview is this ? */
2146 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2147 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2149 /* The region motion is only processed if the pointer is over
2153 if (!tv || !tv->is_track()) {
2154 /* To make sure we hide the verbose canvas cursor when the mouse is
2155 not held over an audiotrack.
2157 _editor->verbose_cursor()->hide ();
2161 framepos_t where = adjusted_current_frame (event);
2162 assert (where >= 0);
2164 double delta = compute_x_delta (event, &after);
2166 framecnt_t amount = _editor->pixel_to_sample (delta);
2168 if (allow_moves_across_tracks) {
2169 // all the originally selected regions were on the same track
2171 framecnt_t adjust = 0;
2172 if (prev_tav && tv != prev_tav) {
2173 // dragged onto a different track
2174 // remove the unselected regions from _views, restore them to their original positions
2175 // and add the regions after the drop point on the new playlist to _views instead.
2176 // undo the effect of rippling the previous playlist, and include the effect of removing
2177 // the dragged region(s) from this track
2179 remove_unselected_from_views (prev_amount, false);
2180 // ripple previous playlist according to the regions that have been removed onto the new playlist
2181 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2184 // move just the selected regions
2185 RegionMoveDrag::motion(event, first_move);
2187 // ensure that the ripple operation on the new playlist inserts selection_length time
2188 adjust = selection_length;
2189 // ripple the new current playlist
2190 tv->playlist()->ripple (where, amount+adjust, exclude);
2192 // add regions after point where drag entered this track to subsequent ripples
2193 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2196 // motion on same track
2197 RegionMoveDrag::motion(event, first_move);
2201 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2202 prev_position = where;
2204 // selection encompasses multiple tracks - just drag
2205 // cross-track drags are forbidden
2206 RegionMoveDrag::motion(event, first_move);
2209 if (!_x_constrained) {
2210 prev_amount += amount;
2213 _last_frame_position = after;
2217 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2219 if (!movement_occurred) {
2223 if (was_double_click() && !_views.empty()) {
2224 DraggingView dv = _views.front();
2225 dv.view->show_region_editor ();
2232 _editor->begin_reversible_command(_("Ripple drag"));
2234 // remove the regions being rippled from the dragging view, updating them to
2235 // their new positions
2236 remove_unselected_from_views (prev_amount, true);
2238 if (allow_moves_across_tracks) {
2240 // if regions were dragged across tracks, we've rippled any later
2241 // regions on the track the regions were dragged off, so we need
2242 // to add the original track to the undo record
2243 orig_tav->playlist()->clear_changes();
2244 vector<Command*> cmds;
2245 orig_tav->playlist()->rdiff (cmds);
2246 _editor->session()->add_commands (cmds);
2248 if (prev_tav && prev_tav != orig_tav) {
2249 prev_tav->playlist()->clear_changes();
2250 vector<Command*> cmds;
2251 prev_tav->playlist()->rdiff (cmds);
2252 _editor->session()->add_commands (cmds);
2255 // selection spanned multiple tracks - all will need adding to undo record
2257 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2258 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2260 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2261 (*pi)->clear_changes();
2262 vector<Command*> cmds;
2263 (*pi)->rdiff (cmds);
2264 _editor->session()->add_commands (cmds);
2268 // other modified playlists are added to undo by RegionMoveDrag::finished()
2269 RegionMoveDrag::finished (event, movement_occurred);
2270 _editor->commit_reversible_command();
2274 RegionRippleDrag::aborted (bool movement_occurred)
2276 RegionMoveDrag::aborted (movement_occurred);
2281 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2283 _view (dynamic_cast<MidiTimeAxisView*> (v))
2285 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2291 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2294 _region = add_midi_region (_view);
2295 _view->playlist()->freeze ();
2298 framepos_t const f = adjusted_current_frame (event);
2299 if (f < grab_frame()) {
2300 _region->set_position (f);
2303 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2304 so that if this region is duplicated, its duplicate starts on
2305 a snap point rather than 1 frame after a snap point. Otherwise things get
2306 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2307 place snapped notes at the start of the region.
2310 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2311 _region->set_length (len < 1 ? 1 : len);
2317 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2319 if (!movement_occurred) {
2320 add_midi_region (_view);
2322 _view->playlist()->thaw ();
2327 RegionCreateDrag::aborted (bool)
2330 _view->playlist()->thaw ();
2336 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2341 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2345 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2347 Gdk::Cursor* cursor;
2348 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2350 float x_fraction = cnote->mouse_x_fraction ();
2352 if (x_fraction > 0.0 && x_fraction < 0.25) {
2353 cursor = _editor->cursors()->left_side_trim;
2356 cursor = _editor->cursors()->right_side_trim;
2360 Drag::start_grab (event, cursor);
2362 region = &cnote->region_view();
2365 temp = region->snap_to_pixel (cnote->x0 (), true);
2366 _snap_delta = temp - cnote->x0 ();
2370 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2376 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2378 if (ms.size() > 1) {
2379 /* has to be relative, may make no sense otherwise */
2383 /* select this note; if it is already selected, preserve the existing selection,
2384 otherwise make this note the only one selected.
2386 region->note_selected (cnote, cnote->selected ());
2388 _editor->begin_reversible_command (_("resize notes"));
2390 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2391 MidiRegionSelection::iterator next;
2394 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2396 mrv->begin_resizing (at_front);
2403 NoteResizeDrag::motion (GdkEvent* event, bool /*first_move*/)
2405 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2406 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2407 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2409 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2413 bool apply_snap_delta = !ArdourKeyboard::indicates_snap_delta (event->button.state);
2415 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2416 if (_editor->snap_mode () != SnapOff) {
2420 if (_editor->snap_mode () == SnapOff) {
2422 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2423 if (!apply_snap_delta) {
2429 if (apply_snap_delta) {
2433 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2439 NoteResizeDrag::finished (GdkEvent* event, bool /*movement_occurred*/)
2441 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2442 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2443 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2445 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2448 bool apply_snap_delta = !ArdourKeyboard::indicates_snap_delta (event->button.state);
2450 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2451 if (_editor->snap_mode () != SnapOff) {
2455 if (_editor->snap_mode () == SnapOff) {
2457 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2458 if (!apply_snap_delta) {
2463 if (apply_snap_delta) {
2466 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2470 _editor->commit_reversible_command ();
2474 NoteResizeDrag::aborted (bool)
2476 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2477 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2478 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2480 mrv->abort_resizing ();
2485 AVDraggingView::AVDraggingView (RegionView* v)
2488 initial_position = v->region()->position ();
2491 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2494 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2497 TrackViewList empty;
2499 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2500 std::list<RegionView*> views = rs.by_layer();
2502 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2503 RegionView* rv = (*i);
2504 if (!rv->region()->video_locked()) {
2507 _views.push_back (AVDraggingView (rv));
2512 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2514 Drag::start_grab (event);
2515 if (_editor->session() == 0) {
2519 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2520 _max_backwards_drag = (
2521 ARDOUR_UI::instance()->video_timeline->get_duration()
2522 + ARDOUR_UI::instance()->video_timeline->get_offset()
2523 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2526 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2527 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2528 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2531 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2534 Timecode::Time timecode;
2535 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2536 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);
2537 show_verbose_cursor_text (buf);
2541 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2543 if (_editor->session() == 0) {
2546 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2550 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2551 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2553 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2554 dt = - _max_backwards_drag;
2557 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2558 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2560 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2561 RegionView* rv = i->view;
2562 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2565 rv->region()->clear_changes ();
2566 rv->region()->suspend_property_changes();
2568 rv->region()->set_position(i->initial_position + dt);
2569 rv->region_changed(ARDOUR::Properties::position);
2572 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2573 Timecode::Time timecode;
2574 Timecode::Time timediff;
2576 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2577 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2578 snprintf (buf, sizeof (buf),
2579 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2580 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2581 , _("Video Start:"),
2582 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2584 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2586 show_verbose_cursor_text (buf);
2590 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2592 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2596 if (!movement_occurred || ! _editor->session()) {
2600 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2602 _editor->begin_reversible_command (_("Move Video"));
2604 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2605 ARDOUR_UI::instance()->video_timeline->save_undo();
2606 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2607 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2609 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2610 i->view->drag_end();
2611 i->view->region()->resume_property_changes ();
2613 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2616 _editor->session()->maybe_update_session_range(
2617 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2618 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2622 _editor->commit_reversible_command ();
2626 VideoTimeLineDrag::aborted (bool)
2628 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2631 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2632 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2634 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2635 i->view->region()->resume_property_changes ();
2636 i->view->region()->set_position(i->initial_position);
2640 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2641 : RegionDrag (e, i, p, v)
2642 , _preserve_fade_anchor (preserve_fade_anchor)
2643 , _jump_position_when_done (false)
2645 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2649 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2652 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2653 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2655 if (tv && tv->is_track()) {
2656 speed = tv->track()->speed();
2659 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2660 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2661 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2663 framepos_t const pf = adjusted_current_frame (event);
2664 setup_snap_delta (region_start);
2666 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2667 /* Move the contents of the region around without changing the region bounds */
2668 _operation = ContentsTrim;
2669 Drag::start_grab (event, _editor->cursors()->trimmer);
2671 /* These will get overridden for a point trim.*/
2672 if (pf < (region_start + region_length/2)) {
2673 /* closer to front */
2674 _operation = StartTrim;
2675 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2676 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2678 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2682 _operation = EndTrim;
2683 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2684 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2686 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2690 /* jump trim disabled for now
2691 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2692 _jump_position_when_done = true;
2696 switch (_operation) {
2698 show_verbose_cursor_time (region_start);
2699 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2700 i->view->trim_front_starting ();
2704 show_verbose_cursor_duration (region_start, region_end);
2707 show_verbose_cursor_time (pf);
2711 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2712 i->view->region()->suspend_property_changes ();
2717 TrimDrag::motion (GdkEvent* event, bool first_move)
2719 RegionView* rv = _primary;
2722 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2723 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2724 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2725 frameoffset_t frame_delta = 0;
2727 if (tv && tv->is_track()) {
2728 speed = tv->track()->speed();
2730 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2731 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2737 switch (_operation) {
2739 trim_type = "Region start trim";
2742 trim_type = "Region end trim";
2745 trim_type = "Region content trim";
2752 _editor->begin_reversible_command (trim_type);
2754 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2755 RegionView* rv = i->view;
2756 rv->enable_display (false);
2757 rv->region()->playlist()->clear_owned_changes ();
2759 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2762 arv->temporarily_hide_envelope ();
2766 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2767 insert_result = _editor->motion_frozen_playlists.insert (pl);
2769 if (insert_result.second) {
2775 bool non_overlap_trim = false;
2777 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2778 non_overlap_trim = true;
2781 /* contstrain trim to fade length */
2782 if (_preserve_fade_anchor) {
2783 switch (_operation) {
2785 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2786 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2788 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2789 if (ar->locked()) continue;
2790 framecnt_t len = ar->fade_in()->back()->when;
2791 if (len < dt) dt = min(dt, len);
2795 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2796 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2798 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2799 if (ar->locked()) continue;
2800 framecnt_t len = ar->fade_out()->back()->when;
2801 if (len < -dt) dt = max(dt, -len);
2810 switch (_operation) {
2812 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2813 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2814 if (changed && _preserve_fade_anchor) {
2815 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2817 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2818 framecnt_t len = ar->fade_in()->back()->when;
2819 framecnt_t diff = ar->first_frame() - i->initial_position;
2820 framepos_t new_length = len - diff;
2821 i->anchored_fade_length = min (ar->length(), new_length);
2822 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2823 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2830 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2831 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2832 if (changed && _preserve_fade_anchor) {
2833 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2835 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2836 framecnt_t len = ar->fade_out()->back()->when;
2837 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2838 framepos_t new_length = len + diff;
2839 i->anchored_fade_length = min (ar->length(), new_length);
2840 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2841 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2849 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2851 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2852 i->view->move_contents (frame_delta);
2858 switch (_operation) {
2860 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2863 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2866 // show_verbose_cursor_time (frame_delta);
2872 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2874 if (movement_occurred) {
2875 motion (event, false);
2877 if (_operation == StartTrim) {
2878 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2880 /* This must happen before the region's StatefulDiffCommand is created, as it may
2881 `correct' (ahem) the region's _start from being negative to being zero. It
2882 needs to be zero in the undo record.
2884 i->view->trim_front_ending ();
2886 if (_preserve_fade_anchor) {
2887 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2889 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2890 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2891 ar->set_fade_in_length(i->anchored_fade_length);
2892 ar->set_fade_in_active(true);
2895 if (_jump_position_when_done) {
2896 i->view->region()->set_position (i->initial_position);
2899 } else if (_operation == EndTrim) {
2900 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2901 if (_preserve_fade_anchor) {
2902 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2904 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2905 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2906 ar->set_fade_out_length(i->anchored_fade_length);
2907 ar->set_fade_out_active(true);
2910 if (_jump_position_when_done) {
2911 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2916 if (!_views.empty()) {
2917 if (_operation == StartTrim) {
2918 _editor->maybe_locate_with_edit_preroll(
2919 _views.begin()->view->region()->position());
2921 if (_operation == EndTrim) {
2922 _editor->maybe_locate_with_edit_preroll(
2923 _views.begin()->view->region()->position() +
2924 _views.begin()->view->region()->length());
2928 if (!_editor->selection->selected (_primary)) {
2929 _primary->thaw_after_trim ();
2932 set<boost::shared_ptr<Playlist> > diffed_playlists;
2934 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2935 i->view->thaw_after_trim ();
2936 i->view->enable_display (true);
2938 /* Trimming one region may affect others on the playlist, so we need
2939 to get undo Commands from the whole playlist rather than just the
2940 region. Use diffed_playlists to make sure we don't diff a given
2941 playlist more than once.
2943 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2944 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2945 vector<Command*> cmds;
2947 _editor->session()->add_commands (cmds);
2948 diffed_playlists.insert (p);
2953 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2957 _editor->motion_frozen_playlists.clear ();
2958 _editor->commit_reversible_command();
2961 /* no mouse movement */
2962 _editor->point_trim (event, adjusted_current_frame (event));
2965 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2966 if (_operation == StartTrim) {
2967 i->view->trim_front_ending ();
2970 i->view->region()->resume_property_changes ();
2975 TrimDrag::aborted (bool movement_occurred)
2977 /* Our motion method is changing model state, so use the Undo system
2978 to cancel. Perhaps not ideal, as this will leave an Undo point
2979 behind which may be slightly odd from the user's point of view.
2984 if (movement_occurred) {
2988 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2989 i->view->region()->resume_property_changes ();
2994 TrimDrag::setup_pointer_frame_offset ()
2996 list<DraggingView>::iterator i = _views.begin ();
2997 while (i != _views.end() && i->view != _primary) {
3001 if (i == _views.end()) {
3005 switch (_operation) {
3007 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3010 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3017 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3021 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3022 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3027 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3029 Drag::start_grab (event, cursor);
3030 show_verbose_cursor_time (adjusted_current_frame(event));
3034 MeterMarkerDrag::setup_pointer_frame_offset ()
3036 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3040 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3042 if (!_marker->meter().movable()) {
3048 // create a dummy marker for visual representation of moving the
3049 // section, because whether its a copy or not, we're going to
3050 // leave or lose the original marker (leave if its a copy; lose if its
3051 // not, because we'll remove it from the map).
3053 MeterSection section (_marker->meter());
3055 if (!section.movable()) {
3060 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3062 _marker = new MeterMarker (
3064 *_editor->meter_group,
3065 ARDOUR_UI::config()->color ("meter marker"),
3067 *new MeterSection (_marker->meter())
3070 /* use the new marker for the grab */
3071 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3074 TempoMap& map (_editor->session()->tempo_map());
3075 /* get current state */
3076 before_state = &map.get_state();
3077 /* remove the section while we drag it */
3078 map.remove_meter (section, true);
3082 framepos_t const pf = adjusted_current_frame (event);
3084 _marker->set_position (pf);
3085 show_verbose_cursor_time (pf);
3089 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3091 if (!movement_occurred) {
3092 if (was_double_click()) {
3093 _editor->edit_meter_marker (*_marker);
3098 if (!_marker->meter().movable()) {
3102 motion (event, false);
3104 Timecode::BBT_Time when;
3106 TempoMap& map (_editor->session()->tempo_map());
3107 map.bbt_time (last_pointer_frame(), when);
3109 if (_copy == true) {
3110 _editor->begin_reversible_command (_("copy meter mark"));
3111 XMLNode &before = map.get_state();
3112 map.add_meter (_marker->meter(), when);
3113 XMLNode &after = map.get_state();
3114 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3115 _editor->commit_reversible_command ();
3118 _editor->begin_reversible_command (_("move meter mark"));
3120 /* we removed it before, so add it back now */
3122 map.add_meter (_marker->meter(), when);
3123 XMLNode &after = map.get_state();
3124 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3125 _editor->commit_reversible_command ();
3128 // delete the dummy marker we used for visual representation while moving.
3129 // a new visual marker will show up automatically.
3134 MeterMarkerDrag::aborted (bool moved)
3136 _marker->set_position (_marker->meter().frame ());
3139 TempoMap& map (_editor->session()->tempo_map());
3140 /* we removed it before, so add it back now */
3141 map.add_meter (_marker->meter(), _marker->meter().frame());
3142 // delete the dummy marker we used for visual representation while moving.
3143 // a new visual marker will show up automatically.
3148 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3152 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3154 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3159 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3161 Drag::start_grab (event, cursor);
3162 show_verbose_cursor_time (adjusted_current_frame (event));
3166 TempoMarkerDrag::setup_pointer_frame_offset ()
3168 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3172 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3174 if (!_marker->tempo().movable()) {
3180 // create a dummy marker for visual representation of moving the
3181 // section, because whether its a copy or not, we're going to
3182 // leave or lose the original marker (leave if its a copy; lose if its
3183 // not, because we'll remove it from the map).
3185 // create a dummy marker for visual representation of moving the copy.
3186 // The actual copying is not done before we reach the finish callback.
3189 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3191 TempoSection section (_marker->tempo());
3193 _marker = new TempoMarker (
3195 *_editor->tempo_group,
3196 ARDOUR_UI::config()->color ("tempo marker"),
3198 *new TempoSection (_marker->tempo())
3201 /* use the new marker for the grab */
3202 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3205 TempoMap& map (_editor->session()->tempo_map());
3206 /* get current state */
3207 before_state = &map.get_state();
3208 /* remove the section while we drag it */
3209 map.remove_tempo (section, true);
3213 framepos_t const pf = adjusted_current_frame (event);
3214 _marker->set_position (pf);
3215 show_verbose_cursor_time (pf);
3219 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3221 if (!movement_occurred) {
3222 if (was_double_click()) {
3223 _editor->edit_tempo_marker (*_marker);
3228 if (!_marker->tempo().movable()) {
3232 motion (event, false);
3234 TempoMap& map (_editor->session()->tempo_map());
3235 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3236 Timecode::BBT_Time when;
3238 map.bbt_time (beat_time, when);
3240 if (_copy == true) {
3241 _editor->begin_reversible_command (_("copy tempo mark"));
3242 XMLNode &before = map.get_state();
3243 map.add_tempo (_marker->tempo(), when);
3244 XMLNode &after = map.get_state();
3245 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3246 _editor->commit_reversible_command ();
3249 _editor->begin_reversible_command (_("move tempo mark"));
3250 /* we removed it before, so add it back now */
3251 map.add_tempo (_marker->tempo(), when);
3252 XMLNode &after = map.get_state();
3253 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3254 _editor->commit_reversible_command ();
3257 // delete the dummy marker we used for visual representation while moving.
3258 // a new visual marker will show up automatically.
3263 TempoMarkerDrag::aborted (bool moved)
3265 _marker->set_position (_marker->tempo().frame());
3267 TempoMap& map (_editor->session()->tempo_map());
3268 /* we removed it before, so add it back now */
3269 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3270 // delete the dummy marker we used for visual representation while moving.
3271 // a new visual marker will show up automatically.
3276 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3277 : Drag (e, &c.track_canvas_item(), false)
3281 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3284 /** Do all the things we do when dragging the playhead to make it look as though
3285 * we have located, without actually doing the locate (because that would cause
3286 * the diskstream buffers to be refilled, which is too slow).
3289 CursorDrag::fake_locate (framepos_t t)
3291 _editor->playhead_cursor->set_position (t);
3293 Session* s = _editor->session ();
3294 if (s->timecode_transmission_suspended ()) {
3295 framepos_t const f = _editor->playhead_cursor->current_frame ();
3296 /* This is asynchronous so it will be sent "now"
3298 s->send_mmc_locate (f);
3299 /* These are synchronous and will be sent during the next
3302 s->queue_full_time_code ();
3303 s->queue_song_position_pointer ();
3306 show_verbose_cursor_time (t);
3307 _editor->UpdateAllTransportClocks (t);
3311 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3313 Drag::start_grab (event, c);
3314 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3316 _grab_zoom = _editor->samples_per_pixel;
3318 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3320 _editor->snap_to_with_modifier (where, event);
3322 _editor->_dragging_playhead = true;
3324 Session* s = _editor->session ();
3326 /* grab the track canvas item as well */
3328 _cursor.track_canvas_item().grab();
3331 if (_was_rolling && _stop) {
3335 if (s->is_auditioning()) {
3336 s->cancel_audition ();
3340 if (AudioEngine::instance()->connected()) {
3342 /* do this only if we're the engine is connected
3343 * because otherwise this request will never be
3344 * serviced and we'll busy wait forever. likewise,
3345 * notice if we are disconnected while waiting for the
3346 * request to be serviced.
3349 s->request_suspend_timecode_transmission ();
3350 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3351 /* twiddle our thumbs */
3356 fake_locate (where - snap_delta (event->button.state));
3360 CursorDrag::motion (GdkEvent* event, bool)
3362 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3363 _editor->snap_to_with_modifier (where, event);
3364 if (where != last_pointer_frame()) {
3365 fake_locate (where - snap_delta (event->button.state));
3370 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3372 _editor->_dragging_playhead = false;
3374 _cursor.track_canvas_item().ungrab();
3376 if (!movement_occurred && _stop) {
3380 motion (event, false);
3382 Session* s = _editor->session ();
3384 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3385 _editor->_pending_locate_request = true;
3386 s->request_resume_timecode_transmission ();
3391 CursorDrag::aborted (bool)
3393 _cursor.track_canvas_item().ungrab();
3395 if (_editor->_dragging_playhead) {
3396 _editor->session()->request_resume_timecode_transmission ();
3397 _editor->_dragging_playhead = false;
3400 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3403 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3404 : RegionDrag (e, i, p, v)
3406 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3410 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3412 Drag::start_grab (event, cursor);
3414 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3415 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3416 setup_snap_delta (r->position ());
3418 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3422 FadeInDrag::setup_pointer_frame_offset ()
3424 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3425 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3426 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3430 FadeInDrag::motion (GdkEvent* event, bool)
3432 framecnt_t fade_length;
3434 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3435 _editor->snap_to_with_modifier (pos, event);
3436 pos -= snap_delta (event->button.state);
3438 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3440 if (pos < (region->position() + 64)) {
3441 fade_length = 64; // this should be a minimum defined somewhere
3442 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3443 fade_length = region->length() - region->fade_out()->back()->when - 1;
3445 fade_length = pos - region->position();
3448 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3450 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3456 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3459 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3463 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3465 if (!movement_occurred) {
3469 framecnt_t fade_length;
3470 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3471 _editor->snap_to_with_modifier (pos, event);
3472 pos -= snap_delta (event->button.state);
3474 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3476 if (pos < (region->position() + 64)) {
3477 fade_length = 64; // this should be a minimum defined somewhere
3478 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3479 fade_length = region->length() - region->fade_out()->back()->when - 1;
3481 fade_length = pos - region->position();
3484 _editor->begin_reversible_command (_("change fade in length"));
3486 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3488 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3494 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3495 XMLNode &before = alist->get_state();
3497 tmp->audio_region()->set_fade_in_length (fade_length);
3498 tmp->audio_region()->set_fade_in_active (true);
3500 XMLNode &after = alist->get_state();
3501 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3504 _editor->commit_reversible_command ();
3508 FadeInDrag::aborted (bool)
3510 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3511 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3517 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3521 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3522 : RegionDrag (e, i, p, v)
3524 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3528 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3530 Drag::start_grab (event, cursor);
3532 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3533 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3534 setup_snap_delta (r->last_frame ());
3536 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3540 FadeOutDrag::setup_pointer_frame_offset ()
3542 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3543 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3544 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3548 FadeOutDrag::motion (GdkEvent* event, bool)
3550 framecnt_t fade_length;
3552 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3553 _editor->snap_to_with_modifier (pos, event);
3554 pos -= snap_delta (event->button.state);
3556 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3558 if (pos > (region->last_frame() - 64)) {
3559 fade_length = 64; // this should really be a minimum fade defined somewhere
3560 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3561 fade_length = region->length() - region->fade_in()->back()->when - 1;
3563 fade_length = region->last_frame() - pos;
3566 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3568 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3574 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3577 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3581 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3583 if (!movement_occurred) {
3587 framecnt_t fade_length;
3589 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3590 _editor->snap_to_with_modifier (pos, event);
3591 pos -= snap_delta (event->button.state);
3593 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3595 if (pos > (region->last_frame() - 64)) {
3596 fade_length = 64; // this should really be a minimum fade defined somewhere
3597 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3598 fade_length = region->length() - region->fade_in()->back()->when - 1;
3600 fade_length = region->last_frame() - pos;
3603 _editor->begin_reversible_command (_("change fade out length"));
3605 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3607 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3613 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3614 XMLNode &before = alist->get_state();
3616 tmp->audio_region()->set_fade_out_length (fade_length);
3617 tmp->audio_region()->set_fade_out_active (true);
3619 XMLNode &after = alist->get_state();
3620 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3623 _editor->commit_reversible_command ();
3627 FadeOutDrag::aborted (bool)
3629 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3630 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3636 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3640 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3643 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3645 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3648 _points.push_back (ArdourCanvas::Duple (0, 0));
3649 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3652 MarkerDrag::~MarkerDrag ()
3654 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3659 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3661 location = new Location (*l);
3662 markers.push_back (m);
3667 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3669 Drag::start_grab (event, cursor);
3673 Location *location = _editor->find_location_from_marker (_marker, is_start);
3674 _editor->_dragging_edit_point = true;
3676 update_item (location);
3678 // _drag_line->show();
3679 // _line->raise_to_top();
3682 show_verbose_cursor_time (location->start());
3684 show_verbose_cursor_time (location->end());
3687 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3690 case Selection::Toggle:
3691 /* we toggle on the button release */
3693 case Selection::Set:
3694 if (!_editor->selection->selected (_marker)) {
3695 _editor->selection->set (_marker);
3698 case Selection::Extend:
3700 Locations::LocationList ll;
3701 list<Marker*> to_add;
3703 _editor->selection->markers.range (s, e);
3704 s = min (_marker->position(), s);
3705 e = max (_marker->position(), e);
3708 if (e < max_framepos) {
3711 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3712 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3713 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3716 to_add.push_back (lm->start);
3719 to_add.push_back (lm->end);
3723 if (!to_add.empty()) {
3724 _editor->selection->add (to_add);
3728 case Selection::Add:
3729 _editor->selection->add (_marker);
3733 /* Set up copies for us to manipulate during the drag
3736 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3738 Location* l = _editor->find_location_from_marker (*i, is_start);
3745 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3747 /* range: check that the other end of the range isn't
3750 CopiedLocationInfo::iterator x;
3751 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3752 if (*(*x).location == *l) {
3756 if (x == _copied_locations.end()) {
3757 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3759 (*x).markers.push_back (*i);
3760 (*x).move_both = true;
3768 MarkerDrag::setup_pointer_frame_offset ()
3771 Location *location = _editor->find_location_from_marker (_marker, is_start);
3772 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3776 MarkerDrag::motion (GdkEvent* event, bool)
3778 framecnt_t f_delta = 0;
3780 bool move_both = false;
3781 Location *real_location;
3782 Location *copy_location = 0;
3784 framepos_t const newframe = adjusted_current_frame (event);
3785 framepos_t next = newframe;
3787 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3791 CopiedLocationInfo::iterator x;
3793 /* find the marker we're dragging, and compute the delta */
3795 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3797 copy_location = (*x).location;
3799 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3801 /* this marker is represented by this
3802 * CopiedLocationMarkerInfo
3805 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3810 if (real_location->is_mark()) {
3811 f_delta = newframe - copy_location->start();
3815 switch (_marker->type()) {
3816 case Marker::SessionStart:
3817 case Marker::RangeStart:
3818 case Marker::LoopStart:
3819 case Marker::PunchIn:
3820 f_delta = newframe - copy_location->start();
3823 case Marker::SessionEnd:
3824 case Marker::RangeEnd:
3825 case Marker::LoopEnd:
3826 case Marker::PunchOut:
3827 f_delta = newframe - copy_location->end();
3830 /* what kind of marker is this ? */
3839 if (x == _copied_locations.end()) {
3840 /* hmm, impossible - we didn't find the dragged marker */
3844 /* now move them all */
3846 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3848 copy_location = x->location;
3850 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3854 if (real_location->locked()) {
3858 if (copy_location->is_mark()) {
3862 copy_location->set_start (copy_location->start() + f_delta);
3866 framepos_t new_start = copy_location->start() + f_delta;
3867 framepos_t new_end = copy_location->end() + f_delta;
3869 if (is_start) { // start-of-range marker
3871 if (move_both || (*x).move_both) {
3872 copy_location->set_start (new_start);
3873 copy_location->set_end (new_end);
3874 } else if (new_start < copy_location->end()) {
3875 copy_location->set_start (new_start);
3876 } else if (newframe > 0) {
3877 _editor->snap_to (next, RoundUpAlways, true);
3878 copy_location->set_end (next);
3879 copy_location->set_start (newframe);
3882 } else { // end marker
3884 if (move_both || (*x).move_both) {
3885 copy_location->set_end (new_end);
3886 copy_location->set_start (new_start);
3887 } else if (new_end > copy_location->start()) {
3888 copy_location->set_end (new_end);
3889 } else if (newframe > 0) {
3890 _editor->snap_to (next, RoundDownAlways, true);
3891 copy_location->set_start (next);
3892 copy_location->set_end (newframe);
3897 update_item (copy_location);
3899 /* now lookup the actual GUI items used to display this
3900 * location and move them to wherever the copy of the location
3901 * is now. This means that the logic in ARDOUR::Location is
3902 * still enforced, even though we are not (yet) modifying
3903 * the real Location itself.
3906 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3909 lm->set_position (copy_location->start(), copy_location->end());
3914 assert (!_copied_locations.empty());
3916 show_verbose_cursor_time (newframe);
3920 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3922 if (!movement_occurred) {
3924 if (was_double_click()) {
3925 _editor->rename_marker (_marker);
3929 /* just a click, do nothing but finish
3930 off the selection process
3933 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3936 case Selection::Set:
3937 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3938 _editor->selection->set (_marker);
3942 case Selection::Toggle:
3943 /* we toggle on the button release, click only */
3944 _editor->selection->toggle (_marker);
3947 case Selection::Extend:
3948 case Selection::Add:
3955 _editor->_dragging_edit_point = false;
3957 _editor->begin_reversible_command ( _("move marker") );
3958 XMLNode &before = _editor->session()->locations()->get_state();
3960 MarkerSelection::iterator i;
3961 CopiedLocationInfo::iterator x;
3964 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3965 x != _copied_locations.end() && i != _editor->selection->markers.end();
3968 Location * location = _editor->find_location_from_marker (*i, is_start);
3972 if (location->locked()) {
3976 if (location->is_mark()) {
3977 location->set_start (((*x).location)->start());
3979 location->set (((*x).location)->start(), ((*x).location)->end());
3984 XMLNode &after = _editor->session()->locations()->get_state();
3985 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3986 _editor->commit_reversible_command ();
3990 MarkerDrag::aborted (bool movement_occured)
3992 if (!movement_occured) {
3996 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3998 /* move all markers to their original location */
4001 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4004 Location * location = _editor->find_location_from_marker (*m, is_start);
4007 (*m)->set_position (is_start ? location->start() : location->end());
4014 MarkerDrag::update_item (Location*)
4019 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4021 _cumulative_x_drag (0),
4022 _cumulative_y_drag (0)
4024 if (_zero_gain_fraction < 0.0) {
4025 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4028 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4030 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4036 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4038 Drag::start_grab (event, _editor->cursors()->fader);
4040 // start the grab at the center of the control point so
4041 // the point doesn't 'jump' to the mouse after the first drag
4042 _fixed_grab_x = _point->get_x();
4043 _fixed_grab_y = _point->get_y();
4045 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4046 setup_snap_delta (pos);
4048 float const fraction = 1 - (_point->get_y() / _point->line().height());
4050 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4052 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4054 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4056 if (!_point->can_slide ()) {
4057 _x_constrained = true;
4062 ControlPointDrag::motion (GdkEvent* event, bool)
4064 double dx = _drags->current_pointer_x() - last_pointer_x();
4065 double dy = current_pointer_y() - last_pointer_y();
4067 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4072 /* coordinate in pixels relative to the start of the region (for region-based automation)
4073 or track (for track-based automation) */
4074 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4075 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4077 // calculate zero crossing point. back off by .01 to stay on the
4078 // positive side of zero
4079 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4081 // make sure we hit zero when passing through
4082 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4086 if (_x_constrained) {
4089 if (_y_constrained) {
4093 _cumulative_x_drag = cx - _fixed_grab_x;
4094 _cumulative_y_drag = cy - _fixed_grab_y;
4098 cy = min ((double) _point->line().height(), cy);
4100 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4102 if (!_x_constrained) {
4103 _editor->snap_to_with_modifier (cx_frames, event);
4106 cx_frames -= snap_delta (event->button.state);
4107 cx_frames = min (cx_frames, _point->line().maximum_time());
4109 float const fraction = 1.0 - (cy / _point->line().height());
4111 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4113 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4117 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4119 if (!movement_occurred) {
4122 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4123 _editor->reset_point_selection ();
4127 motion (event, false);
4130 _point->line().end_drag (_pushing, _final_index);
4131 _editor->commit_reversible_command ();
4135 ControlPointDrag::aborted (bool)
4137 _point->line().reset ();
4141 ControlPointDrag::active (Editing::MouseMode m)
4143 if (m == Editing::MouseDraw) {
4144 /* always active in mouse draw */
4148 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4149 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4152 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4155 , _cumulative_y_drag (0)
4157 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4161 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4163 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4166 _item = &_line->grab_item ();
4168 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4169 origin, and ditto for y.
4172 double cx = event->button.x;
4173 double cy = event->button.y;
4175 _line->parent_group().canvas_to_item (cx, cy);
4177 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4182 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4183 /* no adjacent points */
4187 Drag::start_grab (event, _editor->cursors()->fader);
4189 /* store grab start in parent frame */
4194 double fraction = 1.0 - (cy / _line->height());
4196 _line->start_drag_line (before, after, fraction);
4198 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4202 LineDrag::motion (GdkEvent* event, bool)
4204 double dy = current_pointer_y() - last_pointer_y();
4206 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4210 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4212 _cumulative_y_drag = cy - _fixed_grab_y;
4215 cy = min ((double) _line->height(), cy);
4217 double const fraction = 1.0 - (cy / _line->height());
4220 /* we are ignoring x position for this drag, so we can just pass in anything */
4221 _line->drag_motion (0, fraction, true, false, ignored);
4223 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4227 LineDrag::finished (GdkEvent* event, bool movement_occured)
4229 if (movement_occured) {
4230 motion (event, false);
4231 _line->end_drag (false, 0);
4233 /* add a new control point on the line */
4235 AutomationTimeAxisView* atv;
4237 _line->end_drag (false, 0);
4239 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4240 framepos_t where = _editor->window_event_sample (event, 0, 0);
4241 atv->add_automation_event (event, where, event->button.y, false);
4245 _editor->commit_reversible_command ();
4249 LineDrag::aborted (bool)
4254 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4257 _cumulative_x_drag (0)
4259 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4263 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4265 Drag::start_grab (event);
4267 _line = reinterpret_cast<Line*> (_item);
4270 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4272 double cx = event->button.x;
4273 double cy = event->button.y;
4275 _item->parent()->canvas_to_item (cx, cy);
4277 /* store grab start in parent frame */
4278 _region_view_grab_x = cx;
4280 _before = *(float*) _item->get_data ("position");
4282 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4284 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4288 FeatureLineDrag::motion (GdkEvent*, bool)
4290 double dx = _drags->current_pointer_x() - last_pointer_x();
4292 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4294 _cumulative_x_drag += dx;
4296 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4305 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4307 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4309 float *pos = new float;
4312 _line->set_data ("position", pos);
4318 FeatureLineDrag::finished (GdkEvent*, bool)
4320 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4321 _arv->update_transient(_before, _before);
4325 FeatureLineDrag::aborted (bool)
4330 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4332 , _vertical_only (false)
4334 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4338 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4340 Drag::start_grab (event);
4341 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4345 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4352 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4354 framepos_t grab = grab_frame ();
4355 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4356 _editor->snap_to_with_modifier (grab, event);
4358 grab = raw_grab_frame ();
4361 /* base start and end on initial click position */
4371 if (current_pointer_y() < grab_y()) {
4372 y1 = current_pointer_y();
4375 y2 = current_pointer_y();
4379 if (start != end || y1 != y2) {
4381 double x1 = _editor->sample_to_pixel (start);
4382 double x2 = _editor->sample_to_pixel (end);
4383 const double min_dimension = 2.0;
4385 if (_vertical_only) {
4386 /* fixed 10 pixel width */
4390 x2 = min (x1 - min_dimension, x2);
4392 x2 = max (x1 + min_dimension, x2);
4397 y2 = min (y1 - min_dimension, y2);
4399 y2 = max (y1 + min_dimension, y2);
4402 /* translate rect into item space and set */
4404 ArdourCanvas::Rect r (x1, y1, x2, y2);
4406 /* this drag is a _trackview_only == true drag, so the y1 and
4407 * y2 (computed using current_pointer_y() and grab_y()) will be
4408 * relative to the top of the trackview group). The
4409 * rubberband rect has the same parent/scroll offset as the
4410 * the trackview group, so we can use the "r" rect directly
4411 * to set the shape of the rubberband.
4414 _editor->rubberband_rect->set (r);
4415 _editor->rubberband_rect->show();
4416 _editor->rubberband_rect->raise_to_top();
4418 show_verbose_cursor_time (pf);
4420 do_select_things (event, true);
4425 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4429 framepos_t grab = grab_frame ();
4430 framepos_t lpf = last_pointer_frame ();
4432 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4433 grab = raw_grab_frame ();
4434 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4448 if (current_pointer_y() < grab_y()) {
4449 y1 = current_pointer_y();
4452 y2 = current_pointer_y();
4456 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4460 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4462 if (movement_occurred) {
4464 motion (event, false);
4465 do_select_things (event, false);
4471 bool do_deselect = true;
4472 MidiTimeAxisView* mtv;
4474 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4476 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4477 /* nothing selected */
4478 add_midi_region (mtv);
4479 do_deselect = false;
4483 /* do not deselect if Primary or Tertiary (toggle-select or
4484 * extend-select are pressed.
4487 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4488 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4495 _editor->rubberband_rect->hide();
4499 RubberbandSelectDrag::aborted (bool)
4501 _editor->rubberband_rect->hide ();
4504 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4505 : RegionDrag (e, i, p, v)
4507 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4511 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4513 Drag::start_grab (event, cursor);
4515 _editor->get_selection().add (_primary);
4517 framepos_t where = _primary->region()->position();
4518 setup_snap_delta (where);
4520 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4524 TimeFXDrag::motion (GdkEvent* event, bool)
4526 RegionView* rv = _primary;
4527 StreamView* cv = rv->get_time_axis_view().view ();
4529 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4530 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4531 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4532 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4533 _editor->snap_to_with_modifier (pf, event);
4534 pf -= snap_delta (event->button.state);
4536 if (pf > rv->region()->position()) {
4537 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4540 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4544 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4546 _primary->get_time_axis_view().hide_timestretch ();
4548 if (!movement_occurred) {
4552 if (last_pointer_frame() < _primary->region()->position()) {
4553 /* backwards drag of the left edge - not usable */
4557 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4559 float percentage = (double) newlen / (double) _primary->region()->length();
4561 #ifndef USE_RUBBERBAND
4562 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4563 if (_primary->region()->data_type() == DataType::AUDIO) {
4564 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4568 if (!_editor->get_selection().regions.empty()) {
4569 /* primary will already be included in the selection, and edit
4570 group shared editing will propagate selection across
4571 equivalent regions, so just use the current region
4575 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4576 error << _("An error occurred while executing time stretch operation") << endmsg;
4582 TimeFXDrag::aborted (bool)
4584 _primary->get_time_axis_view().hide_timestretch ();
4587 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4590 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4594 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4596 Drag::start_grab (event);
4600 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4602 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4606 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4608 if (movement_occurred && _editor->session()) {
4609 /* make sure we stop */
4610 _editor->session()->request_transport_speed (0.0);
4615 ScrubDrag::aborted (bool)
4620 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4624 , _time_selection_at_start (!_editor->get_selection().time.empty())
4626 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4628 if (_time_selection_at_start) {
4629 start_at_start = _editor->get_selection().time.start();
4630 end_at_start = _editor->get_selection().time.end_frame();
4635 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4637 if (_editor->session() == 0) {
4641 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4643 switch (_operation) {
4644 case CreateSelection:
4645 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4650 cursor = _editor->cursors()->selector;
4651 Drag::start_grab (event, cursor);
4654 case SelectionStartTrim:
4655 if (_editor->clicked_axisview) {
4656 _editor->clicked_axisview->order_selection_trims (_item, true);
4658 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4661 case SelectionEndTrim:
4662 if (_editor->clicked_axisview) {
4663 _editor->clicked_axisview->order_selection_trims (_item, false);
4665 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4669 Drag::start_grab (event, cursor);
4672 case SelectionExtend:
4673 Drag::start_grab (event, cursor);
4677 if (_operation == SelectionMove) {
4678 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4680 show_verbose_cursor_time (adjusted_current_frame (event));
4685 SelectionDrag::setup_pointer_frame_offset ()
4687 switch (_operation) {
4688 case CreateSelection:
4689 _pointer_frame_offset = 0;
4692 case SelectionStartTrim:
4694 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4697 case SelectionEndTrim:
4698 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4701 case SelectionExtend:
4707 SelectionDrag::motion (GdkEvent* event, bool first_move)
4709 framepos_t start = 0;
4711 framecnt_t length = 0;
4712 framecnt_t distance = 0;
4714 framepos_t const pending_position = adjusted_current_frame (event);
4716 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4720 switch (_operation) {
4721 case CreateSelection:
4723 framepos_t grab = grab_frame ();
4726 grab = adjusted_current_frame (event, false);
4727 if (grab < pending_position) {
4728 _editor->snap_to (grab, RoundDownMaybe);
4730 _editor->snap_to (grab, RoundUpMaybe);
4734 if (pending_position < grab) {
4735 start = pending_position;
4738 end = pending_position;
4742 /* first drag: Either add to the selection
4743 or create a new selection
4750 /* adding to the selection */
4751 _editor->set_selected_track_as_side_effect (Selection::Add);
4752 _editor->clicked_selection = _editor->selection->add (start, end);
4759 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4760 _editor->set_selected_track_as_side_effect (Selection::Set);
4763 _editor->clicked_selection = _editor->selection->set (start, end);
4767 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4768 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4769 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4771 _editor->selection->add (atest);
4775 /* select all tracks within the rectangle that we've marked out so far */
4776 TrackViewList new_selection;
4777 TrackViewList& all_tracks (_editor->track_views);
4779 ArdourCanvas::Coord const top = grab_y();
4780 ArdourCanvas::Coord const bottom = current_pointer_y();
4782 if (top >= 0 && bottom >= 0) {
4784 //first, find the tracks that are covered in the y range selection
4785 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4786 if ((*i)->covered_by_y_range (top, bottom)) {
4787 new_selection.push_back (*i);
4791 //now find any tracks that are GROUPED with the tracks we selected
4792 TrackViewList grouped_add = new_selection;
4793 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4794 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4795 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4796 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4797 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4798 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4799 grouped_add.push_back (*j);
4804 //now compare our list with the current selection, and add or remove as necessary
4805 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4806 TrackViewList tracks_to_add;
4807 TrackViewList tracks_to_remove;
4808 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4809 if ( !_editor->selection->tracks.contains ( *i ) )
4810 tracks_to_add.push_back ( *i );
4811 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4812 if ( !grouped_add.contains ( *i ) )
4813 tracks_to_remove.push_back ( *i );
4814 _editor->selection->add(tracks_to_add);
4815 _editor->selection->remove(tracks_to_remove);
4821 case SelectionStartTrim:
4823 start = _editor->selection->time[_editor->clicked_selection].start;
4824 end = _editor->selection->time[_editor->clicked_selection].end;
4826 if (pending_position > end) {
4829 start = pending_position;
4833 case SelectionEndTrim:
4835 start = _editor->selection->time[_editor->clicked_selection].start;
4836 end = _editor->selection->time[_editor->clicked_selection].end;
4838 if (pending_position < start) {
4841 end = pending_position;
4848 start = _editor->selection->time[_editor->clicked_selection].start;
4849 end = _editor->selection->time[_editor->clicked_selection].end;
4851 length = end - start;
4852 distance = pending_position - start;
4853 start = pending_position;
4854 _editor->snap_to (start);
4856 end = start + length;
4860 case SelectionExtend:
4865 switch (_operation) {
4867 if (_time_selection_at_start) {
4868 _editor->selection->move_time (distance);
4872 _editor->selection->replace (_editor->clicked_selection, start, end);
4876 if (_operation == SelectionMove) {
4877 show_verbose_cursor_time(start);
4879 show_verbose_cursor_time(pending_position);
4884 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4886 Session* s = _editor->session();
4888 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4889 if (movement_occurred) {
4890 motion (event, false);
4891 /* XXX this is not object-oriented programming at all. ick */
4892 if (_editor->selection->time.consolidate()) {
4893 _editor->selection->TimeChanged ();
4896 /* XXX what if its a music time selection? */
4898 if ( s->get_play_range() && s->transport_rolling() ) {
4899 s->request_play_range (&_editor->selection->time, true);
4901 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4902 if (_operation == SelectionEndTrim)
4903 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4905 s->request_locate (_editor->get_selection().time.start());
4911 /* just a click, no pointer movement.
4914 if (_operation == SelectionExtend) {
4915 if (_time_selection_at_start) {
4916 framepos_t pos = adjusted_current_frame (event, false);
4917 framepos_t start = min (pos, start_at_start);
4918 framepos_t end = max (pos, end_at_start);
4919 _editor->selection->set (start, end);
4922 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4923 if (_editor->clicked_selection) {
4924 _editor->selection->remove (_editor->clicked_selection);
4927 if (!_editor->clicked_selection) {
4928 _editor->selection->clear_time();
4933 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4934 _editor->selection->set (_editor->clicked_axisview);
4937 if (s && s->get_play_range () && s->transport_rolling()) {
4938 s->request_stop (false, false);
4943 _editor->stop_canvas_autoscroll ();
4944 _editor->clicked_selection = 0;
4945 _editor->commit_reversible_selection_op ();
4949 SelectionDrag::aborted (bool)
4954 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4955 : Drag (e, i, false),
4959 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4961 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4962 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4963 physical_screen_height (_editor->get_window())));
4964 _drag_rect->hide ();
4966 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4967 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4970 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4972 /* normal canvas items will be cleaned up when their parent group is deleted. But
4973 this item is created as the child of a long-lived parent group, and so we
4974 need to explicitly delete it.
4980 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4982 if (_editor->session() == 0) {
4986 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4988 if (!_editor->temp_location) {
4989 _editor->temp_location = new Location (*_editor->session());
4992 switch (_operation) {
4993 case CreateSkipMarker:
4994 case CreateRangeMarker:
4995 case CreateTransportMarker:
4996 case CreateCDMarker:
4998 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5003 cursor = _editor->cursors()->selector;
5007 Drag::start_grab (event, cursor);
5009 show_verbose_cursor_time (adjusted_current_frame (event));
5013 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5015 framepos_t start = 0;
5017 ArdourCanvas::Rectangle *crect;
5019 switch (_operation) {
5020 case CreateSkipMarker:
5021 crect = _editor->range_bar_drag_rect;
5023 case CreateRangeMarker:
5024 crect = _editor->range_bar_drag_rect;
5026 case CreateTransportMarker:
5027 crect = _editor->transport_bar_drag_rect;
5029 case CreateCDMarker:
5030 crect = _editor->cd_marker_bar_drag_rect;
5033 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5038 framepos_t const pf = adjusted_current_frame (event);
5040 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5041 framepos_t grab = grab_frame ();
5042 _editor->snap_to (grab);
5044 if (pf < grab_frame()) {
5052 /* first drag: Either add to the selection
5053 or create a new selection.
5058 _editor->temp_location->set (start, end);
5062 update_item (_editor->temp_location);
5064 //_drag_rect->raise_to_top();
5070 _editor->temp_location->set (start, end);
5072 double x1 = _editor->sample_to_pixel (start);
5073 double x2 = _editor->sample_to_pixel (end);
5077 update_item (_editor->temp_location);
5080 show_verbose_cursor_time (pf);
5085 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5087 Location * newloc = 0;
5091 if (movement_occurred) {
5092 motion (event, false);
5095 switch (_operation) {
5096 case CreateSkipMarker:
5097 case CreateRangeMarker:
5098 case CreateCDMarker:
5100 XMLNode &before = _editor->session()->locations()->get_state();
5101 if (_operation == CreateSkipMarker) {
5102 _editor->begin_reversible_command (_("new skip marker"));
5103 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5104 flags = Location::IsRangeMarker | Location::IsSkip;
5105 _editor->range_bar_drag_rect->hide();
5106 } else if (_operation == CreateCDMarker) {
5107 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5108 _editor->begin_reversible_command (_("new CD marker"));
5109 flags = Location::IsRangeMarker | Location::IsCDMarker;
5110 _editor->cd_marker_bar_drag_rect->hide();
5112 _editor->begin_reversible_command (_("new skip marker"));
5113 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5114 flags = Location::IsRangeMarker;
5115 _editor->range_bar_drag_rect->hide();
5117 newloc = new Location (
5118 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5121 _editor->session()->locations()->add (newloc, true);
5122 XMLNode &after = _editor->session()->locations()->get_state();
5123 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5124 _editor->commit_reversible_command ();
5128 case CreateTransportMarker:
5129 // popup menu to pick loop or punch
5130 _editor->new_transport_marker_context_menu (&event->button, _item);
5136 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5138 if (_operation == CreateTransportMarker) {
5140 /* didn't drag, so just locate */
5142 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5144 } else if (_operation == CreateCDMarker) {
5146 /* didn't drag, but mark is already created so do
5149 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5154 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5156 if (end == max_framepos) {
5157 end = _editor->session()->current_end_frame ();
5160 if (start == max_framepos) {
5161 start = _editor->session()->current_start_frame ();
5164 switch (_editor->mouse_mode) {
5166 /* find the two markers on either side and then make the selection from it */
5167 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5171 /* find the two markers on either side of the click and make the range out of it */
5172 _editor->selection->set (start, end);
5181 _editor->stop_canvas_autoscroll ();
5185 RangeMarkerBarDrag::aborted (bool movement_occured)
5187 if (movement_occured) {
5188 _drag_rect->hide ();
5193 RangeMarkerBarDrag::update_item (Location* location)
5195 double const x1 = _editor->sample_to_pixel (location->start());
5196 double const x2 = _editor->sample_to_pixel (location->end());
5198 _drag_rect->set_x0 (x1);
5199 _drag_rect->set_x1 (x2);
5202 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5204 , _cumulative_dx (0)
5205 , _cumulative_dy (0)
5207 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5209 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5211 _region = &_primary->region_view ();
5212 _note_height = _region->midi_stream_view()->note_height ();
5216 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5218 Drag::start_grab (event);
5219 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5221 if (!(_was_selected = _primary->selected())) {
5223 /* tertiary-click means extend selection - we'll do that on button release,
5224 so don't add it here, because otherwise we make it hard to figure
5225 out the "extend-to" range.
5228 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5231 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5234 _region->note_selected (_primary, true);
5236 _region->unique_select (_primary);
5239 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5240 _editor->commit_reversible_selection_op();
5245 /** @return Current total drag x change in frames */
5247 NoteDrag::total_dx (const guint state) const
5250 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5252 /* primary note time */
5253 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5255 /* new time of the primary note in session frames */
5256 frameoffset_t st = n + dx + snap_delta (state);
5258 framepos_t const rp = _region->region()->position ();
5260 /* prevent the note being dragged earlier than the region's position */
5263 /* possibly snap and return corresponding delta */
5267 if (ArdourKeyboard::indicates_snap (state)) {
5268 if (_editor->snap_mode () != SnapOff) {
5272 if (_editor->snap_mode () == SnapOff) {
5274 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5275 if (ArdourKeyboard::indicates_snap_delta (state)) {
5281 return _region->snap_frame_to_frame (st - rp, snap) + rp - n - snap_delta (state);
5284 /** @return Current total drag y change in note number */
5286 NoteDrag::total_dy () const
5288 MidiStreamView* msv = _region->midi_stream_view ();
5289 double const y = _region->midi_view()->y_position ();
5290 /* new current note */
5291 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5293 n = max (msv->lowest_note(), n);
5294 n = min (msv->highest_note(), n);
5295 /* and work out delta */
5296 return n - msv->y_to_note (grab_y() - y);
5300 NoteDrag::motion (GdkEvent * event, bool)
5302 /* Total change in x and y since the start of the drag */
5303 frameoffset_t const dx = total_dx (event->button.state);
5304 int8_t const dy = total_dy ();
5306 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5307 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5308 double const tdy = -dy * _note_height - _cumulative_dy;
5311 _cumulative_dx += tdx;
5312 _cumulative_dy += tdy;
5314 int8_t note_delta = total_dy();
5316 _region->move_selection (tdx, tdy, note_delta);
5318 /* the new note value may be the same as the old one, but we
5319 * don't know what that means because the selection may have
5320 * involved more than one note and we might be doing something
5321 * odd with them. so show the note value anyway, always.
5325 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5327 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5328 (int) floor ((double)new_note));
5330 show_verbose_cursor_text (buf);
5335 NoteDrag::finished (GdkEvent* ev, bool moved)
5338 /* no motion - select note */
5340 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5341 _editor->current_mouse_mode() == Editing::MouseDraw) {
5343 bool changed = false;
5345 if (_was_selected) {
5346 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5348 _region->note_deselected (_primary);
5352 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5353 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5355 if (!extend && !add && _region->selection_size() > 1) {
5356 _region->unique_select (_primary);
5358 } else if (extend) {
5359 _region->note_selected (_primary, true, true);
5362 /* it was added during button press */
5367 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5368 _editor->commit_reversible_selection_op();
5372 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5377 NoteDrag::aborted (bool)
5382 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5383 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5384 : Drag (editor, atv->base_item ())
5386 , _y_origin (atv->y_position())
5387 , _nothing_to_drag (false)
5389 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5390 setup (atv->lines ());
5393 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5394 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5395 : Drag (editor, rv->get_canvas_group ())
5397 , _y_origin (rv->get_time_axis_view().y_position())
5398 , _nothing_to_drag (false)
5401 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5403 list<boost::shared_ptr<AutomationLine> > lines;
5405 AudioRegionView* audio_view;
5406 AutomationRegionView* automation_view;
5407 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5408 lines.push_back (audio_view->get_gain_line ());
5409 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5410 lines.push_back (automation_view->line ());
5413 error << _("Automation range drag created for invalid region type") << endmsg;
5419 /** @param lines AutomationLines to drag.
5420 * @param offset Offset from the session start to the points in the AutomationLines.
5423 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5425 /* find the lines that overlap the ranges being dragged */
5426 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5427 while (i != lines.end ()) {
5428 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5431 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5433 /* check this range against all the AudioRanges that we are using */
5434 list<AudioRange>::const_iterator k = _ranges.begin ();
5435 while (k != _ranges.end()) {
5436 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5442 /* add it to our list if it overlaps at all */
5443 if (k != _ranges.end()) {
5448 _lines.push_back (n);
5454 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5458 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5460 return 1.0 - ((global_y - _y_origin) / line->height());
5464 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5466 const double v = list->eval(x);
5467 return _integral ? rint(v) : v;
5471 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5473 Drag::start_grab (event, cursor);
5475 /* Get line states before we start changing things */
5476 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5477 i->state = &i->line->get_state ();
5478 i->original_fraction = y_fraction (i->line, current_pointer_y());
5481 if (_ranges.empty()) {
5483 /* No selected time ranges: drag all points */
5484 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5485 uint32_t const N = i->line->npoints ();
5486 for (uint32_t j = 0; j < N; ++j) {
5487 i->points.push_back (i->line->nth (j));
5493 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5495 framecnt_t const half = (i->start + i->end) / 2;
5497 /* find the line that this audio range starts in */
5498 list<Line>::iterator j = _lines.begin();
5499 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5503 if (j != _lines.end()) {
5504 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5506 /* j is the line that this audio range starts in; fade into it;
5507 64 samples length plucked out of thin air.
5510 framepos_t a = i->start + 64;
5515 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5516 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5518 the_list->editor_add (p, value (the_list, p));
5519 the_list->editor_add (q, value (the_list, q));
5522 /* same thing for the end */
5525 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5529 if (j != _lines.end()) {
5530 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5532 /* j is the line that this audio range starts in; fade out of it;
5533 64 samples length plucked out of thin air.
5536 framepos_t b = i->end - 64;
5541 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5542 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5544 the_list->editor_add (p, value (the_list, p));
5545 the_list->editor_add (q, value (the_list, q));
5549 _nothing_to_drag = true;
5551 /* Find all the points that should be dragged and put them in the relevant
5552 points lists in the Line structs.
5555 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5557 uint32_t const N = i->line->npoints ();
5558 for (uint32_t j = 0; j < N; ++j) {
5560 /* here's a control point on this line */
5561 ControlPoint* p = i->line->nth (j);
5562 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5564 /* see if it's inside a range */
5565 list<AudioRange>::const_iterator k = _ranges.begin ();
5566 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5570 if (k != _ranges.end()) {
5571 /* dragging this point */
5572 _nothing_to_drag = false;
5573 i->points.push_back (p);
5579 if (_nothing_to_drag) {
5583 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5584 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5589 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5591 if (_nothing_to_drag) {
5595 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5596 float const f = y_fraction (l->line, current_pointer_y());
5597 /* we are ignoring x position for this drag, so we can just pass in anything */
5599 l->line->drag_motion (0, f, true, false, ignored);
5600 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5605 AutomationRangeDrag::finished (GdkEvent* event, bool)
5607 if (_nothing_to_drag) {
5611 motion (event, false);
5612 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5613 i->line->end_drag (false, 0);
5616 _editor->commit_reversible_command ();
5620 AutomationRangeDrag::aborted (bool)
5622 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5627 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5629 , initial_time_axis_view (itav)
5631 /* note that time_axis_view may be null if the regionview was created
5632 * as part of a copy operation.
5634 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5635 layer = v->region()->layer ();
5636 initial_y = v->get_canvas_group()->position().y;
5637 initial_playlist = v->region()->playlist ();
5638 initial_position = v->region()->position ();
5639 initial_end = v->region()->position () + v->region()->length ();
5642 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5643 : Drag (e, i->canvas_item ())
5646 , _cumulative_dx (0)
5648 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5649 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5654 PatchChangeDrag::motion (GdkEvent* ev, bool)
5656 framepos_t f = adjusted_current_frame (ev);
5657 boost::shared_ptr<Region> r = _region_view->region ();
5658 f = max (f, r->position ());
5659 f = min (f, r->last_frame ());
5661 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5662 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5663 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5664 _cumulative_dx = dxu;
5668 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5670 if (!movement_occurred) {
5674 boost::shared_ptr<Region> r (_region_view->region ());
5675 framepos_t f = adjusted_current_frame (ev);
5676 f = max (f, r->position ());
5677 f = min (f, r->last_frame ());
5679 _region_view->move_patch_change (
5681 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5686 PatchChangeDrag::aborted (bool)
5688 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5692 PatchChangeDrag::setup_pointer_frame_offset ()
5694 boost::shared_ptr<Region> region = _region_view->region ();
5695 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5698 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5699 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5706 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5708 _region_view->update_drag_selection (
5710 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5714 MidiRubberbandSelectDrag::deselect_things ()
5719 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5720 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5723 _vertical_only = true;
5727 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5729 double const y = _region_view->midi_view()->y_position ();
5731 y1 = max (0.0, y1 - y);
5732 y2 = max (0.0, y2 - y);
5734 _region_view->update_vertical_drag_selection (
5737 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5742 MidiVerticalSelectDrag::deselect_things ()
5747 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5748 : RubberbandSelectDrag (e, i)
5754 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5756 if (drag_in_progress) {
5757 /* We just want to select things at the end of the drag, not during it */
5761 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5763 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5765 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5767 _editor->commit_reversible_selection_op ();
5771 EditorRubberbandSelectDrag::deselect_things ()
5773 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5775 _editor->selection->clear_tracks();
5776 _editor->selection->clear_regions();
5777 _editor->selection->clear_points ();
5778 _editor->selection->clear_lines ();
5779 _editor->selection->clear_midi_notes ();
5781 _editor->commit_reversible_selection_op();
5784 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5789 _note[0] = _note[1] = 0;
5792 NoteCreateDrag::~NoteCreateDrag ()
5798 NoteCreateDrag::grid_frames (framepos_t t) const
5801 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5803 grid_beats = Evoral::Beats(1);
5806 return _region_view->region_beats_to_region_frames (grid_beats);
5810 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5812 Drag::start_grab (event, cursor);
5814 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5816 framepos_t pf = _drags->current_pointer_frame ();
5817 framecnt_t const g = grid_frames (pf);
5819 /* Hack so that we always snap to the note that we are over, instead of snapping
5820 to the next one if we're more than halfway through the one we're over.
5822 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5826 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5827 _note[1] = _note[0];
5829 MidiStreamView* sv = _region_view->midi_stream_view ();
5830 double const x = _editor->sample_to_pixel (_note[0]);
5831 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5833 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5834 _drag_rect->set_outline_all ();
5835 _drag_rect->set_outline_color (0xffffff99);
5836 _drag_rect->set_fill_color (0xffffff66);
5840 NoteCreateDrag::motion (GdkEvent* event, bool)
5842 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5843 double const x0 = _editor->sample_to_pixel (_note[0]);
5844 double const x1 = _editor->sample_to_pixel (_note[1]);
5845 _drag_rect->set_x0 (std::min(x0, x1));
5846 _drag_rect->set_x1 (std::max(x0, x1));
5850 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5852 if (!had_movement) {
5856 framepos_t const start = min (_note[0], _note[1]);
5857 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5859 framecnt_t const g = grid_frames (start);
5860 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5862 if (_editor->snap_mode() == SnapNormal && length < g) {
5866 Evoral::Beats length_beats = max (
5867 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5869 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5873 NoteCreateDrag::y_to_region (double y) const
5876 _region_view->get_canvas_group()->canvas_to_item (x, y);
5881 NoteCreateDrag::aborted (bool)
5886 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5891 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5895 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5897 Drag::start_grab (event, cursor);
5901 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5907 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5910 distance = _drags->current_pointer_x() - grab_x();
5911 len = ar->fade_in()->back()->when;
5913 distance = grab_x() - _drags->current_pointer_x();
5914 len = ar->fade_out()->back()->when;
5917 /* how long should it be ? */
5919 new_length = len + _editor->pixel_to_sample (distance);
5921 /* now check with the region that this is legal */
5923 new_length = ar->verify_xfade_bounds (new_length, start);
5926 arv->reset_fade_in_shape_width (ar, new_length);
5928 arv->reset_fade_out_shape_width (ar, new_length);
5933 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5939 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5942 distance = _drags->current_pointer_x() - grab_x();
5943 len = ar->fade_in()->back()->when;
5945 distance = grab_x() - _drags->current_pointer_x();
5946 len = ar->fade_out()->back()->when;
5949 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5951 _editor->begin_reversible_command ("xfade trim");
5952 ar->playlist()->clear_owned_changes ();
5955 ar->set_fade_in_length (new_length);
5957 ar->set_fade_out_length (new_length);
5960 /* Adjusting the xfade may affect other regions in the playlist, so we need
5961 to get undo Commands from the whole playlist rather than just the
5965 vector<Command*> cmds;
5966 ar->playlist()->rdiff (cmds);
5967 _editor->session()->add_commands (cmds);
5968 _editor->commit_reversible_command ();
5973 CrossfadeEdgeDrag::aborted (bool)
5976 // arv->redraw_start_xfade ();
5978 // arv->redraw_end_xfade ();
5982 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5983 : Drag (e, item, true)
5984 , line (new EditorCursor (*e))
5986 line->set_position (pos);
5990 RegionCutDrag::~RegionCutDrag ()
5996 RegionCutDrag::motion (GdkEvent*, bool)
5998 framepos_t where = _drags->current_pointer_frame();
5999 _editor->snap_to (where);
6001 line->set_position (where);
6005 RegionCutDrag::finished (GdkEvent*, bool)
6007 _editor->get_track_canvas()->canvas()->re_enter();
6009 framepos_t pos = _drags->current_pointer_frame();
6013 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6019 _editor->split_regions_at (pos, rs);
6023 RegionCutDrag::aborted (bool)