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 */
759 struct DraggingViewSorter {
760 bool operator() (const DraggingView& a, const DraggingView& b) {
761 return a.time_axis_view < b.time_axis_view;
766 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
768 double delta_layer = 0;
769 int delta_time_axis_view = 0;
770 int current_pointer_time_axis_view = -1;
772 assert (!_views.empty ());
776 if (initially_vertical()) {
777 _y_constrained = false;
778 _x_constrained = true;
780 _y_constrained = true;
781 _x_constrained = false;
786 #ifdef DEBUG_DROPZONEDRAG
787 printf("--------- LAST AXIS: %d\n", _last_pointer_time_axis_view);
789 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
791 /* Find the TimeAxisView that the pointer is now over */
793 const double cur_y = current_pointer_y ();
795 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
796 TimeAxisView* tv = r.first;
798 if (!tv && cur_y < 0) {
799 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
803 if (tv && tv->view()) {
804 double layer = r.second;
806 if (first_move && tv->view()->layer_display() == Stacked) {
807 tv->view()->set_layer_display (Expanded);
810 /* Here's the current pointer position in terms of time axis view and layer */
811 current_pointer_time_axis_view = find_time_axis_view (tv);
812 assert(current_pointer_time_axis_view >= 0);
813 #ifdef DEBUG_DROPZONEDRAG
814 printf(" On AXIS: %d\n", current_pointer_time_axis_view);
817 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
819 /* Work out the change in y */
821 if (_last_pointer_time_axis_view < 0) {
822 /* Was in the drop-zone, now over a track.
823 * Hence it must be an upward move (from the bottom)
825 * track_index is still -1, so delta must be set to
826 * move up the correct number of tracks from the bottom.
828 * This is necessary because steps may be skipped if
829 * the bottom-most track is not a valid target,
831 #ifdef DEBUG_DROPZONEDRAG
832 printf("MOVE OUT OF THE ZONE...\n");
834 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size ();
836 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
839 /* TODO needs adjustment per DraggingView,
841 * e.g. select one region on the top-layer of a track
842 * and one region which is at the bottom-layer of another track
845 * Indicated drop-zones and layering is wrong.
846 * and may infer additional layers on the target-track
847 * (depending how many layers the original track had).
849 * Or select two regions (different layers) on a same track,
850 * move across a non-layer track.. -> layering info is lost.
851 * on drop either of the regions may be on top.
853 * Proposed solution: screw it :) well,
854 * don't use delta_layer, use an absolute value
855 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
856 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
857 * 3) iterate over all DraggingView, find the one that is over the track with most layers
858 * 4) proportionally scale layer to layers available on target
860 delta_layer = current_pointer_layer - _last_pointer_layer;
863 /* for automation lanes, there is a TimeAxisView but no ->view() */
864 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
865 /* Moving into the drop-zone..
867 * TODO allow moving further down in drop-zone:
868 * e.g. 2 Tracks, select a region on both of them.
870 * A) grab the upper, drag 2 down, both regions are in the dropzone: all fine (works)
872 * B) grab the lower, drag 1 down, region (and mouse) are in dropzone, The End.
873 * upper region is only down one track and cannot be moved into the zone.
876 * keep track of how many regions are in the DZ (private var),
877 * also count from how many tracks the dragged-regions come from (first move)
879 * if not all regions are in the DZ, keep going.
881 * Using 'default height' H for all dropzone regions will make things
882 * a lot simpler: (number_of_DZ_entries * H + Pointer_YPOS - DZ_YPOS) / H.
883 * (because at this point in time PlaylistDropzoneMap is not yet populated)
885 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
886 #ifdef DEBUG_DROPZONEDRAG
887 printf("INTO THE ZONE DELTA: %d\n", delta_time_axis_view);
891 /* Work out the change in x */
892 framepos_t pending_region_position;
893 double const x_delta = compute_x_delta (event, &pending_region_position);
894 _last_frame_position = pending_region_position;
896 /* calculate hidden tracks in current delta */
898 if (_last_pointer_time_axis_view < 0) {
899 // Moving out of the zone, check for hidden tracks at the bottom.
900 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
901 -_time_axis_views.size() - delta_time_axis_view;
902 #ifdef DEBUG_DROPZONEDRAG
903 printf("NOW WHAT?? last: %d delta %d || skip %d\n", _last_pointer_time_axis_view, delta_time_axis_view, delta_skip);
906 // calculate hidden tracks that are skipped by the pointer movement
907 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
908 - _last_pointer_time_axis_view
909 - delta_time_axis_view;
910 #ifdef DEBUG_DROPZONEDRAG
911 printf("Drag from %d to %d || skip %d\n",
912 _last_pointer_time_axis_view,
913 _last_pointer_time_axis_view + delta_time_axis_view,
918 /* Verify change in y */
919 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
920 /* this y movement is not allowed, so do no y movement this time */
921 delta_time_axis_view = 0;
924 #ifdef DEBUG_DROPZONEDRAG
925 printf(" ** NOT ALLOWED\n");
929 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
930 /* haven't reached next snap point, and we're not switching
931 trackviews nor layers. nothing to do.
936 typedef pair<int,double> NewTrackIndexAndPosition;
937 typedef map<boost::shared_ptr<Playlist>,NewTrackIndexAndPosition> PlaylistDropzoneMap;
938 PlaylistDropzoneMap playlist_dropzone_map;
939 int biggest_drop_zone_offset = 0;
941 /* find drop-zone y-position */
942 Coord last_track_bottom_edge;
943 last_track_bottom_edge = 0;
944 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
945 if (!(*t)->hidden()) {
946 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
952 /* sort views by time_axis.
953 * This retains track order in the dropzone, regardless
954 * of actual selection order
956 _views.sort (DraggingViewSorter());
959 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
961 RegionView* rv = i->view;
966 if (rv->region()->locked() || rv->region()->video_locked()) {
973 /* reparent the regionview into a group above all
977 ArdourCanvas::Item* rvg = rv->get_canvas_group();
978 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
979 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
980 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
981 /* move the item so that it continues to appear at the
982 same location now that its parent has changed.
984 rvg->move (rv_canvas_offset - dmg_canvas_offset);
987 /* If we have moved tracks, we'll fudge the layer delta so that the
988 region gets moved back onto layer 0 on its new track; this avoids
989 confusion when dragging regions from non-zero layers onto different
992 double this_delta_layer = delta_layer;
993 if (delta_time_axis_view != 0) {
994 this_delta_layer = - i->layer;
997 int this_delta_time_axis_view = delta_time_axis_view;
998 this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1000 int track_index = i->time_axis_view + this_delta_time_axis_view;
1001 assert(track_index >= 0);
1003 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1004 i->time_axis_view = track_index;
1005 #ifdef DEBUG_DROPZONEDRAG
1006 printf("IN THE ZONE\n");
1008 assert(i->time_axis_view >= _time_axis_views.size());
1012 NewTrackIndexAndPosition ip;
1013 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1015 /* store index of each new playlist as a negative count, starting at -1 */
1017 if (pdz == playlist_dropzone_map.end()) {
1020 * retain the ordering top -> bottom in the drop-zone
1021 * this can be done by sorting the regions according to
1022 * i->time_axis_view Y, prior to iterating over DraggingView
1025 int n = playlist_dropzone_map.size() + 1;
1027 /* compute where this new track (which doesn't exist yet) will live
1031 ip.first = -n; /* in time axis units, negative to signify "in drop zone " */
1032 ip.second = last_track_bottom_edge; /* where to place the top edge of the regionview */
1034 /* How high is this region view ? */
1036 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1037 ArdourCanvas::Rect bbox;
1040 bbox = obbox.get ();
1043 last_track_bottom_edge += bbox.height();
1045 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), ip));
1050 dzoffset = ip.first;
1053 /* values are zero or negative, hence the use of min() */
1054 biggest_drop_zone_offset = min (biggest_drop_zone_offset, dzoffset);
1055 y_delta = ip.second - rv->get_canvas_group()->canvas_origin().y;
1060 /* The TimeAxisView that this region is now over */
1061 TimeAxisView* current_tv = _time_axis_views[track_index];
1063 /* Ensure it is moved from stacked -> expanded if appropriate */
1064 if (current_tv->view()->layer_display() == Stacked) {
1065 current_tv->view()->set_layer_display (Expanded);
1068 /* We're only allowed to go -ve in layer on Expanded views */
1069 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1070 this_delta_layer = - i->layer;
1074 rv->set_height (current_tv->view()->child_height ());
1076 /* Update show/hidden status as the region view may have come from a hidden track,
1077 or have moved to one.
1079 if (current_tv->hidden ()) {
1080 rv->get_canvas_group()->hide ();
1082 rv->get_canvas_group()->show ();
1085 /* Update the DraggingView */
1086 i->time_axis_view = track_index;
1087 i->layer += this_delta_layer;
1090 _editor->mouse_brush_insert_region (rv, pending_region_position);
1094 /* Get the y coordinate of the top of the track that this region is now over */
1095 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1097 /* And adjust for the layer that it should be on */
1098 StreamView* cv = current_tv->view ();
1099 switch (cv->layer_display ()) {
1103 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1106 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1110 /* need to get the parent of the regionview
1111 * canvas group and get its position in
1112 * equivalent coordinate space as the trackview
1113 * we are now dragging over.
1116 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1121 /* Now move the region view */
1122 rv->move (x_delta, y_delta);
1124 } /* foreach region */
1126 _total_x_delta += x_delta;
1128 if (x_delta != 0 && !_brushing) {
1129 show_verbose_cursor_time (_last_frame_position);
1134 /* the pointer is currently over a time axis view */
1136 if (_last_pointer_time_axis_view < 0) {
1138 /* last motion event was not over a time axis view */
1140 if (delta_time_axis_view < 0) {
1141 /* was in the drop zone, moving up */
1142 assert(current_pointer_time_axis_view >= 0);
1143 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1145 /* was in the drop zone, moving down ... not possible */
1150 /* last motion event was also over a time axis view */
1152 _last_pointer_time_axis_view += delta_time_axis_view;
1153 assert(_last_pointer_time_axis_view >= 0);
1158 /* the pointer is not over a time axis view */
1160 _last_pointer_time_axis_view = biggest_drop_zone_offset;
1163 _last_pointer_layer += delta_layer;
1167 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1169 if (_copy && first_move) {
1171 if (_x_constrained) {
1172 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1174 _editor->begin_reversible_command (Operations::region_copy);
1177 /* duplicate the regionview(s) and region(s) */
1179 list<DraggingView> new_regionviews;
1181 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1183 RegionView* rv = i->view;
1184 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1185 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1187 const boost::shared_ptr<const Region> original = rv->region();
1188 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1189 region_copy->set_position (original->position());
1190 /* need to set this so that the drop zone code can work. This doesn't
1191 actually put the region into the playlist, but just sets a weak pointer
1194 region_copy->set_playlist (original->playlist());
1198 boost::shared_ptr<AudioRegion> audioregion_copy
1199 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1201 nrv = new AudioRegionView (*arv, audioregion_copy);
1203 boost::shared_ptr<MidiRegion> midiregion_copy
1204 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1205 nrv = new MidiRegionView (*mrv, midiregion_copy);
1210 nrv->get_canvas_group()->show ();
1211 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1213 /* swap _primary to the copy */
1215 if (rv == _primary) {
1219 /* ..and deselect the one we copied */
1221 rv->set_selected (false);
1224 if (!new_regionviews.empty()) {
1226 /* reflect the fact that we are dragging the copies */
1228 _views = new_regionviews;
1230 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1233 } else if (!_copy && first_move) {
1235 if (_x_constrained) {
1236 _editor->begin_reversible_command (_("fixed time region drag"));
1238 _editor->begin_reversible_command (Operations::region_drag);
1242 RegionMotionDrag::motion (event, first_move);
1246 RegionMotionDrag::finished (GdkEvent *, bool)
1248 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1249 if (!(*i)->view()) {
1253 if ((*i)->view()->layer_display() == Expanded) {
1254 (*i)->view()->set_layer_display (Stacked);
1260 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1262 RegionMotionDrag::finished (ev, movement_occurred);
1264 if (!movement_occurred) {
1268 if (was_double_click() && !_views.empty()) {
1269 DraggingView dv = _views.front();
1270 dv.view->show_region_editor ();
1277 /* reverse this here so that we have the correct logic to finalize
1281 if (Config->get_edit_mode() == Lock) {
1282 _x_constrained = !_x_constrained;
1285 assert (!_views.empty ());
1287 /* We might have hidden region views so that they weren't visible during the drag
1288 (when they have been reparented). Now everything can be shown again, as region
1289 views are back in their track parent groups.
1291 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1292 i->view->get_canvas_group()->show ();
1295 bool const changed_position = (_last_frame_position != _primary->region()->position());
1296 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1297 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1317 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1321 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1323 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1328 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1329 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1330 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1331 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1333 rtav->set_height (original->current_height());
1337 ChanCount one_midi_port (DataType::MIDI, 1);
1338 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1339 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1340 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1342 rtav->set_height (original->current_height());
1347 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1353 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1355 RegionSelection new_views;
1356 PlaylistSet modified_playlists;
1357 RouteTimeAxisView* new_time_axis_view = 0;
1360 /* all changes were made during motion event handlers */
1362 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1366 _editor->commit_reversible_command ();
1370 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1371 PlaylistMapping playlist_mapping;
1373 /* insert the regions into their new playlists */
1374 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1376 RouteTimeAxisView* dest_rtv = 0;
1378 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1384 if (changed_position && !_x_constrained) {
1385 where = i->view->region()->position() - drag_delta;
1387 where = i->view->region()->position();
1390 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
1391 /* dragged to drop zone */
1393 PlaylistMapping::iterator pm;
1395 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1396 /* first region from this original playlist: create a new track */
1397 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1398 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1399 dest_rtv = new_time_axis_view;
1401 /* we already created a new track for regions from this playlist, use it */
1402 dest_rtv = pm->second;
1405 /* destination time axis view is the one we dragged to */
1406 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1409 if (dest_rtv != 0) {
1410 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1411 if (new_view != 0) {
1412 new_views.push_back (new_view);
1416 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1417 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1420 list<DraggingView>::const_iterator next = i;
1426 /* If we've created new regions either by copying or moving
1427 to a new track, we want to replace the old selection with the new ones
1430 if (new_views.size() > 0) {
1431 _editor->selection->set (new_views);
1434 /* write commands for the accumulated diffs for all our modified playlists */
1435 add_stateful_diff_commands_for_playlists (modified_playlists);
1437 _editor->commit_reversible_command ();
1441 RegionMoveDrag::finished_no_copy (
1442 bool const changed_position,
1443 bool const changed_tracks,
1444 framecnt_t const drag_delta
1447 RegionSelection new_views;
1448 PlaylistSet modified_playlists;
1449 PlaylistSet frozen_playlists;
1450 set<RouteTimeAxisView*> views_to_update;
1451 RouteTimeAxisView* new_time_axis_view = 0;
1454 /* all changes were made during motion event handlers */
1455 _editor->commit_reversible_command ();
1459 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1460 PlaylistMapping playlist_mapping;
1462 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1464 RegionView* rv = i->view;
1465 RouteTimeAxisView* dest_rtv = 0;
1467 if (rv->region()->locked() || rv->region()->video_locked()) {
1472 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
1473 /* dragged to drop zone */
1475 PlaylistMapping::iterator pm;
1477 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1478 /* first region from this original playlist: create a new track */
1479 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1480 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1481 dest_rtv = new_time_axis_view;
1483 /* we already created a new track for regions from this playlist, use it */
1484 dest_rtv = pm->second;
1488 /* destination time axis view is the one we dragged to */
1489 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1494 double const dest_layer = i->layer;
1496 views_to_update.insert (dest_rtv);
1500 if (changed_position && !_x_constrained) {
1501 where = rv->region()->position() - drag_delta;
1503 where = rv->region()->position();
1506 if (changed_tracks) {
1508 /* insert into new playlist */
1510 RegionView* new_view = insert_region_into_playlist (
1511 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1514 if (new_view == 0) {
1519 new_views.push_back (new_view);
1521 /* remove from old playlist */
1523 /* the region that used to be in the old playlist is not
1524 moved to the new one - we use a copy of it. as a result,
1525 any existing editor for the region should no longer be
1528 rv->hide_region_editor();
1531 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1535 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1537 /* this movement may result in a crossfade being modified, or a layering change,
1538 so we need to get undo data from the playlist as well as the region.
1541 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1543 playlist->clear_changes ();
1546 rv->region()->clear_changes ();
1549 motion on the same track. plonk the previously reparented region
1550 back to its original canvas group (its streamview).
1551 No need to do anything for copies as they are fake regions which will be deleted.
1554 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1555 rv->get_canvas_group()->set_y_position (i->initial_y);
1558 /* just change the model */
1559 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1560 playlist->set_layer (rv->region(), dest_layer);
1563 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1565 r = frozen_playlists.insert (playlist);
1568 playlist->freeze ();
1571 rv->region()->set_position (where);
1573 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1576 if (changed_tracks) {
1578 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1579 was selected in all of them, then removing it from a playlist will have removed all
1580 trace of it from _views (i.e. there were N regions selected, we removed 1,
1581 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1582 corresponding regionview, and _views is now empty).
1584 This could have invalidated any and all iterators into _views.
1586 The heuristic we use here is: if the region selection is empty, break out of the loop
1587 here. if the region selection is not empty, then restart the loop because we know that
1588 we must have removed at least the region(view) we've just been working on as well as any
1589 that we processed on previous iterations.
1591 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1592 we can just iterate.
1596 if (_views.empty()) {
1607 /* If we've created new regions either by copying or moving
1608 to a new track, we want to replace the old selection with the new ones
1611 if (new_views.size() > 0) {
1612 _editor->selection->set (new_views);
1615 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1619 /* write commands for the accumulated diffs for all our modified playlists */
1620 add_stateful_diff_commands_for_playlists (modified_playlists);
1622 _editor->commit_reversible_command ();
1624 /* We have futzed with the layering of canvas items on our streamviews.
1625 If any region changed layer, this will have resulted in the stream
1626 views being asked to set up their region views, and all will be well.
1627 If not, we might now have badly-ordered region views. Ask the StreamViews
1628 involved to sort themselves out, just in case.
1631 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1632 (*i)->view()->playlist_layered ((*i)->track ());
1636 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1637 * @param region Region to remove.
1638 * @param playlist playlist To remove from.
1639 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1640 * that clear_changes () is only called once per playlist.
1643 RegionMoveDrag::remove_region_from_playlist (
1644 boost::shared_ptr<Region> region,
1645 boost::shared_ptr<Playlist> playlist,
1646 PlaylistSet& modified_playlists
1649 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1652 playlist->clear_changes ();
1655 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1659 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1660 * clearing the playlist's diff history first if necessary.
1661 * @param region Region to insert.
1662 * @param dest_rtv Destination RouteTimeAxisView.
1663 * @param dest_layer Destination layer.
1664 * @param where Destination position.
1665 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1666 * that clear_changes () is only called once per playlist.
1667 * @return New RegionView, or 0 if no insert was performed.
1670 RegionMoveDrag::insert_region_into_playlist (
1671 boost::shared_ptr<Region> region,
1672 RouteTimeAxisView* dest_rtv,
1675 PlaylistSet& modified_playlists
1678 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1679 if (!dest_playlist) {
1683 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1684 _new_region_view = 0;
1685 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1687 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1688 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1690 dest_playlist->clear_changes ();
1693 dest_playlist->add_region (region, where);
1695 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1696 dest_playlist->set_layer (region, dest_layer);
1701 assert (_new_region_view);
1703 return _new_region_view;
1707 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1709 _new_region_view = rv;
1713 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1715 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1716 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1718 _editor->session()->add_command (c);
1727 RegionMoveDrag::aborted (bool movement_occurred)
1731 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1732 list<DraggingView>::const_iterator next = i;
1741 RegionMotionDrag::aborted (movement_occurred);
1746 RegionMotionDrag::aborted (bool)
1748 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1750 StreamView* sview = (*i)->view();
1753 if (sview->layer_display() == Expanded) {
1754 sview->set_layer_display (Stacked);
1759 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1760 RegionView* rv = i->view;
1761 TimeAxisView* tv = &(rv->get_time_axis_view ());
1762 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1764 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1765 rv->get_canvas_group()->set_y_position (0);
1767 rv->move (-_total_x_delta, 0);
1768 rv->set_height (rtv->view()->child_height ());
1772 /** @param b true to brush, otherwise false.
1773 * @param c true to make copies of the regions being moved, otherwise false.
1775 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1776 : RegionMotionDrag (e, i, p, v, b)
1779 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1782 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1783 if (rtv && rtv->is_track()) {
1784 speed = rtv->track()->speed ();
1787 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1791 RegionMoveDrag::setup_pointer_frame_offset ()
1793 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1796 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1797 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1799 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1801 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1802 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1804 _primary = v->view()->create_region_view (r, false, false);
1806 _primary->get_canvas_group()->show ();
1807 _primary->set_position (pos, 0);
1808 _views.push_back (DraggingView (_primary, this, v));
1810 _last_frame_position = pos;
1812 _item = _primary->get_canvas_group ();
1816 RegionInsertDrag::finished (GdkEvent *, bool)
1818 int pos = _views.front().time_axis_view;
1819 assert(pos >= 0 && pos < _time_axis_views.size());
1821 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1823 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1824 _primary->get_canvas_group()->set_y_position (0);
1826 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1828 _editor->begin_reversible_command (Operations::insert_region);
1829 playlist->clear_changes ();
1830 playlist->add_region (_primary->region (), _last_frame_position);
1832 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1833 if (Config->get_edit_mode() == Ripple) {
1834 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1837 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1838 _editor->commit_reversible_command ();
1846 RegionInsertDrag::aborted (bool)
1853 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1854 : RegionMoveDrag (e, i, p, v, false, false)
1856 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1859 struct RegionSelectionByPosition {
1860 bool operator() (RegionView*a, RegionView* b) {
1861 return a->region()->position () < b->region()->position();
1866 RegionSpliceDrag::motion (GdkEvent* event, bool)
1868 /* Which trackview is this ? */
1870 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1871 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1873 /* The region motion is only processed if the pointer is over
1877 if (!tv || !tv->is_track()) {
1878 /* To make sure we hide the verbose canvas cursor when the mouse is
1879 not held over an audio track.
1881 _editor->verbose_cursor()->hide ();
1884 _editor->verbose_cursor()->show ();
1889 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1895 RegionSelection copy;
1896 _editor->selection->regions.by_position(copy);
1898 framepos_t const pf = adjusted_current_frame (event);
1900 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1902 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1908 boost::shared_ptr<Playlist> playlist;
1910 if ((playlist = atv->playlist()) == 0) {
1914 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1919 if (pf < (*i)->region()->last_frame() + 1) {
1923 if (pf > (*i)->region()->first_frame()) {
1929 playlist->shuffle ((*i)->region(), dir);
1934 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1936 RegionMoveDrag::finished (event, movement_occurred);
1940 RegionSpliceDrag::aborted (bool)
1950 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1953 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1955 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1956 RegionSelection to_ripple;
1957 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1958 if ((*i)->position() >= where) {
1959 to_ripple.push_back (rtv->view()->find_view(*i));
1963 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1964 if (!exclude.contains (*i)) {
1965 // the selection has already been added to _views
1967 if (drag_in_progress) {
1968 // do the same things that RegionMotionDrag::motion does when
1969 // first_move is true, for the region views that we're adding
1970 // to _views this time
1973 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1974 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1975 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1976 rvg->reparent (_editor->_drag_motion_group);
1978 // we only need to move in the y direction
1979 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1984 _views.push_back (DraggingView (*i, this, tav));
1990 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1993 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1994 // we added all the regions after the selection
1996 std::list<DraggingView>::iterator to_erase = i++;
1997 if (!_editor->selection->regions.contains (to_erase->view)) {
1998 // restore the non-selected regions to their original playlist & positions,
1999 // and then ripple them back by the length of the regions that were dragged away
2000 // do the same things as RegionMotionDrag::aborted
2002 RegionView *rv = to_erase->view;
2003 TimeAxisView* tv = &(rv->get_time_axis_view ());
2004 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2007 // plonk them back onto their own track
2008 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2009 rv->get_canvas_group()->set_y_position (0);
2013 // move the underlying region to match the view
2014 rv->region()->set_position (rv->region()->position() + amount);
2016 // restore the view to match the underlying region's original position
2017 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2020 rv->set_height (rtv->view()->child_height ());
2021 _views.erase (to_erase);
2027 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2029 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2031 return allow_moves_across_tracks;
2039 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2040 : RegionMoveDrag (e, i, p, v, false, false)
2042 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2043 // compute length of selection
2044 RegionSelection selected_regions = _editor->selection->regions;
2045 selection_length = selected_regions.end_frame() - selected_regions.start();
2047 // we'll only allow dragging to another track in ripple mode if all the regions
2048 // being dragged start off on the same track
2049 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2052 exclude = new RegionList;
2053 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2054 exclude->push_back((*i)->region());
2057 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2058 RegionSelection copy;
2059 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2061 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2062 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2064 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2065 // find ripple start point on each applicable playlist
2066 RegionView *first_selected_on_this_track = NULL;
2067 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2068 if ((*i)->region()->playlist() == (*pi)) {
2069 // region is on this playlist - it's the first, because they're sorted
2070 first_selected_on_this_track = *i;
2074 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2075 add_all_after_to_views (
2076 &first_selected_on_this_track->get_time_axis_view(),
2077 first_selected_on_this_track->region()->position(),
2078 selected_regions, false);
2081 if (allow_moves_across_tracks) {
2082 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2090 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2092 /* Which trackview is this ? */
2094 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2095 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2097 /* The region motion is only processed if the pointer is over
2101 if (!tv || !tv->is_track()) {
2102 /* To make sure we hide the verbose canvas cursor when the mouse is
2103 not held over an audiotrack.
2105 _editor->verbose_cursor()->hide ();
2109 framepos_t where = adjusted_current_frame (event);
2110 assert (where >= 0);
2112 double delta = compute_x_delta (event, &after);
2114 framecnt_t amount = _editor->pixel_to_sample (delta);
2116 if (allow_moves_across_tracks) {
2117 // all the originally selected regions were on the same track
2119 framecnt_t adjust = 0;
2120 if (prev_tav && tv != prev_tav) {
2121 // dragged onto a different track
2122 // remove the unselected regions from _views, restore them to their original positions
2123 // and add the regions after the drop point on the new playlist to _views instead.
2124 // undo the effect of rippling the previous playlist, and include the effect of removing
2125 // the dragged region(s) from this track
2127 remove_unselected_from_views (prev_amount, false);
2128 // ripple previous playlist according to the regions that have been removed onto the new playlist
2129 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2132 // move just the selected regions
2133 RegionMoveDrag::motion(event, first_move);
2135 // ensure that the ripple operation on the new playlist inserts selection_length time
2136 adjust = selection_length;
2137 // ripple the new current playlist
2138 tv->playlist()->ripple (where, amount+adjust, exclude);
2140 // add regions after point where drag entered this track to subsequent ripples
2141 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2144 // motion on same track
2145 RegionMoveDrag::motion(event, first_move);
2149 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2150 prev_position = where;
2152 // selection encompasses multiple tracks - just drag
2153 // cross-track drags are forbidden
2154 RegionMoveDrag::motion(event, first_move);
2157 if (!_x_constrained) {
2158 prev_amount += amount;
2161 _last_frame_position = after;
2165 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2167 if (!movement_occurred) {
2171 if (was_double_click() && !_views.empty()) {
2172 DraggingView dv = _views.front();
2173 dv.view->show_region_editor ();
2180 _editor->begin_reversible_command(_("Ripple drag"));
2182 // remove the regions being rippled from the dragging view, updating them to
2183 // their new positions
2184 remove_unselected_from_views (prev_amount, true);
2186 if (allow_moves_across_tracks) {
2188 // if regions were dragged across tracks, we've rippled any later
2189 // regions on the track the regions were dragged off, so we need
2190 // to add the original track to the undo record
2191 orig_tav->playlist()->clear_changes();
2192 vector<Command*> cmds;
2193 orig_tav->playlist()->rdiff (cmds);
2194 _editor->session()->add_commands (cmds);
2196 if (prev_tav && prev_tav != orig_tav) {
2197 prev_tav->playlist()->clear_changes();
2198 vector<Command*> cmds;
2199 prev_tav->playlist()->rdiff (cmds);
2200 _editor->session()->add_commands (cmds);
2203 // selection spanned multiple tracks - all will need adding to undo record
2205 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2206 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2208 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2209 (*pi)->clear_changes();
2210 vector<Command*> cmds;
2211 (*pi)->rdiff (cmds);
2212 _editor->session()->add_commands (cmds);
2216 // other modified playlists are added to undo by RegionMoveDrag::finished()
2217 RegionMoveDrag::finished (event, movement_occurred);
2218 _editor->commit_reversible_command();
2222 RegionRippleDrag::aborted (bool movement_occurred)
2224 RegionMoveDrag::aborted (movement_occurred);
2229 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2231 _view (dynamic_cast<MidiTimeAxisView*> (v))
2233 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2239 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2242 _region = add_midi_region (_view);
2243 _view->playlist()->freeze ();
2246 framepos_t const f = adjusted_current_frame (event);
2247 if (f < grab_frame()) {
2248 _region->set_position (f);
2251 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2252 so that if this region is duplicated, its duplicate starts on
2253 a snap point rather than 1 frame after a snap point. Otherwise things get
2254 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2255 place snapped notes at the start of the region.
2258 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2259 _region->set_length (len < 1 ? 1 : len);
2265 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2267 if (!movement_occurred) {
2268 add_midi_region (_view);
2270 _view->playlist()->thaw ();
2275 RegionCreateDrag::aborted (bool)
2278 _view->playlist()->thaw ();
2284 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2288 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2292 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2294 Gdk::Cursor* cursor;
2295 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2297 float x_fraction = cnote->mouse_x_fraction ();
2299 if (x_fraction > 0.0 && x_fraction < 0.25) {
2300 cursor = _editor->cursors()->left_side_trim;
2303 cursor = _editor->cursors()->right_side_trim;
2307 Drag::start_grab (event, cursor);
2309 region = &cnote->region_view();
2313 if (event->motion.state & Keyboard::PrimaryModifier) {
2319 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2321 if (ms.size() > 1) {
2322 /* has to be relative, may make no sense otherwise */
2326 /* select this note; if it is already selected, preserve the existing selection,
2327 otherwise make this note the only one selected.
2329 region->note_selected (cnote, cnote->selected ());
2331 _editor->begin_reversible_command (_("resize notes"));
2333 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2334 MidiRegionSelection::iterator next;
2337 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2339 mrv->begin_resizing (at_front);
2346 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2348 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2349 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2350 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2352 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2354 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2360 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2362 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2363 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2364 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2366 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2368 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2372 _editor->commit_reversible_command ();
2376 NoteResizeDrag::aborted (bool)
2378 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2379 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2380 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2382 mrv->abort_resizing ();
2387 AVDraggingView::AVDraggingView (RegionView* v)
2390 initial_position = v->region()->position ();
2393 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2396 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2399 TrackViewList empty;
2401 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2402 std::list<RegionView*> views = rs.by_layer();
2404 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2405 RegionView* rv = (*i);
2406 if (!rv->region()->video_locked()) {
2409 _views.push_back (AVDraggingView (rv));
2414 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2416 Drag::start_grab (event);
2417 if (_editor->session() == 0) {
2421 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2422 _max_backwards_drag = (
2423 ARDOUR_UI::instance()->video_timeline->get_duration()
2424 + ARDOUR_UI::instance()->video_timeline->get_offset()
2425 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2428 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2429 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2430 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2433 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2436 Timecode::Time timecode;
2437 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2438 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);
2439 show_verbose_cursor_text (buf);
2443 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2445 if (_editor->session() == 0) {
2448 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2452 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2453 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2455 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2456 dt = - _max_backwards_drag;
2459 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2460 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2462 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2463 RegionView* rv = i->view;
2464 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2467 rv->region()->clear_changes ();
2468 rv->region()->suspend_property_changes();
2470 rv->region()->set_position(i->initial_position + dt);
2471 rv->region_changed(ARDOUR::Properties::position);
2474 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2475 Timecode::Time timecode;
2476 Timecode::Time timediff;
2478 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2479 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2480 snprintf (buf, sizeof (buf),
2481 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2482 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2483 , _("Video Start:"),
2484 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2486 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2488 show_verbose_cursor_text (buf);
2492 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2494 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2498 if (!movement_occurred || ! _editor->session()) {
2502 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2504 _editor->begin_reversible_command (_("Move Video"));
2506 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2507 ARDOUR_UI::instance()->video_timeline->save_undo();
2508 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2509 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2511 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2512 i->view->drag_end();
2513 i->view->region()->resume_property_changes ();
2515 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2518 _editor->session()->maybe_update_session_range(
2519 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2520 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2524 _editor->commit_reversible_command ();
2528 VideoTimeLineDrag::aborted (bool)
2530 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2533 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2534 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2536 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2537 i->view->region()->resume_property_changes ();
2538 i->view->region()->set_position(i->initial_position);
2542 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2543 : RegionDrag (e, i, p, v)
2544 , _preserve_fade_anchor (preserve_fade_anchor)
2545 , _jump_position_when_done (false)
2547 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2551 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2554 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2555 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2557 if (tv && tv->is_track()) {
2558 speed = tv->track()->speed();
2561 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2562 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2563 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2565 framepos_t const pf = adjusted_current_frame (event);
2567 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2568 /* Move the contents of the region around without changing the region bounds */
2569 _operation = ContentsTrim;
2570 Drag::start_grab (event, _editor->cursors()->trimmer);
2572 /* These will get overridden for a point trim.*/
2573 if (pf < (region_start + region_length/2)) {
2574 /* closer to front */
2575 _operation = StartTrim;
2577 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2578 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2580 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2584 _operation = EndTrim;
2585 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2586 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2588 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2593 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2594 _jump_position_when_done = true;
2597 switch (_operation) {
2599 show_verbose_cursor_time (region_start);
2600 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2601 i->view->trim_front_starting ();
2605 show_verbose_cursor_time (region_end);
2608 show_verbose_cursor_time (pf);
2612 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2613 i->view->region()->suspend_property_changes ();
2618 TrimDrag::motion (GdkEvent* event, bool first_move)
2620 RegionView* rv = _primary;
2623 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2624 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2625 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2626 frameoffset_t frame_delta = 0;
2628 if (tv && tv->is_track()) {
2629 speed = tv->track()->speed();
2632 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2638 switch (_operation) {
2640 trim_type = "Region start trim";
2643 trim_type = "Region end trim";
2646 trim_type = "Region content trim";
2653 _editor->begin_reversible_command (trim_type);
2655 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2656 RegionView* rv = i->view;
2657 rv->enable_display (false);
2658 rv->region()->playlist()->clear_owned_changes ();
2660 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2663 arv->temporarily_hide_envelope ();
2667 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2668 insert_result = _editor->motion_frozen_playlists.insert (pl);
2670 if (insert_result.second) {
2676 bool non_overlap_trim = false;
2678 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2679 non_overlap_trim = true;
2682 /* contstrain trim to fade length */
2683 if (_preserve_fade_anchor) {
2684 switch (_operation) {
2686 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2687 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2689 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2690 if (ar->locked()) continue;
2691 framecnt_t len = ar->fade_in()->back()->when;
2692 if (len < dt) dt = min(dt, len);
2696 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2697 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2699 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2700 if (ar->locked()) continue;
2701 framecnt_t len = ar->fade_out()->back()->when;
2702 if (len < -dt) dt = max(dt, -len);
2711 switch (_operation) {
2713 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2714 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2715 if (changed && _preserve_fade_anchor) {
2716 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2718 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2719 framecnt_t len = ar->fade_in()->back()->when;
2720 framecnt_t diff = ar->first_frame() - i->initial_position;
2721 framepos_t new_length = len - diff;
2722 i->anchored_fade_length = min (ar->length(), new_length);
2723 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2724 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2731 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2732 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2733 if (changed && _preserve_fade_anchor) {
2734 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2736 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2737 framecnt_t len = ar->fade_out()->back()->when;
2738 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2739 framepos_t new_length = len + diff;
2740 i->anchored_fade_length = min (ar->length(), new_length);
2741 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2742 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2750 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2752 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2753 i->view->move_contents (frame_delta);
2759 switch (_operation) {
2761 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2764 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2767 // show_verbose_cursor_time (frame_delta);
2774 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2776 if (movement_occurred) {
2777 motion (event, false);
2779 if (_operation == StartTrim) {
2780 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2782 /* This must happen before the region's StatefulDiffCommand is created, as it may
2783 `correct' (ahem) the region's _start from being negative to being zero. It
2784 needs to be zero in the undo record.
2786 i->view->trim_front_ending ();
2788 if (_preserve_fade_anchor) {
2789 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2791 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2792 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2793 ar->set_fade_in_length(i->anchored_fade_length);
2794 ar->set_fade_in_active(true);
2797 if (_jump_position_when_done) {
2798 i->view->region()->set_position (i->initial_position);
2801 } else if (_operation == EndTrim) {
2802 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2803 if (_preserve_fade_anchor) {
2804 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2806 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2807 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2808 ar->set_fade_out_length(i->anchored_fade_length);
2809 ar->set_fade_out_active(true);
2812 if (_jump_position_when_done) {
2813 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2818 if (!_views.empty()) {
2819 if (_operation == StartTrim) {
2820 _editor->maybe_locate_with_edit_preroll(
2821 _views.begin()->view->region()->position());
2823 if (_operation == EndTrim) {
2824 _editor->maybe_locate_with_edit_preroll(
2825 _views.begin()->view->region()->position() +
2826 _views.begin()->view->region()->length());
2830 if (!_editor->selection->selected (_primary)) {
2831 _primary->thaw_after_trim ();
2834 set<boost::shared_ptr<Playlist> > diffed_playlists;
2836 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2837 i->view->thaw_after_trim ();
2838 i->view->enable_display (true);
2840 /* Trimming one region may affect others on the playlist, so we need
2841 to get undo Commands from the whole playlist rather than just the
2842 region. Use diffed_playlists to make sure we don't diff a given
2843 playlist more than once.
2845 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2846 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2847 vector<Command*> cmds;
2849 _editor->session()->add_commands (cmds);
2850 diffed_playlists.insert (p);
2855 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2859 _editor->motion_frozen_playlists.clear ();
2860 _editor->commit_reversible_command();
2863 /* no mouse movement */
2864 _editor->point_trim (event, adjusted_current_frame (event));
2867 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2868 if (_operation == StartTrim) {
2869 i->view->trim_front_ending ();
2872 i->view->region()->resume_property_changes ();
2877 TrimDrag::aborted (bool movement_occurred)
2879 /* Our motion method is changing model state, so use the Undo system
2880 to cancel. Perhaps not ideal, as this will leave an Undo point
2881 behind which may be slightly odd from the user's point of view.
2886 if (movement_occurred) {
2890 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2891 i->view->region()->resume_property_changes ();
2896 TrimDrag::setup_pointer_frame_offset ()
2898 list<DraggingView>::iterator i = _views.begin ();
2899 while (i != _views.end() && i->view != _primary) {
2903 if (i == _views.end()) {
2907 switch (_operation) {
2909 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2912 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2919 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2923 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2924 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2929 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2931 Drag::start_grab (event, cursor);
2932 show_verbose_cursor_time (adjusted_current_frame(event));
2936 MeterMarkerDrag::setup_pointer_frame_offset ()
2938 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2942 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2944 if (!_marker->meter().movable()) {
2950 // create a dummy marker for visual representation of moving the
2951 // section, because whether its a copy or not, we're going to
2952 // leave or lose the original marker (leave if its a copy; lose if its
2953 // not, because we'll remove it from the map).
2955 MeterSection section (_marker->meter());
2957 if (!section.movable()) {
2962 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2964 _marker = new MeterMarker (
2966 *_editor->meter_group,
2967 ARDOUR_UI::config()->color ("meter marker"),
2969 *new MeterSection (_marker->meter())
2972 /* use the new marker for the grab */
2973 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2976 TempoMap& map (_editor->session()->tempo_map());
2977 /* get current state */
2978 before_state = &map.get_state();
2979 /* remove the section while we drag it */
2980 map.remove_meter (section, true);
2984 framepos_t const pf = adjusted_current_frame (event);
2986 _marker->set_position (pf);
2987 show_verbose_cursor_time (pf);
2991 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2993 if (!movement_occurred) {
2994 if (was_double_click()) {
2995 _editor->edit_meter_marker (*_marker);
3000 if (!_marker->meter().movable()) {
3004 motion (event, false);
3006 Timecode::BBT_Time when;
3008 TempoMap& map (_editor->session()->tempo_map());
3009 map.bbt_time (last_pointer_frame(), when);
3011 if (_copy == true) {
3012 _editor->begin_reversible_command (_("copy meter mark"));
3013 XMLNode &before = map.get_state();
3014 map.add_meter (_marker->meter(), when);
3015 XMLNode &after = map.get_state();
3016 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3017 _editor->commit_reversible_command ();
3020 _editor->begin_reversible_command (_("move meter mark"));
3022 /* we removed it before, so add it back now */
3024 map.add_meter (_marker->meter(), when);
3025 XMLNode &after = map.get_state();
3026 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3027 _editor->commit_reversible_command ();
3030 // delete the dummy marker we used for visual representation while moving.
3031 // a new visual marker will show up automatically.
3036 MeterMarkerDrag::aborted (bool moved)
3038 _marker->set_position (_marker->meter().frame ());
3041 TempoMap& map (_editor->session()->tempo_map());
3042 /* we removed it before, so add it back now */
3043 map.add_meter (_marker->meter(), _marker->meter().frame());
3044 // delete the dummy marker we used for visual representation while moving.
3045 // a new visual marker will show up automatically.
3050 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3054 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3056 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3061 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3063 Drag::start_grab (event, cursor);
3064 show_verbose_cursor_time (adjusted_current_frame (event));
3068 TempoMarkerDrag::setup_pointer_frame_offset ()
3070 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3074 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3076 if (!_marker->tempo().movable()) {
3082 // create a dummy marker for visual representation of moving the
3083 // section, because whether its a copy or not, we're going to
3084 // leave or lose the original marker (leave if its a copy; lose if its
3085 // not, because we'll remove it from the map).
3087 // create a dummy marker for visual representation of moving the copy.
3088 // The actual copying is not done before we reach the finish callback.
3091 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3093 TempoSection section (_marker->tempo());
3095 _marker = new TempoMarker (
3097 *_editor->tempo_group,
3098 ARDOUR_UI::config()->color ("tempo marker"),
3100 *new TempoSection (_marker->tempo())
3103 /* use the new marker for the grab */
3104 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3107 TempoMap& map (_editor->session()->tempo_map());
3108 /* get current state */
3109 before_state = &map.get_state();
3110 /* remove the section while we drag it */
3111 map.remove_tempo (section, true);
3115 framepos_t const pf = adjusted_current_frame (event);
3116 _marker->set_position (pf);
3117 show_verbose_cursor_time (pf);
3121 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3123 if (!movement_occurred) {
3124 if (was_double_click()) {
3125 _editor->edit_tempo_marker (*_marker);
3130 if (!_marker->tempo().movable()) {
3134 motion (event, false);
3136 TempoMap& map (_editor->session()->tempo_map());
3137 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3138 Timecode::BBT_Time when;
3140 map.bbt_time (beat_time, when);
3142 if (_copy == true) {
3143 _editor->begin_reversible_command (_("copy tempo mark"));
3144 XMLNode &before = map.get_state();
3145 map.add_tempo (_marker->tempo(), when);
3146 XMLNode &after = map.get_state();
3147 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3148 _editor->commit_reversible_command ();
3151 _editor->begin_reversible_command (_("move tempo mark"));
3152 /* we removed it before, so add it back now */
3153 map.add_tempo (_marker->tempo(), when);
3154 XMLNode &after = map.get_state();
3155 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3156 _editor->commit_reversible_command ();
3159 // delete the dummy marker we used for visual representation while moving.
3160 // a new visual marker will show up automatically.
3165 TempoMarkerDrag::aborted (bool moved)
3167 _marker->set_position (_marker->tempo().frame());
3169 TempoMap& map (_editor->session()->tempo_map());
3170 /* we removed it before, so add it back now */
3171 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3172 // delete the dummy marker we used for visual representation while moving.
3173 // a new visual marker will show up automatically.
3178 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3179 : Drag (e, &c.track_canvas_item(), false)
3183 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3186 /** Do all the things we do when dragging the playhead to make it look as though
3187 * we have located, without actually doing the locate (because that would cause
3188 * the diskstream buffers to be refilled, which is too slow).
3191 CursorDrag::fake_locate (framepos_t t)
3193 _editor->playhead_cursor->set_position (t);
3195 Session* s = _editor->session ();
3196 if (s->timecode_transmission_suspended ()) {
3197 framepos_t const f = _editor->playhead_cursor->current_frame ();
3198 /* This is asynchronous so it will be sent "now"
3200 s->send_mmc_locate (f);
3201 /* These are synchronous and will be sent during the next
3204 s->queue_full_time_code ();
3205 s->queue_song_position_pointer ();
3208 show_verbose_cursor_time (t);
3209 _editor->UpdateAllTransportClocks (t);
3213 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3215 Drag::start_grab (event, c);
3217 _grab_zoom = _editor->samples_per_pixel;
3219 framepos_t where = _editor->canvas_event_sample (event);
3221 _editor->snap_to_with_modifier (where, event);
3223 _editor->_dragging_playhead = true;
3225 Session* s = _editor->session ();
3227 /* grab the track canvas item as well */
3229 _cursor.track_canvas_item().grab();
3232 if (_was_rolling && _stop) {
3236 if (s->is_auditioning()) {
3237 s->cancel_audition ();
3241 if (AudioEngine::instance()->connected()) {
3243 /* do this only if we're the engine is connected
3244 * because otherwise this request will never be
3245 * serviced and we'll busy wait forever. likewise,
3246 * notice if we are disconnected while waiting for the
3247 * request to be serviced.
3250 s->request_suspend_timecode_transmission ();
3251 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3252 /* twiddle our thumbs */
3257 fake_locate (where);
3261 CursorDrag::motion (GdkEvent* event, bool)
3263 framepos_t const adjusted_frame = adjusted_current_frame (event);
3264 if (adjusted_frame != last_pointer_frame()) {
3265 fake_locate (adjusted_frame);
3270 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3272 _editor->_dragging_playhead = false;
3274 _cursor.track_canvas_item().ungrab();
3276 if (!movement_occurred && _stop) {
3280 motion (event, false);
3282 Session* s = _editor->session ();
3284 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3285 _editor->_pending_locate_request = true;
3286 s->request_resume_timecode_transmission ();
3291 CursorDrag::aborted (bool)
3293 _cursor.track_canvas_item().ungrab();
3295 if (_editor->_dragging_playhead) {
3296 _editor->session()->request_resume_timecode_transmission ();
3297 _editor->_dragging_playhead = false;
3300 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3303 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3304 : RegionDrag (e, i, p, v)
3306 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3310 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3312 Drag::start_grab (event, cursor);
3314 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3315 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3317 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3321 FadeInDrag::setup_pointer_frame_offset ()
3323 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3324 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3325 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3329 FadeInDrag::motion (GdkEvent* event, bool)
3331 framecnt_t fade_length;
3332 framepos_t const pos = adjusted_current_frame (event);
3333 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3335 if (pos < (region->position() + 64)) {
3336 fade_length = 64; // this should be a minimum defined somewhere
3337 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3338 fade_length = region->length() - region->fade_out()->back()->when - 1;
3340 fade_length = pos - region->position();
3343 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3345 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3351 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3354 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3358 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3360 if (!movement_occurred) {
3364 framecnt_t fade_length;
3366 framepos_t const pos = adjusted_current_frame (event);
3368 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3370 if (pos < (region->position() + 64)) {
3371 fade_length = 64; // this should be a minimum defined somewhere
3372 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3373 fade_length = region->length() - region->fade_out()->back()->when - 1;
3375 fade_length = pos - region->position();
3378 _editor->begin_reversible_command (_("change fade in length"));
3380 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3382 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3388 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3389 XMLNode &before = alist->get_state();
3391 tmp->audio_region()->set_fade_in_length (fade_length);
3392 tmp->audio_region()->set_fade_in_active (true);
3394 XMLNode &after = alist->get_state();
3395 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3398 _editor->commit_reversible_command ();
3402 FadeInDrag::aborted (bool)
3404 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3405 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3411 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3415 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3416 : RegionDrag (e, i, p, v)
3418 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3422 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3424 Drag::start_grab (event, cursor);
3426 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3427 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3429 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3433 FadeOutDrag::setup_pointer_frame_offset ()
3435 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3436 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3437 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3441 FadeOutDrag::motion (GdkEvent* event, bool)
3443 framecnt_t fade_length;
3445 framepos_t const pos = adjusted_current_frame (event);
3447 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3449 if (pos > (region->last_frame() - 64)) {
3450 fade_length = 64; // this should really be a minimum fade defined somewhere
3451 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3452 fade_length = region->length() - region->fade_in()->back()->when - 1;
3454 fade_length = region->last_frame() - pos;
3457 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3459 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3465 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3468 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3472 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3474 if (!movement_occurred) {
3478 framecnt_t fade_length;
3480 framepos_t const pos = adjusted_current_frame (event);
3482 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3484 if (pos > (region->last_frame() - 64)) {
3485 fade_length = 64; // this should really be a minimum fade defined somewhere
3486 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3487 fade_length = region->length() - region->fade_in()->back()->when - 1;
3489 fade_length = region->last_frame() - pos;
3492 _editor->begin_reversible_command (_("change fade out length"));
3494 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3496 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3502 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3503 XMLNode &before = alist->get_state();
3505 tmp->audio_region()->set_fade_out_length (fade_length);
3506 tmp->audio_region()->set_fade_out_active (true);
3508 XMLNode &after = alist->get_state();
3509 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3512 _editor->commit_reversible_command ();
3516 FadeOutDrag::aborted (bool)
3518 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3519 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3525 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3529 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3532 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3534 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3537 _points.push_back (ArdourCanvas::Duple (0, 0));
3538 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3541 MarkerDrag::~MarkerDrag ()
3543 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3548 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3550 location = new Location (*l);
3551 markers.push_back (m);
3556 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3558 Drag::start_grab (event, cursor);
3562 Location *location = _editor->find_location_from_marker (_marker, is_start);
3563 _editor->_dragging_edit_point = true;
3565 update_item (location);
3567 // _drag_line->show();
3568 // _line->raise_to_top();
3571 show_verbose_cursor_time (location->start());
3573 show_verbose_cursor_time (location->end());
3576 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3579 case Selection::Toggle:
3580 /* we toggle on the button release */
3582 case Selection::Set:
3583 if (!_editor->selection->selected (_marker)) {
3584 _editor->selection->set (_marker);
3587 case Selection::Extend:
3589 Locations::LocationList ll;
3590 list<Marker*> to_add;
3592 _editor->selection->markers.range (s, e);
3593 s = min (_marker->position(), s);
3594 e = max (_marker->position(), e);
3597 if (e < max_framepos) {
3600 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3601 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3602 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3605 to_add.push_back (lm->start);
3608 to_add.push_back (lm->end);
3612 if (!to_add.empty()) {
3613 _editor->selection->add (to_add);
3617 case Selection::Add:
3618 _editor->selection->add (_marker);
3622 /* Set up copies for us to manipulate during the drag
3625 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3627 Location* l = _editor->find_location_from_marker (*i, is_start);
3634 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3636 /* range: check that the other end of the range isn't
3639 CopiedLocationInfo::iterator x;
3640 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3641 if (*(*x).location == *l) {
3645 if (x == _copied_locations.end()) {
3646 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3648 (*x).markers.push_back (*i);
3649 (*x).move_both = true;
3657 MarkerDrag::setup_pointer_frame_offset ()
3660 Location *location = _editor->find_location_from_marker (_marker, is_start);
3661 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3665 MarkerDrag::motion (GdkEvent* event, bool)
3667 framecnt_t f_delta = 0;
3669 bool move_both = false;
3670 Location *real_location;
3671 Location *copy_location = 0;
3673 framepos_t const newframe = adjusted_current_frame (event);
3674 framepos_t next = newframe;
3676 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3680 CopiedLocationInfo::iterator x;
3682 /* find the marker we're dragging, and compute the delta */
3684 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3686 copy_location = (*x).location;
3688 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3690 /* this marker is represented by this
3691 * CopiedLocationMarkerInfo
3694 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3699 if (real_location->is_mark()) {
3700 f_delta = newframe - copy_location->start();
3704 switch (_marker->type()) {
3705 case Marker::SessionStart:
3706 case Marker::RangeStart:
3707 case Marker::LoopStart:
3708 case Marker::PunchIn:
3709 f_delta = newframe - copy_location->start();
3712 case Marker::SessionEnd:
3713 case Marker::RangeEnd:
3714 case Marker::LoopEnd:
3715 case Marker::PunchOut:
3716 f_delta = newframe - copy_location->end();
3719 /* what kind of marker is this ? */
3728 if (x == _copied_locations.end()) {
3729 /* hmm, impossible - we didn't find the dragged marker */
3733 /* now move them all */
3735 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3737 copy_location = x->location;
3739 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3743 if (real_location->locked()) {
3747 if (copy_location->is_mark()) {
3751 copy_location->set_start (copy_location->start() + f_delta);
3755 framepos_t new_start = copy_location->start() + f_delta;
3756 framepos_t new_end = copy_location->end() + f_delta;
3758 if (is_start) { // start-of-range marker
3760 if (move_both || (*x).move_both) {
3761 copy_location->set_start (new_start);
3762 copy_location->set_end (new_end);
3763 } else if (new_start < copy_location->end()) {
3764 copy_location->set_start (new_start);
3765 } else if (newframe > 0) {
3766 _editor->snap_to (next, RoundUpAlways, true);
3767 copy_location->set_end (next);
3768 copy_location->set_start (newframe);
3771 } else { // end marker
3773 if (move_both || (*x).move_both) {
3774 copy_location->set_end (new_end);
3775 copy_location->set_start (new_start);
3776 } else if (new_end > copy_location->start()) {
3777 copy_location->set_end (new_end);
3778 } else if (newframe > 0) {
3779 _editor->snap_to (next, RoundDownAlways, true);
3780 copy_location->set_start (next);
3781 copy_location->set_end (newframe);
3786 update_item (copy_location);
3788 /* now lookup the actual GUI items used to display this
3789 * location and move them to wherever the copy of the location
3790 * is now. This means that the logic in ARDOUR::Location is
3791 * still enforced, even though we are not (yet) modifying
3792 * the real Location itself.
3795 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3798 lm->set_position (copy_location->start(), copy_location->end());
3803 assert (!_copied_locations.empty());
3805 show_verbose_cursor_time (newframe);
3809 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3811 if (!movement_occurred) {
3813 if (was_double_click()) {
3814 _editor->rename_marker (_marker);
3818 /* just a click, do nothing but finish
3819 off the selection process
3822 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3825 case Selection::Set:
3826 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3827 _editor->selection->set (_marker);
3831 case Selection::Toggle:
3832 /* we toggle on the button release, click only */
3833 _editor->selection->toggle (_marker);
3836 case Selection::Extend:
3837 case Selection::Add:
3844 _editor->_dragging_edit_point = false;
3846 _editor->begin_reversible_command ( _("move marker") );
3847 XMLNode &before = _editor->session()->locations()->get_state();
3849 MarkerSelection::iterator i;
3850 CopiedLocationInfo::iterator x;
3853 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3854 x != _copied_locations.end() && i != _editor->selection->markers.end();
3857 Location * location = _editor->find_location_from_marker (*i, is_start);
3861 if (location->locked()) {
3865 if (location->is_mark()) {
3866 location->set_start (((*x).location)->start());
3868 location->set (((*x).location)->start(), ((*x).location)->end());
3873 XMLNode &after = _editor->session()->locations()->get_state();
3874 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3875 _editor->commit_reversible_command ();
3879 MarkerDrag::aborted (bool movement_occured)
3881 if (!movement_occured) {
3885 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3887 /* move all markers to their original location */
3890 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3893 Location * location = _editor->find_location_from_marker (*m, is_start);
3896 (*m)->set_position (is_start ? location->start() : location->end());
3903 MarkerDrag::update_item (Location*)
3908 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3910 _cumulative_x_drag (0),
3911 _cumulative_y_drag (0)
3913 if (_zero_gain_fraction < 0.0) {
3914 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3917 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3919 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3925 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3927 Drag::start_grab (event, _editor->cursors()->fader);
3929 // start the grab at the center of the control point so
3930 // the point doesn't 'jump' to the mouse after the first drag
3931 _fixed_grab_x = _point->get_x();
3932 _fixed_grab_y = _point->get_y();
3934 float const fraction = 1 - (_point->get_y() / _point->line().height());
3936 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3938 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3940 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3942 if (!_point->can_slide ()) {
3943 _x_constrained = true;
3948 ControlPointDrag::motion (GdkEvent* event, bool)
3950 double dx = _drags->current_pointer_x() - last_pointer_x();
3951 double dy = current_pointer_y() - last_pointer_y();
3953 if (event->button.state & Keyboard::SecondaryModifier) {
3958 /* coordinate in pixels relative to the start of the region (for region-based automation)
3959 or track (for track-based automation) */
3960 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3961 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3963 // calculate zero crossing point. back off by .01 to stay on the
3964 // positive side of zero
3965 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3967 // make sure we hit zero when passing through
3968 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3972 if (_x_constrained) {
3975 if (_y_constrained) {
3979 _cumulative_x_drag = cx - _fixed_grab_x;
3980 _cumulative_y_drag = cy - _fixed_grab_y;
3984 cy = min ((double) _point->line().height(), cy);
3986 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3988 if (!_x_constrained) {
3989 _editor->snap_to_with_modifier (cx_frames, event);
3992 cx_frames = min (cx_frames, _point->line().maximum_time());
3994 float const fraction = 1.0 - (cy / _point->line().height());
3996 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3998 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4002 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4004 if (!movement_occurred) {
4008 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4009 _editor->reset_point_selection ();
4013 motion (event, false);
4016 _point->line().end_drag (_pushing, _final_index);
4017 _editor->commit_reversible_command ();
4021 ControlPointDrag::aborted (bool)
4023 _point->line().reset ();
4027 ControlPointDrag::active (Editing::MouseMode m)
4029 if (m == Editing::MouseDraw) {
4030 /* always active in mouse draw */
4034 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4035 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4038 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4041 _cumulative_y_drag (0)
4043 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4047 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4049 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4052 _item = &_line->grab_item ();
4054 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4055 origin, and ditto for y.
4058 double cx = event->button.x;
4059 double cy = event->button.y;
4061 _line->parent_group().canvas_to_item (cx, cy);
4063 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4068 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4069 /* no adjacent points */
4073 Drag::start_grab (event, _editor->cursors()->fader);
4075 /* store grab start in parent frame */
4080 double fraction = 1.0 - (cy / _line->height());
4082 _line->start_drag_line (before, after, fraction);
4084 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4088 LineDrag::motion (GdkEvent* event, bool)
4090 double dy = current_pointer_y() - last_pointer_y();
4092 if (event->button.state & Keyboard::SecondaryModifier) {
4096 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4098 _cumulative_y_drag = cy - _fixed_grab_y;
4101 cy = min ((double) _line->height(), cy);
4103 double const fraction = 1.0 - (cy / _line->height());
4106 /* we are ignoring x position for this drag, so we can just pass in anything */
4107 _line->drag_motion (0, fraction, true, false, ignored);
4109 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4113 LineDrag::finished (GdkEvent* event, bool movement_occured)
4115 if (movement_occured) {
4116 motion (event, false);
4117 _line->end_drag (false, 0);
4119 /* add a new control point on the line */
4121 AutomationTimeAxisView* atv;
4123 _line->end_drag (false, 0);
4125 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4126 framepos_t where = _editor->window_event_sample (event, 0, 0);
4127 atv->add_automation_event (event, where, event->button.y, false);
4131 _editor->commit_reversible_command ();
4135 LineDrag::aborted (bool)
4140 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4143 _cumulative_x_drag (0)
4145 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4149 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4151 Drag::start_grab (event);
4153 _line = reinterpret_cast<Line*> (_item);
4156 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4158 double cx = event->button.x;
4159 double cy = event->button.y;
4161 _item->parent()->canvas_to_item (cx, cy);
4163 /* store grab start in parent frame */
4164 _region_view_grab_x = cx;
4166 _before = *(float*) _item->get_data ("position");
4168 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4170 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4174 FeatureLineDrag::motion (GdkEvent*, bool)
4176 double dx = _drags->current_pointer_x() - last_pointer_x();
4178 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4180 _cumulative_x_drag += dx;
4182 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4191 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4193 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4195 float *pos = new float;
4198 _line->set_data ("position", pos);
4204 FeatureLineDrag::finished (GdkEvent*, bool)
4206 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4207 _arv->update_transient(_before, _before);
4211 FeatureLineDrag::aborted (bool)
4216 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4218 , _vertical_only (false)
4220 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4224 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4226 Drag::start_grab (event);
4227 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4231 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4238 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4240 framepos_t grab = grab_frame ();
4241 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4242 _editor->snap_to_with_modifier (grab, event);
4244 grab = raw_grab_frame ();
4247 /* base start and end on initial click position */
4257 if (current_pointer_y() < grab_y()) {
4258 y1 = current_pointer_y();
4261 y2 = current_pointer_y();
4265 if (start != end || y1 != y2) {
4267 double x1 = _editor->sample_to_pixel (start);
4268 double x2 = _editor->sample_to_pixel (end);
4269 const double min_dimension = 2.0;
4271 if (_vertical_only) {
4272 /* fixed 10 pixel width */
4276 x2 = min (x1 - min_dimension, x2);
4278 x2 = max (x1 + min_dimension, x2);
4283 y2 = min (y1 - min_dimension, y2);
4285 y2 = max (y1 + min_dimension, y2);
4288 /* translate rect into item space and set */
4290 ArdourCanvas::Rect r (x1, y1, x2, y2);
4292 /* this drag is a _trackview_only == true drag, so the y1 and
4293 * y2 (computed using current_pointer_y() and grab_y()) will be
4294 * relative to the top of the trackview group). The
4295 * rubberband rect has the same parent/scroll offset as the
4296 * the trackview group, so we can use the "r" rect directly
4297 * to set the shape of the rubberband.
4300 _editor->rubberband_rect->set (r);
4301 _editor->rubberband_rect->show();
4302 _editor->rubberband_rect->raise_to_top();
4304 show_verbose_cursor_time (pf);
4306 do_select_things (event, true);
4311 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4315 framepos_t grab = grab_frame ();
4316 framepos_t lpf = last_pointer_frame ();
4318 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4319 grab = raw_grab_frame ();
4320 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4334 if (current_pointer_y() < grab_y()) {
4335 y1 = current_pointer_y();
4338 y2 = current_pointer_y();
4342 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4346 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4348 if (movement_occurred) {
4350 motion (event, false);
4351 do_select_things (event, false);
4357 bool do_deselect = true;
4358 MidiTimeAxisView* mtv;
4360 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4362 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4363 /* nothing selected */
4364 add_midi_region (mtv);
4365 do_deselect = false;
4369 /* do not deselect if Primary or Tertiary (toggle-select or
4370 * extend-select are pressed.
4373 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4374 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4381 _editor->rubberband_rect->hide();
4385 RubberbandSelectDrag::aborted (bool)
4387 _editor->rubberband_rect->hide ();
4390 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4391 : RegionDrag (e, i, p, v)
4393 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4397 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4399 Drag::start_grab (event, cursor);
4401 show_verbose_cursor_time (adjusted_current_frame (event));
4405 TimeFXDrag::motion (GdkEvent* event, bool)
4407 RegionView* rv = _primary;
4408 StreamView* cv = rv->get_time_axis_view().view ();
4410 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4411 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4412 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4414 framepos_t const pf = adjusted_current_frame (event);
4416 if (pf > rv->region()->position()) {
4417 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4420 show_verbose_cursor_time (pf);
4424 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4426 _primary->get_time_axis_view().hide_timestretch ();
4428 if (!movement_occurred) {
4432 if (last_pointer_frame() < _primary->region()->position()) {
4433 /* backwards drag of the left edge - not usable */
4437 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4439 float percentage = (double) newlen / (double) _primary->region()->length();
4441 #ifndef USE_RUBBERBAND
4442 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4443 if (_primary->region()->data_type() == DataType::AUDIO) {
4444 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4448 if (!_editor->get_selection().regions.empty()) {
4449 /* primary will already be included in the selection, and edit
4450 group shared editing will propagate selection across
4451 equivalent regions, so just use the current region
4455 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4456 error << _("An error occurred while executing time stretch operation") << endmsg;
4462 TimeFXDrag::aborted (bool)
4464 _primary->get_time_axis_view().hide_timestretch ();
4467 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4470 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4474 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4476 Drag::start_grab (event);
4480 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4482 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4486 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4488 if (movement_occurred && _editor->session()) {
4489 /* make sure we stop */
4490 _editor->session()->request_transport_speed (0.0);
4495 ScrubDrag::aborted (bool)
4500 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4504 , _time_selection_at_start (!_editor->get_selection().time.empty())
4506 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4508 if (_time_selection_at_start) {
4509 start_at_start = _editor->get_selection().time.start();
4510 end_at_start = _editor->get_selection().time.end_frame();
4515 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4517 if (_editor->session() == 0) {
4521 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4523 switch (_operation) {
4524 case CreateSelection:
4525 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4530 cursor = _editor->cursors()->selector;
4531 Drag::start_grab (event, cursor);
4534 case SelectionStartTrim:
4535 if (_editor->clicked_axisview) {
4536 _editor->clicked_axisview->order_selection_trims (_item, true);
4538 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4541 case SelectionEndTrim:
4542 if (_editor->clicked_axisview) {
4543 _editor->clicked_axisview->order_selection_trims (_item, false);
4545 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4549 Drag::start_grab (event, cursor);
4552 case SelectionExtend:
4553 Drag::start_grab (event, cursor);
4557 if (_operation == SelectionMove) {
4558 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4560 show_verbose_cursor_time (adjusted_current_frame (event));
4565 SelectionDrag::setup_pointer_frame_offset ()
4567 switch (_operation) {
4568 case CreateSelection:
4569 _pointer_frame_offset = 0;
4572 case SelectionStartTrim:
4574 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4577 case SelectionEndTrim:
4578 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4581 case SelectionExtend:
4587 SelectionDrag::motion (GdkEvent* event, bool first_move)
4589 framepos_t start = 0;
4591 framecnt_t length = 0;
4592 framecnt_t distance = 0;
4594 framepos_t const pending_position = adjusted_current_frame (event);
4596 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4600 switch (_operation) {
4601 case CreateSelection:
4603 framepos_t grab = grab_frame ();
4606 grab = adjusted_current_frame (event, false);
4607 if (grab < pending_position) {
4608 _editor->snap_to (grab, RoundDownMaybe);
4610 _editor->snap_to (grab, RoundUpMaybe);
4614 if (pending_position < grab) {
4615 start = pending_position;
4618 end = pending_position;
4622 /* first drag: Either add to the selection
4623 or create a new selection
4630 /* adding to the selection */
4631 _editor->set_selected_track_as_side_effect (Selection::Add);
4632 _editor->clicked_selection = _editor->selection->add (start, end);
4639 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4640 _editor->set_selected_track_as_side_effect (Selection::Set);
4643 _editor->clicked_selection = _editor->selection->set (start, end);
4647 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4648 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4649 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4651 _editor->selection->add (atest);
4655 /* select all tracks within the rectangle that we've marked out so far */
4656 TrackViewList new_selection;
4657 TrackViewList& all_tracks (_editor->track_views);
4659 ArdourCanvas::Coord const top = grab_y();
4660 ArdourCanvas::Coord const bottom = current_pointer_y();
4662 if (top >= 0 && bottom >= 0) {
4664 //first, find the tracks that are covered in the y range selection
4665 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4666 if ((*i)->covered_by_y_range (top, bottom)) {
4667 new_selection.push_back (*i);
4671 //now find any tracks that are GROUPED with the tracks we selected
4672 TrackViewList grouped_add = new_selection;
4673 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4674 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4675 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4676 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4677 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4678 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4679 grouped_add.push_back (*j);
4684 //now compare our list with the current selection, and add or remove as necessary
4685 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4686 TrackViewList tracks_to_add;
4687 TrackViewList tracks_to_remove;
4688 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4689 if ( !_editor->selection->tracks.contains ( *i ) )
4690 tracks_to_add.push_back ( *i );
4691 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4692 if ( !grouped_add.contains ( *i ) )
4693 tracks_to_remove.push_back ( *i );
4694 _editor->selection->add(tracks_to_add);
4695 _editor->selection->remove(tracks_to_remove);
4701 case SelectionStartTrim:
4703 start = _editor->selection->time[_editor->clicked_selection].start;
4704 end = _editor->selection->time[_editor->clicked_selection].end;
4706 if (pending_position > end) {
4709 start = pending_position;
4713 case SelectionEndTrim:
4715 start = _editor->selection->time[_editor->clicked_selection].start;
4716 end = _editor->selection->time[_editor->clicked_selection].end;
4718 if (pending_position < start) {
4721 end = pending_position;
4728 start = _editor->selection->time[_editor->clicked_selection].start;
4729 end = _editor->selection->time[_editor->clicked_selection].end;
4731 length = end - start;
4732 distance = pending_position - start;
4733 start = pending_position;
4734 _editor->snap_to (start);
4736 end = start + length;
4740 case SelectionExtend:
4745 switch (_operation) {
4747 if (_time_selection_at_start) {
4748 _editor->selection->move_time (distance);
4752 _editor->selection->replace (_editor->clicked_selection, start, end);
4756 if (_operation == SelectionMove) {
4757 show_verbose_cursor_time(start);
4759 show_verbose_cursor_time(pending_position);
4764 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4766 Session* s = _editor->session();
4768 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4769 if (movement_occurred) {
4770 motion (event, false);
4771 /* XXX this is not object-oriented programming at all. ick */
4772 if (_editor->selection->time.consolidate()) {
4773 _editor->selection->TimeChanged ();
4776 /* XXX what if its a music time selection? */
4778 if ( s->get_play_range() && s->transport_rolling() ) {
4779 s->request_play_range (&_editor->selection->time, true);
4781 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4782 if (_operation == SelectionEndTrim)
4783 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4785 s->request_locate (_editor->get_selection().time.start());
4791 /* just a click, no pointer movement.
4794 if (_operation == SelectionExtend) {
4795 if (_time_selection_at_start) {
4796 framepos_t pos = adjusted_current_frame (event, false);
4797 framepos_t start = min (pos, start_at_start);
4798 framepos_t end = max (pos, end_at_start);
4799 _editor->selection->set (start, end);
4802 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4803 if (_editor->clicked_selection) {
4804 _editor->selection->remove (_editor->clicked_selection);
4807 if (!_editor->clicked_selection) {
4808 _editor->selection->clear_time();
4813 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4814 _editor->selection->set (_editor->clicked_axisview);
4817 if (s && s->get_play_range () && s->transport_rolling()) {
4818 s->request_stop (false, false);
4823 _editor->stop_canvas_autoscroll ();
4824 _editor->clicked_selection = 0;
4825 _editor->commit_reversible_selection_op ();
4829 SelectionDrag::aborted (bool)
4834 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4835 : Drag (e, i, false),
4839 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4841 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4842 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4843 physical_screen_height (_editor->get_window())));
4844 _drag_rect->hide ();
4846 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4847 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4850 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4852 /* normal canvas items will be cleaned up when their parent group is deleted. But
4853 this item is created as the child of a long-lived parent group, and so we
4854 need to explicitly delete it.
4860 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4862 if (_editor->session() == 0) {
4866 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4868 if (!_editor->temp_location) {
4869 _editor->temp_location = new Location (*_editor->session());
4872 switch (_operation) {
4873 case CreateSkipMarker:
4874 case CreateRangeMarker:
4875 case CreateTransportMarker:
4876 case CreateCDMarker:
4878 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4883 cursor = _editor->cursors()->selector;
4887 Drag::start_grab (event, cursor);
4889 show_verbose_cursor_time (adjusted_current_frame (event));
4893 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4895 framepos_t start = 0;
4897 ArdourCanvas::Rectangle *crect;
4899 switch (_operation) {
4900 case CreateSkipMarker:
4901 crect = _editor->range_bar_drag_rect;
4903 case CreateRangeMarker:
4904 crect = _editor->range_bar_drag_rect;
4906 case CreateTransportMarker:
4907 crect = _editor->transport_bar_drag_rect;
4909 case CreateCDMarker:
4910 crect = _editor->cd_marker_bar_drag_rect;
4913 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4918 framepos_t const pf = adjusted_current_frame (event);
4920 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4921 framepos_t grab = grab_frame ();
4922 _editor->snap_to (grab);
4924 if (pf < grab_frame()) {
4932 /* first drag: Either add to the selection
4933 or create a new selection.
4938 _editor->temp_location->set (start, end);
4942 update_item (_editor->temp_location);
4944 //_drag_rect->raise_to_top();
4950 _editor->temp_location->set (start, end);
4952 double x1 = _editor->sample_to_pixel (start);
4953 double x2 = _editor->sample_to_pixel (end);
4957 update_item (_editor->temp_location);
4960 show_verbose_cursor_time (pf);
4965 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4967 Location * newloc = 0;
4971 if (movement_occurred) {
4972 motion (event, false);
4975 switch (_operation) {
4976 case CreateSkipMarker:
4977 case CreateRangeMarker:
4978 case CreateCDMarker:
4980 XMLNode &before = _editor->session()->locations()->get_state();
4981 if (_operation == CreateSkipMarker) {
4982 _editor->begin_reversible_command (_("new skip marker"));
4983 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4984 flags = Location::IsRangeMarker | Location::IsSkip;
4985 _editor->range_bar_drag_rect->hide();
4986 } else if (_operation == CreateCDMarker) {
4987 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4988 _editor->begin_reversible_command (_("new CD marker"));
4989 flags = Location::IsRangeMarker | Location::IsCDMarker;
4990 _editor->cd_marker_bar_drag_rect->hide();
4992 _editor->begin_reversible_command (_("new skip marker"));
4993 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4994 flags = Location::IsRangeMarker;
4995 _editor->range_bar_drag_rect->hide();
4997 newloc = new Location (
4998 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5001 _editor->session()->locations()->add (newloc, true);
5002 XMLNode &after = _editor->session()->locations()->get_state();
5003 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5004 _editor->commit_reversible_command ();
5008 case CreateTransportMarker:
5009 // popup menu to pick loop or punch
5010 _editor->new_transport_marker_context_menu (&event->button, _item);
5016 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5018 if (_operation == CreateTransportMarker) {
5020 /* didn't drag, so just locate */
5022 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5024 } else if (_operation == CreateCDMarker) {
5026 /* didn't drag, but mark is already created so do
5029 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5034 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5036 if (end == max_framepos) {
5037 end = _editor->session()->current_end_frame ();
5040 if (start == max_framepos) {
5041 start = _editor->session()->current_start_frame ();
5044 switch (_editor->mouse_mode) {
5046 /* find the two markers on either side and then make the selection from it */
5047 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5051 /* find the two markers on either side of the click and make the range out of it */
5052 _editor->selection->set (start, end);
5061 _editor->stop_canvas_autoscroll ();
5065 RangeMarkerBarDrag::aborted (bool movement_occured)
5067 if (movement_occured) {
5068 _drag_rect->hide ();
5073 RangeMarkerBarDrag::update_item (Location* location)
5075 double const x1 = _editor->sample_to_pixel (location->start());
5076 double const x2 = _editor->sample_to_pixel (location->end());
5078 _drag_rect->set_x0 (x1);
5079 _drag_rect->set_x1 (x2);
5082 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5084 , _cumulative_dx (0)
5085 , _cumulative_dy (0)
5087 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5089 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5091 _region = &_primary->region_view ();
5092 _note_height = _region->midi_stream_view()->note_height ();
5096 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5098 Drag::start_grab (event);
5100 if (!(_was_selected = _primary->selected())) {
5102 /* tertiary-click means extend selection - we'll do that on button release,
5103 so don't add it here, because otherwise we make it hard to figure
5104 out the "extend-to" range.
5107 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5110 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5113 _region->note_selected (_primary, true);
5115 _region->unique_select (_primary);
5118 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5119 _editor->commit_reversible_selection_op();
5124 /** @return Current total drag x change in frames */
5126 NoteDrag::total_dx () const
5129 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5131 /* primary note time */
5132 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5134 /* new time of the primary note in session frames */
5135 frameoffset_t st = n + dx;
5137 framepos_t const rp = _region->region()->position ();
5139 /* prevent the note being dragged earlier than the region's position */
5142 /* snap and return corresponding delta */
5143 return _region->snap_frame_to_frame (st - rp) + rp - n;
5146 /** @return Current total drag y change in note number */
5148 NoteDrag::total_dy () const
5150 MidiStreamView* msv = _region->midi_stream_view ();
5151 double const y = _region->midi_view()->y_position ();
5152 /* new current note */
5153 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5155 n = max (msv->lowest_note(), n);
5156 n = min (msv->highest_note(), n);
5157 /* and work out delta */
5158 return n - msv->y_to_note (grab_y() - y);
5162 NoteDrag::motion (GdkEvent *, bool)
5164 /* Total change in x and y since the start of the drag */
5165 frameoffset_t const dx = total_dx ();
5166 int8_t const dy = total_dy ();
5168 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5169 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5170 double const tdy = -dy * _note_height - _cumulative_dy;
5173 _cumulative_dx += tdx;
5174 _cumulative_dy += tdy;
5176 int8_t note_delta = total_dy();
5178 _region->move_selection (tdx, tdy, note_delta);
5180 /* the new note value may be the same as the old one, but we
5181 * don't know what that means because the selection may have
5182 * involved more than one note and we might be doing something
5183 * odd with them. so show the note value anyway, always.
5187 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5189 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5190 (int) floor ((double)new_note));
5192 show_verbose_cursor_text (buf);
5197 NoteDrag::finished (GdkEvent* ev, bool moved)
5200 /* no motion - select note */
5202 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5203 _editor->current_mouse_mode() == Editing::MouseDraw) {
5205 bool changed = false;
5207 if (_was_selected) {
5208 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5210 _region->note_deselected (_primary);
5214 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5215 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5217 if (!extend && !add && _region->selection_size() > 1) {
5218 _region->unique_select (_primary);
5220 } else if (extend) {
5221 _region->note_selected (_primary, true, true);
5224 /* it was added during button press */
5229 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5230 _editor->commit_reversible_selection_op();
5234 _region->note_dropped (_primary, total_dx(), total_dy());
5239 NoteDrag::aborted (bool)
5244 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5245 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5246 : Drag (editor, atv->base_item ())
5248 , _y_origin (atv->y_position())
5249 , _nothing_to_drag (false)
5251 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5252 setup (atv->lines ());
5255 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5256 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5257 : Drag (editor, rv->get_canvas_group ())
5259 , _y_origin (rv->get_time_axis_view().y_position())
5260 , _nothing_to_drag (false)
5263 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5265 list<boost::shared_ptr<AutomationLine> > lines;
5267 AudioRegionView* audio_view;
5268 AutomationRegionView* automation_view;
5269 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5270 lines.push_back (audio_view->get_gain_line ());
5271 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5272 lines.push_back (automation_view->line ());
5275 error << _("Automation range drag created for invalid region type") << endmsg;
5281 /** @param lines AutomationLines to drag.
5282 * @param offset Offset from the session start to the points in the AutomationLines.
5285 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5287 /* find the lines that overlap the ranges being dragged */
5288 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5289 while (i != lines.end ()) {
5290 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5293 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5295 /* check this range against all the AudioRanges that we are using */
5296 list<AudioRange>::const_iterator k = _ranges.begin ();
5297 while (k != _ranges.end()) {
5298 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5304 /* add it to our list if it overlaps at all */
5305 if (k != _ranges.end()) {
5310 _lines.push_back (n);
5316 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5320 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5322 return 1.0 - ((global_y - _y_origin) / line->height());
5326 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5328 const double v = list->eval(x);
5329 return _integral ? rint(v) : v;
5333 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5335 Drag::start_grab (event, cursor);
5337 /* Get line states before we start changing things */
5338 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5339 i->state = &i->line->get_state ();
5340 i->original_fraction = y_fraction (i->line, current_pointer_y());
5343 if (_ranges.empty()) {
5345 /* No selected time ranges: drag all points */
5346 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5347 uint32_t const N = i->line->npoints ();
5348 for (uint32_t j = 0; j < N; ++j) {
5349 i->points.push_back (i->line->nth (j));
5355 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5357 framecnt_t const half = (i->start + i->end) / 2;
5359 /* find the line that this audio range starts in */
5360 list<Line>::iterator j = _lines.begin();
5361 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5365 if (j != _lines.end()) {
5366 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5368 /* j is the line that this audio range starts in; fade into it;
5369 64 samples length plucked out of thin air.
5372 framepos_t a = i->start + 64;
5377 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5378 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5380 the_list->editor_add (p, value (the_list, p));
5381 the_list->editor_add (q, value (the_list, q));
5384 /* same thing for the end */
5387 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5391 if (j != _lines.end()) {
5392 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5394 /* j is the line that this audio range starts in; fade out of it;
5395 64 samples length plucked out of thin air.
5398 framepos_t b = i->end - 64;
5403 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5404 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5406 the_list->editor_add (p, value (the_list, p));
5407 the_list->editor_add (q, value (the_list, q));
5411 _nothing_to_drag = true;
5413 /* Find all the points that should be dragged and put them in the relevant
5414 points lists in the Line structs.
5417 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5419 uint32_t const N = i->line->npoints ();
5420 for (uint32_t j = 0; j < N; ++j) {
5422 /* here's a control point on this line */
5423 ControlPoint* p = i->line->nth (j);
5424 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5426 /* see if it's inside a range */
5427 list<AudioRange>::const_iterator k = _ranges.begin ();
5428 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5432 if (k != _ranges.end()) {
5433 /* dragging this point */
5434 _nothing_to_drag = false;
5435 i->points.push_back (p);
5441 if (_nothing_to_drag) {
5445 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5446 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5451 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5453 if (_nothing_to_drag) {
5457 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5458 float const f = y_fraction (l->line, current_pointer_y());
5459 /* we are ignoring x position for this drag, so we can just pass in anything */
5461 l->line->drag_motion (0, f, true, false, ignored);
5462 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5467 AutomationRangeDrag::finished (GdkEvent* event, bool)
5469 if (_nothing_to_drag) {
5473 motion (event, false);
5474 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5475 i->line->end_drag (false, 0);
5478 _editor->commit_reversible_command ();
5482 AutomationRangeDrag::aborted (bool)
5484 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5489 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5491 , initial_time_axis_view (itav)
5493 /* note that time_axis_view may be null if the regionview was created
5494 * as part of a copy operation.
5496 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5497 layer = v->region()->layer ();
5498 initial_y = v->get_canvas_group()->position().y;
5499 initial_playlist = v->region()->playlist ();
5500 initial_position = v->region()->position ();
5501 initial_end = v->region()->position () + v->region()->length ();
5504 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5505 : Drag (e, i->canvas_item ())
5508 , _cumulative_dx (0)
5510 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5511 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5516 PatchChangeDrag::motion (GdkEvent* ev, bool)
5518 framepos_t f = adjusted_current_frame (ev);
5519 boost::shared_ptr<Region> r = _region_view->region ();
5520 f = max (f, r->position ());
5521 f = min (f, r->last_frame ());
5523 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5524 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5525 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5526 _cumulative_dx = dxu;
5530 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5532 if (!movement_occurred) {
5536 boost::shared_ptr<Region> r (_region_view->region ());
5537 framepos_t f = adjusted_current_frame (ev);
5538 f = max (f, r->position ());
5539 f = min (f, r->last_frame ());
5541 _region_view->move_patch_change (
5543 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5548 PatchChangeDrag::aborted (bool)
5550 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5554 PatchChangeDrag::setup_pointer_frame_offset ()
5556 boost::shared_ptr<Region> region = _region_view->region ();
5557 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5560 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5561 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5568 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5570 _region_view->update_drag_selection (
5572 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5576 MidiRubberbandSelectDrag::deselect_things ()
5581 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5582 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5585 _vertical_only = true;
5589 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5591 double const y = _region_view->midi_view()->y_position ();
5593 y1 = max (0.0, y1 - y);
5594 y2 = max (0.0, y2 - y);
5596 _region_view->update_vertical_drag_selection (
5599 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5604 MidiVerticalSelectDrag::deselect_things ()
5609 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5610 : RubberbandSelectDrag (e, i)
5616 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5618 if (drag_in_progress) {
5619 /* We just want to select things at the end of the drag, not during it */
5623 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5625 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5627 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5629 _editor->commit_reversible_selection_op ();
5633 EditorRubberbandSelectDrag::deselect_things ()
5635 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5637 _editor->selection->clear_tracks();
5638 _editor->selection->clear_regions();
5639 _editor->selection->clear_points ();
5640 _editor->selection->clear_lines ();
5641 _editor->selection->clear_midi_notes ();
5643 _editor->commit_reversible_selection_op();
5646 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5651 _note[0] = _note[1] = 0;
5654 NoteCreateDrag::~NoteCreateDrag ()
5660 NoteCreateDrag::grid_frames (framepos_t t) const
5663 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5665 grid_beats = Evoral::Beats(1);
5668 return _region_view->region_beats_to_region_frames (grid_beats);
5672 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5674 Drag::start_grab (event, cursor);
5676 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5678 framepos_t pf = _drags->current_pointer_frame ();
5679 framecnt_t const g = grid_frames (pf);
5681 /* Hack so that we always snap to the note that we are over, instead of snapping
5682 to the next one if we're more than halfway through the one we're over.
5684 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5688 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5689 _note[1] = _note[0];
5691 MidiStreamView* sv = _region_view->midi_stream_view ();
5692 double const x = _editor->sample_to_pixel (_note[0]);
5693 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5695 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5696 _drag_rect->set_outline_all ();
5697 _drag_rect->set_outline_color (0xffffff99);
5698 _drag_rect->set_fill_color (0xffffff66);
5702 NoteCreateDrag::motion (GdkEvent* event, bool)
5704 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5705 double const x0 = _editor->sample_to_pixel (_note[0]);
5706 double const x1 = _editor->sample_to_pixel (_note[1]);
5707 _drag_rect->set_x0 (std::min(x0, x1));
5708 _drag_rect->set_x1 (std::max(x0, x1));
5712 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5714 if (!had_movement) {
5718 framepos_t const start = min (_note[0], _note[1]);
5719 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5721 framecnt_t const g = grid_frames (start);
5722 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5724 if (_editor->snap_mode() == SnapNormal && length < g) {
5728 Evoral::Beats length_beats = max (
5729 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5731 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5735 NoteCreateDrag::y_to_region (double y) const
5738 _region_view->get_canvas_group()->canvas_to_item (x, y);
5743 NoteCreateDrag::aborted (bool)
5748 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5753 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5757 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5759 Drag::start_grab (event, cursor);
5763 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5769 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5772 distance = _drags->current_pointer_x() - grab_x();
5773 len = ar->fade_in()->back()->when;
5775 distance = grab_x() - _drags->current_pointer_x();
5776 len = ar->fade_out()->back()->when;
5779 /* how long should it be ? */
5781 new_length = len + _editor->pixel_to_sample (distance);
5783 /* now check with the region that this is legal */
5785 new_length = ar->verify_xfade_bounds (new_length, start);
5788 arv->reset_fade_in_shape_width (ar, new_length);
5790 arv->reset_fade_out_shape_width (ar, new_length);
5795 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5801 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5804 distance = _drags->current_pointer_x() - grab_x();
5805 len = ar->fade_in()->back()->when;
5807 distance = grab_x() - _drags->current_pointer_x();
5808 len = ar->fade_out()->back()->when;
5811 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5813 _editor->begin_reversible_command ("xfade trim");
5814 ar->playlist()->clear_owned_changes ();
5817 ar->set_fade_in_length (new_length);
5819 ar->set_fade_out_length (new_length);
5822 /* Adjusting the xfade may affect other regions in the playlist, so we need
5823 to get undo Commands from the whole playlist rather than just the
5827 vector<Command*> cmds;
5828 ar->playlist()->rdiff (cmds);
5829 _editor->session()->add_commands (cmds);
5830 _editor->commit_reversible_command ();
5835 CrossfadeEdgeDrag::aborted (bool)
5838 // arv->redraw_start_xfade ();
5840 // arv->redraw_end_xfade ();
5844 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5845 : Drag (e, item, true)
5846 , line (new EditorCursor (*e))
5848 line->set_position (pos);
5852 RegionCutDrag::~RegionCutDrag ()
5858 RegionCutDrag::motion (GdkEvent*, bool)
5860 framepos_t where = _drags->current_pointer_frame();
5861 _editor->snap_to (where);
5863 line->set_position (where);
5867 RegionCutDrag::finished (GdkEvent*, bool)
5869 _editor->get_track_canvas()->canvas()->re_enter();
5871 framepos_t pos = _drags->current_pointer_frame();
5875 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5881 _editor->split_regions_at (pos, rs);
5885 RegionCutDrag::aborted (bool)