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 , _trackview_only (trackview_only)
222 , _move_threshold_passed (false)
223 , _starting_point_passed (false)
224 , _initially_vertical (false)
225 , _was_double_click (false)
226 , _raw_grab_frame (0)
228 , _last_pointer_frame (0)
234 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
240 _cursor_ctx = CursorContext::create (*_editor, cursor);
242 _cursor_ctx->change (cursor);
249 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
251 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
253 if (Keyboard::is_button2_event (&event->button)) {
254 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
255 _y_constrained = true;
256 _x_constrained = false;
258 _y_constrained = false;
259 _x_constrained = true;
262 _x_constrained = false;
263 _y_constrained = false;
266 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
267 setup_pointer_frame_offset ();
268 _grab_frame = adjusted_frame (_raw_grab_frame, event);
269 _last_pointer_frame = _grab_frame;
270 _last_pointer_x = _grab_x;
272 if (_trackview_only) {
273 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
276 _last_pointer_y = _grab_y;
280 if (!_editor->cursors()->is_invalid (cursor)) {
281 /* CAIROCANVAS need a variant here that passes *cursor */
282 _cursor_ctx = CursorContext::create (*_editor, cursor);
285 if (_editor->session() && _editor->session()->transport_rolling()) {
288 _was_rolling = false;
291 switch (_editor->snap_type()) {
292 case SnapToRegionStart:
293 case SnapToRegionEnd:
294 case SnapToRegionSync:
295 case SnapToRegionBoundary:
296 _editor->build_region_boundary_cache ();
303 /** Call to end a drag `successfully'. Ungrabs item and calls
304 * subclass' finished() method.
306 * @param event GDK event, or 0.
307 * @return true if some movement occurred, otherwise false.
310 Drag::end_grab (GdkEvent* event)
312 _editor->stop_canvas_autoscroll ();
316 finished (event, _move_threshold_passed);
318 _editor->verbose_cursor()->hide ();
321 return _move_threshold_passed;
325 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
329 if (f > _pointer_frame_offset) {
330 pos = f - _pointer_frame_offset;
334 _editor->snap_to_with_modifier (pos, event);
341 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
343 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
347 Drag::current_pointer_x() const
349 return _drags->current_pointer_x ();
353 Drag::current_pointer_y () const
355 if (!_trackview_only) {
356 return _drags->current_pointer_y ();
359 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
363 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
365 /* check to see if we have moved in any way that matters since the last motion event */
366 if (_move_threshold_passed &&
367 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
368 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
372 pair<framecnt_t, int> const threshold = move_threshold ();
374 bool const old_move_threshold_passed = _move_threshold_passed;
376 if (!_move_threshold_passed) {
378 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
379 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
381 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
384 if (active (_editor->mouse_mode) && _move_threshold_passed) {
386 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
388 if (old_move_threshold_passed != _move_threshold_passed) {
392 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
393 _initially_vertical = true;
395 _initially_vertical = false;
399 if (!from_autoscroll) {
400 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
403 if (!_editor->autoscroll_active() || from_autoscroll) {
406 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
408 motion (event, first_move && !_starting_point_passed);
410 if (first_move && !_starting_point_passed) {
411 _starting_point_passed = true;
414 _last_pointer_x = _drags->current_pointer_x ();
415 _last_pointer_y = current_pointer_y ();
416 _last_pointer_frame = adjusted_current_frame (event);
426 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
434 aborted (_move_threshold_passed);
436 _editor->stop_canvas_autoscroll ();
437 _editor->verbose_cursor()->hide ();
441 Drag::show_verbose_cursor_time (framepos_t frame)
443 _editor->verbose_cursor()->set_time (frame);
444 _editor->verbose_cursor()->show ();
448 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
450 _editor->verbose_cursor()->set_duration (start, end);
451 _editor->verbose_cursor()->show ();
455 Drag::show_verbose_cursor_text (string const & text)
457 _editor->verbose_cursor()->set (text);
458 _editor->verbose_cursor()->show ();
461 boost::shared_ptr<Region>
462 Drag::add_midi_region (MidiTimeAxisView* view)
464 if (_editor->session()) {
465 const TempoMap& map (_editor->session()->tempo_map());
466 framecnt_t pos = grab_frame();
467 const Meter& m = map.meter_at (pos);
468 /* not that the frame rate used here can be affected by pull up/down which
471 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
472 return view->add_region (grab_frame(), len, true);
475 return boost::shared_ptr<Region>();
478 struct EditorOrderTimeAxisViewSorter {
479 bool operator() (TimeAxisView* a, TimeAxisView* b) {
480 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
481 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
483 return ra->route()->order_key () < rb->route()->order_key ();
487 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
491 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
493 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
494 as some of the regions we are dragging may be on such tracks.
497 TrackViewList track_views = _editor->track_views;
498 track_views.sort (EditorOrderTimeAxisViewSorter ());
500 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
501 _time_axis_views.push_back (*i);
503 TimeAxisView::Children children_list = (*i)->get_child_list ();
504 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
505 _time_axis_views.push_back (j->get());
509 /* the list of views can be empty at this point if this is a region list-insert drag
512 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
513 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
516 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
520 RegionDrag::region_going_away (RegionView* v)
522 list<DraggingView>::iterator i = _views.begin ();
523 while (i != _views.end() && i->view != v) {
527 if (i != _views.end()) {
532 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
533 * or -1 if it is not found.
536 RegionDrag::find_time_axis_view (TimeAxisView* t) const
539 int const N = _time_axis_views.size ();
540 while (i < N && _time_axis_views[i] != t) {
544 if (_time_axis_views[i] != t) {
551 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
552 : RegionDrag (e, i, p, v)
555 , _last_pointer_time_axis_view (0)
556 , _last_pointer_layer (0)
557 , _single_axis (false)
559 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
563 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
565 Drag::start_grab (event, cursor);
567 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
571 show_verbose_cursor_time (_last_frame_position);
573 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
575 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
576 assert(_last_pointer_time_axis_view >= 0);
577 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
582 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
584 /* compute the amount of pointer motion in frames, and where
585 the region would be if we moved it by that much.
587 *pending_region_position = adjusted_current_frame (event);
589 framepos_t sync_frame;
590 framecnt_t sync_offset;
593 sync_offset = _primary->region()->sync_offset (sync_dir);
595 /* we don't handle a sync point that lies before zero.
597 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
599 sync_frame = *pending_region_position + (sync_dir*sync_offset);
601 _editor->snap_to_with_modifier (sync_frame, event);
603 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
606 *pending_region_position = _last_frame_position;
609 if (*pending_region_position > max_framepos - _primary->region()->length()) {
610 *pending_region_position = _last_frame_position;
615 /* in locked edit mode, reverse the usual meaning of _x_constrained */
616 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
618 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
620 /* x movement since last time (in pixels) */
621 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
623 /* total x movement */
624 framecnt_t total_dx = *pending_region_position;
625 if (regions_came_from_canvas()) {
626 total_dx = total_dx - grab_frame ();
629 /* check that no regions have gone off the start of the session */
630 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
631 if ((i->view->region()->position() + total_dx) < 0) {
633 *pending_region_position = _last_frame_position;
644 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
650 const int dt = delta > 0 ? +1 : -1;
652 int target = start + delta - skip;
654 assert (current < 0 || current >= _time_axis_views.size() || !_time_axis_views[current]->hidden());
655 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
657 #ifdef DEBUG_DROPZONEDRAG
658 if (current >= _time_axis_views.size() && target >= 0 && target < _time_axis_views.size()) {
659 printf("MOVE OUT OF THE ZONE cur: %d d: %d s: %d\n", start, delta, skip);
661 printf("CALC DISTANCE cur: %d d: %d s: %d\n", start, delta, skip);
665 while (current >= 0 && current != target) {
667 if (current < 0 && dt < 0) {
668 #ifdef DEBUG_DROPZONEDRAG
669 printf("BREAK AT BOTTOM\n");
673 if (current >= _time_axis_views.size() && dt > 0) {
674 #ifdef DEBUG_DROPZONEDRAG
675 printf("BREAK AT TOP\n");
679 if (current < 0 || current >= _time_axis_views.size()) {
683 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
684 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
688 if (distance_only && current == start + delta) {
689 #ifdef DEBUG_DROPZONEDRAG
690 printf("BREAK AFTER DISTANCE\n");
699 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
701 if (_y_constrained) {
705 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
706 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
707 #ifdef DEBUG_DROPZONEDRAG
708 printf("Y MOVEMENT CHECK: from %d to %d skip: %d\n",
709 i->time_axis_view, i->time_axis_view + delta_track,
712 assert (n < 0 || n >= _time_axis_views.size() || !_time_axis_views[n]->hidden());
714 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
715 /* already in the drop zone */
716 if (delta_track >= 0) {
717 /* downward motion - might be OK if others are still not in the dropzone,
718 so check at the end of the loop if that is the case.
723 /* upward motion - set n to the track we would end up in if motion
724 is successful, and check validity below. */
725 n = _time_axis_views.size() + delta_track;
731 } else if (n >= int (_time_axis_views.size())) {
732 /* downward motion into drop zone. That's fine. */
736 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
737 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
738 /* not a track, or the wrong type */
742 double const l = i->layer + delta_layer;
744 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
745 mode to allow the user to place a region below another on layer 0.
747 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
748 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
749 If it has, the layers will be munged later anyway, so it's ok.
755 /* all regions being dragged are ok with this change */
760 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
762 double delta_layer = 0;
763 int delta_time_axis_view = 0;
764 int current_pointer_time_axis_view = -1;
766 assert (!_views.empty ());
770 if (initially_vertical()) {
771 _y_constrained = false;
772 _x_constrained = true;
774 _y_constrained = true;
775 _x_constrained = false;
780 #ifdef DEBUG_DROPZONEDRAG
781 printf("--------- LAST AXIS: %d\n", _last_pointer_time_axis_view);
783 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
785 /* Find the TimeAxisView that the pointer is now over */
787 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
788 TimeAxisView* tv = r.first;
790 if (!tv && current_pointer_y() < 0) {
791 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
795 if (tv && tv->view()) {
796 double layer = r.second;
798 if (first_move && tv->view()->layer_display() == Stacked) {
799 tv->view()->set_layer_display (Expanded);
802 /* Here's the current pointer position in terms of time axis view and layer */
803 current_pointer_time_axis_view = find_time_axis_view (tv);
804 assert(current_pointer_time_axis_view >= 0);
805 #ifdef DEBUG_DROPZONEDRAG
806 printf(" On AXIS: %d\n", current_pointer_time_axis_view);
809 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
811 /* Work out the change in y */
813 if (_last_pointer_time_axis_view < 0) {
814 /* Was in the drop-zone, now over a track.
815 * Hence it must be an upward move (from the bottom)
817 * track_index is still -1, so delta must be set to
818 * move up the correct number of tracks from the bottom.
820 * This is necessary because steps may be skipped if
821 * the bottom-most track is not a valid target,
823 #ifdef DEBUG_DROPZONEDRAG
824 printf("MOVE OUT OF THE ZONE...\n");
826 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size ();
828 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
831 /* TODO needs adjustment per DraggingView,
833 * e.g. select one region on the top-layer of a track
834 * and one region which is at the bottom-layer of another track
837 * Indicated drop-zones and layering is wrong.
838 * and may infer additional layers on the target-track
839 * (depending how many layers the original track had).
841 * Or select two regions (different layers) on a same track,
842 * move across a non-layer track.. -> layering info is lost.
843 * on drop either of the regions may be on top.
845 * Proposed solution: screw it :) well,
846 * don't use delta_layer, use an absolute value
847 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
848 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
849 * 3) iterate over all DraggingView, find the one that is over the track with most layers
850 * 4) proportionally scale layer to layers available on target
852 delta_layer = current_pointer_layer - _last_pointer_layer;
855 /* for automation lanes, there is a TimeAxisView but no ->view() */
856 else if (!tv && current_pointer_y() >= 0 && _last_pointer_time_axis_view >= 0) {
857 /* Moving into the drop-zone..
859 * TODO allow moving further down in drop-zone:
860 * e.g. 2 Tracks, select a region on both of them.
862 * A) grab the upper, drag 2 down, both regions are in the dropzone: all fine (works)
864 * B) grab the lower, drag 1 down, region (and mouse) are in dropzone, The End.
865 * upper region is only down one track and cannot be moved into the zone.
868 * keep track of how many regions are in the DZ (private var),
869 * also count from how many tracks the dragged-regions come from (first move)
871 * if not all regions are in the DZ, keep going.
873 * Using 'default height' H for all dropzone regions will make things
874 * a lot simpler: (number_of_DZ_entries * H + Pointer_YPOS - DZ_YPOS) / H.
875 * (because at this point in time PlaylistDropzoneMap is not yet populated)
877 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
878 #ifdef DEBUG_DROPZONEDRAG
879 printf("INTO THE ZONE DELTA: %d\n", delta_time_axis_view);
883 /* Work out the change in x */
884 framepos_t pending_region_position;
885 double const x_delta = compute_x_delta (event, &pending_region_position);
886 _last_frame_position = pending_region_position;
888 /* calculate hidden tracks in current delta */
890 if (_last_pointer_time_axis_view < 0) {
891 // Moving out of the zone, check for hidden tracks at the bottom.
892 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
893 -_time_axis_views.size() - delta_time_axis_view;
894 #ifdef DEBUG_DROPZONEDRAG
895 printf("NOW WHAT?? last: %d delta %d || skip %d\n", _last_pointer_time_axis_view, delta_time_axis_view, delta_skip);
898 // calculate hidden tracks that are skipped by the pointer movement
899 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
900 - _last_pointer_time_axis_view
901 - delta_time_axis_view;
902 #ifdef DEBUG_DROPZONEDRAG
903 printf("Drag from %d to %d || skip %d\n",
904 _last_pointer_time_axis_view,
905 _last_pointer_time_axis_view + delta_time_axis_view,
910 /* Verify change in y */
911 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
912 /* this y movement is not allowed, so do no y movement this time */
913 delta_time_axis_view = 0;
916 #ifdef DEBUG_DROPZONEDRAG
917 printf(" ** NOT ALLOWED\n");
921 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
922 /* haven't reached next snap point, and we're not switching
923 trackviews nor layers. nothing to do.
928 typedef pair<int,double> NewTrackIndexAndPosition;
929 typedef map<boost::shared_ptr<Playlist>,NewTrackIndexAndPosition> PlaylistDropzoneMap;
930 PlaylistDropzoneMap playlist_dropzone_map;
931 int biggest_drop_zone_offset = 0;
933 /* find drop-zone y-position */
934 Coord last_track_bottom_edge;
935 last_track_bottom_edge = 0;
936 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
937 if (!(*t)->hidden()) {
938 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
943 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
945 RegionView* rv = i->view;
950 if (rv->region()->locked() || rv->region()->video_locked()) {
957 /* reparent the regionview into a group above all
961 ArdourCanvas::Item* rvg = rv->get_canvas_group();
962 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
963 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
964 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
965 /* move the item so that it continues to appear at the
966 same location now that its parent has changed.
968 rvg->move (rv_canvas_offset - dmg_canvas_offset);
971 /* If we have moved tracks, we'll fudge the layer delta so that the
972 region gets moved back onto layer 0 on its new track; this avoids
973 confusion when dragging regions from non-zero layers onto different
976 double this_delta_layer = delta_layer;
977 if (delta_time_axis_view != 0) {
978 this_delta_layer = - i->layer;
981 int this_delta_time_axis_view = delta_time_axis_view;
982 this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
984 int track_index = i->time_axis_view + this_delta_time_axis_view;
985 assert(track_index >= 0);
987 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
988 i->time_axis_view = track_index;
989 #ifdef DEBUG_DROPZONEDRAG
990 printf("IN THE ZONE\n");
992 assert(i->time_axis_view >= _time_axis_views.size());
993 if (current_pointer_y() >= 0) {
996 NewTrackIndexAndPosition ip;
997 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
999 /* store index of each new playlist as a negative count, starting at -1 */
1001 if (pdz == playlist_dropzone_map.end()) {
1004 * retain the ordering top -> bottom in the drop-zone
1005 * this can be done by sorting the regions according to
1006 * i->time_axis_view Y, prior to iterating over DraggingView
1009 int n = playlist_dropzone_map.size() + 1;
1011 /* compute where this new track (which doesn't exist yet) will live
1015 ip.first = -n; /* in time axis units, negative to signify "in drop zone " */
1016 ip.second = last_track_bottom_edge; /* where to place the top edge of the regionview */
1018 /* How high is this region view ? */
1020 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1021 ArdourCanvas::Rect bbox;
1024 bbox = obbox.get ();
1027 last_track_bottom_edge += bbox.height();
1029 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), ip));
1034 dzoffset = ip.first;
1037 /* values are zero or negative, hence the use of min() */
1038 biggest_drop_zone_offset = min (biggest_drop_zone_offset, dzoffset);
1039 y_delta = ip.second - rv->get_canvas_group()->canvas_origin().y;
1044 /* The TimeAxisView that this region is now over */
1045 TimeAxisView* current_tv = _time_axis_views[track_index];
1047 /* Ensure it is moved from stacked -> expanded if appropriate */
1048 if (current_tv->view()->layer_display() == Stacked) {
1049 current_tv->view()->set_layer_display (Expanded);
1052 /* We're only allowed to go -ve in layer on Expanded views */
1053 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1054 this_delta_layer = - i->layer;
1058 rv->set_height (current_tv->view()->child_height ());
1060 /* Update show/hidden status as the region view may have come from a hidden track,
1061 or have moved to one.
1063 if (current_tv->hidden ()) {
1064 rv->get_canvas_group()->hide ();
1066 rv->get_canvas_group()->show ();
1069 /* Update the DraggingView */
1070 i->time_axis_view = track_index;
1071 i->layer += this_delta_layer;
1074 _editor->mouse_brush_insert_region (rv, pending_region_position);
1078 /* Get the y coordinate of the top of the track that this region is now over */
1079 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1081 /* And adjust for the layer that it should be on */
1082 StreamView* cv = current_tv->view ();
1083 switch (cv->layer_display ()) {
1087 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1090 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1094 /* need to get the parent of the regionview
1095 * canvas group and get its position in
1096 * equivalent coordinate space as the trackview
1097 * we are now dragging over.
1100 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1105 /* Now move the region view */
1106 rv->move (x_delta, y_delta);
1108 } /* foreach region */
1110 _total_x_delta += x_delta;
1112 if (x_delta != 0 && !_brushing) {
1113 show_verbose_cursor_time (_last_frame_position);
1118 /* the pointer is currently over a time axis view */
1120 if (_last_pointer_time_axis_view < 0) {
1122 /* last motion event was not over a time axis view */
1124 if (delta_time_axis_view < 0) {
1125 /* was in the drop zone, moving up */
1126 assert(current_pointer_time_axis_view >= 0);
1127 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1129 /* was in the drop zone, moving down ... not possible */
1134 /* last motion event was also over a time axis view */
1136 _last_pointer_time_axis_view += delta_time_axis_view;
1137 assert(_last_pointer_time_axis_view >= 0);
1142 /* the pointer is not over a time axis view */
1144 _last_pointer_time_axis_view = biggest_drop_zone_offset;
1147 _last_pointer_layer += delta_layer;
1151 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1153 if (_copy && first_move) {
1155 if (_x_constrained) {
1156 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1158 _editor->begin_reversible_command (Operations::region_copy);
1161 /* duplicate the regionview(s) and region(s) */
1163 list<DraggingView> new_regionviews;
1165 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1167 RegionView* rv = i->view;
1168 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1169 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1171 const boost::shared_ptr<const Region> original = rv->region();
1172 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1173 region_copy->set_position (original->position());
1174 /* need to set this so that the drop zone code can work. This doesn't
1175 actually put the region into the playlist, but just sets a weak pointer
1178 region_copy->set_playlist (original->playlist());
1182 boost::shared_ptr<AudioRegion> audioregion_copy
1183 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1185 nrv = new AudioRegionView (*arv, audioregion_copy);
1187 boost::shared_ptr<MidiRegion> midiregion_copy
1188 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1189 nrv = new MidiRegionView (*mrv, midiregion_copy);
1194 nrv->get_canvas_group()->show ();
1195 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1197 /* swap _primary to the copy */
1199 if (rv == _primary) {
1203 /* ..and deselect the one we copied */
1205 rv->set_selected (false);
1208 if (!new_regionviews.empty()) {
1210 /* reflect the fact that we are dragging the copies */
1212 _views = new_regionviews;
1214 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1217 } else if (!_copy && first_move) {
1219 if (_x_constrained) {
1220 _editor->begin_reversible_command (_("fixed time region drag"));
1222 _editor->begin_reversible_command (Operations::region_drag);
1226 RegionMotionDrag::motion (event, first_move);
1230 RegionMotionDrag::finished (GdkEvent *, bool)
1232 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1233 if (!(*i)->view()) {
1237 if ((*i)->view()->layer_display() == Expanded) {
1238 (*i)->view()->set_layer_display (Stacked);
1244 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1246 RegionMotionDrag::finished (ev, movement_occurred);
1248 if (!movement_occurred) {
1252 if (was_double_click() && !_views.empty()) {
1253 DraggingView dv = _views.front();
1254 dv.view->show_region_editor ();
1261 /* reverse this here so that we have the correct logic to finalize
1265 if (Config->get_edit_mode() == Lock) {
1266 _x_constrained = !_x_constrained;
1269 assert (!_views.empty ());
1271 /* We might have hidden region views so that they weren't visible during the drag
1272 (when they have been reparented). Now everything can be shown again, as region
1273 views are back in their track parent groups.
1275 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1276 i->view->get_canvas_group()->show ();
1279 bool const changed_position = (_last_frame_position != _primary->region()->position());
1280 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1281 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1301 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1305 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1307 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1312 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1313 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1314 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1315 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1317 rtav->set_height (original->current_height());
1321 ChanCount one_midi_port (DataType::MIDI, 1);
1322 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1323 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1324 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1326 rtav->set_height (original->current_height());
1331 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1337 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1339 RegionSelection new_views;
1340 PlaylistSet modified_playlists;
1341 RouteTimeAxisView* new_time_axis_view = 0;
1344 /* all changes were made during motion event handlers */
1346 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1350 _editor->commit_reversible_command ();
1354 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1355 PlaylistMapping playlist_mapping;
1357 /* insert the regions into their new playlists */
1358 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1360 RouteTimeAxisView* dest_rtv = 0;
1362 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1368 if (changed_position && !_x_constrained) {
1369 where = i->view->region()->position() - drag_delta;
1371 where = i->view->region()->position();
1374 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
1375 /* dragged to drop zone */
1377 PlaylistMapping::iterator pm;
1379 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1380 /* first region from this original playlist: create a new track */
1381 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1382 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1383 dest_rtv = new_time_axis_view;
1385 /* we already created a new track for regions from this playlist, use it */
1386 dest_rtv = pm->second;
1389 /* destination time axis view is the one we dragged to */
1390 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1393 if (dest_rtv != 0) {
1394 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1395 if (new_view != 0) {
1396 new_views.push_back (new_view);
1400 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1401 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1404 list<DraggingView>::const_iterator next = i;
1410 /* If we've created new regions either by copying or moving
1411 to a new track, we want to replace the old selection with the new ones
1414 if (new_views.size() > 0) {
1415 _editor->selection->set (new_views);
1418 /* write commands for the accumulated diffs for all our modified playlists */
1419 add_stateful_diff_commands_for_playlists (modified_playlists);
1421 _editor->commit_reversible_command ();
1425 RegionMoveDrag::finished_no_copy (
1426 bool const changed_position,
1427 bool const changed_tracks,
1428 framecnt_t const drag_delta
1431 RegionSelection new_views;
1432 PlaylistSet modified_playlists;
1433 PlaylistSet frozen_playlists;
1434 set<RouteTimeAxisView*> views_to_update;
1435 RouteTimeAxisView* new_time_axis_view = 0;
1438 /* all changes were made during motion event handlers */
1439 _editor->commit_reversible_command ();
1443 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1444 PlaylistMapping playlist_mapping;
1446 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1448 RegionView* rv = i->view;
1449 RouteTimeAxisView* dest_rtv = 0;
1451 if (rv->region()->locked() || rv->region()->video_locked()) {
1456 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
1457 /* dragged to drop zone */
1459 PlaylistMapping::iterator pm;
1461 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1462 /* first region from this original playlist: create a new track */
1463 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1464 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1465 dest_rtv = new_time_axis_view;
1467 /* we already created a new track for regions from this playlist, use it */
1468 dest_rtv = pm->second;
1472 /* destination time axis view is the one we dragged to */
1473 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1478 double const dest_layer = i->layer;
1480 views_to_update.insert (dest_rtv);
1484 if (changed_position && !_x_constrained) {
1485 where = rv->region()->position() - drag_delta;
1487 where = rv->region()->position();
1490 if (changed_tracks) {
1492 /* insert into new playlist */
1494 RegionView* new_view = insert_region_into_playlist (
1495 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1498 if (new_view == 0) {
1503 new_views.push_back (new_view);
1505 /* remove from old playlist */
1507 /* the region that used to be in the old playlist is not
1508 moved to the new one - we use a copy of it. as a result,
1509 any existing editor for the region should no longer be
1512 rv->hide_region_editor();
1515 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1519 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1521 /* this movement may result in a crossfade being modified, or a layering change,
1522 so we need to get undo data from the playlist as well as the region.
1525 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1527 playlist->clear_changes ();
1530 rv->region()->clear_changes ();
1533 motion on the same track. plonk the previously reparented region
1534 back to its original canvas group (its streamview).
1535 No need to do anything for copies as they are fake regions which will be deleted.
1538 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1539 rv->get_canvas_group()->set_y_position (i->initial_y);
1542 /* just change the model */
1543 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1544 playlist->set_layer (rv->region(), dest_layer);
1547 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1549 r = frozen_playlists.insert (playlist);
1552 playlist->freeze ();
1555 rv->region()->set_position (where);
1557 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1560 if (changed_tracks) {
1562 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1563 was selected in all of them, then removing it from a playlist will have removed all
1564 trace of it from _views (i.e. there were N regions selected, we removed 1,
1565 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1566 corresponding regionview, and _views is now empty).
1568 This could have invalidated any and all iterators into _views.
1570 The heuristic we use here is: if the region selection is empty, break out of the loop
1571 here. if the region selection is not empty, then restart the loop because we know that
1572 we must have removed at least the region(view) we've just been working on as well as any
1573 that we processed on previous iterations.
1575 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1576 we can just iterate.
1580 if (_views.empty()) {
1591 /* If we've created new regions either by copying or moving
1592 to a new track, we want to replace the old selection with the new ones
1595 if (new_views.size() > 0) {
1596 _editor->selection->set (new_views);
1599 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1603 /* write commands for the accumulated diffs for all our modified playlists */
1604 add_stateful_diff_commands_for_playlists (modified_playlists);
1606 _editor->commit_reversible_command ();
1608 /* We have futzed with the layering of canvas items on our streamviews.
1609 If any region changed layer, this will have resulted in the stream
1610 views being asked to set up their region views, and all will be well.
1611 If not, we might now have badly-ordered region views. Ask the StreamViews
1612 involved to sort themselves out, just in case.
1615 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1616 (*i)->view()->playlist_layered ((*i)->track ());
1620 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1621 * @param region Region to remove.
1622 * @param playlist playlist To remove from.
1623 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1624 * that clear_changes () is only called once per playlist.
1627 RegionMoveDrag::remove_region_from_playlist (
1628 boost::shared_ptr<Region> region,
1629 boost::shared_ptr<Playlist> playlist,
1630 PlaylistSet& modified_playlists
1633 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1636 playlist->clear_changes ();
1639 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1643 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1644 * clearing the playlist's diff history first if necessary.
1645 * @param region Region to insert.
1646 * @param dest_rtv Destination RouteTimeAxisView.
1647 * @param dest_layer Destination layer.
1648 * @param where Destination position.
1649 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1650 * that clear_changes () is only called once per playlist.
1651 * @return New RegionView, or 0 if no insert was performed.
1654 RegionMoveDrag::insert_region_into_playlist (
1655 boost::shared_ptr<Region> region,
1656 RouteTimeAxisView* dest_rtv,
1659 PlaylistSet& modified_playlists
1662 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1663 if (!dest_playlist) {
1667 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1668 _new_region_view = 0;
1669 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1671 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1672 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1674 dest_playlist->clear_changes ();
1677 dest_playlist->add_region (region, where);
1679 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1680 dest_playlist->set_layer (region, dest_layer);
1685 assert (_new_region_view);
1687 return _new_region_view;
1691 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1693 _new_region_view = rv;
1697 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1699 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1700 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1702 _editor->session()->add_command (c);
1711 RegionMoveDrag::aborted (bool movement_occurred)
1715 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1716 list<DraggingView>::const_iterator next = i;
1725 RegionMotionDrag::aborted (movement_occurred);
1730 RegionMotionDrag::aborted (bool)
1732 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1734 StreamView* sview = (*i)->view();
1737 if (sview->layer_display() == Expanded) {
1738 sview->set_layer_display (Stacked);
1743 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1744 RegionView* rv = i->view;
1745 TimeAxisView* tv = &(rv->get_time_axis_view ());
1746 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1748 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1749 rv->get_canvas_group()->set_y_position (0);
1751 rv->move (-_total_x_delta, 0);
1752 rv->set_height (rtv->view()->child_height ());
1756 /** @param b true to brush, otherwise false.
1757 * @param c true to make copies of the regions being moved, otherwise false.
1759 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1760 : RegionMotionDrag (e, i, p, v, b)
1763 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1766 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1767 if (rtv && rtv->is_track()) {
1768 speed = rtv->track()->speed ();
1771 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1775 RegionMoveDrag::setup_pointer_frame_offset ()
1777 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1780 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1781 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1783 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1785 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1786 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1788 _primary = v->view()->create_region_view (r, false, false);
1790 _primary->get_canvas_group()->show ();
1791 _primary->set_position (pos, 0);
1792 _views.push_back (DraggingView (_primary, this, v));
1794 _last_frame_position = pos;
1796 _item = _primary->get_canvas_group ();
1800 RegionInsertDrag::finished (GdkEvent *, bool)
1802 int pos = _views.front().time_axis_view;
1803 assert(pos >= 0 && pos < _time_axis_views.size());
1805 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1807 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1808 _primary->get_canvas_group()->set_y_position (0);
1810 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1812 _editor->begin_reversible_command (Operations::insert_region);
1813 playlist->clear_changes ();
1814 playlist->add_region (_primary->region (), _last_frame_position);
1816 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1817 if (Config->get_edit_mode() == Ripple) {
1818 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1821 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1822 _editor->commit_reversible_command ();
1830 RegionInsertDrag::aborted (bool)
1837 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1838 : RegionMoveDrag (e, i, p, v, false, false)
1840 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1843 struct RegionSelectionByPosition {
1844 bool operator() (RegionView*a, RegionView* b) {
1845 return a->region()->position () < b->region()->position();
1850 RegionSpliceDrag::motion (GdkEvent* event, bool)
1852 /* Which trackview is this ? */
1854 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1855 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1857 /* The region motion is only processed if the pointer is over
1861 if (!tv || !tv->is_track()) {
1862 /* To make sure we hide the verbose canvas cursor when the mouse is
1863 not held over an audio track.
1865 _editor->verbose_cursor()->hide ();
1868 _editor->verbose_cursor()->show ();
1873 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1879 RegionSelection copy;
1880 _editor->selection->regions.by_position(copy);
1882 framepos_t const pf = adjusted_current_frame (event);
1884 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1886 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1892 boost::shared_ptr<Playlist> playlist;
1894 if ((playlist = atv->playlist()) == 0) {
1898 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1903 if (pf < (*i)->region()->last_frame() + 1) {
1907 if (pf > (*i)->region()->first_frame()) {
1913 playlist->shuffle ((*i)->region(), dir);
1918 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1920 RegionMoveDrag::finished (event, movement_occurred);
1924 RegionSpliceDrag::aborted (bool)
1934 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1937 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1939 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1940 RegionSelection to_ripple;
1941 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1942 if ((*i)->position() >= where) {
1943 to_ripple.push_back (rtv->view()->find_view(*i));
1947 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1948 if (!exclude.contains (*i)) {
1949 // the selection has already been added to _views
1951 if (drag_in_progress) {
1952 // do the same things that RegionMotionDrag::motion does when
1953 // first_move is true, for the region views that we're adding
1954 // to _views this time
1957 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1958 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1959 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1960 rvg->reparent (_editor->_drag_motion_group);
1962 // we only need to move in the y direction
1963 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1968 _views.push_back (DraggingView (*i, this, tav));
1974 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1977 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1978 // we added all the regions after the selection
1980 std::list<DraggingView>::iterator to_erase = i++;
1981 if (!_editor->selection->regions.contains (to_erase->view)) {
1982 // restore the non-selected regions to their original playlist & positions,
1983 // and then ripple them back by the length of the regions that were dragged away
1984 // do the same things as RegionMotionDrag::aborted
1986 RegionView *rv = to_erase->view;
1987 TimeAxisView* tv = &(rv->get_time_axis_view ());
1988 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1991 // plonk them back onto their own track
1992 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1993 rv->get_canvas_group()->set_y_position (0);
1997 // move the underlying region to match the view
1998 rv->region()->set_position (rv->region()->position() + amount);
2000 // restore the view to match the underlying region's original position
2001 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2004 rv->set_height (rtv->view()->child_height ());
2005 _views.erase (to_erase);
2011 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2013 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2015 return allow_moves_across_tracks;
2023 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2024 : RegionMoveDrag (e, i, p, v, false, false)
2026 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2027 // compute length of selection
2028 RegionSelection selected_regions = _editor->selection->regions;
2029 selection_length = selected_regions.end_frame() - selected_regions.start();
2031 // we'll only allow dragging to another track in ripple mode if all the regions
2032 // being dragged start off on the same track
2033 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2036 exclude = new RegionList;
2037 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2038 exclude->push_back((*i)->region());
2041 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2042 RegionSelection copy;
2043 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2045 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2046 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2048 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2049 // find ripple start point on each applicable playlist
2050 RegionView *first_selected_on_this_track = NULL;
2051 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2052 if ((*i)->region()->playlist() == (*pi)) {
2053 // region is on this playlist - it's the first, because they're sorted
2054 first_selected_on_this_track = *i;
2058 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2059 add_all_after_to_views (
2060 &first_selected_on_this_track->get_time_axis_view(),
2061 first_selected_on_this_track->region()->position(),
2062 selected_regions, false);
2065 if (allow_moves_across_tracks) {
2066 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2074 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2076 /* Which trackview is this ? */
2078 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2079 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2081 /* The region motion is only processed if the pointer is over
2085 if (!tv || !tv->is_track()) {
2086 /* To make sure we hide the verbose canvas cursor when the mouse is
2087 not held over an audiotrack.
2089 _editor->verbose_cursor()->hide ();
2093 framepos_t where = adjusted_current_frame (event);
2094 assert (where >= 0);
2096 double delta = compute_x_delta (event, &after);
2098 framecnt_t amount = _editor->pixel_to_sample (delta);
2100 if (allow_moves_across_tracks) {
2101 // all the originally selected regions were on the same track
2103 framecnt_t adjust = 0;
2104 if (prev_tav && tv != prev_tav) {
2105 // dragged onto a different track
2106 // remove the unselected regions from _views, restore them to their original positions
2107 // and add the regions after the drop point on the new playlist to _views instead.
2108 // undo the effect of rippling the previous playlist, and include the effect of removing
2109 // the dragged region(s) from this track
2111 remove_unselected_from_views (prev_amount, false);
2112 // ripple previous playlist according to the regions that have been removed onto the new playlist
2113 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2116 // move just the selected regions
2117 RegionMoveDrag::motion(event, first_move);
2119 // ensure that the ripple operation on the new playlist inserts selection_length time
2120 adjust = selection_length;
2121 // ripple the new current playlist
2122 tv->playlist()->ripple (where, amount+adjust, exclude);
2124 // add regions after point where drag entered this track to subsequent ripples
2125 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2128 // motion on same track
2129 RegionMoveDrag::motion(event, first_move);
2133 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2134 prev_position = where;
2136 // selection encompasses multiple tracks - just drag
2137 // cross-track drags are forbidden
2138 RegionMoveDrag::motion(event, first_move);
2141 if (!_x_constrained) {
2142 prev_amount += amount;
2145 _last_frame_position = after;
2149 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2151 if (!movement_occurred) {
2155 if (was_double_click() && !_views.empty()) {
2156 DraggingView dv = _views.front();
2157 dv.view->show_region_editor ();
2164 _editor->begin_reversible_command(_("Ripple drag"));
2166 // remove the regions being rippled from the dragging view, updating them to
2167 // their new positions
2168 remove_unselected_from_views (prev_amount, true);
2170 if (allow_moves_across_tracks) {
2172 // if regions were dragged across tracks, we've rippled any later
2173 // regions on the track the regions were dragged off, so we need
2174 // to add the original track to the undo record
2175 orig_tav->playlist()->clear_changes();
2176 vector<Command*> cmds;
2177 orig_tav->playlist()->rdiff (cmds);
2178 _editor->session()->add_commands (cmds);
2180 if (prev_tav && prev_tav != orig_tav) {
2181 prev_tav->playlist()->clear_changes();
2182 vector<Command*> cmds;
2183 prev_tav->playlist()->rdiff (cmds);
2184 _editor->session()->add_commands (cmds);
2187 // selection spanned multiple tracks - all will need adding to undo record
2189 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2190 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2192 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2193 (*pi)->clear_changes();
2194 vector<Command*> cmds;
2195 (*pi)->rdiff (cmds);
2196 _editor->session()->add_commands (cmds);
2200 // other modified playlists are added to undo by RegionMoveDrag::finished()
2201 RegionMoveDrag::finished (event, movement_occurred);
2202 _editor->commit_reversible_command();
2206 RegionRippleDrag::aborted (bool movement_occurred)
2208 RegionMoveDrag::aborted (movement_occurred);
2213 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2215 _view (dynamic_cast<MidiTimeAxisView*> (v))
2217 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2223 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2226 _region = add_midi_region (_view);
2227 _view->playlist()->freeze ();
2230 framepos_t const f = adjusted_current_frame (event);
2231 if (f < grab_frame()) {
2232 _region->set_position (f);
2235 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2236 so that if this region is duplicated, its duplicate starts on
2237 a snap point rather than 1 frame after a snap point. Otherwise things get
2238 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2239 place snapped notes at the start of the region.
2242 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2243 _region->set_length (len < 1 ? 1 : len);
2249 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2251 if (!movement_occurred) {
2252 add_midi_region (_view);
2254 _view->playlist()->thaw ();
2259 RegionCreateDrag::aborted (bool)
2262 _view->playlist()->thaw ();
2268 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2272 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2276 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2278 Gdk::Cursor* cursor;
2279 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2281 float x_fraction = cnote->mouse_x_fraction ();
2283 if (x_fraction > 0.0 && x_fraction < 0.25) {
2284 cursor = _editor->cursors()->left_side_trim;
2287 cursor = _editor->cursors()->right_side_trim;
2291 Drag::start_grab (event, cursor);
2293 region = &cnote->region_view();
2297 if (event->motion.state & Keyboard::PrimaryModifier) {
2303 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2305 if (ms.size() > 1) {
2306 /* has to be relative, may make no sense otherwise */
2310 /* select this note; if it is already selected, preserve the existing selection,
2311 otherwise make this note the only one selected.
2313 region->note_selected (cnote, cnote->selected ());
2315 _editor->begin_reversible_command (_("resize notes"));
2317 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2318 MidiRegionSelection::iterator next;
2321 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2323 mrv->begin_resizing (at_front);
2330 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2332 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2333 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2334 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2336 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2338 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2344 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2346 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2347 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2348 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2350 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2352 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2356 _editor->commit_reversible_command ();
2360 NoteResizeDrag::aborted (bool)
2362 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2363 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2364 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2366 mrv->abort_resizing ();
2371 AVDraggingView::AVDraggingView (RegionView* v)
2374 initial_position = v->region()->position ();
2377 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2380 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2383 TrackViewList empty;
2385 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2386 std::list<RegionView*> views = rs.by_layer();
2388 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2389 RegionView* rv = (*i);
2390 if (!rv->region()->video_locked()) {
2393 _views.push_back (AVDraggingView (rv));
2398 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2400 Drag::start_grab (event);
2401 if (_editor->session() == 0) {
2405 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2406 _max_backwards_drag = (
2407 ARDOUR_UI::instance()->video_timeline->get_duration()
2408 + ARDOUR_UI::instance()->video_timeline->get_offset()
2409 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2412 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2413 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2414 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2417 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2420 Timecode::Time timecode;
2421 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2422 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);
2423 show_verbose_cursor_text (buf);
2427 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2429 if (_editor->session() == 0) {
2432 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2436 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2437 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2439 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2440 dt = - _max_backwards_drag;
2443 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2444 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2446 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2447 RegionView* rv = i->view;
2448 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2451 rv->region()->clear_changes ();
2452 rv->region()->suspend_property_changes();
2454 rv->region()->set_position(i->initial_position + dt);
2455 rv->region_changed(ARDOUR::Properties::position);
2458 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2459 Timecode::Time timecode;
2460 Timecode::Time timediff;
2462 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2463 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2464 snprintf (buf, sizeof (buf),
2465 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2466 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2467 , _("Video Start:"),
2468 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2470 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2472 show_verbose_cursor_text (buf);
2476 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2478 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2482 if (!movement_occurred || ! _editor->session()) {
2486 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2488 _editor->begin_reversible_command (_("Move Video"));
2490 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2491 ARDOUR_UI::instance()->video_timeline->save_undo();
2492 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2493 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2495 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2496 i->view->drag_end();
2497 i->view->region()->resume_property_changes ();
2499 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2502 _editor->session()->maybe_update_session_range(
2503 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2504 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2508 _editor->commit_reversible_command ();
2512 VideoTimeLineDrag::aborted (bool)
2514 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2517 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2518 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2520 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2521 i->view->region()->resume_property_changes ();
2522 i->view->region()->set_position(i->initial_position);
2526 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2527 : RegionDrag (e, i, p, v)
2528 , _preserve_fade_anchor (preserve_fade_anchor)
2529 , _jump_position_when_done (false)
2531 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2535 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2538 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2539 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2541 if (tv && tv->is_track()) {
2542 speed = tv->track()->speed();
2545 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2546 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2547 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2549 framepos_t const pf = adjusted_current_frame (event);
2551 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2552 /* Move the contents of the region around without changing the region bounds */
2553 _operation = ContentsTrim;
2554 Drag::start_grab (event, _editor->cursors()->trimmer);
2556 /* These will get overridden for a point trim.*/
2557 if (pf < (region_start + region_length/2)) {
2558 /* closer to front */
2559 _operation = StartTrim;
2561 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2562 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2564 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2568 _operation = EndTrim;
2569 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2570 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2572 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2577 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2578 _jump_position_when_done = true;
2581 switch (_operation) {
2583 show_verbose_cursor_time (region_start);
2584 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2585 i->view->trim_front_starting ();
2589 show_verbose_cursor_time (region_end);
2592 show_verbose_cursor_time (pf);
2596 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2597 i->view->region()->suspend_property_changes ();
2602 TrimDrag::motion (GdkEvent* event, bool first_move)
2604 RegionView* rv = _primary;
2607 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2608 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2609 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2610 frameoffset_t frame_delta = 0;
2612 if (tv && tv->is_track()) {
2613 speed = tv->track()->speed();
2616 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2622 switch (_operation) {
2624 trim_type = "Region start trim";
2627 trim_type = "Region end trim";
2630 trim_type = "Region content trim";
2637 _editor->begin_reversible_command (trim_type);
2639 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2640 RegionView* rv = i->view;
2641 rv->enable_display (false);
2642 rv->region()->playlist()->clear_owned_changes ();
2644 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2647 arv->temporarily_hide_envelope ();
2651 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2652 insert_result = _editor->motion_frozen_playlists.insert (pl);
2654 if (insert_result.second) {
2660 bool non_overlap_trim = false;
2662 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2663 non_overlap_trim = true;
2666 /* contstrain trim to fade length */
2667 if (_preserve_fade_anchor) {
2668 switch (_operation) {
2670 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2671 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2673 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2674 if (ar->locked()) continue;
2675 framecnt_t len = ar->fade_in()->back()->when;
2676 if (len < dt) dt = min(dt, len);
2680 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2681 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2683 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2684 if (ar->locked()) continue;
2685 framecnt_t len = ar->fade_out()->back()->when;
2686 if (len < -dt) dt = max(dt, -len);
2695 switch (_operation) {
2697 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2698 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2699 if (changed && _preserve_fade_anchor) {
2700 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2702 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2703 framecnt_t len = ar->fade_in()->back()->when;
2704 framecnt_t diff = ar->first_frame() - i->initial_position;
2705 framepos_t new_length = len - diff;
2706 i->anchored_fade_length = min (ar->length(), new_length);
2707 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2708 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2715 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2716 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2717 if (changed && _preserve_fade_anchor) {
2718 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2720 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2721 framecnt_t len = ar->fade_out()->back()->when;
2722 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2723 framepos_t new_length = len + diff;
2724 i->anchored_fade_length = min (ar->length(), new_length);
2725 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2726 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2734 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2736 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2737 i->view->move_contents (frame_delta);
2743 switch (_operation) {
2745 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2748 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2751 // show_verbose_cursor_time (frame_delta);
2758 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2760 if (movement_occurred) {
2761 motion (event, false);
2763 if (_operation == StartTrim) {
2764 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2766 /* This must happen before the region's StatefulDiffCommand is created, as it may
2767 `correct' (ahem) the region's _start from being negative to being zero. It
2768 needs to be zero in the undo record.
2770 i->view->trim_front_ending ();
2772 if (_preserve_fade_anchor) {
2773 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2775 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2776 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2777 ar->set_fade_in_length(i->anchored_fade_length);
2778 ar->set_fade_in_active(true);
2781 if (_jump_position_when_done) {
2782 i->view->region()->set_position (i->initial_position);
2785 } else if (_operation == EndTrim) {
2786 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2787 if (_preserve_fade_anchor) {
2788 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2790 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2791 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2792 ar->set_fade_out_length(i->anchored_fade_length);
2793 ar->set_fade_out_active(true);
2796 if (_jump_position_when_done) {
2797 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2802 if (!_views.empty()) {
2803 if (_operation == StartTrim) {
2804 _editor->maybe_locate_with_edit_preroll(
2805 _views.begin()->view->region()->position());
2807 if (_operation == EndTrim) {
2808 _editor->maybe_locate_with_edit_preroll(
2809 _views.begin()->view->region()->position() +
2810 _views.begin()->view->region()->length());
2814 if (!_editor->selection->selected (_primary)) {
2815 _primary->thaw_after_trim ();
2818 set<boost::shared_ptr<Playlist> > diffed_playlists;
2820 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2821 i->view->thaw_after_trim ();
2822 i->view->enable_display (true);
2824 /* Trimming one region may affect others on the playlist, so we need
2825 to get undo Commands from the whole playlist rather than just the
2826 region. Use diffed_playlists to make sure we don't diff a given
2827 playlist more than once.
2829 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2830 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2831 vector<Command*> cmds;
2833 _editor->session()->add_commands (cmds);
2834 diffed_playlists.insert (p);
2839 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2843 _editor->motion_frozen_playlists.clear ();
2844 _editor->commit_reversible_command();
2847 /* no mouse movement */
2848 _editor->point_trim (event, adjusted_current_frame (event));
2851 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2852 if (_operation == StartTrim) {
2853 i->view->trim_front_ending ();
2856 i->view->region()->resume_property_changes ();
2861 TrimDrag::aborted (bool movement_occurred)
2863 /* Our motion method is changing model state, so use the Undo system
2864 to cancel. Perhaps not ideal, as this will leave an Undo point
2865 behind which may be slightly odd from the user's point of view.
2870 if (movement_occurred) {
2874 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2875 i->view->region()->resume_property_changes ();
2880 TrimDrag::setup_pointer_frame_offset ()
2882 list<DraggingView>::iterator i = _views.begin ();
2883 while (i != _views.end() && i->view != _primary) {
2887 if (i == _views.end()) {
2891 switch (_operation) {
2893 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2896 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2903 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2907 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2908 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2913 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2915 Drag::start_grab (event, cursor);
2916 show_verbose_cursor_time (adjusted_current_frame(event));
2920 MeterMarkerDrag::setup_pointer_frame_offset ()
2922 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2926 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2928 if (!_marker->meter().movable()) {
2934 // create a dummy marker for visual representation of moving the
2935 // section, because whether its a copy or not, we're going to
2936 // leave or lose the original marker (leave if its a copy; lose if its
2937 // not, because we'll remove it from the map).
2939 MeterSection section (_marker->meter());
2941 if (!section.movable()) {
2946 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2948 _marker = new MeterMarker (
2950 *_editor->meter_group,
2951 ARDOUR_UI::config()->color ("meter marker"),
2953 *new MeterSection (_marker->meter())
2956 /* use the new marker for the grab */
2957 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2960 TempoMap& map (_editor->session()->tempo_map());
2961 /* get current state */
2962 before_state = &map.get_state();
2963 /* remove the section while we drag it */
2964 map.remove_meter (section, true);
2968 framepos_t const pf = adjusted_current_frame (event);
2970 _marker->set_position (pf);
2971 show_verbose_cursor_time (pf);
2975 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2977 if (!movement_occurred) {
2978 if (was_double_click()) {
2979 _editor->edit_meter_marker (*_marker);
2984 if (!_marker->meter().movable()) {
2988 motion (event, false);
2990 Timecode::BBT_Time when;
2992 TempoMap& map (_editor->session()->tempo_map());
2993 map.bbt_time (last_pointer_frame(), when);
2995 if (_copy == true) {
2996 _editor->begin_reversible_command (_("copy meter mark"));
2997 XMLNode &before = map.get_state();
2998 map.add_meter (_marker->meter(), when);
2999 XMLNode &after = map.get_state();
3000 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3001 _editor->commit_reversible_command ();
3004 _editor->begin_reversible_command (_("move meter mark"));
3006 /* we removed it before, so add it back now */
3008 map.add_meter (_marker->meter(), when);
3009 XMLNode &after = map.get_state();
3010 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3011 _editor->commit_reversible_command ();
3014 // delete the dummy marker we used for visual representation while moving.
3015 // a new visual marker will show up automatically.
3020 MeterMarkerDrag::aborted (bool moved)
3022 _marker->set_position (_marker->meter().frame ());
3025 TempoMap& map (_editor->session()->tempo_map());
3026 /* we removed it before, so add it back now */
3027 map.add_meter (_marker->meter(), _marker->meter().frame());
3028 // delete the dummy marker we used for visual representation while moving.
3029 // a new visual marker will show up automatically.
3034 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3038 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3040 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3045 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3047 Drag::start_grab (event, cursor);
3048 show_verbose_cursor_time (adjusted_current_frame (event));
3052 TempoMarkerDrag::setup_pointer_frame_offset ()
3054 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3058 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3060 if (!_marker->tempo().movable()) {
3066 // create a dummy marker for visual representation of moving the
3067 // section, because whether its a copy or not, we're going to
3068 // leave or lose the original marker (leave if its a copy; lose if its
3069 // not, because we'll remove it from the map).
3071 // create a dummy marker for visual representation of moving the copy.
3072 // The actual copying is not done before we reach the finish callback.
3075 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3077 TempoSection section (_marker->tempo());
3079 _marker = new TempoMarker (
3081 *_editor->tempo_group,
3082 ARDOUR_UI::config()->color ("tempo marker"),
3084 *new TempoSection (_marker->tempo())
3087 /* use the new marker for the grab */
3088 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3091 TempoMap& map (_editor->session()->tempo_map());
3092 /* get current state */
3093 before_state = &map.get_state();
3094 /* remove the section while we drag it */
3095 map.remove_tempo (section, true);
3099 framepos_t const pf = adjusted_current_frame (event);
3100 _marker->set_position (pf);
3101 show_verbose_cursor_time (pf);
3105 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3107 if (!movement_occurred) {
3108 if (was_double_click()) {
3109 _editor->edit_tempo_marker (*_marker);
3114 if (!_marker->tempo().movable()) {
3118 motion (event, false);
3120 TempoMap& map (_editor->session()->tempo_map());
3121 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3122 Timecode::BBT_Time when;
3124 map.bbt_time (beat_time, when);
3126 if (_copy == true) {
3127 _editor->begin_reversible_command (_("copy tempo mark"));
3128 XMLNode &before = map.get_state();
3129 map.add_tempo (_marker->tempo(), when);
3130 XMLNode &after = map.get_state();
3131 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3132 _editor->commit_reversible_command ();
3135 _editor->begin_reversible_command (_("move tempo mark"));
3136 /* we removed it before, so add it back now */
3137 map.add_tempo (_marker->tempo(), when);
3138 XMLNode &after = map.get_state();
3139 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3140 _editor->commit_reversible_command ();
3143 // delete the dummy marker we used for visual representation while moving.
3144 // a new visual marker will show up automatically.
3149 TempoMarkerDrag::aborted (bool moved)
3151 _marker->set_position (_marker->tempo().frame());
3153 TempoMap& map (_editor->session()->tempo_map());
3154 /* we removed it before, so add it back now */
3155 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3156 // delete the dummy marker we used for visual representation while moving.
3157 // a new visual marker will show up automatically.
3162 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3163 : Drag (e, &c.track_canvas_item(), false)
3167 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3170 /** Do all the things we do when dragging the playhead to make it look as though
3171 * we have located, without actually doing the locate (because that would cause
3172 * the diskstream buffers to be refilled, which is too slow).
3175 CursorDrag::fake_locate (framepos_t t)
3177 _editor->playhead_cursor->set_position (t);
3179 Session* s = _editor->session ();
3180 if (s->timecode_transmission_suspended ()) {
3181 framepos_t const f = _editor->playhead_cursor->current_frame ();
3182 /* This is asynchronous so it will be sent "now"
3184 s->send_mmc_locate (f);
3185 /* These are synchronous and will be sent during the next
3188 s->queue_full_time_code ();
3189 s->queue_song_position_pointer ();
3192 show_verbose_cursor_time (t);
3193 _editor->UpdateAllTransportClocks (t);
3197 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3199 Drag::start_grab (event, c);
3201 _grab_zoom = _editor->samples_per_pixel;
3203 framepos_t where = _editor->canvas_event_sample (event);
3205 _editor->snap_to_with_modifier (where, event);
3207 _editor->_dragging_playhead = true;
3209 Session* s = _editor->session ();
3211 /* grab the track canvas item as well */
3213 _cursor.track_canvas_item().grab();
3216 if (_was_rolling && _stop) {
3220 if (s->is_auditioning()) {
3221 s->cancel_audition ();
3225 if (AudioEngine::instance()->connected()) {
3227 /* do this only if we're the engine is connected
3228 * because otherwise this request will never be
3229 * serviced and we'll busy wait forever. likewise,
3230 * notice if we are disconnected while waiting for the
3231 * request to be serviced.
3234 s->request_suspend_timecode_transmission ();
3235 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3236 /* twiddle our thumbs */
3241 fake_locate (where);
3245 CursorDrag::motion (GdkEvent* event, bool)
3247 framepos_t const adjusted_frame = adjusted_current_frame (event);
3248 if (adjusted_frame != last_pointer_frame()) {
3249 fake_locate (adjusted_frame);
3254 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3256 _editor->_dragging_playhead = false;
3258 _cursor.track_canvas_item().ungrab();
3260 if (!movement_occurred && _stop) {
3264 motion (event, false);
3266 Session* s = _editor->session ();
3268 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3269 _editor->_pending_locate_request = true;
3270 s->request_resume_timecode_transmission ();
3275 CursorDrag::aborted (bool)
3277 _cursor.track_canvas_item().ungrab();
3279 if (_editor->_dragging_playhead) {
3280 _editor->session()->request_resume_timecode_transmission ();
3281 _editor->_dragging_playhead = false;
3284 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3287 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3288 : RegionDrag (e, i, p, v)
3290 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3294 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3296 Drag::start_grab (event, cursor);
3298 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3299 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3301 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3305 FadeInDrag::setup_pointer_frame_offset ()
3307 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3308 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3309 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3313 FadeInDrag::motion (GdkEvent* event, bool)
3315 framecnt_t fade_length;
3316 framepos_t const pos = adjusted_current_frame (event);
3317 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3319 if (pos < (region->position() + 64)) {
3320 fade_length = 64; // this should be a minimum defined somewhere
3321 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3322 fade_length = region->length() - region->fade_out()->back()->when - 1;
3324 fade_length = pos - region->position();
3327 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3329 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3335 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3338 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3342 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3344 if (!movement_occurred) {
3348 framecnt_t fade_length;
3350 framepos_t const pos = adjusted_current_frame (event);
3352 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3354 if (pos < (region->position() + 64)) {
3355 fade_length = 64; // this should be a minimum defined somewhere
3356 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3357 fade_length = region->length() - region->fade_out()->back()->when - 1;
3359 fade_length = pos - region->position();
3362 _editor->begin_reversible_command (_("change fade in length"));
3364 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3366 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3372 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3373 XMLNode &before = alist->get_state();
3375 tmp->audio_region()->set_fade_in_length (fade_length);
3376 tmp->audio_region()->set_fade_in_active (true);
3378 XMLNode &after = alist->get_state();
3379 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3382 _editor->commit_reversible_command ();
3386 FadeInDrag::aborted (bool)
3388 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3389 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3395 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3399 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3400 : RegionDrag (e, i, p, v)
3402 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3406 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3408 Drag::start_grab (event, cursor);
3410 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3411 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3413 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3417 FadeOutDrag::setup_pointer_frame_offset ()
3419 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3420 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3421 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3425 FadeOutDrag::motion (GdkEvent* event, bool)
3427 framecnt_t fade_length;
3429 framepos_t const pos = adjusted_current_frame (event);
3431 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3433 if (pos > (region->last_frame() - 64)) {
3434 fade_length = 64; // this should really be a minimum fade defined somewhere
3435 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3436 fade_length = region->length() - region->fade_in()->back()->when - 1;
3438 fade_length = region->last_frame() - pos;
3441 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3443 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3449 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3452 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3456 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3458 if (!movement_occurred) {
3462 framecnt_t fade_length;
3464 framepos_t const pos = adjusted_current_frame (event);
3466 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3468 if (pos > (region->last_frame() - 64)) {
3469 fade_length = 64; // this should really be a minimum fade defined somewhere
3470 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3471 fade_length = region->length() - region->fade_in()->back()->when - 1;
3473 fade_length = region->last_frame() - pos;
3476 _editor->begin_reversible_command (_("change fade out length"));
3478 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3480 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3486 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3487 XMLNode &before = alist->get_state();
3489 tmp->audio_region()->set_fade_out_length (fade_length);
3490 tmp->audio_region()->set_fade_out_active (true);
3492 XMLNode &after = alist->get_state();
3493 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3496 _editor->commit_reversible_command ();
3500 FadeOutDrag::aborted (bool)
3502 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3503 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3509 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3513 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3516 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3518 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3521 _points.push_back (ArdourCanvas::Duple (0, 0));
3522 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3525 MarkerDrag::~MarkerDrag ()
3527 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3532 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3534 location = new Location (*l);
3535 markers.push_back (m);
3540 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3542 Drag::start_grab (event, cursor);
3546 Location *location = _editor->find_location_from_marker (_marker, is_start);
3547 _editor->_dragging_edit_point = true;
3549 update_item (location);
3551 // _drag_line->show();
3552 // _line->raise_to_top();
3555 show_verbose_cursor_time (location->start());
3557 show_verbose_cursor_time (location->end());
3560 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3563 case Selection::Toggle:
3564 /* we toggle on the button release */
3566 case Selection::Set:
3567 if (!_editor->selection->selected (_marker)) {
3568 _editor->selection->set (_marker);
3571 case Selection::Extend:
3573 Locations::LocationList ll;
3574 list<Marker*> to_add;
3576 _editor->selection->markers.range (s, e);
3577 s = min (_marker->position(), s);
3578 e = max (_marker->position(), e);
3581 if (e < max_framepos) {
3584 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3585 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3586 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3589 to_add.push_back (lm->start);
3592 to_add.push_back (lm->end);
3596 if (!to_add.empty()) {
3597 _editor->selection->add (to_add);
3601 case Selection::Add:
3602 _editor->selection->add (_marker);
3606 /* Set up copies for us to manipulate during the drag
3609 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3611 Location* l = _editor->find_location_from_marker (*i, is_start);
3618 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3620 /* range: check that the other end of the range isn't
3623 CopiedLocationInfo::iterator x;
3624 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3625 if (*(*x).location == *l) {
3629 if (x == _copied_locations.end()) {
3630 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3632 (*x).markers.push_back (*i);
3633 (*x).move_both = true;
3641 MarkerDrag::setup_pointer_frame_offset ()
3644 Location *location = _editor->find_location_from_marker (_marker, is_start);
3645 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3649 MarkerDrag::motion (GdkEvent* event, bool)
3651 framecnt_t f_delta = 0;
3653 bool move_both = false;
3654 Location *real_location;
3655 Location *copy_location = 0;
3657 framepos_t const newframe = adjusted_current_frame (event);
3658 framepos_t next = newframe;
3660 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3664 CopiedLocationInfo::iterator x;
3666 /* find the marker we're dragging, and compute the delta */
3668 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3670 copy_location = (*x).location;
3672 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3674 /* this marker is represented by this
3675 * CopiedLocationMarkerInfo
3678 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3683 if (real_location->is_mark()) {
3684 f_delta = newframe - copy_location->start();
3688 switch (_marker->type()) {
3689 case Marker::SessionStart:
3690 case Marker::RangeStart:
3691 case Marker::LoopStart:
3692 case Marker::PunchIn:
3693 f_delta = newframe - copy_location->start();
3696 case Marker::SessionEnd:
3697 case Marker::RangeEnd:
3698 case Marker::LoopEnd:
3699 case Marker::PunchOut:
3700 f_delta = newframe - copy_location->end();
3703 /* what kind of marker is this ? */
3712 if (x == _copied_locations.end()) {
3713 /* hmm, impossible - we didn't find the dragged marker */
3717 /* now move them all */
3719 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3721 copy_location = x->location;
3723 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3727 if (real_location->locked()) {
3731 if (copy_location->is_mark()) {
3735 copy_location->set_start (copy_location->start() + f_delta);
3739 framepos_t new_start = copy_location->start() + f_delta;
3740 framepos_t new_end = copy_location->end() + f_delta;
3742 if (is_start) { // start-of-range marker
3744 if (move_both || (*x).move_both) {
3745 copy_location->set_start (new_start);
3746 copy_location->set_end (new_end);
3747 } else if (new_start < copy_location->end()) {
3748 copy_location->set_start (new_start);
3749 } else if (newframe > 0) {
3750 _editor->snap_to (next, RoundUpAlways, true);
3751 copy_location->set_end (next);
3752 copy_location->set_start (newframe);
3755 } else { // end marker
3757 if (move_both || (*x).move_both) {
3758 copy_location->set_end (new_end);
3759 copy_location->set_start (new_start);
3760 } else if (new_end > copy_location->start()) {
3761 copy_location->set_end (new_end);
3762 } else if (newframe > 0) {
3763 _editor->snap_to (next, RoundDownAlways, true);
3764 copy_location->set_start (next);
3765 copy_location->set_end (newframe);
3770 update_item (copy_location);
3772 /* now lookup the actual GUI items used to display this
3773 * location and move them to wherever the copy of the location
3774 * is now. This means that the logic in ARDOUR::Location is
3775 * still enforced, even though we are not (yet) modifying
3776 * the real Location itself.
3779 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3782 lm->set_position (copy_location->start(), copy_location->end());
3787 assert (!_copied_locations.empty());
3789 show_verbose_cursor_time (newframe);
3793 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3795 if (!movement_occurred) {
3797 if (was_double_click()) {
3798 _editor->rename_marker (_marker);
3802 /* just a click, do nothing but finish
3803 off the selection process
3806 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3809 case Selection::Set:
3810 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3811 _editor->selection->set (_marker);
3815 case Selection::Toggle:
3816 /* we toggle on the button release, click only */
3817 _editor->selection->toggle (_marker);
3820 case Selection::Extend:
3821 case Selection::Add:
3828 _editor->_dragging_edit_point = false;
3830 _editor->begin_reversible_command ( _("move marker") );
3831 XMLNode &before = _editor->session()->locations()->get_state();
3833 MarkerSelection::iterator i;
3834 CopiedLocationInfo::iterator x;
3837 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3838 x != _copied_locations.end() && i != _editor->selection->markers.end();
3841 Location * location = _editor->find_location_from_marker (*i, is_start);
3845 if (location->locked()) {
3849 if (location->is_mark()) {
3850 location->set_start (((*x).location)->start());
3852 location->set (((*x).location)->start(), ((*x).location)->end());
3857 XMLNode &after = _editor->session()->locations()->get_state();
3858 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3859 _editor->commit_reversible_command ();
3863 MarkerDrag::aborted (bool movement_occured)
3865 if (!movement_occured) {
3869 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3871 /* move all markers to their original location */
3874 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3877 Location * location = _editor->find_location_from_marker (*m, is_start);
3880 (*m)->set_position (is_start ? location->start() : location->end());
3887 MarkerDrag::update_item (Location*)
3892 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3894 _cumulative_x_drag (0),
3895 _cumulative_y_drag (0)
3897 if (_zero_gain_fraction < 0.0) {
3898 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3901 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3903 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3909 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3911 Drag::start_grab (event, _editor->cursors()->fader);
3913 // start the grab at the center of the control point so
3914 // the point doesn't 'jump' to the mouse after the first drag
3915 _fixed_grab_x = _point->get_x();
3916 _fixed_grab_y = _point->get_y();
3918 float const fraction = 1 - (_point->get_y() / _point->line().height());
3920 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3922 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3924 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3926 if (!_point->can_slide ()) {
3927 _x_constrained = true;
3932 ControlPointDrag::motion (GdkEvent* event, bool)
3934 double dx = _drags->current_pointer_x() - last_pointer_x();
3935 double dy = current_pointer_y() - last_pointer_y();
3937 if (event->button.state & Keyboard::SecondaryModifier) {
3942 /* coordinate in pixels relative to the start of the region (for region-based automation)
3943 or track (for track-based automation) */
3944 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3945 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3947 // calculate zero crossing point. back off by .01 to stay on the
3948 // positive side of zero
3949 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3951 // make sure we hit zero when passing through
3952 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3956 if (_x_constrained) {
3959 if (_y_constrained) {
3963 _cumulative_x_drag = cx - _fixed_grab_x;
3964 _cumulative_y_drag = cy - _fixed_grab_y;
3968 cy = min ((double) _point->line().height(), cy);
3970 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3972 if (!_x_constrained) {
3973 _editor->snap_to_with_modifier (cx_frames, event);
3976 cx_frames = min (cx_frames, _point->line().maximum_time());
3978 float const fraction = 1.0 - (cy / _point->line().height());
3980 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3982 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3986 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3988 if (!movement_occurred) {
3992 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3993 _editor->reset_point_selection ();
3997 motion (event, false);
4000 _point->line().end_drag (_pushing, _final_index);
4001 _editor->commit_reversible_command ();
4005 ControlPointDrag::aborted (bool)
4007 _point->line().reset ();
4011 ControlPointDrag::active (Editing::MouseMode m)
4013 if (m == Editing::MouseDraw) {
4014 /* always active in mouse draw */
4018 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4019 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4022 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4025 _cumulative_y_drag (0)
4027 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4031 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4033 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4036 _item = &_line->grab_item ();
4038 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4039 origin, and ditto for y.
4042 double cx = event->button.x;
4043 double cy = event->button.y;
4045 _line->parent_group().canvas_to_item (cx, cy);
4047 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4052 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4053 /* no adjacent points */
4057 Drag::start_grab (event, _editor->cursors()->fader);
4059 /* store grab start in parent frame */
4064 double fraction = 1.0 - (cy / _line->height());
4066 _line->start_drag_line (before, after, fraction);
4068 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4072 LineDrag::motion (GdkEvent* event, bool)
4074 double dy = current_pointer_y() - last_pointer_y();
4076 if (event->button.state & Keyboard::SecondaryModifier) {
4080 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4082 _cumulative_y_drag = cy - _fixed_grab_y;
4085 cy = min ((double) _line->height(), cy);
4087 double const fraction = 1.0 - (cy / _line->height());
4090 /* we are ignoring x position for this drag, so we can just pass in anything */
4091 _line->drag_motion (0, fraction, true, false, ignored);
4093 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4097 LineDrag::finished (GdkEvent* event, bool movement_occured)
4099 if (movement_occured) {
4100 motion (event, false);
4101 _line->end_drag (false, 0);
4103 /* add a new control point on the line */
4105 AutomationTimeAxisView* atv;
4107 _line->end_drag (false, 0);
4109 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4110 framepos_t where = _editor->window_event_sample (event, 0, 0);
4111 atv->add_automation_event (event, where, event->button.y, false);
4115 _editor->commit_reversible_command ();
4119 LineDrag::aborted (bool)
4124 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4127 _cumulative_x_drag (0)
4129 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4133 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4135 Drag::start_grab (event);
4137 _line = reinterpret_cast<Line*> (_item);
4140 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4142 double cx = event->button.x;
4143 double cy = event->button.y;
4145 _item->parent()->canvas_to_item (cx, cy);
4147 /* store grab start in parent frame */
4148 _region_view_grab_x = cx;
4150 _before = *(float*) _item->get_data ("position");
4152 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4154 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4158 FeatureLineDrag::motion (GdkEvent*, bool)
4160 double dx = _drags->current_pointer_x() - last_pointer_x();
4162 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4164 _cumulative_x_drag += dx;
4166 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4175 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4177 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4179 float *pos = new float;
4182 _line->set_data ("position", pos);
4188 FeatureLineDrag::finished (GdkEvent*, bool)
4190 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4191 _arv->update_transient(_before, _before);
4195 FeatureLineDrag::aborted (bool)
4200 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4202 , _vertical_only (false)
4204 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4208 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4210 Drag::start_grab (event);
4211 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4215 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4222 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4224 framepos_t grab = grab_frame ();
4225 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4226 _editor->snap_to_with_modifier (grab, event);
4228 grab = raw_grab_frame ();
4231 /* base start and end on initial click position */
4241 if (current_pointer_y() < grab_y()) {
4242 y1 = current_pointer_y();
4245 y2 = current_pointer_y();
4249 if (start != end || y1 != y2) {
4251 double x1 = _editor->sample_to_pixel (start);
4252 double x2 = _editor->sample_to_pixel (end);
4253 const double min_dimension = 2.0;
4255 if (_vertical_only) {
4256 /* fixed 10 pixel width */
4260 x2 = min (x1 - min_dimension, x2);
4262 x2 = max (x1 + min_dimension, x2);
4267 y2 = min (y1 - min_dimension, y2);
4269 y2 = max (y1 + min_dimension, y2);
4272 /* translate rect into item space and set */
4274 ArdourCanvas::Rect r (x1, y1, x2, y2);
4276 /* this drag is a _trackview_only == true drag, so the y1 and
4277 * y2 (computed using current_pointer_y() and grab_y()) will be
4278 * relative to the top of the trackview group). The
4279 * rubberband rect has the same parent/scroll offset as the
4280 * the trackview group, so we can use the "r" rect directly
4281 * to set the shape of the rubberband.
4284 _editor->rubberband_rect->set (r);
4285 _editor->rubberband_rect->show();
4286 _editor->rubberband_rect->raise_to_top();
4288 show_verbose_cursor_time (pf);
4290 do_select_things (event, true);
4295 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4299 framepos_t grab = grab_frame ();
4300 framepos_t lpf = last_pointer_frame ();
4302 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4303 grab = raw_grab_frame ();
4304 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4318 if (current_pointer_y() < grab_y()) {
4319 y1 = current_pointer_y();
4322 y2 = current_pointer_y();
4326 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4330 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4332 if (movement_occurred) {
4334 motion (event, false);
4335 do_select_things (event, false);
4341 bool do_deselect = true;
4342 MidiTimeAxisView* mtv;
4344 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4346 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4347 /* nothing selected */
4348 add_midi_region (mtv);
4349 do_deselect = false;
4353 /* do not deselect if Primary or Tertiary (toggle-select or
4354 * extend-select are pressed.
4357 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4358 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4365 _editor->rubberband_rect->hide();
4369 RubberbandSelectDrag::aborted (bool)
4371 _editor->rubberband_rect->hide ();
4374 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4375 : RegionDrag (e, i, p, v)
4377 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4381 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4383 Drag::start_grab (event, cursor);
4385 show_verbose_cursor_time (adjusted_current_frame (event));
4389 TimeFXDrag::motion (GdkEvent* event, bool)
4391 RegionView* rv = _primary;
4392 StreamView* cv = rv->get_time_axis_view().view ();
4394 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4395 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4396 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4398 framepos_t const pf = adjusted_current_frame (event);
4400 if (pf > rv->region()->position()) {
4401 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4404 show_verbose_cursor_time (pf);
4408 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4410 _primary->get_time_axis_view().hide_timestretch ();
4412 if (!movement_occurred) {
4416 if (last_pointer_frame() < _primary->region()->position()) {
4417 /* backwards drag of the left edge - not usable */
4421 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4423 float percentage = (double) newlen / (double) _primary->region()->length();
4425 #ifndef USE_RUBBERBAND
4426 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4427 if (_primary->region()->data_type() == DataType::AUDIO) {
4428 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4432 if (!_editor->get_selection().regions.empty()) {
4433 /* primary will already be included in the selection, and edit
4434 group shared editing will propagate selection across
4435 equivalent regions, so just use the current region
4439 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4440 error << _("An error occurred while executing time stretch operation") << endmsg;
4446 TimeFXDrag::aborted (bool)
4448 _primary->get_time_axis_view().hide_timestretch ();
4451 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4454 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4458 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4460 Drag::start_grab (event);
4464 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4466 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4470 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4472 if (movement_occurred && _editor->session()) {
4473 /* make sure we stop */
4474 _editor->session()->request_transport_speed (0.0);
4479 ScrubDrag::aborted (bool)
4484 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4488 , _original_pointer_time_axis (-1)
4489 , _time_selection_at_start (!_editor->get_selection().time.empty())
4491 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4493 if (_time_selection_at_start) {
4494 start_at_start = _editor->get_selection().time.start();
4495 end_at_start = _editor->get_selection().time.end_frame();
4500 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4502 if (_editor->session() == 0) {
4506 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4508 switch (_operation) {
4509 case CreateSelection:
4510 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4515 cursor = _editor->cursors()->selector;
4516 Drag::start_grab (event, cursor);
4519 case SelectionStartTrim:
4520 if (_editor->clicked_axisview) {
4521 _editor->clicked_axisview->order_selection_trims (_item, true);
4523 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4526 case SelectionEndTrim:
4527 if (_editor->clicked_axisview) {
4528 _editor->clicked_axisview->order_selection_trims (_item, false);
4530 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4534 Drag::start_grab (event, cursor);
4537 case SelectionExtend:
4538 Drag::start_grab (event, cursor);
4542 if (_operation == SelectionMove) {
4543 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4545 show_verbose_cursor_time (adjusted_current_frame (event));
4548 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4552 SelectionDrag::setup_pointer_frame_offset ()
4554 switch (_operation) {
4555 case CreateSelection:
4556 _pointer_frame_offset = 0;
4559 case SelectionStartTrim:
4561 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4564 case SelectionEndTrim:
4565 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4568 case SelectionExtend:
4574 SelectionDrag::motion (GdkEvent* event, bool first_move)
4576 framepos_t start = 0;
4578 framecnt_t length = 0;
4579 framecnt_t distance = 0;
4581 framepos_t const pending_position = adjusted_current_frame (event);
4583 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4587 switch (_operation) {
4588 case CreateSelection:
4590 framepos_t grab = grab_frame ();
4593 grab = adjusted_current_frame (event, false);
4594 if (grab < pending_position) {
4595 _editor->snap_to (grab, RoundDownMaybe);
4597 _editor->snap_to (grab, RoundUpMaybe);
4601 if (pending_position < grab) {
4602 start = pending_position;
4605 end = pending_position;
4609 /* first drag: Either add to the selection
4610 or create a new selection
4617 /* adding to the selection */
4618 _editor->set_selected_track_as_side_effect (Selection::Add);
4619 _editor->clicked_selection = _editor->selection->add (start, end);
4626 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4627 _editor->set_selected_track_as_side_effect (Selection::Set);
4630 _editor->clicked_selection = _editor->selection->set (start, end);
4634 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4635 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4636 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4638 _editor->selection->add (atest);
4642 /* select all tracks within the rectangle that we've marked out so far */
4643 TrackViewList new_selection;
4644 TrackViewList& all_tracks (_editor->track_views);
4646 ArdourCanvas::Coord const top = grab_y();
4647 ArdourCanvas::Coord const bottom = current_pointer_y();
4649 if (top >= 0 && bottom >= 0) {
4651 //first, find the tracks that are covered in the y range selection
4652 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4653 if ((*i)->covered_by_y_range (top, bottom)) {
4654 new_selection.push_back (*i);
4658 //now find any tracks that are GROUPED with the tracks we selected
4659 TrackViewList grouped_add = new_selection;
4660 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4661 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4662 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4663 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4664 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4665 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4666 grouped_add.push_back (*j);
4671 //now compare our list with the current selection, and add or remove as necessary
4672 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4673 TrackViewList tracks_to_add;
4674 TrackViewList tracks_to_remove;
4675 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4676 if ( !_editor->selection->tracks.contains ( *i ) )
4677 tracks_to_add.push_back ( *i );
4678 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4679 if ( !grouped_add.contains ( *i ) )
4680 tracks_to_remove.push_back ( *i );
4681 _editor->selection->add(tracks_to_add);
4682 _editor->selection->remove(tracks_to_remove);
4688 case SelectionStartTrim:
4690 start = _editor->selection->time[_editor->clicked_selection].start;
4691 end = _editor->selection->time[_editor->clicked_selection].end;
4693 if (pending_position > end) {
4696 start = pending_position;
4700 case SelectionEndTrim:
4702 start = _editor->selection->time[_editor->clicked_selection].start;
4703 end = _editor->selection->time[_editor->clicked_selection].end;
4705 if (pending_position < start) {
4708 end = pending_position;
4715 start = _editor->selection->time[_editor->clicked_selection].start;
4716 end = _editor->selection->time[_editor->clicked_selection].end;
4718 length = end - start;
4719 distance = pending_position - start;
4720 start = pending_position;
4721 _editor->snap_to (start);
4723 end = start + length;
4727 case SelectionExtend:
4732 switch (_operation) {
4734 if (_time_selection_at_start) {
4735 _editor->selection->move_time (distance);
4739 _editor->selection->replace (_editor->clicked_selection, start, end);
4743 if (_operation == SelectionMove) {
4744 show_verbose_cursor_time(start);
4746 show_verbose_cursor_time(pending_position);
4751 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4753 Session* s = _editor->session();
4755 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4756 if (movement_occurred) {
4757 motion (event, false);
4758 /* XXX this is not object-oriented programming at all. ick */
4759 if (_editor->selection->time.consolidate()) {
4760 _editor->selection->TimeChanged ();
4763 /* XXX what if its a music time selection? */
4765 if ( s->get_play_range() && s->transport_rolling() ) {
4766 s->request_play_range (&_editor->selection->time, true);
4768 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4769 if (_operation == SelectionEndTrim)
4770 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4772 s->request_locate (_editor->get_selection().time.start());
4778 /* just a click, no pointer movement.
4781 if (_operation == SelectionExtend) {
4782 if (_time_selection_at_start) {
4783 framepos_t pos = adjusted_current_frame (event, false);
4784 framepos_t start = min (pos, start_at_start);
4785 framepos_t end = max (pos, end_at_start);
4786 _editor->selection->set (start, end);
4789 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4790 if (_editor->clicked_selection) {
4791 _editor->selection->remove (_editor->clicked_selection);
4794 if (!_editor->clicked_selection) {
4795 _editor->selection->clear_time();
4800 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4801 _editor->selection->set (_editor->clicked_axisview);
4804 if (s && s->get_play_range () && s->transport_rolling()) {
4805 s->request_stop (false, false);
4810 _editor->stop_canvas_autoscroll ();
4811 _editor->clicked_selection = 0;
4812 _editor->commit_reversible_selection_op ();
4816 SelectionDrag::aborted (bool)
4821 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4822 : Drag (e, i, false),
4826 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4828 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4829 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4830 physical_screen_height (_editor->get_window())));
4831 _drag_rect->hide ();
4833 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4834 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4837 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4839 /* normal canvas items will be cleaned up when their parent group is deleted. But
4840 this item is created as the child of a long-lived parent group, and so we
4841 need to explicitly delete it.
4847 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4849 if (_editor->session() == 0) {
4853 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4855 if (!_editor->temp_location) {
4856 _editor->temp_location = new Location (*_editor->session());
4859 switch (_operation) {
4860 case CreateSkipMarker:
4861 case CreateRangeMarker:
4862 case CreateTransportMarker:
4863 case CreateCDMarker:
4865 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4870 cursor = _editor->cursors()->selector;
4874 Drag::start_grab (event, cursor);
4876 show_verbose_cursor_time (adjusted_current_frame (event));
4880 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4882 framepos_t start = 0;
4884 ArdourCanvas::Rectangle *crect;
4886 switch (_operation) {
4887 case CreateSkipMarker:
4888 crect = _editor->range_bar_drag_rect;
4890 case CreateRangeMarker:
4891 crect = _editor->range_bar_drag_rect;
4893 case CreateTransportMarker:
4894 crect = _editor->transport_bar_drag_rect;
4896 case CreateCDMarker:
4897 crect = _editor->cd_marker_bar_drag_rect;
4900 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4905 framepos_t const pf = adjusted_current_frame (event);
4907 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4908 framepos_t grab = grab_frame ();
4909 _editor->snap_to (grab);
4911 if (pf < grab_frame()) {
4919 /* first drag: Either add to the selection
4920 or create a new selection.
4925 _editor->temp_location->set (start, end);
4929 update_item (_editor->temp_location);
4931 //_drag_rect->raise_to_top();
4937 _editor->temp_location->set (start, end);
4939 double x1 = _editor->sample_to_pixel (start);
4940 double x2 = _editor->sample_to_pixel (end);
4944 update_item (_editor->temp_location);
4947 show_verbose_cursor_time (pf);
4952 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4954 Location * newloc = 0;
4958 if (movement_occurred) {
4959 motion (event, false);
4962 switch (_operation) {
4963 case CreateSkipMarker:
4964 case CreateRangeMarker:
4965 case CreateCDMarker:
4967 XMLNode &before = _editor->session()->locations()->get_state();
4968 if (_operation == CreateSkipMarker) {
4969 _editor->begin_reversible_command (_("new skip marker"));
4970 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4971 flags = Location::IsRangeMarker | Location::IsSkip;
4972 _editor->range_bar_drag_rect->hide();
4973 } else if (_operation == CreateCDMarker) {
4974 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4975 _editor->begin_reversible_command (_("new CD marker"));
4976 flags = Location::IsRangeMarker | Location::IsCDMarker;
4977 _editor->cd_marker_bar_drag_rect->hide();
4979 _editor->begin_reversible_command (_("new skip marker"));
4980 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4981 flags = Location::IsRangeMarker;
4982 _editor->range_bar_drag_rect->hide();
4984 newloc = new Location (
4985 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4988 _editor->session()->locations()->add (newloc, true);
4989 XMLNode &after = _editor->session()->locations()->get_state();
4990 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4991 _editor->commit_reversible_command ();
4995 case CreateTransportMarker:
4996 // popup menu to pick loop or punch
4997 _editor->new_transport_marker_context_menu (&event->button, _item);
5003 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5005 if (_operation == CreateTransportMarker) {
5007 /* didn't drag, so just locate */
5009 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5011 } else if (_operation == CreateCDMarker) {
5013 /* didn't drag, but mark is already created so do
5016 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5021 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5023 if (end == max_framepos) {
5024 end = _editor->session()->current_end_frame ();
5027 if (start == max_framepos) {
5028 start = _editor->session()->current_start_frame ();
5031 switch (_editor->mouse_mode) {
5033 /* find the two markers on either side and then make the selection from it */
5034 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5038 /* find the two markers on either side of the click and make the range out of it */
5039 _editor->selection->set (start, end);
5048 _editor->stop_canvas_autoscroll ();
5052 RangeMarkerBarDrag::aborted (bool movement_occured)
5054 if (movement_occured) {
5055 _drag_rect->hide ();
5060 RangeMarkerBarDrag::update_item (Location* location)
5062 double const x1 = _editor->sample_to_pixel (location->start());
5063 double const x2 = _editor->sample_to_pixel (location->end());
5065 _drag_rect->set_x0 (x1);
5066 _drag_rect->set_x1 (x2);
5069 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5071 , _cumulative_dx (0)
5072 , _cumulative_dy (0)
5074 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5076 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5078 _region = &_primary->region_view ();
5079 _note_height = _region->midi_stream_view()->note_height ();
5083 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5085 Drag::start_grab (event);
5087 if (!(_was_selected = _primary->selected())) {
5089 /* tertiary-click means extend selection - we'll do that on button release,
5090 so don't add it here, because otherwise we make it hard to figure
5091 out the "extend-to" range.
5094 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5097 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5100 _region->note_selected (_primary, true);
5102 _region->unique_select (_primary);
5105 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5106 _editor->commit_reversible_selection_op();
5111 /** @return Current total drag x change in frames */
5113 NoteDrag::total_dx () const
5116 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5118 /* primary note time */
5119 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5121 /* new time of the primary note in session frames */
5122 frameoffset_t st = n + dx;
5124 framepos_t const rp = _region->region()->position ();
5126 /* prevent the note being dragged earlier than the region's position */
5129 /* snap and return corresponding delta */
5130 return _region->snap_frame_to_frame (st - rp) + rp - n;
5133 /** @return Current total drag y change in note number */
5135 NoteDrag::total_dy () const
5137 MidiStreamView* msv = _region->midi_stream_view ();
5138 double const y = _region->midi_view()->y_position ();
5139 /* new current note */
5140 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5142 n = max (msv->lowest_note(), n);
5143 n = min (msv->highest_note(), n);
5144 /* and work out delta */
5145 return n - msv->y_to_note (grab_y() - y);
5149 NoteDrag::motion (GdkEvent *, bool)
5151 /* Total change in x and y since the start of the drag */
5152 frameoffset_t const dx = total_dx ();
5153 int8_t const dy = total_dy ();
5155 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5156 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5157 double const tdy = -dy * _note_height - _cumulative_dy;
5160 _cumulative_dx += tdx;
5161 _cumulative_dy += tdy;
5163 int8_t note_delta = total_dy();
5165 _region->move_selection (tdx, tdy, note_delta);
5167 /* the new note value may be the same as the old one, but we
5168 * don't know what that means because the selection may have
5169 * involved more than one note and we might be doing something
5170 * odd with them. so show the note value anyway, always.
5174 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5176 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5177 (int) floor ((double)new_note));
5179 show_verbose_cursor_text (buf);
5184 NoteDrag::finished (GdkEvent* ev, bool moved)
5187 /* no motion - select note */
5189 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5190 _editor->current_mouse_mode() == Editing::MouseDraw) {
5192 bool changed = false;
5194 if (_was_selected) {
5195 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5197 _region->note_deselected (_primary);
5201 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5202 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5204 if (!extend && !add && _region->selection_size() > 1) {
5205 _region->unique_select (_primary);
5207 } else if (extend) {
5208 _region->note_selected (_primary, true, true);
5211 /* it was added during button press */
5216 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5217 _editor->commit_reversible_selection_op();
5221 _region->note_dropped (_primary, total_dx(), total_dy());
5226 NoteDrag::aborted (bool)
5231 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5232 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5233 : Drag (editor, atv->base_item ())
5235 , _y_origin (atv->y_position())
5236 , _nothing_to_drag (false)
5238 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5239 setup (atv->lines ());
5242 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5243 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5244 : Drag (editor, rv->get_canvas_group ())
5246 , _y_origin (rv->get_time_axis_view().y_position())
5247 , _nothing_to_drag (false)
5250 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5252 list<boost::shared_ptr<AutomationLine> > lines;
5254 AudioRegionView* audio_view;
5255 AutomationRegionView* automation_view;
5256 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5257 lines.push_back (audio_view->get_gain_line ());
5258 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5259 lines.push_back (automation_view->line ());
5262 error << _("Automation range drag created for invalid region type") << endmsg;
5268 /** @param lines AutomationLines to drag.
5269 * @param offset Offset from the session start to the points in the AutomationLines.
5272 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5274 /* find the lines that overlap the ranges being dragged */
5275 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5276 while (i != lines.end ()) {
5277 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5280 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5282 /* check this range against all the AudioRanges that we are using */
5283 list<AudioRange>::const_iterator k = _ranges.begin ();
5284 while (k != _ranges.end()) {
5285 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5291 /* add it to our list if it overlaps at all */
5292 if (k != _ranges.end()) {
5297 _lines.push_back (n);
5303 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5307 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5309 return 1.0 - ((global_y - _y_origin) / line->height());
5313 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5315 const double v = list->eval(x);
5316 return _integral ? rint(v) : v;
5320 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5322 Drag::start_grab (event, cursor);
5324 /* Get line states before we start changing things */
5325 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5326 i->state = &i->line->get_state ();
5327 i->original_fraction = y_fraction (i->line, current_pointer_y());
5330 if (_ranges.empty()) {
5332 /* No selected time ranges: drag all points */
5333 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5334 uint32_t const N = i->line->npoints ();
5335 for (uint32_t j = 0; j < N; ++j) {
5336 i->points.push_back (i->line->nth (j));
5342 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5344 framecnt_t const half = (i->start + i->end) / 2;
5346 /* find the line that this audio range starts in */
5347 list<Line>::iterator j = _lines.begin();
5348 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5352 if (j != _lines.end()) {
5353 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5355 /* j is the line that this audio range starts in; fade into it;
5356 64 samples length plucked out of thin air.
5359 framepos_t a = i->start + 64;
5364 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5365 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5367 the_list->editor_add (p, value (the_list, p));
5368 the_list->editor_add (q, value (the_list, q));
5371 /* same thing for the end */
5374 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5378 if (j != _lines.end()) {
5379 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5381 /* j is the line that this audio range starts in; fade out of it;
5382 64 samples length plucked out of thin air.
5385 framepos_t b = i->end - 64;
5390 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5391 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5393 the_list->editor_add (p, value (the_list, p));
5394 the_list->editor_add (q, value (the_list, q));
5398 _nothing_to_drag = true;
5400 /* Find all the points that should be dragged and put them in the relevant
5401 points lists in the Line structs.
5404 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5406 uint32_t const N = i->line->npoints ();
5407 for (uint32_t j = 0; j < N; ++j) {
5409 /* here's a control point on this line */
5410 ControlPoint* p = i->line->nth (j);
5411 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5413 /* see if it's inside a range */
5414 list<AudioRange>::const_iterator k = _ranges.begin ();
5415 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5419 if (k != _ranges.end()) {
5420 /* dragging this point */
5421 _nothing_to_drag = false;
5422 i->points.push_back (p);
5428 if (_nothing_to_drag) {
5432 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5433 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5438 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5440 if (_nothing_to_drag) {
5444 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5445 float const f = y_fraction (l->line, current_pointer_y());
5446 /* we are ignoring x position for this drag, so we can just pass in anything */
5448 l->line->drag_motion (0, f, true, false, ignored);
5449 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5454 AutomationRangeDrag::finished (GdkEvent* event, bool)
5456 if (_nothing_to_drag) {
5460 motion (event, false);
5461 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5462 i->line->end_drag (false, 0);
5465 _editor->commit_reversible_command ();
5469 AutomationRangeDrag::aborted (bool)
5471 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5476 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5478 , initial_time_axis_view (itav)
5480 /* note that time_axis_view may be null if the regionview was created
5481 * as part of a copy operation.
5483 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5484 layer = v->region()->layer ();
5485 initial_y = v->get_canvas_group()->position().y;
5486 initial_playlist = v->region()->playlist ();
5487 initial_position = v->region()->position ();
5488 initial_end = v->region()->position () + v->region()->length ();
5491 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5492 : Drag (e, i->canvas_item ())
5495 , _cumulative_dx (0)
5497 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5498 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5503 PatchChangeDrag::motion (GdkEvent* ev, bool)
5505 framepos_t f = adjusted_current_frame (ev);
5506 boost::shared_ptr<Region> r = _region_view->region ();
5507 f = max (f, r->position ());
5508 f = min (f, r->last_frame ());
5510 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5511 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5512 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5513 _cumulative_dx = dxu;
5517 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5519 if (!movement_occurred) {
5523 boost::shared_ptr<Region> r (_region_view->region ());
5524 framepos_t f = adjusted_current_frame (ev);
5525 f = max (f, r->position ());
5526 f = min (f, r->last_frame ());
5528 _region_view->move_patch_change (
5530 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5535 PatchChangeDrag::aborted (bool)
5537 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5541 PatchChangeDrag::setup_pointer_frame_offset ()
5543 boost::shared_ptr<Region> region = _region_view->region ();
5544 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5547 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5548 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5555 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5557 _region_view->update_drag_selection (
5559 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5563 MidiRubberbandSelectDrag::deselect_things ()
5568 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5569 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5572 _vertical_only = true;
5576 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5578 double const y = _region_view->midi_view()->y_position ();
5580 y1 = max (0.0, y1 - y);
5581 y2 = max (0.0, y2 - y);
5583 _region_view->update_vertical_drag_selection (
5586 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5591 MidiVerticalSelectDrag::deselect_things ()
5596 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5597 : RubberbandSelectDrag (e, i)
5603 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5605 if (drag_in_progress) {
5606 /* We just want to select things at the end of the drag, not during it */
5610 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5612 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5614 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5616 _editor->commit_reversible_selection_op ();
5620 EditorRubberbandSelectDrag::deselect_things ()
5622 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5624 _editor->selection->clear_tracks();
5625 _editor->selection->clear_regions();
5626 _editor->selection->clear_points ();
5627 _editor->selection->clear_lines ();
5628 _editor->selection->clear_midi_notes ();
5630 _editor->commit_reversible_selection_op();
5633 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5638 _note[0] = _note[1] = 0;
5641 NoteCreateDrag::~NoteCreateDrag ()
5647 NoteCreateDrag::grid_frames (framepos_t t) const
5650 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5652 grid_beats = Evoral::Beats(1);
5655 return _region_view->region_beats_to_region_frames (grid_beats);
5659 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5661 Drag::start_grab (event, cursor);
5663 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5665 framepos_t pf = _drags->current_pointer_frame ();
5666 framecnt_t const g = grid_frames (pf);
5668 /* Hack so that we always snap to the note that we are over, instead of snapping
5669 to the next one if we're more than halfway through the one we're over.
5671 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5675 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5676 _note[1] = _note[0];
5678 MidiStreamView* sv = _region_view->midi_stream_view ();
5679 double const x = _editor->sample_to_pixel (_note[0]);
5680 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5682 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5683 _drag_rect->set_outline_all ();
5684 _drag_rect->set_outline_color (0xffffff99);
5685 _drag_rect->set_fill_color (0xffffff66);
5689 NoteCreateDrag::motion (GdkEvent* event, bool)
5691 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5692 double const x0 = _editor->sample_to_pixel (_note[0]);
5693 double const x1 = _editor->sample_to_pixel (_note[1]);
5694 _drag_rect->set_x0 (std::min(x0, x1));
5695 _drag_rect->set_x1 (std::max(x0, x1));
5699 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5701 if (!had_movement) {
5705 framepos_t const start = min (_note[0], _note[1]);
5706 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5708 framecnt_t const g = grid_frames (start);
5709 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5711 if (_editor->snap_mode() == SnapNormal && length < g) {
5715 Evoral::Beats length_beats = max (
5716 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5718 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5722 NoteCreateDrag::y_to_region (double y) const
5725 _region_view->get_canvas_group()->canvas_to_item (x, y);
5730 NoteCreateDrag::aborted (bool)
5735 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5740 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5744 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5746 Drag::start_grab (event, cursor);
5750 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5756 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5759 distance = _drags->current_pointer_x() - grab_x();
5760 len = ar->fade_in()->back()->when;
5762 distance = grab_x() - _drags->current_pointer_x();
5763 len = ar->fade_out()->back()->when;
5766 /* how long should it be ? */
5768 new_length = len + _editor->pixel_to_sample (distance);
5770 /* now check with the region that this is legal */
5772 new_length = ar->verify_xfade_bounds (new_length, start);
5775 arv->reset_fade_in_shape_width (ar, new_length);
5777 arv->reset_fade_out_shape_width (ar, new_length);
5782 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5788 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5791 distance = _drags->current_pointer_x() - grab_x();
5792 len = ar->fade_in()->back()->when;
5794 distance = grab_x() - _drags->current_pointer_x();
5795 len = ar->fade_out()->back()->when;
5798 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5800 _editor->begin_reversible_command ("xfade trim");
5801 ar->playlist()->clear_owned_changes ();
5804 ar->set_fade_in_length (new_length);
5806 ar->set_fade_out_length (new_length);
5809 /* Adjusting the xfade may affect other regions in the playlist, so we need
5810 to get undo Commands from the whole playlist rather than just the
5814 vector<Command*> cmds;
5815 ar->playlist()->rdiff (cmds);
5816 _editor->session()->add_commands (cmds);
5817 _editor->commit_reversible_command ();
5822 CrossfadeEdgeDrag::aborted (bool)
5825 // arv->redraw_start_xfade ();
5827 // arv->redraw_end_xfade ();
5831 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5832 : Drag (e, item, true)
5833 , line (new EditorCursor (*e))
5835 line->set_position (pos);
5839 RegionCutDrag::~RegionCutDrag ()
5845 RegionCutDrag::motion (GdkEvent*, bool)
5847 framepos_t where = _drags->current_pointer_frame();
5848 _editor->snap_to (where);
5850 line->set_position (where);
5854 RegionCutDrag::finished (GdkEvent*, bool)
5856 _editor->get_track_canvas()->canvas()->re_enter();
5858 framepos_t pos = _drags->current_pointer_frame();
5862 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5868 _editor->split_regions_at (pos, rs);
5872 RegionCutDrag::aborted (bool)