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
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);
663 while (current >= 0 && current != target) {
665 if (current < 0 || current >= _time_axis_views.size()) {
668 if (_time_axis_views[current]->hidden()) {
676 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
678 if (_y_constrained) {
682 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
683 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
684 #ifdef DEBUG_DROPZONEDRAG
685 printf("Y MOVEMENT CHECK: from %d to %d skip: %d\n", i->time_axis_view, i->time_axis_view + delta_track, skip_invisible);
687 assert (n < 0 || n >= _time_axis_views.size() || !_time_axis_views[n]->hidden());
689 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
690 /* already in the drop zone */
691 if (delta_track >= 0) {
692 /* downward motion - might be OK if others are still not in the dropzone,
693 so check at the end of the loop if that is the case.
698 /* upward motion - set n to the track we would end up in if motion
699 is successful, and check validity below. */
700 n = _time_axis_views.size() + delta_track;
706 } else if (n >= int (_time_axis_views.size())) {
707 /* downward motion into drop zone. That's fine. */
711 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
712 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
713 /* not a track, or the wrong type */
717 double const l = i->layer + delta_layer;
719 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
720 mode to allow the user to place a region below another on layer 0.
722 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
723 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
724 If it has, the layers will be munged later anyway, so it's ok.
730 /* all regions being dragged are ok with this change */
735 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
737 double delta_layer = 0;
738 int delta_time_axis_view = 0;
739 int current_pointer_time_axis_view = -1;
741 assert (!_views.empty ());
745 if (initially_vertical()) {
746 _y_constrained = false;
747 _x_constrained = true;
749 _y_constrained = true;
750 _x_constrained = false;
755 #ifdef DEBUG_DROPZONEDRAG
756 printf("--------- LAST AXIS: %d\n", _last_pointer_time_axis_view);
758 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
760 /* Find the TimeAxisView that the pointer is now over */
762 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
763 TimeAxisView* tv = r.first;
765 if (!tv && current_pointer_y() < 0) {
766 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
770 if (tv && tv->view()) {
771 double layer = r.second;
773 if (first_move && tv->view()->layer_display() == Stacked) {
774 tv->view()->set_layer_display (Expanded);
777 /* Here's the current pointer position in terms of time axis view and layer */
778 current_pointer_time_axis_view = find_time_axis_view (tv);
779 assert(current_pointer_time_axis_view >= 0);
781 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
783 /* Work out the change in y */
785 if (_last_pointer_time_axis_view < 0) {
786 /* Was in the drop-zone, now over a track.
787 * Hence it must be an upward move (from the bottom)
789 * track_index is still -1, so delta must be set to
790 * move up the correct number of tracks from the bottom.
792 * This is necessary because steps may be skipped if
793 * the bottom-most track is not a valid target,
795 #ifdef DEBUG_DROPZONEDRAG
796 printf("MOVE OUT OF THE ZONE...\n");
798 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size ();
800 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
803 /* TODO needs adjustment per DraggingView,
805 * e.g. select one region on the top-layer of a track
806 * and one region which is at the bottom-layer of another track
809 * Indicated drop-zones and layering is wrong.
810 * and may infer additional layers on the target-track
811 * (depending how many layers the original track had).
813 * Or select two regions (different layers) on a same track,
814 * move across a non-layer track.. -> layering info is lost.
815 * on drop either of the regions may be on top.
817 * Proposed solution: screw it :) well,
818 * don't use delta_layer, use an absolute value
819 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
820 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
821 * 3) iterate over all DraggingView, find the one that is over the track with most layers
822 * 4) proportionally scale layer to layers available on target
824 delta_layer = current_pointer_layer - _last_pointer_layer;
827 /* for automation lanes, there is a TimeAxisView but no ->view() */
828 else if (!tv && current_pointer_y() >= 0 && _last_pointer_time_axis_view >= 0) {
829 /* Moving into the drop-zone..
831 * TODO allow moving further down in drop-zone:
832 * e.g. 2 Tracks, select a region on both of them.
834 * A) grab the upper, drag 2 down, both regions are in the dropzone: all fine (works)
836 * B) grab the lower, drag 1 down, region (and mouse) are in dropzone, The End.
837 * upper region is only down one track and cannot be moved into the zone.
840 * keep track of how many regions are in the DZ (private var),
841 * also count from how many tracks the dragged-regions come from (first move)
843 * if not all regions are in the DZ, keep going.
845 * Using 'default height' H for all dropzone regions will make things
846 * a lot simpler: (number_of_DZ_entries * H + Pointer_YPOS - DZ_YPOS) / H.
847 * (because at this point in time PlaylistDropzoneMap is not yet populated)
849 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
850 #ifdef DEBUG_DROPZONEDRAG
851 printf("INTO THE ZONE DELTA: %d\n", delta_time_axis_view);
855 /* Work out the change in x */
856 framepos_t pending_region_position;
857 double const x_delta = compute_x_delta (event, &pending_region_position);
858 _last_frame_position = pending_region_position;
860 /* calculate hidden tracks in current delta */
862 if (_last_pointer_time_axis_view < 0) {
863 // Moving out of the zone, check for hidden tracks at the bottom.
864 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0)
865 -_time_axis_views.size() - delta_time_axis_view;
866 #ifdef DEBUG_DROPZONEDRAG
867 printf("NOW WHAT?? last: %d delta %d || skip %d\n", _last_pointer_time_axis_view, delta_time_axis_view, delta_skip);
870 // calculate hidden tracks that are skipped by the pointer movement
871 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0)
872 - _last_pointer_time_axis_view
873 - delta_time_axis_view;
874 #ifdef DEBUG_DROPZONEDRAG
875 printf("Drag from %d to %d || skip %d\n",
876 _last_pointer_time_axis_view,
877 _last_pointer_time_axis_view + delta_time_axis_view,
882 /* Verify change in y */
883 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
884 /* this y movement is not allowed, so do no y movement this time */
885 delta_time_axis_view = 0;
888 #ifdef DEBUG_DROPZONEDRAG
889 printf(" ** NOT ALLOWED\n");
893 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
894 /* haven't reached next snap point, and we're not switching
895 trackviews nor layers. nothing to do.
900 typedef pair<int,double> NewTrackIndexAndPosition;
901 typedef map<boost::shared_ptr<Playlist>,NewTrackIndexAndPosition> PlaylistDropzoneMap;
902 PlaylistDropzoneMap playlist_dropzone_map;
903 int biggest_drop_zone_offset = 0;
905 /* find drop-zone y-position */
906 Coord last_track_bottom_edge;
907 last_track_bottom_edge = 0;
908 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
909 if (!(*t)->hidden()) {
910 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
915 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
917 RegionView* rv = i->view;
922 if (rv->region()->locked() || rv->region()->video_locked()) {
929 /* reparent the regionview into a group above all
933 ArdourCanvas::Item* rvg = rv->get_canvas_group();
934 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
935 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
936 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
937 /* move the item so that it continues to appear at the
938 same location now that its parent has changed.
940 rvg->move (rv_canvas_offset - dmg_canvas_offset);
943 /* If we have moved tracks, we'll fudge the layer delta so that the
944 region gets moved back onto layer 0 on its new track; this avoids
945 confusion when dragging regions from non-zero layers onto different
948 double this_delta_layer = delta_layer;
949 if (delta_time_axis_view != 0) {
950 this_delta_layer = - i->layer;
953 int this_delta_time_axis_view = delta_time_axis_view;
954 this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
956 int track_index = i->time_axis_view + this_delta_time_axis_view;
957 assert(track_index >= 0);
959 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
960 i->time_axis_view = track_index;
961 #ifdef DEBUG_DROPZONEDRAG
962 printf("IN THE ZONE\n");
964 assert(i->time_axis_view >= _time_axis_views.size());
965 if (current_pointer_y() >= 0) {
968 NewTrackIndexAndPosition ip;
969 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
971 /* store index of each new playlist as a negative count, starting at -1 */
973 if (pdz == playlist_dropzone_map.end()) {
976 * retain the ordering top -> bottom in the drop-zone
977 * this can be done by sorting the regions according to
978 * i->time_axis_view Y, prior to iterating over DraggingView
981 int n = playlist_dropzone_map.size() + 1;
983 /* compute where this new track (which doesn't exist yet) will live
987 ip.first = -n; /* in time axis units, negative to signify "in drop zone " */
988 ip.second = last_track_bottom_edge; /* where to place the top edge of the regionview */
990 /* How high is this region view ? */
992 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
993 ArdourCanvas::Rect bbox;
999 last_track_bottom_edge += bbox.height();
1001 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), ip));
1006 dzoffset = ip.first;
1009 /* values are zero or negative, hence the use of min() */
1010 biggest_drop_zone_offset = min (biggest_drop_zone_offset, dzoffset);
1011 y_delta = ip.second - rv->get_canvas_group()->canvas_origin().y;
1016 /* The TimeAxisView that this region is now over */
1017 TimeAxisView* current_tv = _time_axis_views[track_index];
1019 /* Ensure it is moved from stacked -> expanded if appropriate */
1020 if (current_tv->view()->layer_display() == Stacked) {
1021 current_tv->view()->set_layer_display (Expanded);
1024 /* We're only allowed to go -ve in layer on Expanded views */
1025 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1026 this_delta_layer = - i->layer;
1030 rv->set_height (current_tv->view()->child_height ());
1032 /* Update show/hidden status as the region view may have come from a hidden track,
1033 or have moved to one.
1035 if (current_tv->hidden ()) {
1036 rv->get_canvas_group()->hide ();
1038 rv->get_canvas_group()->show ();
1041 /* Update the DraggingView */
1042 i->time_axis_view = track_index;
1043 i->layer += this_delta_layer;
1046 _editor->mouse_brush_insert_region (rv, pending_region_position);
1050 /* Get the y coordinate of the top of the track that this region is now over */
1051 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1053 /* And adjust for the layer that it should be on */
1054 StreamView* cv = current_tv->view ();
1055 switch (cv->layer_display ()) {
1059 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1062 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1066 /* need to get the parent of the regionview
1067 * canvas group and get its position in
1068 * equivalent coordinate space as the trackview
1069 * we are now dragging over.
1072 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1077 /* Now move the region view */
1078 rv->move (x_delta, y_delta);
1080 } /* foreach region */
1082 _total_x_delta += x_delta;
1084 if (x_delta != 0 && !_brushing) {
1085 show_verbose_cursor_time (_last_frame_position);
1090 /* the pointer is currently over a time axis view */
1092 if (_last_pointer_time_axis_view < 0) {
1094 /* last motion event was not over a time axis view */
1096 if (delta_time_axis_view < 0) {
1097 /* was in the drop zone, moving up */
1098 assert(current_pointer_time_axis_view >= 0);
1099 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1101 /* was in the drop zone, moving down ... not possible */
1106 /* last motion event was also over a time axis view */
1108 _last_pointer_time_axis_view += delta_time_axis_view;
1109 assert(_last_pointer_time_axis_view >= 0);
1114 /* the pointer is not over a time axis view */
1116 _last_pointer_time_axis_view = biggest_drop_zone_offset;
1119 _last_pointer_layer += delta_layer;
1123 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1125 if (_copy && first_move) {
1127 if (_x_constrained) {
1128 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1130 _editor->begin_reversible_command (Operations::region_copy);
1133 /* duplicate the regionview(s) and region(s) */
1135 list<DraggingView> new_regionviews;
1137 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1139 RegionView* rv = i->view;
1140 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1141 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1143 const boost::shared_ptr<const Region> original = rv->region();
1144 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1145 region_copy->set_position (original->position());
1146 /* need to set this so that the drop zone code can work. This doesn't
1147 actually put the region into the playlist, but just sets a weak pointer
1150 region_copy->set_playlist (original->playlist());
1154 boost::shared_ptr<AudioRegion> audioregion_copy
1155 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1157 nrv = new AudioRegionView (*arv, audioregion_copy);
1159 boost::shared_ptr<MidiRegion> midiregion_copy
1160 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1161 nrv = new MidiRegionView (*mrv, midiregion_copy);
1166 nrv->get_canvas_group()->show ();
1167 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1169 /* swap _primary to the copy */
1171 if (rv == _primary) {
1175 /* ..and deselect the one we copied */
1177 rv->set_selected (false);
1180 if (!new_regionviews.empty()) {
1182 /* reflect the fact that we are dragging the copies */
1184 _views = new_regionviews;
1186 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1189 } else if (!_copy && first_move) {
1191 if (_x_constrained) {
1192 _editor->begin_reversible_command (_("fixed time region drag"));
1194 _editor->begin_reversible_command (Operations::region_drag);
1198 RegionMotionDrag::motion (event, first_move);
1202 RegionMotionDrag::finished (GdkEvent *, bool)
1204 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1205 if (!(*i)->view()) {
1209 if ((*i)->view()->layer_display() == Expanded) {
1210 (*i)->view()->set_layer_display (Stacked);
1216 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1218 RegionMotionDrag::finished (ev, movement_occurred);
1220 if (!movement_occurred) {
1224 if (was_double_click() && !_views.empty()) {
1225 DraggingView dv = _views.front();
1226 dv.view->show_region_editor ();
1233 /* reverse this here so that we have the correct logic to finalize
1237 if (Config->get_edit_mode() == Lock) {
1238 _x_constrained = !_x_constrained;
1241 assert (!_views.empty ());
1243 /* We might have hidden region views so that they weren't visible during the drag
1244 (when they have been reparented). Now everything can be shown again, as region
1245 views are back in their track parent groups.
1247 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1248 i->view->get_canvas_group()->show ();
1251 bool const changed_position = (_last_frame_position != _primary->region()->position());
1252 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1253 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1273 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1277 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1279 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1284 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1285 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1286 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1287 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1289 rtav->set_height (original->current_height());
1293 ChanCount one_midi_port (DataType::MIDI, 1);
1294 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1295 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1296 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1298 rtav->set_height (original->current_height());
1303 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1309 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1311 RegionSelection new_views;
1312 PlaylistSet modified_playlists;
1313 RouteTimeAxisView* new_time_axis_view = 0;
1316 /* all changes were made during motion event handlers */
1318 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1322 _editor->commit_reversible_command ();
1326 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1327 PlaylistMapping playlist_mapping;
1329 /* insert the regions into their new playlists */
1330 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1332 RouteTimeAxisView* dest_rtv = 0;
1334 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1340 if (changed_position && !_x_constrained) {
1341 where = i->view->region()->position() - drag_delta;
1343 where = i->view->region()->position();
1346 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
1347 /* dragged to drop zone */
1349 PlaylistMapping::iterator pm;
1351 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1352 /* first region from this original playlist: create a new track */
1353 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1354 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1355 dest_rtv = new_time_axis_view;
1357 /* we already created a new track for regions from this playlist, use it */
1358 dest_rtv = pm->second;
1361 /* destination time axis view is the one we dragged to */
1362 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1365 if (dest_rtv != 0) {
1366 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1367 if (new_view != 0) {
1368 new_views.push_back (new_view);
1372 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1373 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1376 list<DraggingView>::const_iterator next = i;
1382 /* If we've created new regions either by copying or moving
1383 to a new track, we want to replace the old selection with the new ones
1386 if (new_views.size() > 0) {
1387 _editor->selection->set (new_views);
1390 /* write commands for the accumulated diffs for all our modified playlists */
1391 add_stateful_diff_commands_for_playlists (modified_playlists);
1393 _editor->commit_reversible_command ();
1397 RegionMoveDrag::finished_no_copy (
1398 bool const changed_position,
1399 bool const changed_tracks,
1400 framecnt_t const drag_delta
1403 RegionSelection new_views;
1404 PlaylistSet modified_playlists;
1405 PlaylistSet frozen_playlists;
1406 set<RouteTimeAxisView*> views_to_update;
1407 RouteTimeAxisView* new_time_axis_view = 0;
1410 /* all changes were made during motion event handlers */
1411 _editor->commit_reversible_command ();
1415 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1416 PlaylistMapping playlist_mapping;
1418 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1420 RegionView* rv = i->view;
1421 RouteTimeAxisView* dest_rtv = 0;
1423 if (rv->region()->locked() || rv->region()->video_locked()) {
1428 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
1429 /* dragged to drop zone */
1431 PlaylistMapping::iterator pm;
1433 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1434 /* first region from this original playlist: create a new track */
1435 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1436 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1437 dest_rtv = new_time_axis_view;
1439 /* we already created a new track for regions from this playlist, use it */
1440 dest_rtv = pm->second;
1444 /* destination time axis view is the one we dragged to */
1445 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1450 double const dest_layer = i->layer;
1452 views_to_update.insert (dest_rtv);
1456 if (changed_position && !_x_constrained) {
1457 where = rv->region()->position() - drag_delta;
1459 where = rv->region()->position();
1462 if (changed_tracks) {
1464 /* insert into new playlist */
1466 RegionView* new_view = insert_region_into_playlist (
1467 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1470 if (new_view == 0) {
1475 new_views.push_back (new_view);
1477 /* remove from old playlist */
1479 /* the region that used to be in the old playlist is not
1480 moved to the new one - we use a copy of it. as a result,
1481 any existing editor for the region should no longer be
1484 rv->hide_region_editor();
1487 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1491 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1493 /* this movement may result in a crossfade being modified, or a layering change,
1494 so we need to get undo data from the playlist as well as the region.
1497 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1499 playlist->clear_changes ();
1502 rv->region()->clear_changes ();
1505 motion on the same track. plonk the previously reparented region
1506 back to its original canvas group (its streamview).
1507 No need to do anything for copies as they are fake regions which will be deleted.
1510 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1511 rv->get_canvas_group()->set_y_position (i->initial_y);
1514 /* just change the model */
1515 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1516 playlist->set_layer (rv->region(), dest_layer);
1519 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1521 r = frozen_playlists.insert (playlist);
1524 playlist->freeze ();
1527 rv->region()->set_position (where);
1529 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1532 if (changed_tracks) {
1534 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1535 was selected in all of them, then removing it from a playlist will have removed all
1536 trace of it from _views (i.e. there were N regions selected, we removed 1,
1537 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1538 corresponding regionview, and _views is now empty).
1540 This could have invalidated any and all iterators into _views.
1542 The heuristic we use here is: if the region selection is empty, break out of the loop
1543 here. if the region selection is not empty, then restart the loop because we know that
1544 we must have removed at least the region(view) we've just been working on as well as any
1545 that we processed on previous iterations.
1547 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1548 we can just iterate.
1552 if (_views.empty()) {
1563 /* If we've created new regions either by copying or moving
1564 to a new track, we want to replace the old selection with the new ones
1567 if (new_views.size() > 0) {
1568 _editor->selection->set (new_views);
1571 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1575 /* write commands for the accumulated diffs for all our modified playlists */
1576 add_stateful_diff_commands_for_playlists (modified_playlists);
1578 _editor->commit_reversible_command ();
1580 /* We have futzed with the layering of canvas items on our streamviews.
1581 If any region changed layer, this will have resulted in the stream
1582 views being asked to set up their region views, and all will be well.
1583 If not, we might now have badly-ordered region views. Ask the StreamViews
1584 involved to sort themselves out, just in case.
1587 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1588 (*i)->view()->playlist_layered ((*i)->track ());
1592 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1593 * @param region Region to remove.
1594 * @param playlist playlist To remove from.
1595 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1596 * that clear_changes () is only called once per playlist.
1599 RegionMoveDrag::remove_region_from_playlist (
1600 boost::shared_ptr<Region> region,
1601 boost::shared_ptr<Playlist> playlist,
1602 PlaylistSet& modified_playlists
1605 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1608 playlist->clear_changes ();
1611 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1615 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1616 * clearing the playlist's diff history first if necessary.
1617 * @param region Region to insert.
1618 * @param dest_rtv Destination RouteTimeAxisView.
1619 * @param dest_layer Destination layer.
1620 * @param where Destination position.
1621 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1622 * that clear_changes () is only called once per playlist.
1623 * @return New RegionView, or 0 if no insert was performed.
1626 RegionMoveDrag::insert_region_into_playlist (
1627 boost::shared_ptr<Region> region,
1628 RouteTimeAxisView* dest_rtv,
1631 PlaylistSet& modified_playlists
1634 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1635 if (!dest_playlist) {
1639 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1640 _new_region_view = 0;
1641 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1643 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1644 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1646 dest_playlist->clear_changes ();
1649 dest_playlist->add_region (region, where);
1651 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1652 dest_playlist->set_layer (region, dest_layer);
1657 assert (_new_region_view);
1659 return _new_region_view;
1663 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1665 _new_region_view = rv;
1669 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1671 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1672 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1674 _editor->session()->add_command (c);
1683 RegionMoveDrag::aborted (bool movement_occurred)
1687 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1688 list<DraggingView>::const_iterator next = i;
1697 RegionMotionDrag::aborted (movement_occurred);
1702 RegionMotionDrag::aborted (bool)
1704 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1706 StreamView* sview = (*i)->view();
1709 if (sview->layer_display() == Expanded) {
1710 sview->set_layer_display (Stacked);
1715 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1716 RegionView* rv = i->view;
1717 TimeAxisView* tv = &(rv->get_time_axis_view ());
1718 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1720 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1721 rv->get_canvas_group()->set_y_position (0);
1723 rv->move (-_total_x_delta, 0);
1724 rv->set_height (rtv->view()->child_height ());
1728 /** @param b true to brush, otherwise false.
1729 * @param c true to make copies of the regions being moved, otherwise false.
1731 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1732 : RegionMotionDrag (e, i, p, v, b)
1735 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1738 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1739 if (rtv && rtv->is_track()) {
1740 speed = rtv->track()->speed ();
1743 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1747 RegionMoveDrag::setup_pointer_frame_offset ()
1749 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1752 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1753 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1755 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1757 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1758 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1760 _primary = v->view()->create_region_view (r, false, false);
1762 _primary->get_canvas_group()->show ();
1763 _primary->set_position (pos, 0);
1764 _views.push_back (DraggingView (_primary, this, v));
1766 _last_frame_position = pos;
1768 _item = _primary->get_canvas_group ();
1772 RegionInsertDrag::finished (GdkEvent *, bool)
1774 int pos = _views.front().time_axis_view;
1775 assert(pos >= 0 && pos < _time_axis_views.size());
1777 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1779 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1780 _primary->get_canvas_group()->set_y_position (0);
1782 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1784 _editor->begin_reversible_command (Operations::insert_region);
1785 playlist->clear_changes ();
1786 playlist->add_region (_primary->region (), _last_frame_position);
1788 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1789 if (Config->get_edit_mode() == Ripple) {
1790 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1793 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1794 _editor->commit_reversible_command ();
1802 RegionInsertDrag::aborted (bool)
1809 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1810 : RegionMoveDrag (e, i, p, v, false, false)
1812 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1815 struct RegionSelectionByPosition {
1816 bool operator() (RegionView*a, RegionView* b) {
1817 return a->region()->position () < b->region()->position();
1822 RegionSpliceDrag::motion (GdkEvent* event, bool)
1824 /* Which trackview is this ? */
1826 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1827 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1829 /* The region motion is only processed if the pointer is over
1833 if (!tv || !tv->is_track()) {
1834 /* To make sure we hide the verbose canvas cursor when the mouse is
1835 not held over an audio track.
1837 _editor->verbose_cursor()->hide ();
1840 _editor->verbose_cursor()->show ();
1845 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1851 RegionSelection copy;
1852 _editor->selection->regions.by_position(copy);
1854 framepos_t const pf = adjusted_current_frame (event);
1856 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1858 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1864 boost::shared_ptr<Playlist> playlist;
1866 if ((playlist = atv->playlist()) == 0) {
1870 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1875 if (pf < (*i)->region()->last_frame() + 1) {
1879 if (pf > (*i)->region()->first_frame()) {
1885 playlist->shuffle ((*i)->region(), dir);
1890 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1892 RegionMoveDrag::finished (event, movement_occurred);
1896 RegionSpliceDrag::aborted (bool)
1906 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1909 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1911 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1912 RegionSelection to_ripple;
1913 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1914 if ((*i)->position() >= where) {
1915 to_ripple.push_back (rtv->view()->find_view(*i));
1919 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1920 if (!exclude.contains (*i)) {
1921 // the selection has already been added to _views
1923 if (drag_in_progress) {
1924 // do the same things that RegionMotionDrag::motion does when
1925 // first_move is true, for the region views that we're adding
1926 // to _views this time
1929 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1930 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1931 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1932 rvg->reparent (_editor->_drag_motion_group);
1934 // we only need to move in the y direction
1935 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1940 _views.push_back (DraggingView (*i, this, tav));
1946 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1949 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1950 // we added all the regions after the selection
1952 std::list<DraggingView>::iterator to_erase = i++;
1953 if (!_editor->selection->regions.contains (to_erase->view)) {
1954 // restore the non-selected regions to their original playlist & positions,
1955 // and then ripple them back by the length of the regions that were dragged away
1956 // do the same things as RegionMotionDrag::aborted
1958 RegionView *rv = to_erase->view;
1959 TimeAxisView* tv = &(rv->get_time_axis_view ());
1960 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1963 // plonk them back onto their own track
1964 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1965 rv->get_canvas_group()->set_y_position (0);
1969 // move the underlying region to match the view
1970 rv->region()->set_position (rv->region()->position() + amount);
1972 // restore the view to match the underlying region's original position
1973 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1976 rv->set_height (rtv->view()->child_height ());
1977 _views.erase (to_erase);
1983 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
1985 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
1987 return allow_moves_across_tracks;
1995 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1996 : RegionMoveDrag (e, i, p, v, false, false)
1998 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1999 // compute length of selection
2000 RegionSelection selected_regions = _editor->selection->regions;
2001 selection_length = selected_regions.end_frame() - selected_regions.start();
2003 // we'll only allow dragging to another track in ripple mode if all the regions
2004 // being dragged start off on the same track
2005 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2008 exclude = new RegionList;
2009 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2010 exclude->push_back((*i)->region());
2013 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2014 RegionSelection copy;
2015 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2017 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2018 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2020 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2021 // find ripple start point on each applicable playlist
2022 RegionView *first_selected_on_this_track = NULL;
2023 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2024 if ((*i)->region()->playlist() == (*pi)) {
2025 // region is on this playlist - it's the first, because they're sorted
2026 first_selected_on_this_track = *i;
2030 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2031 add_all_after_to_views (
2032 &first_selected_on_this_track->get_time_axis_view(),
2033 first_selected_on_this_track->region()->position(),
2034 selected_regions, false);
2037 if (allow_moves_across_tracks) {
2038 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2046 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2048 /* Which trackview is this ? */
2050 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2051 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2053 /* The region motion is only processed if the pointer is over
2057 if (!tv || !tv->is_track()) {
2058 /* To make sure we hide the verbose canvas cursor when the mouse is
2059 not held over an audiotrack.
2061 _editor->verbose_cursor()->hide ();
2065 framepos_t where = adjusted_current_frame (event);
2066 assert (where >= 0);
2068 double delta = compute_x_delta (event, &after);
2070 framecnt_t amount = _editor->pixel_to_sample (delta);
2072 if (allow_moves_across_tracks) {
2073 // all the originally selected regions were on the same track
2075 framecnt_t adjust = 0;
2076 if (prev_tav && tv != prev_tav) {
2077 // dragged onto a different track
2078 // remove the unselected regions from _views, restore them to their original positions
2079 // and add the regions after the drop point on the new playlist to _views instead.
2080 // undo the effect of rippling the previous playlist, and include the effect of removing
2081 // the dragged region(s) from this track
2083 remove_unselected_from_views (prev_amount, false);
2084 // ripple previous playlist according to the regions that have been removed onto the new playlist
2085 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2088 // move just the selected regions
2089 RegionMoveDrag::motion(event, first_move);
2091 // ensure that the ripple operation on the new playlist inserts selection_length time
2092 adjust = selection_length;
2093 // ripple the new current playlist
2094 tv->playlist()->ripple (where, amount+adjust, exclude);
2096 // add regions after point where drag entered this track to subsequent ripples
2097 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2100 // motion on same track
2101 RegionMoveDrag::motion(event, first_move);
2105 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2106 prev_position = where;
2108 // selection encompasses multiple tracks - just drag
2109 // cross-track drags are forbidden
2110 RegionMoveDrag::motion(event, first_move);
2113 if (!_x_constrained) {
2114 prev_amount += amount;
2117 _last_frame_position = after;
2121 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2123 if (!movement_occurred) {
2127 if (was_double_click() && !_views.empty()) {
2128 DraggingView dv = _views.front();
2129 dv.view->show_region_editor ();
2136 _editor->begin_reversible_command(_("Ripple drag"));
2138 // remove the regions being rippled from the dragging view, updating them to
2139 // their new positions
2140 remove_unselected_from_views (prev_amount, true);
2142 if (allow_moves_across_tracks) {
2144 // if regions were dragged across tracks, we've rippled any later
2145 // regions on the track the regions were dragged off, so we need
2146 // to add the original track to the undo record
2147 orig_tav->playlist()->clear_changes();
2148 vector<Command*> cmds;
2149 orig_tav->playlist()->rdiff (cmds);
2150 _editor->session()->add_commands (cmds);
2152 if (prev_tav && prev_tav != orig_tav) {
2153 prev_tav->playlist()->clear_changes();
2154 vector<Command*> cmds;
2155 prev_tav->playlist()->rdiff (cmds);
2156 _editor->session()->add_commands (cmds);
2159 // selection spanned multiple tracks - all will need adding to undo record
2161 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2162 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2164 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2165 (*pi)->clear_changes();
2166 vector<Command*> cmds;
2167 (*pi)->rdiff (cmds);
2168 _editor->session()->add_commands (cmds);
2172 // other modified playlists are added to undo by RegionMoveDrag::finished()
2173 RegionMoveDrag::finished (event, movement_occurred);
2174 _editor->commit_reversible_command();
2178 RegionRippleDrag::aborted (bool movement_occurred)
2180 RegionMoveDrag::aborted (movement_occurred);
2185 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2187 _view (dynamic_cast<MidiTimeAxisView*> (v))
2189 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2195 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2198 _region = add_midi_region (_view);
2199 _view->playlist()->freeze ();
2202 framepos_t const f = adjusted_current_frame (event);
2203 if (f < grab_frame()) {
2204 _region->set_position (f);
2207 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2208 so that if this region is duplicated, its duplicate starts on
2209 a snap point rather than 1 frame after a snap point. Otherwise things get
2210 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2211 place snapped notes at the start of the region.
2214 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2215 _region->set_length (len < 1 ? 1 : len);
2221 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2223 if (!movement_occurred) {
2224 add_midi_region (_view);
2226 _view->playlist()->thaw ();
2231 RegionCreateDrag::aborted (bool)
2234 _view->playlist()->thaw ();
2240 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2244 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2248 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2250 Gdk::Cursor* cursor;
2251 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2253 float x_fraction = cnote->mouse_x_fraction ();
2255 if (x_fraction > 0.0 && x_fraction < 0.25) {
2256 cursor = _editor->cursors()->left_side_trim;
2259 cursor = _editor->cursors()->right_side_trim;
2263 Drag::start_grab (event, cursor);
2265 region = &cnote->region_view();
2269 if (event->motion.state & Keyboard::PrimaryModifier) {
2275 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2277 if (ms.size() > 1) {
2278 /* has to be relative, may make no sense otherwise */
2282 /* select this note; if it is already selected, preserve the existing selection,
2283 otherwise make this note the only one selected.
2285 region->note_selected (cnote, cnote->selected ());
2287 _editor->begin_reversible_command (_("resize notes"));
2289 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2290 MidiRegionSelection::iterator next;
2293 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2295 mrv->begin_resizing (at_front);
2302 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2304 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2305 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2306 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2308 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2310 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2316 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2318 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2319 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2320 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2322 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2324 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2328 _editor->commit_reversible_command ();
2332 NoteResizeDrag::aborted (bool)
2334 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2335 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2336 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2338 mrv->abort_resizing ();
2343 AVDraggingView::AVDraggingView (RegionView* v)
2346 initial_position = v->region()->position ();
2349 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2352 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2355 TrackViewList empty;
2357 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2358 std::list<RegionView*> views = rs.by_layer();
2360 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2361 RegionView* rv = (*i);
2362 if (!rv->region()->video_locked()) {
2365 _views.push_back (AVDraggingView (rv));
2370 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2372 Drag::start_grab (event);
2373 if (_editor->session() == 0) {
2377 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2378 _max_backwards_drag = (
2379 ARDOUR_UI::instance()->video_timeline->get_duration()
2380 + ARDOUR_UI::instance()->video_timeline->get_offset()
2381 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2384 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2385 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2386 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2389 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2392 Timecode::Time timecode;
2393 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2394 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);
2395 show_verbose_cursor_text (buf);
2399 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2401 if (_editor->session() == 0) {
2404 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2408 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2409 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2411 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2412 dt = - _max_backwards_drag;
2415 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2416 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2418 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2419 RegionView* rv = i->view;
2420 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2423 rv->region()->clear_changes ();
2424 rv->region()->suspend_property_changes();
2426 rv->region()->set_position(i->initial_position + dt);
2427 rv->region_changed(ARDOUR::Properties::position);
2430 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2431 Timecode::Time timecode;
2432 Timecode::Time timediff;
2434 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2435 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2436 snprintf (buf, sizeof (buf),
2437 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2438 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2439 , _("Video Start:"),
2440 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2442 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2444 show_verbose_cursor_text (buf);
2448 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2450 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2454 if (!movement_occurred || ! _editor->session()) {
2458 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2460 _editor->begin_reversible_command (_("Move Video"));
2462 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2463 ARDOUR_UI::instance()->video_timeline->save_undo();
2464 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2465 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2467 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2468 i->view->drag_end();
2469 i->view->region()->resume_property_changes ();
2471 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2474 _editor->session()->maybe_update_session_range(
2475 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2476 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2480 _editor->commit_reversible_command ();
2484 VideoTimeLineDrag::aborted (bool)
2486 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2489 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2490 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2492 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2493 i->view->region()->resume_property_changes ();
2494 i->view->region()->set_position(i->initial_position);
2498 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2499 : RegionDrag (e, i, p, v)
2500 , _preserve_fade_anchor (preserve_fade_anchor)
2501 , _jump_position_when_done (false)
2503 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2507 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2510 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2511 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2513 if (tv && tv->is_track()) {
2514 speed = tv->track()->speed();
2517 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2518 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2519 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2521 framepos_t const pf = adjusted_current_frame (event);
2523 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2524 /* Move the contents of the region around without changing the region bounds */
2525 _operation = ContentsTrim;
2526 Drag::start_grab (event, _editor->cursors()->trimmer);
2528 /* These will get overridden for a point trim.*/
2529 if (pf < (region_start + region_length/2)) {
2530 /* closer to front */
2531 _operation = StartTrim;
2533 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2534 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2536 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2540 _operation = EndTrim;
2541 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2542 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2544 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2549 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2550 _jump_position_when_done = true;
2553 switch (_operation) {
2555 show_verbose_cursor_time (region_start);
2556 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2557 i->view->trim_front_starting ();
2561 show_verbose_cursor_time (region_end);
2564 show_verbose_cursor_time (pf);
2568 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2569 i->view->region()->suspend_property_changes ();
2574 TrimDrag::motion (GdkEvent* event, bool first_move)
2576 RegionView* rv = _primary;
2579 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2580 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2581 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2582 frameoffset_t frame_delta = 0;
2584 if (tv && tv->is_track()) {
2585 speed = tv->track()->speed();
2588 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2594 switch (_operation) {
2596 trim_type = "Region start trim";
2599 trim_type = "Region end trim";
2602 trim_type = "Region content trim";
2609 _editor->begin_reversible_command (trim_type);
2611 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2612 RegionView* rv = i->view;
2613 rv->enable_display (false);
2614 rv->region()->playlist()->clear_owned_changes ();
2616 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2619 arv->temporarily_hide_envelope ();
2623 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2624 insert_result = _editor->motion_frozen_playlists.insert (pl);
2626 if (insert_result.second) {
2632 bool non_overlap_trim = false;
2634 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2635 non_overlap_trim = true;
2638 /* contstrain trim to fade length */
2639 if (_preserve_fade_anchor) {
2640 switch (_operation) {
2642 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2643 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2645 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2646 if (ar->locked()) continue;
2647 framecnt_t len = ar->fade_in()->back()->when;
2648 if (len < dt) dt = min(dt, len);
2652 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2653 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2655 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2656 if (ar->locked()) continue;
2657 framecnt_t len = ar->fade_out()->back()->when;
2658 if (len < -dt) dt = max(dt, -len);
2667 switch (_operation) {
2669 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2670 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2671 if (changed && _preserve_fade_anchor) {
2672 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2674 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2675 framecnt_t len = ar->fade_in()->back()->when;
2676 framecnt_t diff = ar->first_frame() - i->initial_position;
2677 framepos_t new_length = len - diff;
2678 i->anchored_fade_length = min (ar->length(), new_length);
2679 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2680 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2687 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2688 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2689 if (changed && _preserve_fade_anchor) {
2690 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2692 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2693 framecnt_t len = ar->fade_out()->back()->when;
2694 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2695 framepos_t new_length = len + diff;
2696 i->anchored_fade_length = min (ar->length(), new_length);
2697 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2698 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2706 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2708 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2709 i->view->move_contents (frame_delta);
2715 switch (_operation) {
2717 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2720 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2723 // show_verbose_cursor_time (frame_delta);
2730 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2732 if (movement_occurred) {
2733 motion (event, false);
2735 if (_operation == StartTrim) {
2736 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2738 /* This must happen before the region's StatefulDiffCommand is created, as it may
2739 `correct' (ahem) the region's _start from being negative to being zero. It
2740 needs to be zero in the undo record.
2742 i->view->trim_front_ending ();
2744 if (_preserve_fade_anchor) {
2745 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2747 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2748 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2749 ar->set_fade_in_length(i->anchored_fade_length);
2750 ar->set_fade_in_active(true);
2753 if (_jump_position_when_done) {
2754 i->view->region()->set_position (i->initial_position);
2757 } else if (_operation == EndTrim) {
2758 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2759 if (_preserve_fade_anchor) {
2760 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2762 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2763 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2764 ar->set_fade_out_length(i->anchored_fade_length);
2765 ar->set_fade_out_active(true);
2768 if (_jump_position_when_done) {
2769 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2774 if (!_views.empty()) {
2775 if (_operation == StartTrim) {
2776 _editor->maybe_locate_with_edit_preroll(
2777 _views.begin()->view->region()->position());
2779 if (_operation == EndTrim) {
2780 _editor->maybe_locate_with_edit_preroll(
2781 _views.begin()->view->region()->position() +
2782 _views.begin()->view->region()->length());
2786 if (!_editor->selection->selected (_primary)) {
2787 _primary->thaw_after_trim ();
2790 set<boost::shared_ptr<Playlist> > diffed_playlists;
2792 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2793 i->view->thaw_after_trim ();
2794 i->view->enable_display (true);
2796 /* Trimming one region may affect others on the playlist, so we need
2797 to get undo Commands from the whole playlist rather than just the
2798 region. Use diffed_playlists to make sure we don't diff a given
2799 playlist more than once.
2801 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2802 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2803 vector<Command*> cmds;
2805 _editor->session()->add_commands (cmds);
2806 diffed_playlists.insert (p);
2811 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2815 _editor->motion_frozen_playlists.clear ();
2816 _editor->commit_reversible_command();
2819 /* no mouse movement */
2820 _editor->point_trim (event, adjusted_current_frame (event));
2823 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2824 if (_operation == StartTrim) {
2825 i->view->trim_front_ending ();
2828 i->view->region()->resume_property_changes ();
2833 TrimDrag::aborted (bool movement_occurred)
2835 /* Our motion method is changing model state, so use the Undo system
2836 to cancel. Perhaps not ideal, as this will leave an Undo point
2837 behind which may be slightly odd from the user's point of view.
2842 if (movement_occurred) {
2846 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2847 i->view->region()->resume_property_changes ();
2852 TrimDrag::setup_pointer_frame_offset ()
2854 list<DraggingView>::iterator i = _views.begin ();
2855 while (i != _views.end() && i->view != _primary) {
2859 if (i == _views.end()) {
2863 switch (_operation) {
2865 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2868 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2875 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2879 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2880 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2885 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2887 Drag::start_grab (event, cursor);
2888 show_verbose_cursor_time (adjusted_current_frame(event));
2892 MeterMarkerDrag::setup_pointer_frame_offset ()
2894 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2898 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2900 if (!_marker->meter().movable()) {
2906 // create a dummy marker for visual representation of moving the
2907 // section, because whether its a copy or not, we're going to
2908 // leave or lose the original marker (leave if its a copy; lose if its
2909 // not, because we'll remove it from the map).
2911 MeterSection section (_marker->meter());
2913 if (!section.movable()) {
2918 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2920 _marker = new MeterMarker (
2922 *_editor->meter_group,
2923 ARDOUR_UI::config()->color ("meter marker"),
2925 *new MeterSection (_marker->meter())
2928 /* use the new marker for the grab */
2929 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2932 TempoMap& map (_editor->session()->tempo_map());
2933 /* get current state */
2934 before_state = &map.get_state();
2935 /* remove the section while we drag it */
2936 map.remove_meter (section, true);
2940 framepos_t const pf = adjusted_current_frame (event);
2942 _marker->set_position (pf);
2943 show_verbose_cursor_time (pf);
2947 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2949 if (!movement_occurred) {
2950 if (was_double_click()) {
2951 _editor->edit_meter_marker (*_marker);
2956 if (!_marker->meter().movable()) {
2960 motion (event, false);
2962 Timecode::BBT_Time when;
2964 TempoMap& map (_editor->session()->tempo_map());
2965 map.bbt_time (last_pointer_frame(), when);
2967 if (_copy == true) {
2968 _editor->begin_reversible_command (_("copy meter mark"));
2969 XMLNode &before = map.get_state();
2970 map.add_meter (_marker->meter(), when);
2971 XMLNode &after = map.get_state();
2972 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2973 _editor->commit_reversible_command ();
2976 _editor->begin_reversible_command (_("move meter mark"));
2978 /* we removed it before, so add it back now */
2980 map.add_meter (_marker->meter(), when);
2981 XMLNode &after = map.get_state();
2982 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2983 _editor->commit_reversible_command ();
2986 // delete the dummy marker we used for visual representation while moving.
2987 // a new visual marker will show up automatically.
2992 MeterMarkerDrag::aborted (bool moved)
2994 _marker->set_position (_marker->meter().frame ());
2997 TempoMap& map (_editor->session()->tempo_map());
2998 /* we removed it before, so add it back now */
2999 map.add_meter (_marker->meter(), _marker->meter().frame());
3000 // delete the dummy marker we used for visual representation while moving.
3001 // a new visual marker will show up automatically.
3006 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3010 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3012 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3017 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3019 Drag::start_grab (event, cursor);
3020 show_verbose_cursor_time (adjusted_current_frame (event));
3024 TempoMarkerDrag::setup_pointer_frame_offset ()
3026 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3030 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3032 if (!_marker->tempo().movable()) {
3038 // create a dummy marker for visual representation of moving the
3039 // section, because whether its a copy or not, we're going to
3040 // leave or lose the original marker (leave if its a copy; lose if its
3041 // not, because we'll remove it from the map).
3043 // create a dummy marker for visual representation of moving the copy.
3044 // The actual copying is not done before we reach the finish callback.
3047 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3049 TempoSection section (_marker->tempo());
3051 _marker = new TempoMarker (
3053 *_editor->tempo_group,
3054 ARDOUR_UI::config()->color ("tempo marker"),
3056 *new TempoSection (_marker->tempo())
3059 /* use the new marker for the grab */
3060 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3063 TempoMap& map (_editor->session()->tempo_map());
3064 /* get current state */
3065 before_state = &map.get_state();
3066 /* remove the section while we drag it */
3067 map.remove_tempo (section, true);
3071 framepos_t const pf = adjusted_current_frame (event);
3072 _marker->set_position (pf);
3073 show_verbose_cursor_time (pf);
3077 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3079 if (!movement_occurred) {
3080 if (was_double_click()) {
3081 _editor->edit_tempo_marker (*_marker);
3086 if (!_marker->tempo().movable()) {
3090 motion (event, false);
3092 TempoMap& map (_editor->session()->tempo_map());
3093 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3094 Timecode::BBT_Time when;
3096 map.bbt_time (beat_time, when);
3098 if (_copy == true) {
3099 _editor->begin_reversible_command (_("copy tempo mark"));
3100 XMLNode &before = map.get_state();
3101 map.add_tempo (_marker->tempo(), when);
3102 XMLNode &after = map.get_state();
3103 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3104 _editor->commit_reversible_command ();
3107 _editor->begin_reversible_command (_("move tempo mark"));
3108 /* we removed it before, so add it back now */
3109 map.add_tempo (_marker->tempo(), when);
3110 XMLNode &after = map.get_state();
3111 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3112 _editor->commit_reversible_command ();
3115 // delete the dummy marker we used for visual representation while moving.
3116 // a new visual marker will show up automatically.
3121 TempoMarkerDrag::aborted (bool moved)
3123 _marker->set_position (_marker->tempo().frame());
3125 TempoMap& map (_editor->session()->tempo_map());
3126 /* we removed it before, so add it back now */
3127 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3128 // delete the dummy marker we used for visual representation while moving.
3129 // a new visual marker will show up automatically.
3134 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3135 : Drag (e, &c.track_canvas_item(), false)
3139 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3142 /** Do all the things we do when dragging the playhead to make it look as though
3143 * we have located, without actually doing the locate (because that would cause
3144 * the diskstream buffers to be refilled, which is too slow).
3147 CursorDrag::fake_locate (framepos_t t)
3149 _editor->playhead_cursor->set_position (t);
3151 Session* s = _editor->session ();
3152 if (s->timecode_transmission_suspended ()) {
3153 framepos_t const f = _editor->playhead_cursor->current_frame ();
3154 /* This is asynchronous so it will be sent "now"
3156 s->send_mmc_locate (f);
3157 /* These are synchronous and will be sent during the next
3160 s->queue_full_time_code ();
3161 s->queue_song_position_pointer ();
3164 show_verbose_cursor_time (t);
3165 _editor->UpdateAllTransportClocks (t);
3169 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3171 Drag::start_grab (event, c);
3173 _grab_zoom = _editor->samples_per_pixel;
3175 framepos_t where = _editor->canvas_event_sample (event);
3177 _editor->snap_to_with_modifier (where, event);
3179 _editor->_dragging_playhead = true;
3181 Session* s = _editor->session ();
3183 /* grab the track canvas item as well */
3185 _cursor.track_canvas_item().grab();
3188 if (_was_rolling && _stop) {
3192 if (s->is_auditioning()) {
3193 s->cancel_audition ();
3197 if (AudioEngine::instance()->connected()) {
3199 /* do this only if we're the engine is connected
3200 * because otherwise this request will never be
3201 * serviced and we'll busy wait forever. likewise,
3202 * notice if we are disconnected while waiting for the
3203 * request to be serviced.
3206 s->request_suspend_timecode_transmission ();
3207 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3208 /* twiddle our thumbs */
3213 fake_locate (where);
3217 CursorDrag::motion (GdkEvent* event, bool)
3219 framepos_t const adjusted_frame = adjusted_current_frame (event);
3220 if (adjusted_frame != last_pointer_frame()) {
3221 fake_locate (adjusted_frame);
3226 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3228 _editor->_dragging_playhead = false;
3230 _cursor.track_canvas_item().ungrab();
3232 if (!movement_occurred && _stop) {
3236 motion (event, false);
3238 Session* s = _editor->session ();
3240 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3241 _editor->_pending_locate_request = true;
3242 s->request_resume_timecode_transmission ();
3247 CursorDrag::aborted (bool)
3249 _cursor.track_canvas_item().ungrab();
3251 if (_editor->_dragging_playhead) {
3252 _editor->session()->request_resume_timecode_transmission ();
3253 _editor->_dragging_playhead = false;
3256 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3259 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3260 : RegionDrag (e, i, p, v)
3262 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3266 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3268 Drag::start_grab (event, cursor);
3270 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3271 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3273 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3277 FadeInDrag::setup_pointer_frame_offset ()
3279 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3280 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3281 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3285 FadeInDrag::motion (GdkEvent* event, bool)
3287 framecnt_t fade_length;
3288 framepos_t const pos = adjusted_current_frame (event);
3289 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3291 if (pos < (region->position() + 64)) {
3292 fade_length = 64; // this should be a minimum defined somewhere
3293 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3294 fade_length = region->length() - region->fade_out()->back()->when - 1;
3296 fade_length = pos - region->position();
3299 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3301 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3307 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3310 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3314 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3316 if (!movement_occurred) {
3320 framecnt_t fade_length;
3322 framepos_t const pos = adjusted_current_frame (event);
3324 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3326 if (pos < (region->position() + 64)) {
3327 fade_length = 64; // this should be a minimum defined somewhere
3328 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3329 fade_length = region->length() - region->fade_out()->back()->when - 1;
3331 fade_length = pos - region->position();
3334 _editor->begin_reversible_command (_("change fade in length"));
3336 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3338 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3344 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3345 XMLNode &before = alist->get_state();
3347 tmp->audio_region()->set_fade_in_length (fade_length);
3348 tmp->audio_region()->set_fade_in_active (true);
3350 XMLNode &after = alist->get_state();
3351 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3354 _editor->commit_reversible_command ();
3358 FadeInDrag::aborted (bool)
3360 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3361 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3367 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3371 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3372 : RegionDrag (e, i, p, v)
3374 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3378 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3380 Drag::start_grab (event, cursor);
3382 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3383 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3385 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3389 FadeOutDrag::setup_pointer_frame_offset ()
3391 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3392 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3393 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3397 FadeOutDrag::motion (GdkEvent* event, bool)
3399 framecnt_t fade_length;
3401 framepos_t const pos = adjusted_current_frame (event);
3403 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3405 if (pos > (region->last_frame() - 64)) {
3406 fade_length = 64; // this should really be a minimum fade defined somewhere
3407 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3408 fade_length = region->length() - region->fade_in()->back()->when - 1;
3410 fade_length = region->last_frame() - pos;
3413 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3415 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3421 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3424 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3428 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3430 if (!movement_occurred) {
3434 framecnt_t fade_length;
3436 framepos_t const pos = adjusted_current_frame (event);
3438 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3440 if (pos > (region->last_frame() - 64)) {
3441 fade_length = 64; // this should really be a minimum fade defined somewhere
3442 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3443 fade_length = region->length() - region->fade_in()->back()->when - 1;
3445 fade_length = region->last_frame() - pos;
3448 _editor->begin_reversible_command (_("change fade out length"));
3450 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3452 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3458 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3459 XMLNode &before = alist->get_state();
3461 tmp->audio_region()->set_fade_out_length (fade_length);
3462 tmp->audio_region()->set_fade_out_active (true);
3464 XMLNode &after = alist->get_state();
3465 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3468 _editor->commit_reversible_command ();
3472 FadeOutDrag::aborted (bool)
3474 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3475 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3481 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3485 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3488 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3490 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3493 _points.push_back (ArdourCanvas::Duple (0, 0));
3494 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3497 MarkerDrag::~MarkerDrag ()
3499 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3504 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3506 location = new Location (*l);
3507 markers.push_back (m);
3512 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3514 Drag::start_grab (event, cursor);
3518 Location *location = _editor->find_location_from_marker (_marker, is_start);
3519 _editor->_dragging_edit_point = true;
3521 update_item (location);
3523 // _drag_line->show();
3524 // _line->raise_to_top();
3527 show_verbose_cursor_time (location->start());
3529 show_verbose_cursor_time (location->end());
3532 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3535 case Selection::Toggle:
3536 /* we toggle on the button release */
3538 case Selection::Set:
3539 if (!_editor->selection->selected (_marker)) {
3540 _editor->selection->set (_marker);
3543 case Selection::Extend:
3545 Locations::LocationList ll;
3546 list<Marker*> to_add;
3548 _editor->selection->markers.range (s, e);
3549 s = min (_marker->position(), s);
3550 e = max (_marker->position(), e);
3553 if (e < max_framepos) {
3556 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3557 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3558 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3561 to_add.push_back (lm->start);
3564 to_add.push_back (lm->end);
3568 if (!to_add.empty()) {
3569 _editor->selection->add (to_add);
3573 case Selection::Add:
3574 _editor->selection->add (_marker);
3578 /* Set up copies for us to manipulate during the drag
3581 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3583 Location* l = _editor->find_location_from_marker (*i, is_start);
3590 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3592 /* range: check that the other end of the range isn't
3595 CopiedLocationInfo::iterator x;
3596 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3597 if (*(*x).location == *l) {
3601 if (x == _copied_locations.end()) {
3602 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3604 (*x).markers.push_back (*i);
3605 (*x).move_both = true;
3613 MarkerDrag::setup_pointer_frame_offset ()
3616 Location *location = _editor->find_location_from_marker (_marker, is_start);
3617 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3621 MarkerDrag::motion (GdkEvent* event, bool)
3623 framecnt_t f_delta = 0;
3625 bool move_both = false;
3626 Location *real_location;
3627 Location *copy_location = 0;
3629 framepos_t const newframe = adjusted_current_frame (event);
3630 framepos_t next = newframe;
3632 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3636 CopiedLocationInfo::iterator x;
3638 /* find the marker we're dragging, and compute the delta */
3640 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3642 copy_location = (*x).location;
3644 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3646 /* this marker is represented by this
3647 * CopiedLocationMarkerInfo
3650 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3655 if (real_location->is_mark()) {
3656 f_delta = newframe - copy_location->start();
3660 switch (_marker->type()) {
3661 case Marker::SessionStart:
3662 case Marker::RangeStart:
3663 case Marker::LoopStart:
3664 case Marker::PunchIn:
3665 f_delta = newframe - copy_location->start();
3668 case Marker::SessionEnd:
3669 case Marker::RangeEnd:
3670 case Marker::LoopEnd:
3671 case Marker::PunchOut:
3672 f_delta = newframe - copy_location->end();
3675 /* what kind of marker is this ? */
3684 if (x == _copied_locations.end()) {
3685 /* hmm, impossible - we didn't find the dragged marker */
3689 /* now move them all */
3691 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3693 copy_location = x->location;
3695 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3699 if (real_location->locked()) {
3703 if (copy_location->is_mark()) {
3707 copy_location->set_start (copy_location->start() + f_delta);
3711 framepos_t new_start = copy_location->start() + f_delta;
3712 framepos_t new_end = copy_location->end() + f_delta;
3714 if (is_start) { // start-of-range marker
3716 if (move_both || (*x).move_both) {
3717 copy_location->set_start (new_start);
3718 copy_location->set_end (new_end);
3719 } else if (new_start < copy_location->end()) {
3720 copy_location->set_start (new_start);
3721 } else if (newframe > 0) {
3722 _editor->snap_to (next, RoundUpAlways, true);
3723 copy_location->set_end (next);
3724 copy_location->set_start (newframe);
3727 } else { // end marker
3729 if (move_both || (*x).move_both) {
3730 copy_location->set_end (new_end);
3731 copy_location->set_start (new_start);
3732 } else if (new_end > copy_location->start()) {
3733 copy_location->set_end (new_end);
3734 } else if (newframe > 0) {
3735 _editor->snap_to (next, RoundDownAlways, true);
3736 copy_location->set_start (next);
3737 copy_location->set_end (newframe);
3742 update_item (copy_location);
3744 /* now lookup the actual GUI items used to display this
3745 * location and move them to wherever the copy of the location
3746 * is now. This means that the logic in ARDOUR::Location is
3747 * still enforced, even though we are not (yet) modifying
3748 * the real Location itself.
3751 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3754 lm->set_position (copy_location->start(), copy_location->end());
3759 assert (!_copied_locations.empty());
3761 show_verbose_cursor_time (newframe);
3765 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3767 if (!movement_occurred) {
3769 if (was_double_click()) {
3770 _editor->rename_marker (_marker);
3774 /* just a click, do nothing but finish
3775 off the selection process
3778 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3781 case Selection::Set:
3782 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3783 _editor->selection->set (_marker);
3787 case Selection::Toggle:
3788 /* we toggle on the button release, click only */
3789 _editor->selection->toggle (_marker);
3792 case Selection::Extend:
3793 case Selection::Add:
3800 _editor->_dragging_edit_point = false;
3802 _editor->begin_reversible_command ( _("move marker") );
3803 XMLNode &before = _editor->session()->locations()->get_state();
3805 MarkerSelection::iterator i;
3806 CopiedLocationInfo::iterator x;
3809 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3810 x != _copied_locations.end() && i != _editor->selection->markers.end();
3813 Location * location = _editor->find_location_from_marker (*i, is_start);
3817 if (location->locked()) {
3821 if (location->is_mark()) {
3822 location->set_start (((*x).location)->start());
3824 location->set (((*x).location)->start(), ((*x).location)->end());
3829 XMLNode &after = _editor->session()->locations()->get_state();
3830 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3831 _editor->commit_reversible_command ();
3835 MarkerDrag::aborted (bool movement_occured)
3837 if (!movement_occured) {
3841 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3843 /* move all markers to their original location */
3846 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3849 Location * location = _editor->find_location_from_marker (*m, is_start);
3852 (*m)->set_position (is_start ? location->start() : location->end());
3859 MarkerDrag::update_item (Location*)
3864 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3866 _cumulative_x_drag (0),
3867 _cumulative_y_drag (0)
3869 if (_zero_gain_fraction < 0.0) {
3870 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3873 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3875 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3881 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3883 Drag::start_grab (event, _editor->cursors()->fader);
3885 // start the grab at the center of the control point so
3886 // the point doesn't 'jump' to the mouse after the first drag
3887 _fixed_grab_x = _point->get_x();
3888 _fixed_grab_y = _point->get_y();
3890 float const fraction = 1 - (_point->get_y() / _point->line().height());
3892 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3894 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3896 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3898 if (!_point->can_slide ()) {
3899 _x_constrained = true;
3904 ControlPointDrag::motion (GdkEvent* event, bool)
3906 double dx = _drags->current_pointer_x() - last_pointer_x();
3907 double dy = current_pointer_y() - last_pointer_y();
3909 if (event->button.state & Keyboard::SecondaryModifier) {
3914 /* coordinate in pixels relative to the start of the region (for region-based automation)
3915 or track (for track-based automation) */
3916 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3917 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3919 // calculate zero crossing point. back off by .01 to stay on the
3920 // positive side of zero
3921 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3923 // make sure we hit zero when passing through
3924 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3928 if (_x_constrained) {
3931 if (_y_constrained) {
3935 _cumulative_x_drag = cx - _fixed_grab_x;
3936 _cumulative_y_drag = cy - _fixed_grab_y;
3940 cy = min ((double) _point->line().height(), cy);
3942 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3944 if (!_x_constrained) {
3945 _editor->snap_to_with_modifier (cx_frames, event);
3948 cx_frames = min (cx_frames, _point->line().maximum_time());
3950 float const fraction = 1.0 - (cy / _point->line().height());
3952 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3954 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3958 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3960 if (!movement_occurred) {
3964 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3965 _editor->reset_point_selection ();
3969 motion (event, false);
3972 _point->line().end_drag (_pushing, _final_index);
3973 _editor->commit_reversible_command ();
3977 ControlPointDrag::aborted (bool)
3979 _point->line().reset ();
3983 ControlPointDrag::active (Editing::MouseMode m)
3985 if (m == Editing::MouseDraw) {
3986 /* always active in mouse draw */
3990 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3991 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3994 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3997 _cumulative_y_drag (0)
3999 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4003 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4005 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4008 _item = &_line->grab_item ();
4010 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4011 origin, and ditto for y.
4014 double cx = event->button.x;
4015 double cy = event->button.y;
4017 _line->parent_group().canvas_to_item (cx, cy);
4019 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4024 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4025 /* no adjacent points */
4029 Drag::start_grab (event, _editor->cursors()->fader);
4031 /* store grab start in parent frame */
4036 double fraction = 1.0 - (cy / _line->height());
4038 _line->start_drag_line (before, after, fraction);
4040 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4044 LineDrag::motion (GdkEvent* event, bool)
4046 double dy = current_pointer_y() - last_pointer_y();
4048 if (event->button.state & Keyboard::SecondaryModifier) {
4052 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4054 _cumulative_y_drag = cy - _fixed_grab_y;
4057 cy = min ((double) _line->height(), cy);
4059 double const fraction = 1.0 - (cy / _line->height());
4062 /* we are ignoring x position for this drag, so we can just pass in anything */
4063 _line->drag_motion (0, fraction, true, false, ignored);
4065 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4069 LineDrag::finished (GdkEvent* event, bool movement_occured)
4071 if (movement_occured) {
4072 motion (event, false);
4073 _line->end_drag (false, 0);
4075 /* add a new control point on the line */
4077 AutomationTimeAxisView* atv;
4079 _line->end_drag (false, 0);
4081 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4082 framepos_t where = _editor->window_event_sample (event, 0, 0);
4083 atv->add_automation_event (event, where, event->button.y, false);
4087 _editor->commit_reversible_command ();
4091 LineDrag::aborted (bool)
4096 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4099 _cumulative_x_drag (0)
4101 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4105 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4107 Drag::start_grab (event);
4109 _line = reinterpret_cast<Line*> (_item);
4112 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4114 double cx = event->button.x;
4115 double cy = event->button.y;
4117 _item->parent()->canvas_to_item (cx, cy);
4119 /* store grab start in parent frame */
4120 _region_view_grab_x = cx;
4122 _before = *(float*) _item->get_data ("position");
4124 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4126 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4130 FeatureLineDrag::motion (GdkEvent*, bool)
4132 double dx = _drags->current_pointer_x() - last_pointer_x();
4134 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4136 _cumulative_x_drag += dx;
4138 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4147 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4149 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4151 float *pos = new float;
4154 _line->set_data ("position", pos);
4160 FeatureLineDrag::finished (GdkEvent*, bool)
4162 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4163 _arv->update_transient(_before, _before);
4167 FeatureLineDrag::aborted (bool)
4172 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4174 , _vertical_only (false)
4176 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4180 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4182 Drag::start_grab (event);
4183 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4187 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4194 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4196 framepos_t grab = grab_frame ();
4197 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4198 _editor->snap_to_with_modifier (grab, event);
4200 grab = raw_grab_frame ();
4203 /* base start and end on initial click position */
4213 if (current_pointer_y() < grab_y()) {
4214 y1 = current_pointer_y();
4217 y2 = current_pointer_y();
4221 if (start != end || y1 != y2) {
4223 double x1 = _editor->sample_to_pixel (start);
4224 double x2 = _editor->sample_to_pixel (end);
4225 const double min_dimension = 2.0;
4227 if (_vertical_only) {
4228 /* fixed 10 pixel width */
4232 x2 = min (x1 - min_dimension, x2);
4234 x2 = max (x1 + min_dimension, x2);
4239 y2 = min (y1 - min_dimension, y2);
4241 y2 = max (y1 + min_dimension, y2);
4244 /* translate rect into item space and set */
4246 ArdourCanvas::Rect r (x1, y1, x2, y2);
4248 /* this drag is a _trackview_only == true drag, so the y1 and
4249 * y2 (computed using current_pointer_y() and grab_y()) will be
4250 * relative to the top of the trackview group). The
4251 * rubberband rect has the same parent/scroll offset as the
4252 * the trackview group, so we can use the "r" rect directly
4253 * to set the shape of the rubberband.
4256 _editor->rubberband_rect->set (r);
4257 _editor->rubberband_rect->show();
4258 _editor->rubberband_rect->raise_to_top();
4260 show_verbose_cursor_time (pf);
4262 do_select_things (event, true);
4267 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4271 framepos_t grab = grab_frame ();
4272 framepos_t lpf = last_pointer_frame ();
4274 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4275 grab = raw_grab_frame ();
4276 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4290 if (current_pointer_y() < grab_y()) {
4291 y1 = current_pointer_y();
4294 y2 = current_pointer_y();
4298 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4302 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4304 if (movement_occurred) {
4306 motion (event, false);
4307 do_select_things (event, false);
4313 bool do_deselect = true;
4314 MidiTimeAxisView* mtv;
4316 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4318 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4319 /* nothing selected */
4320 add_midi_region (mtv);
4321 do_deselect = false;
4325 /* do not deselect if Primary or Tertiary (toggle-select or
4326 * extend-select are pressed.
4329 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4330 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4337 _editor->rubberband_rect->hide();
4341 RubberbandSelectDrag::aborted (bool)
4343 _editor->rubberband_rect->hide ();
4346 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4347 : RegionDrag (e, i, p, v)
4349 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4353 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4355 Drag::start_grab (event, cursor);
4357 show_verbose_cursor_time (adjusted_current_frame (event));
4361 TimeFXDrag::motion (GdkEvent* event, bool)
4363 RegionView* rv = _primary;
4364 StreamView* cv = rv->get_time_axis_view().view ();
4366 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4367 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4368 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4370 framepos_t const pf = adjusted_current_frame (event);
4372 if (pf > rv->region()->position()) {
4373 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4376 show_verbose_cursor_time (pf);
4380 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4382 _primary->get_time_axis_view().hide_timestretch ();
4384 if (!movement_occurred) {
4388 if (last_pointer_frame() < _primary->region()->position()) {
4389 /* backwards drag of the left edge - not usable */
4393 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4395 float percentage = (double) newlen / (double) _primary->region()->length();
4397 #ifndef USE_RUBBERBAND
4398 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4399 if (_primary->region()->data_type() == DataType::AUDIO) {
4400 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4404 if (!_editor->get_selection().regions.empty()) {
4405 /* primary will already be included in the selection, and edit
4406 group shared editing will propagate selection across
4407 equivalent regions, so just use the current region
4411 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4412 error << _("An error occurred while executing time stretch operation") << endmsg;
4418 TimeFXDrag::aborted (bool)
4420 _primary->get_time_axis_view().hide_timestretch ();
4423 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4426 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4430 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4432 Drag::start_grab (event);
4436 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4438 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4442 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4444 if (movement_occurred && _editor->session()) {
4445 /* make sure we stop */
4446 _editor->session()->request_transport_speed (0.0);
4451 ScrubDrag::aborted (bool)
4456 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4460 , _original_pointer_time_axis (-1)
4461 , _time_selection_at_start (!_editor->get_selection().time.empty())
4463 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4465 if (_time_selection_at_start) {
4466 start_at_start = _editor->get_selection().time.start();
4467 end_at_start = _editor->get_selection().time.end_frame();
4472 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4474 if (_editor->session() == 0) {
4478 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4480 switch (_operation) {
4481 case CreateSelection:
4482 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4487 cursor = _editor->cursors()->selector;
4488 Drag::start_grab (event, cursor);
4491 case SelectionStartTrim:
4492 if (_editor->clicked_axisview) {
4493 _editor->clicked_axisview->order_selection_trims (_item, true);
4495 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4498 case SelectionEndTrim:
4499 if (_editor->clicked_axisview) {
4500 _editor->clicked_axisview->order_selection_trims (_item, false);
4502 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4506 Drag::start_grab (event, cursor);
4509 case SelectionExtend:
4510 Drag::start_grab (event, cursor);
4514 if (_operation == SelectionMove) {
4515 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4517 show_verbose_cursor_time (adjusted_current_frame (event));
4520 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4524 SelectionDrag::setup_pointer_frame_offset ()
4526 switch (_operation) {
4527 case CreateSelection:
4528 _pointer_frame_offset = 0;
4531 case SelectionStartTrim:
4533 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4536 case SelectionEndTrim:
4537 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4540 case SelectionExtend:
4546 SelectionDrag::motion (GdkEvent* event, bool first_move)
4548 framepos_t start = 0;
4550 framecnt_t length = 0;
4551 framecnt_t distance = 0;
4553 framepos_t const pending_position = adjusted_current_frame (event);
4555 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4559 switch (_operation) {
4560 case CreateSelection:
4562 framepos_t grab = grab_frame ();
4565 grab = adjusted_current_frame (event, false);
4566 if (grab < pending_position) {
4567 _editor->snap_to (grab, RoundDownMaybe);
4569 _editor->snap_to (grab, RoundUpMaybe);
4573 if (pending_position < grab) {
4574 start = pending_position;
4577 end = pending_position;
4581 /* first drag: Either add to the selection
4582 or create a new selection
4589 /* adding to the selection */
4590 _editor->set_selected_track_as_side_effect (Selection::Add);
4591 _editor->clicked_selection = _editor->selection->add (start, end);
4598 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4599 _editor->set_selected_track_as_side_effect (Selection::Set);
4602 _editor->clicked_selection = _editor->selection->set (start, end);
4606 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4607 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4608 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4610 _editor->selection->add (atest);
4614 /* select all tracks within the rectangle that we've marked out so far */
4615 TrackViewList new_selection;
4616 TrackViewList& all_tracks (_editor->track_views);
4618 ArdourCanvas::Coord const top = grab_y();
4619 ArdourCanvas::Coord const bottom = current_pointer_y();
4621 if (top >= 0 && bottom >= 0) {
4623 //first, find the tracks that are covered in the y range selection
4624 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4625 if ((*i)->covered_by_y_range (top, bottom)) {
4626 new_selection.push_back (*i);
4630 //now find any tracks that are GROUPED with the tracks we selected
4631 TrackViewList grouped_add = new_selection;
4632 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4633 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4634 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4635 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4636 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4637 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4638 grouped_add.push_back (*j);
4643 //now compare our list with the current selection, and add or remove as necessary
4644 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4645 TrackViewList tracks_to_add;
4646 TrackViewList tracks_to_remove;
4647 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4648 if ( !_editor->selection->tracks.contains ( *i ) )
4649 tracks_to_add.push_back ( *i );
4650 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4651 if ( !grouped_add.contains ( *i ) )
4652 tracks_to_remove.push_back ( *i );
4653 _editor->selection->add(tracks_to_add);
4654 _editor->selection->remove(tracks_to_remove);
4660 case SelectionStartTrim:
4662 start = _editor->selection->time[_editor->clicked_selection].start;
4663 end = _editor->selection->time[_editor->clicked_selection].end;
4665 if (pending_position > end) {
4668 start = pending_position;
4672 case SelectionEndTrim:
4674 start = _editor->selection->time[_editor->clicked_selection].start;
4675 end = _editor->selection->time[_editor->clicked_selection].end;
4677 if (pending_position < start) {
4680 end = pending_position;
4687 start = _editor->selection->time[_editor->clicked_selection].start;
4688 end = _editor->selection->time[_editor->clicked_selection].end;
4690 length = end - start;
4691 distance = pending_position - start;
4692 start = pending_position;
4693 _editor->snap_to (start);
4695 end = start + length;
4699 case SelectionExtend:
4704 switch (_operation) {
4706 if (_time_selection_at_start) {
4707 _editor->selection->move_time (distance);
4711 _editor->selection->replace (_editor->clicked_selection, start, end);
4715 if (_operation == SelectionMove) {
4716 show_verbose_cursor_time(start);
4718 show_verbose_cursor_time(pending_position);
4723 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4725 Session* s = _editor->session();
4727 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4728 if (movement_occurred) {
4729 motion (event, false);
4730 /* XXX this is not object-oriented programming at all. ick */
4731 if (_editor->selection->time.consolidate()) {
4732 _editor->selection->TimeChanged ();
4735 /* XXX what if its a music time selection? */
4737 if ( s->get_play_range() && s->transport_rolling() ) {
4738 s->request_play_range (&_editor->selection->time, true);
4740 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4741 if (_operation == SelectionEndTrim)
4742 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4744 s->request_locate (_editor->get_selection().time.start());
4750 /* just a click, no pointer movement.
4753 if (_operation == SelectionExtend) {
4754 if (_time_selection_at_start) {
4755 framepos_t pos = adjusted_current_frame (event, false);
4756 framepos_t start = min (pos, start_at_start);
4757 framepos_t end = max (pos, end_at_start);
4758 _editor->selection->set (start, end);
4761 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4762 if (_editor->clicked_selection) {
4763 _editor->selection->remove (_editor->clicked_selection);
4766 if (!_editor->clicked_selection) {
4767 _editor->selection->clear_time();
4772 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4773 _editor->selection->set (_editor->clicked_axisview);
4776 if (s && s->get_play_range () && s->transport_rolling()) {
4777 s->request_stop (false, false);
4782 _editor->stop_canvas_autoscroll ();
4783 _editor->clicked_selection = 0;
4784 _editor->commit_reversible_selection_op ();
4788 SelectionDrag::aborted (bool)
4793 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4794 : Drag (e, i, false),
4798 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4800 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4801 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4802 physical_screen_height (_editor->get_window())));
4803 _drag_rect->hide ();
4805 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4806 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4809 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4811 /* normal canvas items will be cleaned up when their parent group is deleted. But
4812 this item is created as the child of a long-lived parent group, and so we
4813 need to explicitly delete it.
4819 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4821 if (_editor->session() == 0) {
4825 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4827 if (!_editor->temp_location) {
4828 _editor->temp_location = new Location (*_editor->session());
4831 switch (_operation) {
4832 case CreateSkipMarker:
4833 case CreateRangeMarker:
4834 case CreateTransportMarker:
4835 case CreateCDMarker:
4837 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4842 cursor = _editor->cursors()->selector;
4846 Drag::start_grab (event, cursor);
4848 show_verbose_cursor_time (adjusted_current_frame (event));
4852 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4854 framepos_t start = 0;
4856 ArdourCanvas::Rectangle *crect;
4858 switch (_operation) {
4859 case CreateSkipMarker:
4860 crect = _editor->range_bar_drag_rect;
4862 case CreateRangeMarker:
4863 crect = _editor->range_bar_drag_rect;
4865 case CreateTransportMarker:
4866 crect = _editor->transport_bar_drag_rect;
4868 case CreateCDMarker:
4869 crect = _editor->cd_marker_bar_drag_rect;
4872 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4877 framepos_t const pf = adjusted_current_frame (event);
4879 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4880 framepos_t grab = grab_frame ();
4881 _editor->snap_to (grab);
4883 if (pf < grab_frame()) {
4891 /* first drag: Either add to the selection
4892 or create a new selection.
4897 _editor->temp_location->set (start, end);
4901 update_item (_editor->temp_location);
4903 //_drag_rect->raise_to_top();
4909 _editor->temp_location->set (start, end);
4911 double x1 = _editor->sample_to_pixel (start);
4912 double x2 = _editor->sample_to_pixel (end);
4916 update_item (_editor->temp_location);
4919 show_verbose_cursor_time (pf);
4924 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4926 Location * newloc = 0;
4930 if (movement_occurred) {
4931 motion (event, false);
4934 switch (_operation) {
4935 case CreateSkipMarker:
4936 case CreateRangeMarker:
4937 case CreateCDMarker:
4939 XMLNode &before = _editor->session()->locations()->get_state();
4940 if (_operation == CreateSkipMarker) {
4941 _editor->begin_reversible_command (_("new skip marker"));
4942 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4943 flags = Location::IsRangeMarker | Location::IsSkip;
4944 _editor->range_bar_drag_rect->hide();
4945 } else if (_operation == CreateCDMarker) {
4946 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4947 _editor->begin_reversible_command (_("new CD marker"));
4948 flags = Location::IsRangeMarker | Location::IsCDMarker;
4949 _editor->cd_marker_bar_drag_rect->hide();
4951 _editor->begin_reversible_command (_("new skip marker"));
4952 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4953 flags = Location::IsRangeMarker;
4954 _editor->range_bar_drag_rect->hide();
4956 newloc = new Location (
4957 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4960 _editor->session()->locations()->add (newloc, true);
4961 XMLNode &after = _editor->session()->locations()->get_state();
4962 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4963 _editor->commit_reversible_command ();
4967 case CreateTransportMarker:
4968 // popup menu to pick loop or punch
4969 _editor->new_transport_marker_context_menu (&event->button, _item);
4975 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4977 if (_operation == CreateTransportMarker) {
4979 /* didn't drag, so just locate */
4981 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4983 } else if (_operation == CreateCDMarker) {
4985 /* didn't drag, but mark is already created so do
4988 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4993 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4995 if (end == max_framepos) {
4996 end = _editor->session()->current_end_frame ();
4999 if (start == max_framepos) {
5000 start = _editor->session()->current_start_frame ();
5003 switch (_editor->mouse_mode) {
5005 /* find the two markers on either side and then make the selection from it */
5006 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5010 /* find the two markers on either side of the click and make the range out of it */
5011 _editor->selection->set (start, end);
5020 _editor->stop_canvas_autoscroll ();
5024 RangeMarkerBarDrag::aborted (bool movement_occured)
5026 if (movement_occured) {
5027 _drag_rect->hide ();
5032 RangeMarkerBarDrag::update_item (Location* location)
5034 double const x1 = _editor->sample_to_pixel (location->start());
5035 double const x2 = _editor->sample_to_pixel (location->end());
5037 _drag_rect->set_x0 (x1);
5038 _drag_rect->set_x1 (x2);
5041 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5043 , _cumulative_dx (0)
5044 , _cumulative_dy (0)
5046 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5048 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5050 _region = &_primary->region_view ();
5051 _note_height = _region->midi_stream_view()->note_height ();
5055 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5057 Drag::start_grab (event);
5059 if (!(_was_selected = _primary->selected())) {
5061 /* tertiary-click means extend selection - we'll do that on button release,
5062 so don't add it here, because otherwise we make it hard to figure
5063 out the "extend-to" range.
5066 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5069 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5072 _region->note_selected (_primary, true);
5074 _region->unique_select (_primary);
5077 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5078 _editor->commit_reversible_selection_op();
5083 /** @return Current total drag x change in frames */
5085 NoteDrag::total_dx () const
5088 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5090 /* primary note time */
5091 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5093 /* new time of the primary note in session frames */
5094 frameoffset_t st = n + dx;
5096 framepos_t const rp = _region->region()->position ();
5098 /* prevent the note being dragged earlier than the region's position */
5101 /* snap and return corresponding delta */
5102 return _region->snap_frame_to_frame (st - rp) + rp - n;
5105 /** @return Current total drag y change in note number */
5107 NoteDrag::total_dy () const
5109 MidiStreamView* msv = _region->midi_stream_view ();
5110 double const y = _region->midi_view()->y_position ();
5111 /* new current note */
5112 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5114 n = max (msv->lowest_note(), n);
5115 n = min (msv->highest_note(), n);
5116 /* and work out delta */
5117 return n - msv->y_to_note (grab_y() - y);
5121 NoteDrag::motion (GdkEvent *, bool)
5123 /* Total change in x and y since the start of the drag */
5124 frameoffset_t const dx = total_dx ();
5125 int8_t const dy = total_dy ();
5127 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5128 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5129 double const tdy = -dy * _note_height - _cumulative_dy;
5132 _cumulative_dx += tdx;
5133 _cumulative_dy += tdy;
5135 int8_t note_delta = total_dy();
5137 _region->move_selection (tdx, tdy, note_delta);
5139 /* the new note value may be the same as the old one, but we
5140 * don't know what that means because the selection may have
5141 * involved more than one note and we might be doing something
5142 * odd with them. so show the note value anyway, always.
5146 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5148 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5149 (int) floor ((double)new_note));
5151 show_verbose_cursor_text (buf);
5156 NoteDrag::finished (GdkEvent* ev, bool moved)
5159 /* no motion - select note */
5161 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5162 _editor->current_mouse_mode() == Editing::MouseDraw) {
5164 bool changed = false;
5166 if (_was_selected) {
5167 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5169 _region->note_deselected (_primary);
5173 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5174 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5176 if (!extend && !add && _region->selection_size() > 1) {
5177 _region->unique_select (_primary);
5179 } else if (extend) {
5180 _region->note_selected (_primary, true, true);
5183 /* it was added during button press */
5188 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5189 _editor->commit_reversible_selection_op();
5193 _region->note_dropped (_primary, total_dx(), total_dy());
5198 NoteDrag::aborted (bool)
5203 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5204 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5205 : Drag (editor, atv->base_item ())
5207 , _y_origin (atv->y_position())
5208 , _nothing_to_drag (false)
5210 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5211 setup (atv->lines ());
5214 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5215 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5216 : Drag (editor, rv->get_canvas_group ())
5218 , _y_origin (rv->get_time_axis_view().y_position())
5219 , _nothing_to_drag (false)
5222 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5224 list<boost::shared_ptr<AutomationLine> > lines;
5226 AudioRegionView* audio_view;
5227 AutomationRegionView* automation_view;
5228 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5229 lines.push_back (audio_view->get_gain_line ());
5230 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5231 lines.push_back (automation_view->line ());
5234 error << _("Automation range drag created for invalid region type") << endmsg;
5240 /** @param lines AutomationLines to drag.
5241 * @param offset Offset from the session start to the points in the AutomationLines.
5244 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5246 /* find the lines that overlap the ranges being dragged */
5247 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5248 while (i != lines.end ()) {
5249 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5252 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5254 /* check this range against all the AudioRanges that we are using */
5255 list<AudioRange>::const_iterator k = _ranges.begin ();
5256 while (k != _ranges.end()) {
5257 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5263 /* add it to our list if it overlaps at all */
5264 if (k != _ranges.end()) {
5269 _lines.push_back (n);
5275 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5279 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5281 return 1.0 - ((global_y - _y_origin) / line->height());
5285 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5287 const double v = list->eval(x);
5288 return _integral ? rint(v) : v;
5292 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5294 Drag::start_grab (event, cursor);
5296 /* Get line states before we start changing things */
5297 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5298 i->state = &i->line->get_state ();
5299 i->original_fraction = y_fraction (i->line, current_pointer_y());
5302 if (_ranges.empty()) {
5304 /* No selected time ranges: drag all points */
5305 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5306 uint32_t const N = i->line->npoints ();
5307 for (uint32_t j = 0; j < N; ++j) {
5308 i->points.push_back (i->line->nth (j));
5314 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5316 framecnt_t const half = (i->start + i->end) / 2;
5318 /* find the line that this audio range starts in */
5319 list<Line>::iterator j = _lines.begin();
5320 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5324 if (j != _lines.end()) {
5325 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5327 /* j is the line that this audio range starts in; fade into it;
5328 64 samples length plucked out of thin air.
5331 framepos_t a = i->start + 64;
5336 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5337 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5339 the_list->editor_add (p, value (the_list, p));
5340 the_list->editor_add (q, value (the_list, q));
5343 /* same thing for the end */
5346 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5350 if (j != _lines.end()) {
5351 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5353 /* j is the line that this audio range starts in; fade out of it;
5354 64 samples length plucked out of thin air.
5357 framepos_t b = i->end - 64;
5362 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5363 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5365 the_list->editor_add (p, value (the_list, p));
5366 the_list->editor_add (q, value (the_list, q));
5370 _nothing_to_drag = true;
5372 /* Find all the points that should be dragged and put them in the relevant
5373 points lists in the Line structs.
5376 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5378 uint32_t const N = i->line->npoints ();
5379 for (uint32_t j = 0; j < N; ++j) {
5381 /* here's a control point on this line */
5382 ControlPoint* p = i->line->nth (j);
5383 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5385 /* see if it's inside a range */
5386 list<AudioRange>::const_iterator k = _ranges.begin ();
5387 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5391 if (k != _ranges.end()) {
5392 /* dragging this point */
5393 _nothing_to_drag = false;
5394 i->points.push_back (p);
5400 if (_nothing_to_drag) {
5404 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5405 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5410 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5412 if (_nothing_to_drag) {
5416 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5417 float const f = y_fraction (l->line, current_pointer_y());
5418 /* we are ignoring x position for this drag, so we can just pass in anything */
5420 l->line->drag_motion (0, f, true, false, ignored);
5421 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5426 AutomationRangeDrag::finished (GdkEvent* event, bool)
5428 if (_nothing_to_drag) {
5432 motion (event, false);
5433 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5434 i->line->end_drag (false, 0);
5437 _editor->commit_reversible_command ();
5441 AutomationRangeDrag::aborted (bool)
5443 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5448 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5450 , initial_time_axis_view (itav)
5452 /* note that time_axis_view may be null if the regionview was created
5453 * as part of a copy operation.
5455 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5456 layer = v->region()->layer ();
5457 initial_y = v->get_canvas_group()->position().y;
5458 initial_playlist = v->region()->playlist ();
5459 initial_position = v->region()->position ();
5460 initial_end = v->region()->position () + v->region()->length ();
5463 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5464 : Drag (e, i->canvas_item ())
5467 , _cumulative_dx (0)
5469 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5470 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5475 PatchChangeDrag::motion (GdkEvent* ev, bool)
5477 framepos_t f = adjusted_current_frame (ev);
5478 boost::shared_ptr<Region> r = _region_view->region ();
5479 f = max (f, r->position ());
5480 f = min (f, r->last_frame ());
5482 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5483 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5484 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5485 _cumulative_dx = dxu;
5489 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5491 if (!movement_occurred) {
5495 boost::shared_ptr<Region> r (_region_view->region ());
5496 framepos_t f = adjusted_current_frame (ev);
5497 f = max (f, r->position ());
5498 f = min (f, r->last_frame ());
5500 _region_view->move_patch_change (
5502 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5507 PatchChangeDrag::aborted (bool)
5509 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5513 PatchChangeDrag::setup_pointer_frame_offset ()
5515 boost::shared_ptr<Region> region = _region_view->region ();
5516 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5519 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5520 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5527 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5529 _region_view->update_drag_selection (
5531 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5535 MidiRubberbandSelectDrag::deselect_things ()
5540 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5541 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5544 _vertical_only = true;
5548 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5550 double const y = _region_view->midi_view()->y_position ();
5552 y1 = max (0.0, y1 - y);
5553 y2 = max (0.0, y2 - y);
5555 _region_view->update_vertical_drag_selection (
5558 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5563 MidiVerticalSelectDrag::deselect_things ()
5568 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5569 : RubberbandSelectDrag (e, i)
5575 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5577 if (drag_in_progress) {
5578 /* We just want to select things at the end of the drag, not during it */
5582 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5584 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5586 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5588 _editor->commit_reversible_selection_op ();
5592 EditorRubberbandSelectDrag::deselect_things ()
5594 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5596 _editor->selection->clear_tracks();
5597 _editor->selection->clear_regions();
5598 _editor->selection->clear_points ();
5599 _editor->selection->clear_lines ();
5600 _editor->selection->clear_midi_notes ();
5602 _editor->commit_reversible_selection_op();
5605 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5610 _note[0] = _note[1] = 0;
5613 NoteCreateDrag::~NoteCreateDrag ()
5619 NoteCreateDrag::grid_frames (framepos_t t) const
5622 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5624 grid_beats = Evoral::Beats(1);
5627 return _region_view->region_beats_to_region_frames (grid_beats);
5631 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5633 Drag::start_grab (event, cursor);
5635 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5637 framepos_t pf = _drags->current_pointer_frame ();
5638 framecnt_t const g = grid_frames (pf);
5640 /* Hack so that we always snap to the note that we are over, instead of snapping
5641 to the next one if we're more than halfway through the one we're over.
5643 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5647 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5648 _note[1] = _note[0];
5650 MidiStreamView* sv = _region_view->midi_stream_view ();
5651 double const x = _editor->sample_to_pixel (_note[0]);
5652 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5654 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5655 _drag_rect->set_outline_all ();
5656 _drag_rect->set_outline_color (0xffffff99);
5657 _drag_rect->set_fill_color (0xffffff66);
5661 NoteCreateDrag::motion (GdkEvent* event, bool)
5663 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5664 double const x0 = _editor->sample_to_pixel (_note[0]);
5665 double const x1 = _editor->sample_to_pixel (_note[1]);
5666 _drag_rect->set_x0 (std::min(x0, x1));
5667 _drag_rect->set_x1 (std::max(x0, x1));
5671 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5673 if (!had_movement) {
5677 framepos_t const start = min (_note[0], _note[1]);
5678 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5680 framecnt_t const g = grid_frames (start);
5681 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5683 if (_editor->snap_mode() == SnapNormal && length < g) {
5687 Evoral::Beats length_beats = max (
5688 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5690 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5694 NoteCreateDrag::y_to_region (double y) const
5697 _region_view->get_canvas_group()->canvas_to_item (x, y);
5702 NoteCreateDrag::aborted (bool)
5707 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5712 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5716 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5718 Drag::start_grab (event, cursor);
5722 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5728 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5731 distance = _drags->current_pointer_x() - grab_x();
5732 len = ar->fade_in()->back()->when;
5734 distance = grab_x() - _drags->current_pointer_x();
5735 len = ar->fade_out()->back()->when;
5738 /* how long should it be ? */
5740 new_length = len + _editor->pixel_to_sample (distance);
5742 /* now check with the region that this is legal */
5744 new_length = ar->verify_xfade_bounds (new_length, start);
5747 arv->reset_fade_in_shape_width (ar, new_length);
5749 arv->reset_fade_out_shape_width (ar, new_length);
5754 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5760 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5763 distance = _drags->current_pointer_x() - grab_x();
5764 len = ar->fade_in()->back()->when;
5766 distance = grab_x() - _drags->current_pointer_x();
5767 len = ar->fade_out()->back()->when;
5770 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5772 _editor->begin_reversible_command ("xfade trim");
5773 ar->playlist()->clear_owned_changes ();
5776 ar->set_fade_in_length (new_length);
5778 ar->set_fade_out_length (new_length);
5781 /* Adjusting the xfade may affect other regions in the playlist, so we need
5782 to get undo Commands from the whole playlist rather than just the
5786 vector<Command*> cmds;
5787 ar->playlist()->rdiff (cmds);
5788 _editor->session()->add_commands (cmds);
5789 _editor->commit_reversible_command ();
5794 CrossfadeEdgeDrag::aborted (bool)
5797 // arv->redraw_start_xfade ();
5799 // arv->redraw_end_xfade ();
5803 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5804 : Drag (e, item, true)
5805 , line (new EditorCursor (*e))
5807 line->set_position (pos);
5811 RegionCutDrag::~RegionCutDrag ()
5817 RegionCutDrag::motion (GdkEvent*, bool)
5819 framepos_t where = _drags->current_pointer_frame();
5820 _editor->snap_to (where);
5822 line->set_position (where);
5826 RegionCutDrag::finished (GdkEvent*, bool)
5828 _editor->get_track_canvas()->canvas()->re_enter();
5830 framepos_t pos = _drags->current_pointer_frame();
5834 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5840 _editor->split_regions_at (pos, rs);
5844 RegionCutDrag::aborted (bool)