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) {
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 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
581 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
583 /* compute the amount of pointer motion in frames, and where
584 the region would be if we moved it by that much.
586 *pending_region_position = adjusted_current_frame (event);
588 framepos_t sync_frame;
589 framecnt_t sync_offset;
592 sync_offset = _primary->region()->sync_offset (sync_dir);
594 /* we don't handle a sync point that lies before zero.
596 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
598 sync_frame = *pending_region_position + (sync_dir*sync_offset);
600 _editor->snap_to_with_modifier (sync_frame, event);
602 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
605 *pending_region_position = _last_frame_position;
608 if (*pending_region_position > max_framepos - _primary->region()->length()) {
609 *pending_region_position = _last_frame_position;
614 /* in locked edit mode, reverse the usual meaning of _x_constrained */
615 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
617 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
619 /* x movement since last time (in pixels) */
620 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
622 /* total x movement */
623 framecnt_t total_dx = *pending_region_position;
624 if (regions_came_from_canvas()) {
625 total_dx = total_dx - grab_frame ();
628 /* check that no regions have gone off the start of the session */
629 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
630 if ((i->view->region()->position() + total_dx) < 0) {
632 *pending_region_position = _last_frame_position;
643 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
645 if (_y_constrained) {
649 bool all_in_drop_zone = true;
651 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
652 int n = i->time_axis_view + delta_track;
653 if (i->time_axis_view < 0) {
654 /* already in the drop zone */
655 if (delta_track > 0) {
656 /* downward motion - might be OK if others are still not in the dropzone,
657 so check at the end of the loop if that is the case.
661 /* upward motion, and this view is currently in the drop zone. That's fine, so
662 we have to set all_in_drop_zone correctly to avoid blocking upward motion.
664 all_in_drop_zone = false;
668 all_in_drop_zone = false;
676 if (n >= int (_time_axis_views.size())) {
677 /* downward motion into drop zone. That's fine. */
681 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
682 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
683 /* not a track, or the wrong type */
687 double const l = i->layer + delta_layer;
689 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
690 mode to allow the user to place a region below another on layer 0.
692 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
693 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
694 If it has, the layers will be munged later anyway, so it's ok.
700 /* all regions being dragged are ok with this change */
701 return !all_in_drop_zone;
705 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
707 double delta_layer = 0;
708 int delta_time_axis_view = 0;
709 int current_pointer_time_axis_view;
711 assert (!_views.empty ());
715 if (initially_vertical()) {
716 _y_constrained = false;
717 _x_constrained = true;
719 _y_constrained = true;
720 _x_constrained = false;
725 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
727 /* Find the TimeAxisView that the pointer is now over */
729 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
730 TimeAxisView* tv = r.first;
732 if (tv && tv->view()) {
733 double layer = r.second;
735 if (first_move && tv->view()->layer_display() == Stacked) {
736 tv->view()->set_layer_display (Expanded);
739 /* Here's the current pointer position in terms of time axis view and layer */
740 current_pointer_time_axis_view = find_time_axis_view (tv);
742 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
744 /* Work out the change in y */
746 if (_last_pointer_time_axis_view < 0) {
747 /* we can only move up */
748 delta_time_axis_view = -1;
750 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
753 delta_layer = current_pointer_layer - _last_pointer_layer;
756 /* Work out the change in x */
757 framepos_t pending_region_position;
758 double const x_delta = compute_x_delta (event, &pending_region_position);
759 _last_frame_position = pending_region_position;
761 /* Verify change in y */
762 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
763 /* this y movement is not allowed, so do no y movement this time */
764 delta_time_axis_view = 0;
768 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
769 /* haven't reached next snap point, and we're not switching
770 trackviews nor layers. nothing to do.
775 typedef pair<int,double> NewTrackIndexAndPosition;
776 typedef map<boost::shared_ptr<Playlist>,NewTrackIndexAndPosition> PlaylistDropzoneMap;
777 PlaylistDropzoneMap playlist_dropzone_map;
778 int biggest_drop_zone_offset = 0;
780 Coord last_track_bottom_edge;
782 if (_time_axis_views.empty()) {
783 last_track_bottom_edge = 0;
785 TimeAxisView* last = _time_axis_views.back();
786 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
789 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
791 RegionView* rv = i->view;
796 if (rv->region()->locked() || rv->region()->video_locked()) {
803 /* reparent the regionview into a group above all
807 ArdourCanvas::Item* rvg = rv->get_canvas_group();
808 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
809 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
810 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
811 /* move the item so that it continues to appear at the
812 same location now that its parent has changed.
814 rvg->move (rv_canvas_offset - dmg_canvas_offset);
817 /* If we have moved tracks, we'll fudge the layer delta so that the
818 region gets moved back onto layer 0 on its new track; this avoids
819 confusion when dragging regions from non-zero layers onto different
822 double this_delta_layer = delta_layer;
823 if (delta_time_axis_view != 0) {
824 this_delta_layer = - i->layer;
831 if (i->time_axis_view >= 0) {
832 track_index = i->time_axis_view + delta_time_axis_view;
834 /* delta time axis view will be at least -1, because we can only
835 be moving up if some dragged views have i->time_axis_view negative.
837 the final real time axis view has index _time_axis_views.size() - 1.
839 so for DTAV = -1, the first drop zone track (i->tav == -1) should
840 move to the final TAV.
842 the second drop zone track should move to i->tav == -1 (i.e. becomes
843 the first drop zone track). and so on.
846 int index_from_bottom = i->time_axis_view - delta_time_axis_view;
847 int const bottom = _time_axis_views.size() - 1;
849 if (index_from_bottom >= 0) {
850 track_index = bottom - index_from_bottom;
852 track_index = index_from_bottom;
856 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
860 /* The TimeAxisView that this region is now over */
861 TimeAxisView* current_tv = _time_axis_views[track_index];
863 /* Ensure it is moved from stacked -> expanded if appropriate */
864 if (current_tv->view()->layer_display() == Stacked) {
865 current_tv->view()->set_layer_display (Expanded);
868 /* We're only allowed to go -ve in layer on Expanded views */
869 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
870 this_delta_layer = - i->layer;
874 rv->set_height (current_tv->view()->child_height ());
876 /* Update show/hidden status as the region view may have come from a hidden track,
877 or have moved to one.
879 if (current_tv->hidden ()) {
880 rv->get_canvas_group()->hide ();
882 rv->get_canvas_group()->show ();
885 /* Update the DraggingView */
886 i->time_axis_view = track_index;
887 i->layer += this_delta_layer;
890 _editor->mouse_brush_insert_region (rv, pending_region_position);
894 /* Get the y coordinate of the top of the track that this region is now over */
895 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
897 /* And adjust for the layer that it should be on */
898 StreamView* cv = current_tv->view ();
899 switch (cv->layer_display ()) {
903 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
906 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
910 /* need to get the parent of the regionview
911 * canvas group and get its position in
912 * equivalent coordinate space as the trackview
913 * we are now dragging over.
916 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
925 if (current_pointer_y() >= 0) {
927 NewTrackIndexAndPosition ip;
928 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
930 /* store index of each new playlist as a negative count, starting at -1 */
932 if (pdz == playlist_dropzone_map.end()) {
934 int n = playlist_dropzone_map.size() + 1;
936 /* compute where this new track (which doesn't exist yet) will live
940 ip.first = -n; /* in time axis units, negative to signify "in drop zone " */
941 ip.second = last_track_bottom_edge; /* where to place the top edge of the regionview */
943 /* How high is this region view ? */
945 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
946 ArdourCanvas::Rect bbox;
952 last_track_bottom_edge += bbox.height();
954 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), ip));
955 i->time_axis_view = -n;
959 i->time_axis_view = ip.first;
962 /* values are zero or negative, hence the use of min() */
963 biggest_drop_zone_offset = min (biggest_drop_zone_offset, i->time_axis_view);
965 y_delta = ip.second - rv->get_canvas_group()->canvas_origin().y;
969 /* Now move the region view */
970 rv->move (x_delta, y_delta);
972 } /* foreach region */
974 _total_x_delta += x_delta;
976 if (x_delta != 0 && !_brushing) {
977 show_verbose_cursor_time (_last_frame_position);
982 /* the pointer is currently over a time axis view */
984 if (_last_pointer_time_axis_view < 0) {
986 /* last motion event was not over a time axis view */
988 if (delta_time_axis_view < 0) {
989 /* was in the drop zone, moving up */
990 _last_pointer_time_axis_view = current_pointer_time_axis_view;
992 /* was in the drop zone, moving down ... not possible */
997 /* last motion event was also over a time axis view */
999 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1004 /* the pointer is not over a time axis view */
1006 _last_pointer_time_axis_view = biggest_drop_zone_offset;
1009 _last_pointer_layer += delta_layer;
1013 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1015 if (_copy && first_move) {
1017 if (_x_constrained) {
1018 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1020 _editor->begin_reversible_command (Operations::region_copy);
1023 /* duplicate the regionview(s) and region(s) */
1025 list<DraggingView> new_regionviews;
1027 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1029 RegionView* rv = i->view;
1030 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1031 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1033 const boost::shared_ptr<const Region> original = rv->region();
1034 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1035 region_copy->set_position (original->position());
1036 /* need to set this so that the drop zone code can work. This doesn't
1037 actually put the region into the playlist, but just sets a weak pointer
1040 region_copy->set_playlist (original->playlist());
1044 boost::shared_ptr<AudioRegion> audioregion_copy
1045 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1047 nrv = new AudioRegionView (*arv, audioregion_copy);
1049 boost::shared_ptr<MidiRegion> midiregion_copy
1050 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1051 nrv = new MidiRegionView (*mrv, midiregion_copy);
1056 nrv->get_canvas_group()->show ();
1057 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1059 /* swap _primary to the copy */
1061 if (rv == _primary) {
1065 /* ..and deselect the one we copied */
1067 rv->set_selected (false);
1070 if (!new_regionviews.empty()) {
1072 /* reflect the fact that we are dragging the copies */
1074 _views = new_regionviews;
1076 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1079 } else if (!_copy && first_move) {
1081 if (_x_constrained) {
1082 _editor->begin_reversible_command (_("fixed time region drag"));
1084 _editor->begin_reversible_command (Operations::region_drag);
1088 RegionMotionDrag::motion (event, first_move);
1092 RegionMotionDrag::finished (GdkEvent *, bool)
1094 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1095 if (!(*i)->view()) {
1099 if ((*i)->view()->layer_display() == Expanded) {
1100 (*i)->view()->set_layer_display (Stacked);
1106 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1108 RegionMotionDrag::finished (ev, movement_occurred);
1110 if (!movement_occurred) {
1114 if (was_double_click() && !_views.empty()) {
1115 DraggingView dv = _views.front();
1116 dv.view->show_region_editor ();
1123 /* reverse this here so that we have the correct logic to finalize
1127 if (Config->get_edit_mode() == Lock) {
1128 _x_constrained = !_x_constrained;
1131 assert (!_views.empty ());
1133 /* We might have hidden region views so that they weren't visible during the drag
1134 (when they have been reparented). Now everything can be shown again, as region
1135 views are back in their track parent groups.
1137 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1138 i->view->get_canvas_group()->show ();
1141 bool const changed_position = (_last_frame_position != _primary->region()->position());
1142 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1143 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1163 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1167 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1169 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1174 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1175 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1176 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1177 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1179 rtav->set_height (original->current_height());
1183 ChanCount one_midi_port (DataType::MIDI, 1);
1184 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1185 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1186 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1188 rtav->set_height (original->current_height());
1193 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1199 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1201 RegionSelection new_views;
1202 PlaylistSet modified_playlists;
1203 RouteTimeAxisView* new_time_axis_view = 0;
1206 /* all changes were made during motion event handlers */
1208 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1212 _editor->commit_reversible_command ();
1216 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1217 PlaylistMapping playlist_mapping;
1219 /* insert the regions into their new playlists */
1220 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1222 RouteTimeAxisView* dest_rtv = 0;
1224 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1230 if (changed_position && !_x_constrained) {
1231 where = i->view->region()->position() - drag_delta;
1233 where = i->view->region()->position();
1236 if (i->time_axis_view < 0) {
1237 /* dragged to drop zone */
1239 PlaylistMapping::iterator pm;
1241 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1242 /* first region from this original playlist: create a new track */
1243 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1244 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1245 dest_rtv = new_time_axis_view;
1247 /* we already created a new track for regions from this playlist, use it */
1248 dest_rtv = pm->second;
1251 /* destination time axis view is the one we dragged to */
1252 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1255 if (dest_rtv != 0) {
1256 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1257 if (new_view != 0) {
1258 new_views.push_back (new_view);
1262 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1263 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1266 list<DraggingView>::const_iterator next = i;
1272 /* If we've created new regions either by copying or moving
1273 to a new track, we want to replace the old selection with the new ones
1276 if (new_views.size() > 0) {
1277 _editor->selection->set (new_views);
1280 /* write commands for the accumulated diffs for all our modified playlists */
1281 add_stateful_diff_commands_for_playlists (modified_playlists);
1283 _editor->commit_reversible_command ();
1287 RegionMoveDrag::finished_no_copy (
1288 bool const changed_position,
1289 bool const changed_tracks,
1290 framecnt_t const drag_delta
1293 RegionSelection new_views;
1294 PlaylistSet modified_playlists;
1295 PlaylistSet frozen_playlists;
1296 set<RouteTimeAxisView*> views_to_update;
1297 RouteTimeAxisView* new_time_axis_view = 0;
1300 /* all changes were made during motion event handlers */
1301 _editor->commit_reversible_command ();
1305 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1306 PlaylistMapping playlist_mapping;
1308 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1310 RegionView* rv = i->view;
1311 RouteTimeAxisView* dest_rtv = 0;
1313 if (rv->region()->locked() || rv->region()->video_locked()) {
1318 if (i->time_axis_view < 0) {
1319 /* dragged to drop zone */
1321 PlaylistMapping::iterator pm;
1323 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1324 /* first region from this original playlist: create a new track */
1325 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1326 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1327 dest_rtv = new_time_axis_view;
1329 /* we already created a new track for regions from this playlist, use it */
1330 dest_rtv = pm->second;
1334 /* destination time axis view is the one we dragged to */
1335 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1340 double const dest_layer = i->layer;
1342 views_to_update.insert (dest_rtv);
1346 if (changed_position && !_x_constrained) {
1347 where = rv->region()->position() - drag_delta;
1349 where = rv->region()->position();
1352 if (changed_tracks) {
1354 /* insert into new playlist */
1356 RegionView* new_view = insert_region_into_playlist (
1357 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1360 if (new_view == 0) {
1365 new_views.push_back (new_view);
1367 /* remove from old playlist */
1369 /* the region that used to be in the old playlist is not
1370 moved to the new one - we use a copy of it. as a result,
1371 any existing editor for the region should no longer be
1374 rv->hide_region_editor();
1377 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1381 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1383 /* this movement may result in a crossfade being modified, or a layering change,
1384 so we need to get undo data from the playlist as well as the region.
1387 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1389 playlist->clear_changes ();
1392 rv->region()->clear_changes ();
1395 motion on the same track. plonk the previously reparented region
1396 back to its original canvas group (its streamview).
1397 No need to do anything for copies as they are fake regions which will be deleted.
1400 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1401 rv->get_canvas_group()->set_y_position (i->initial_y);
1404 /* just change the model */
1405 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1406 playlist->set_layer (rv->region(), dest_layer);
1409 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1411 r = frozen_playlists.insert (playlist);
1414 playlist->freeze ();
1417 rv->region()->set_position (where);
1419 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1422 if (changed_tracks) {
1424 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1425 was selected in all of them, then removing it from a playlist will have removed all
1426 trace of it from _views (i.e. there were N regions selected, we removed 1,
1427 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1428 corresponding regionview, and _views is now empty).
1430 This could have invalidated any and all iterators into _views.
1432 The heuristic we use here is: if the region selection is empty, break out of the loop
1433 here. if the region selection is not empty, then restart the loop because we know that
1434 we must have removed at least the region(view) we've just been working on as well as any
1435 that we processed on previous iterations.
1437 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1438 we can just iterate.
1442 if (_views.empty()) {
1453 /* If we've created new regions either by copying or moving
1454 to a new track, we want to replace the old selection with the new ones
1457 if (new_views.size() > 0) {
1458 _editor->selection->set (new_views);
1461 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1465 /* write commands for the accumulated diffs for all our modified playlists */
1466 add_stateful_diff_commands_for_playlists (modified_playlists);
1468 _editor->commit_reversible_command ();
1470 /* We have futzed with the layering of canvas items on our streamviews.
1471 If any region changed layer, this will have resulted in the stream
1472 views being asked to set up their region views, and all will be well.
1473 If not, we might now have badly-ordered region views. Ask the StreamViews
1474 involved to sort themselves out, just in case.
1477 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1478 (*i)->view()->playlist_layered ((*i)->track ());
1482 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1483 * @param region Region to remove.
1484 * @param playlist playlist To remove from.
1485 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1486 * that clear_changes () is only called once per playlist.
1489 RegionMoveDrag::remove_region_from_playlist (
1490 boost::shared_ptr<Region> region,
1491 boost::shared_ptr<Playlist> playlist,
1492 PlaylistSet& modified_playlists
1495 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1498 playlist->clear_changes ();
1501 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1505 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1506 * clearing the playlist's diff history first if necessary.
1507 * @param region Region to insert.
1508 * @param dest_rtv Destination RouteTimeAxisView.
1509 * @param dest_layer Destination layer.
1510 * @param where Destination position.
1511 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1512 * that clear_changes () is only called once per playlist.
1513 * @return New RegionView, or 0 if no insert was performed.
1516 RegionMoveDrag::insert_region_into_playlist (
1517 boost::shared_ptr<Region> region,
1518 RouteTimeAxisView* dest_rtv,
1521 PlaylistSet& modified_playlists
1524 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1525 if (!dest_playlist) {
1529 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1530 _new_region_view = 0;
1531 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1533 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1534 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1536 dest_playlist->clear_changes ();
1539 dest_playlist->add_region (region, where);
1541 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1542 dest_playlist->set_layer (region, dest_layer);
1547 assert (_new_region_view);
1549 return _new_region_view;
1553 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1555 _new_region_view = rv;
1559 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1561 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1562 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1564 _editor->session()->add_command (c);
1573 RegionMoveDrag::aborted (bool movement_occurred)
1577 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1578 list<DraggingView>::const_iterator next = i;
1587 RegionMotionDrag::aborted (movement_occurred);
1592 RegionMotionDrag::aborted (bool)
1594 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1596 StreamView* sview = (*i)->view();
1599 if (sview->layer_display() == Expanded) {
1600 sview->set_layer_display (Stacked);
1605 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1606 RegionView* rv = i->view;
1607 TimeAxisView* tv = &(rv->get_time_axis_view ());
1608 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1610 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1611 rv->get_canvas_group()->set_y_position (0);
1613 rv->move (-_total_x_delta, 0);
1614 rv->set_height (rtv->view()->child_height ());
1618 /** @param b true to brush, otherwise false.
1619 * @param c true to make copies of the regions being moved, otherwise false.
1621 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1622 : RegionMotionDrag (e, i, p, v, b)
1625 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1628 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1629 if (rtv && rtv->is_track()) {
1630 speed = rtv->track()->speed ();
1633 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1637 RegionMoveDrag::setup_pointer_frame_offset ()
1639 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1642 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1643 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1645 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1647 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1648 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1650 _primary = v->view()->create_region_view (r, false, false);
1652 _primary->get_canvas_group()->show ();
1653 _primary->set_position (pos, 0);
1654 _views.push_back (DraggingView (_primary, this, v));
1656 _last_frame_position = pos;
1658 _item = _primary->get_canvas_group ();
1662 RegionInsertDrag::finished (GdkEvent *, bool)
1664 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1666 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1667 _primary->get_canvas_group()->set_y_position (0);
1669 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1671 _editor->begin_reversible_command (Operations::insert_region);
1672 playlist->clear_changes ();
1673 playlist->add_region (_primary->region (), _last_frame_position);
1675 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1676 if (Config->get_edit_mode() == Ripple) {
1677 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1680 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1681 _editor->commit_reversible_command ();
1689 RegionInsertDrag::aborted (bool)
1696 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1697 : RegionMoveDrag (e, i, p, v, false, false)
1699 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1702 struct RegionSelectionByPosition {
1703 bool operator() (RegionView*a, RegionView* b) {
1704 return a->region()->position () < b->region()->position();
1709 RegionSpliceDrag::motion (GdkEvent* event, bool)
1711 /* Which trackview is this ? */
1713 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1714 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1716 /* The region motion is only processed if the pointer is over
1720 if (!tv || !tv->is_track()) {
1721 /* To make sure we hide the verbose canvas cursor when the mouse is
1722 not held over an audio track.
1724 _editor->verbose_cursor()->hide ();
1727 _editor->verbose_cursor()->show ();
1732 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1738 RegionSelection copy;
1739 _editor->selection->regions.by_position(copy);
1741 framepos_t const pf = adjusted_current_frame (event);
1743 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1745 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1751 boost::shared_ptr<Playlist> playlist;
1753 if ((playlist = atv->playlist()) == 0) {
1757 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1762 if (pf < (*i)->region()->last_frame() + 1) {
1766 if (pf > (*i)->region()->first_frame()) {
1772 playlist->shuffle ((*i)->region(), dir);
1777 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1779 RegionMoveDrag::finished (event, movement_occurred);
1783 RegionSpliceDrag::aborted (bool)
1793 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1796 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1798 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1799 RegionSelection to_ripple;
1800 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1801 if ((*i)->position() >= where) {
1802 to_ripple.push_back (rtv->view()->find_view(*i));
1806 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1807 if (!exclude.contains (*i)) {
1808 // the selection has already been added to _views
1810 if (drag_in_progress) {
1811 // do the same things that RegionMotionDrag::motion does when
1812 // first_move is true, for the region views that we're adding
1813 // to _views this time
1816 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1817 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1818 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1819 rvg->reparent (_editor->_drag_motion_group);
1821 // we only need to move in the y direction
1822 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1827 _views.push_back (DraggingView (*i, this, tav));
1833 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1836 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1837 // we added all the regions after the selection
1839 std::list<DraggingView>::iterator to_erase = i++;
1840 if (!_editor->selection->regions.contains (to_erase->view)) {
1841 // restore the non-selected regions to their original playlist & positions,
1842 // and then ripple them back by the length of the regions that were dragged away
1843 // do the same things as RegionMotionDrag::aborted
1845 RegionView *rv = to_erase->view;
1846 TimeAxisView* tv = &(rv->get_time_axis_view ());
1847 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1850 // plonk them back onto their own track
1851 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1852 rv->get_canvas_group()->set_y_position (0);
1856 // move the underlying region to match the view
1857 rv->region()->set_position (rv->region()->position() + amount);
1859 // restore the view to match the underlying region's original position
1860 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1863 rv->set_height (rtv->view()->child_height ());
1864 _views.erase (to_erase);
1870 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1872 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1874 return allow_moves_across_tracks;
1882 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1883 : RegionMoveDrag (e, i, p, v, false, false)
1885 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1886 // compute length of selection
1887 RegionSelection selected_regions = _editor->selection->regions;
1888 selection_length = selected_regions.end_frame() - selected_regions.start();
1890 // we'll only allow dragging to another track in ripple mode if all the regions
1891 // being dragged start off on the same track
1892 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1895 exclude = new RegionList;
1896 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1897 exclude->push_back((*i)->region());
1900 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1901 RegionSelection copy;
1902 selected_regions.by_position(copy); // get selected regions sorted by position into copy
1904 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1905 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1907 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1908 // find ripple start point on each applicable playlist
1909 RegionView *first_selected_on_this_track = NULL;
1910 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1911 if ((*i)->region()->playlist() == (*pi)) {
1912 // region is on this playlist - it's the first, because they're sorted
1913 first_selected_on_this_track = *i;
1917 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1918 add_all_after_to_views (
1919 &first_selected_on_this_track->get_time_axis_view(),
1920 first_selected_on_this_track->region()->position(),
1921 selected_regions, false);
1924 if (allow_moves_across_tracks) {
1925 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1933 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1935 /* Which trackview is this ? */
1937 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1938 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1940 /* The region motion is only processed if the pointer is over
1944 if (!tv || !tv->is_track()) {
1945 /* To make sure we hide the verbose canvas cursor when the mouse is
1946 not held over an audiotrack.
1948 _editor->verbose_cursor()->hide ();
1952 framepos_t where = adjusted_current_frame (event);
1953 assert (where >= 0);
1955 double delta = compute_x_delta (event, &after);
1957 framecnt_t amount = _editor->pixel_to_sample (delta);
1959 if (allow_moves_across_tracks) {
1960 // all the originally selected regions were on the same track
1962 framecnt_t adjust = 0;
1963 if (prev_tav && tv != prev_tav) {
1964 // dragged onto a different track
1965 // remove the unselected regions from _views, restore them to their original positions
1966 // and add the regions after the drop point on the new playlist to _views instead.
1967 // undo the effect of rippling the previous playlist, and include the effect of removing
1968 // the dragged region(s) from this track
1970 remove_unselected_from_views (prev_amount, false);
1971 // ripple previous playlist according to the regions that have been removed onto the new playlist
1972 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1975 // move just the selected regions
1976 RegionMoveDrag::motion(event, first_move);
1978 // ensure that the ripple operation on the new playlist inserts selection_length time
1979 adjust = selection_length;
1980 // ripple the new current playlist
1981 tv->playlist()->ripple (where, amount+adjust, exclude);
1983 // add regions after point where drag entered this track to subsequent ripples
1984 add_all_after_to_views (tv, where, _editor->selection->regions, true);
1987 // motion on same track
1988 RegionMoveDrag::motion(event, first_move);
1992 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1993 prev_position = where;
1995 // selection encompasses multiple tracks - just drag
1996 // cross-track drags are forbidden
1997 RegionMoveDrag::motion(event, first_move);
2000 if (!_x_constrained) {
2001 prev_amount += amount;
2004 _last_frame_position = after;
2008 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2010 if (!movement_occurred) {
2014 if (was_double_click() && !_views.empty()) {
2015 DraggingView dv = _views.front();
2016 dv.view->show_region_editor ();
2023 _editor->begin_reversible_command(_("Ripple drag"));
2025 // remove the regions being rippled from the dragging view, updating them to
2026 // their new positions
2027 remove_unselected_from_views (prev_amount, true);
2029 if (allow_moves_across_tracks) {
2031 // if regions were dragged across tracks, we've rippled any later
2032 // regions on the track the regions were dragged off, so we need
2033 // to add the original track to the undo record
2034 orig_tav->playlist()->clear_changes();
2035 vector<Command*> cmds;
2036 orig_tav->playlist()->rdiff (cmds);
2037 _editor->session()->add_commands (cmds);
2039 if (prev_tav && prev_tav != orig_tav) {
2040 prev_tav->playlist()->clear_changes();
2041 vector<Command*> cmds;
2042 prev_tav->playlist()->rdiff (cmds);
2043 _editor->session()->add_commands (cmds);
2046 // selection spanned multiple tracks - all will need adding to undo record
2048 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2049 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2051 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2052 (*pi)->clear_changes();
2053 vector<Command*> cmds;
2054 (*pi)->rdiff (cmds);
2055 _editor->session()->add_commands (cmds);
2059 // other modified playlists are added to undo by RegionMoveDrag::finished()
2060 RegionMoveDrag::finished (event, movement_occurred);
2061 _editor->commit_reversible_command();
2065 RegionRippleDrag::aborted (bool movement_occurred)
2067 RegionMoveDrag::aborted (movement_occurred);
2072 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2074 _view (dynamic_cast<MidiTimeAxisView*> (v))
2076 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2082 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2085 _region = add_midi_region (_view);
2086 _view->playlist()->freeze ();
2089 framepos_t const f = adjusted_current_frame (event);
2090 if (f < grab_frame()) {
2091 _region->set_position (f);
2094 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2095 so that if this region is duplicated, its duplicate starts on
2096 a snap point rather than 1 frame after a snap point. Otherwise things get
2097 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2098 place snapped notes at the start of the region.
2101 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2102 _region->set_length (len < 1 ? 1 : len);
2108 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2110 if (!movement_occurred) {
2111 add_midi_region (_view);
2113 _view->playlist()->thaw ();
2118 RegionCreateDrag::aborted (bool)
2121 _view->playlist()->thaw ();
2127 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2131 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2135 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2137 Gdk::Cursor* cursor;
2138 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2140 float x_fraction = cnote->mouse_x_fraction ();
2142 if (x_fraction > 0.0 && x_fraction < 0.25) {
2143 cursor = _editor->cursors()->left_side_trim;
2146 cursor = _editor->cursors()->right_side_trim;
2150 Drag::start_grab (event, cursor);
2152 region = &cnote->region_view();
2156 if (event->motion.state & Keyboard::PrimaryModifier) {
2162 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2164 if (ms.size() > 1) {
2165 /* has to be relative, may make no sense otherwise */
2169 /* select this note; if it is already selected, preserve the existing selection,
2170 otherwise make this note the only one selected.
2172 region->note_selected (cnote, cnote->selected ());
2174 _editor->begin_reversible_command (_("resize notes"));
2176 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2177 MidiRegionSelection::iterator next;
2180 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2182 mrv->begin_resizing (at_front);
2189 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2191 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2192 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2193 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2195 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2197 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2203 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2205 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2206 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2207 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2209 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2211 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2215 _editor->commit_reversible_command ();
2219 NoteResizeDrag::aborted (bool)
2221 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2222 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2223 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2225 mrv->abort_resizing ();
2230 AVDraggingView::AVDraggingView (RegionView* v)
2233 initial_position = v->region()->position ();
2236 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2239 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2242 TrackViewList empty;
2244 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2245 std::list<RegionView*> views = rs.by_layer();
2247 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2248 RegionView* rv = (*i);
2249 if (!rv->region()->video_locked()) {
2252 _views.push_back (AVDraggingView (rv));
2257 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2259 Drag::start_grab (event);
2260 if (_editor->session() == 0) {
2264 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2265 _max_backwards_drag = (
2266 ARDOUR_UI::instance()->video_timeline->get_duration()
2267 + ARDOUR_UI::instance()->video_timeline->get_offset()
2268 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2271 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2272 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2273 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2276 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2279 Timecode::Time timecode;
2280 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2281 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);
2282 show_verbose_cursor_text (buf);
2286 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2288 if (_editor->session() == 0) {
2291 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2295 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2296 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2298 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2299 dt = - _max_backwards_drag;
2302 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2303 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2305 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2306 RegionView* rv = i->view;
2307 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2310 rv->region()->clear_changes ();
2311 rv->region()->suspend_property_changes();
2313 rv->region()->set_position(i->initial_position + dt);
2314 rv->region_changed(ARDOUR::Properties::position);
2317 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2318 Timecode::Time timecode;
2319 Timecode::Time timediff;
2321 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2322 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2323 snprintf (buf, sizeof (buf),
2324 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2325 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2326 , _("Video Start:"),
2327 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2329 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2331 show_verbose_cursor_text (buf);
2335 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2337 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2341 if (!movement_occurred || ! _editor->session()) {
2345 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2347 _editor->begin_reversible_command (_("Move Video"));
2349 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2350 ARDOUR_UI::instance()->video_timeline->save_undo();
2351 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2352 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2354 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2355 i->view->drag_end();
2356 i->view->region()->resume_property_changes ();
2358 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2361 _editor->session()->maybe_update_session_range(
2362 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2363 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2367 _editor->commit_reversible_command ();
2371 VideoTimeLineDrag::aborted (bool)
2373 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2376 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2377 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2379 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2380 i->view->region()->resume_property_changes ();
2381 i->view->region()->set_position(i->initial_position);
2385 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2386 : RegionDrag (e, i, p, v)
2387 , _preserve_fade_anchor (preserve_fade_anchor)
2388 , _jump_position_when_done (false)
2390 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2394 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2397 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2398 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2400 if (tv && tv->is_track()) {
2401 speed = tv->track()->speed();
2404 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2405 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2406 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2408 framepos_t const pf = adjusted_current_frame (event);
2410 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2411 /* Move the contents of the region around without changing the region bounds */
2412 _operation = ContentsTrim;
2413 Drag::start_grab (event, _editor->cursors()->trimmer);
2415 /* These will get overridden for a point trim.*/
2416 if (pf < (region_start + region_length/2)) {
2417 /* closer to front */
2418 _operation = StartTrim;
2420 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2421 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2423 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2427 _operation = EndTrim;
2428 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2429 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2431 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2436 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2437 _jump_position_when_done = true;
2440 switch (_operation) {
2442 show_verbose_cursor_time (region_start);
2443 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2444 i->view->trim_front_starting ();
2448 show_verbose_cursor_time (region_end);
2451 show_verbose_cursor_time (pf);
2455 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2456 i->view->region()->suspend_property_changes ();
2461 TrimDrag::motion (GdkEvent* event, bool first_move)
2463 RegionView* rv = _primary;
2466 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2467 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2468 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2469 frameoffset_t frame_delta = 0;
2471 if (tv && tv->is_track()) {
2472 speed = tv->track()->speed();
2475 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2481 switch (_operation) {
2483 trim_type = "Region start trim";
2486 trim_type = "Region end trim";
2489 trim_type = "Region content trim";
2496 _editor->begin_reversible_command (trim_type);
2498 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2499 RegionView* rv = i->view;
2500 rv->enable_display (false);
2501 rv->region()->playlist()->clear_owned_changes ();
2503 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2506 arv->temporarily_hide_envelope ();
2510 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2511 insert_result = _editor->motion_frozen_playlists.insert (pl);
2513 if (insert_result.second) {
2519 bool non_overlap_trim = false;
2521 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2522 non_overlap_trim = true;
2525 /* contstrain trim to fade length */
2526 if (_preserve_fade_anchor) {
2527 switch (_operation) {
2529 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2530 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2532 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2533 if (ar->locked()) continue;
2534 framecnt_t len = ar->fade_in()->back()->when;
2535 if (len < dt) dt = min(dt, len);
2539 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2540 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2542 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2543 if (ar->locked()) continue;
2544 framecnt_t len = ar->fade_out()->back()->when;
2545 if (len < -dt) dt = max(dt, -len);
2554 switch (_operation) {
2556 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2557 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2558 if (changed && _preserve_fade_anchor) {
2559 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2561 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2562 framecnt_t len = ar->fade_in()->back()->when;
2563 framecnt_t diff = ar->first_frame() - i->initial_position;
2564 framepos_t new_length = len - diff;
2565 i->anchored_fade_length = min (ar->length(), new_length);
2566 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2567 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2574 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2575 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2576 if (changed && _preserve_fade_anchor) {
2577 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2579 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2580 framecnt_t len = ar->fade_out()->back()->when;
2581 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2582 framepos_t new_length = len + diff;
2583 i->anchored_fade_length = min (ar->length(), new_length);
2584 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2585 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2593 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2595 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2596 i->view->move_contents (frame_delta);
2602 switch (_operation) {
2604 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2607 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2610 // show_verbose_cursor_time (frame_delta);
2617 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2619 if (movement_occurred) {
2620 motion (event, false);
2622 if (_operation == StartTrim) {
2623 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2625 /* This must happen before the region's StatefulDiffCommand is created, as it may
2626 `correct' (ahem) the region's _start from being negative to being zero. It
2627 needs to be zero in the undo record.
2629 i->view->trim_front_ending ();
2631 if (_preserve_fade_anchor) {
2632 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2634 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2635 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2636 ar->set_fade_in_length(i->anchored_fade_length);
2637 ar->set_fade_in_active(true);
2640 if (_jump_position_when_done) {
2641 i->view->region()->set_position (i->initial_position);
2644 } else if (_operation == EndTrim) {
2645 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2646 if (_preserve_fade_anchor) {
2647 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2649 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2650 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2651 ar->set_fade_out_length(i->anchored_fade_length);
2652 ar->set_fade_out_active(true);
2655 if (_jump_position_when_done) {
2656 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2661 if (!_views.empty()) {
2662 if (_operation == StartTrim) {
2663 _editor->maybe_locate_with_edit_preroll(
2664 _views.begin()->view->region()->position());
2666 if (_operation == EndTrim) {
2667 _editor->maybe_locate_with_edit_preroll(
2668 _views.begin()->view->region()->position() +
2669 _views.begin()->view->region()->length());
2673 if (!_editor->selection->selected (_primary)) {
2674 _primary->thaw_after_trim ();
2677 set<boost::shared_ptr<Playlist> > diffed_playlists;
2679 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2680 i->view->thaw_after_trim ();
2681 i->view->enable_display (true);
2683 /* Trimming one region may affect others on the playlist, so we need
2684 to get undo Commands from the whole playlist rather than just the
2685 region. Use diffed_playlists to make sure we don't diff a given
2686 playlist more than once.
2688 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2689 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2690 vector<Command*> cmds;
2692 _editor->session()->add_commands (cmds);
2693 diffed_playlists.insert (p);
2698 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2702 _editor->motion_frozen_playlists.clear ();
2703 _editor->commit_reversible_command();
2706 /* no mouse movement */
2707 _editor->point_trim (event, adjusted_current_frame (event));
2710 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2711 if (_operation == StartTrim) {
2712 i->view->trim_front_ending ();
2715 i->view->region()->resume_property_changes ();
2720 TrimDrag::aborted (bool movement_occurred)
2722 /* Our motion method is changing model state, so use the Undo system
2723 to cancel. Perhaps not ideal, as this will leave an Undo point
2724 behind which may be slightly odd from the user's point of view.
2729 if (movement_occurred) {
2733 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2734 i->view->region()->resume_property_changes ();
2739 TrimDrag::setup_pointer_frame_offset ()
2741 list<DraggingView>::iterator i = _views.begin ();
2742 while (i != _views.end() && i->view != _primary) {
2746 if (i == _views.end()) {
2750 switch (_operation) {
2752 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2755 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2762 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2766 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2767 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2772 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2774 Drag::start_grab (event, cursor);
2775 show_verbose_cursor_time (adjusted_current_frame(event));
2779 MeterMarkerDrag::setup_pointer_frame_offset ()
2781 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2785 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2787 if (!_marker->meter().movable()) {
2793 // create a dummy marker for visual representation of moving the
2794 // section, because whether its a copy or not, we're going to
2795 // leave or lose the original marker (leave if its a copy; lose if its
2796 // not, because we'll remove it from the map).
2798 MeterSection section (_marker->meter());
2800 if (!section.movable()) {
2805 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2807 _marker = new MeterMarker (
2809 *_editor->meter_group,
2810 ARDOUR_UI::config()->color ("meter marker"),
2812 *new MeterSection (_marker->meter())
2815 /* use the new marker for the grab */
2816 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2819 TempoMap& map (_editor->session()->tempo_map());
2820 /* get current state */
2821 before_state = &map.get_state();
2822 /* remove the section while we drag it */
2823 map.remove_meter (section, true);
2827 framepos_t const pf = adjusted_current_frame (event);
2829 _marker->set_position (pf);
2830 show_verbose_cursor_time (pf);
2834 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2836 if (!movement_occurred) {
2837 if (was_double_click()) {
2838 _editor->edit_meter_marker (*_marker);
2843 if (!_marker->meter().movable()) {
2847 motion (event, false);
2849 Timecode::BBT_Time when;
2851 TempoMap& map (_editor->session()->tempo_map());
2852 map.bbt_time (last_pointer_frame(), when);
2854 if (_copy == true) {
2855 _editor->begin_reversible_command (_("copy meter mark"));
2856 XMLNode &before = map.get_state();
2857 map.add_meter (_marker->meter(), when);
2858 XMLNode &after = map.get_state();
2859 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2860 _editor->commit_reversible_command ();
2863 _editor->begin_reversible_command (_("move meter mark"));
2865 /* we removed it before, so add it back now */
2867 map.add_meter (_marker->meter(), when);
2868 XMLNode &after = map.get_state();
2869 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2870 _editor->commit_reversible_command ();
2873 // delete the dummy marker we used for visual representation while moving.
2874 // a new visual marker will show up automatically.
2879 MeterMarkerDrag::aborted (bool moved)
2881 _marker->set_position (_marker->meter().frame ());
2884 TempoMap& map (_editor->session()->tempo_map());
2885 /* we removed it before, so add it back now */
2886 map.add_meter (_marker->meter(), _marker->meter().frame());
2887 // delete the dummy marker we used for visual representation while moving.
2888 // a new visual marker will show up automatically.
2893 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2897 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2899 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2904 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2906 Drag::start_grab (event, cursor);
2907 show_verbose_cursor_time (adjusted_current_frame (event));
2911 TempoMarkerDrag::setup_pointer_frame_offset ()
2913 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2917 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2919 if (!_marker->tempo().movable()) {
2925 // create a dummy marker for visual representation of moving the
2926 // section, because whether its a copy or not, we're going to
2927 // leave or lose the original marker (leave if its a copy; lose if its
2928 // not, because we'll remove it from the map).
2930 // create a dummy marker for visual representation of moving the copy.
2931 // The actual copying is not done before we reach the finish callback.
2934 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2936 TempoSection section (_marker->tempo());
2938 _marker = new TempoMarker (
2940 *_editor->tempo_group,
2941 ARDOUR_UI::config()->color ("tempo marker"),
2943 *new TempoSection (_marker->tempo())
2946 /* use the new marker for the grab */
2947 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2950 TempoMap& map (_editor->session()->tempo_map());
2951 /* get current state */
2952 before_state = &map.get_state();
2953 /* remove the section while we drag it */
2954 map.remove_tempo (section, true);
2958 framepos_t const pf = adjusted_current_frame (event);
2959 _marker->set_position (pf);
2960 show_verbose_cursor_time (pf);
2964 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2966 if (!movement_occurred) {
2967 if (was_double_click()) {
2968 _editor->edit_tempo_marker (*_marker);
2973 if (!_marker->tempo().movable()) {
2977 motion (event, false);
2979 TempoMap& map (_editor->session()->tempo_map());
2980 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
2981 Timecode::BBT_Time when;
2983 map.bbt_time (beat_time, when);
2985 if (_copy == true) {
2986 _editor->begin_reversible_command (_("copy tempo mark"));
2987 XMLNode &before = map.get_state();
2988 map.add_tempo (_marker->tempo(), when);
2989 XMLNode &after = map.get_state();
2990 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2991 _editor->commit_reversible_command ();
2994 _editor->begin_reversible_command (_("move tempo mark"));
2995 /* we removed it before, so add it back now */
2996 map.add_tempo (_marker->tempo(), when);
2997 XMLNode &after = map.get_state();
2998 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2999 _editor->commit_reversible_command ();
3002 // delete the dummy marker we used for visual representation while moving.
3003 // a new visual marker will show up automatically.
3008 TempoMarkerDrag::aborted (bool moved)
3010 _marker->set_position (_marker->tempo().frame());
3012 TempoMap& map (_editor->session()->tempo_map());
3013 /* we removed it before, so add it back now */
3014 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3015 // delete the dummy marker we used for visual representation while moving.
3016 // a new visual marker will show up automatically.
3021 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3022 : Drag (e, &c.track_canvas_item(), false)
3026 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3029 /** Do all the things we do when dragging the playhead to make it look as though
3030 * we have located, without actually doing the locate (because that would cause
3031 * the diskstream buffers to be refilled, which is too slow).
3034 CursorDrag::fake_locate (framepos_t t)
3036 _editor->playhead_cursor->set_position (t);
3038 Session* s = _editor->session ();
3039 if (s->timecode_transmission_suspended ()) {
3040 framepos_t const f = _editor->playhead_cursor->current_frame ();
3041 /* This is asynchronous so it will be sent "now"
3043 s->send_mmc_locate (f);
3044 /* These are synchronous and will be sent during the next
3047 s->queue_full_time_code ();
3048 s->queue_song_position_pointer ();
3051 show_verbose_cursor_time (t);
3052 _editor->UpdateAllTransportClocks (t);
3056 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3058 Drag::start_grab (event, c);
3060 _grab_zoom = _editor->samples_per_pixel;
3062 framepos_t where = _editor->canvas_event_sample (event);
3064 _editor->snap_to_with_modifier (where, event);
3066 _editor->_dragging_playhead = true;
3068 Session* s = _editor->session ();
3070 /* grab the track canvas item as well */
3072 _cursor.track_canvas_item().grab();
3075 if (_was_rolling && _stop) {
3079 if (s->is_auditioning()) {
3080 s->cancel_audition ();
3084 if (AudioEngine::instance()->connected()) {
3086 /* do this only if we're the engine is connected
3087 * because otherwise this request will never be
3088 * serviced and we'll busy wait forever. likewise,
3089 * notice if we are disconnected while waiting for the
3090 * request to be serviced.
3093 s->request_suspend_timecode_transmission ();
3094 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3095 /* twiddle our thumbs */
3100 fake_locate (where);
3104 CursorDrag::motion (GdkEvent* event, bool)
3106 framepos_t const adjusted_frame = adjusted_current_frame (event);
3107 if (adjusted_frame != last_pointer_frame()) {
3108 fake_locate (adjusted_frame);
3113 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3115 _editor->_dragging_playhead = false;
3117 _cursor.track_canvas_item().ungrab();
3119 if (!movement_occurred && _stop) {
3123 motion (event, false);
3125 Session* s = _editor->session ();
3127 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3128 _editor->_pending_locate_request = true;
3129 s->request_resume_timecode_transmission ();
3134 CursorDrag::aborted (bool)
3136 _cursor.track_canvas_item().ungrab();
3138 if (_editor->_dragging_playhead) {
3139 _editor->session()->request_resume_timecode_transmission ();
3140 _editor->_dragging_playhead = false;
3143 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3146 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3147 : RegionDrag (e, i, p, v)
3149 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3153 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3155 Drag::start_grab (event, cursor);
3157 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3158 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3160 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3164 FadeInDrag::setup_pointer_frame_offset ()
3166 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3167 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3168 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3172 FadeInDrag::motion (GdkEvent* event, bool)
3174 framecnt_t fade_length;
3175 framepos_t const pos = adjusted_current_frame (event);
3176 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3178 if (pos < (region->position() + 64)) {
3179 fade_length = 64; // this should be a minimum defined somewhere
3180 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3181 fade_length = region->length() - region->fade_out()->back()->when - 1;
3183 fade_length = pos - region->position();
3186 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3188 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3194 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3197 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3201 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3203 if (!movement_occurred) {
3207 framecnt_t fade_length;
3209 framepos_t const pos = adjusted_current_frame (event);
3211 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3213 if (pos < (region->position() + 64)) {
3214 fade_length = 64; // this should be a minimum defined somewhere
3215 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3216 fade_length = region->length() - region->fade_out()->back()->when - 1;
3218 fade_length = pos - region->position();
3221 _editor->begin_reversible_command (_("change fade in length"));
3223 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3225 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3231 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3232 XMLNode &before = alist->get_state();
3234 tmp->audio_region()->set_fade_in_length (fade_length);
3235 tmp->audio_region()->set_fade_in_active (true);
3237 XMLNode &after = alist->get_state();
3238 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3241 _editor->commit_reversible_command ();
3245 FadeInDrag::aborted (bool)
3247 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3248 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3254 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3258 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3259 : RegionDrag (e, i, p, v)
3261 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3265 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3267 Drag::start_grab (event, cursor);
3269 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3270 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3272 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3276 FadeOutDrag::setup_pointer_frame_offset ()
3278 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3279 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3280 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3284 FadeOutDrag::motion (GdkEvent* event, bool)
3286 framecnt_t fade_length;
3288 framepos_t const pos = adjusted_current_frame (event);
3290 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3292 if (pos > (region->last_frame() - 64)) {
3293 fade_length = 64; // this should really be a minimum fade defined somewhere
3294 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3295 fade_length = region->length() - region->fade_in()->back()->when - 1;
3297 fade_length = region->last_frame() - pos;
3300 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3302 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3308 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3311 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3315 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3317 if (!movement_occurred) {
3321 framecnt_t fade_length;
3323 framepos_t const pos = adjusted_current_frame (event);
3325 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3327 if (pos > (region->last_frame() - 64)) {
3328 fade_length = 64; // this should really be a minimum fade defined somewhere
3329 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3330 fade_length = region->length() - region->fade_in()->back()->when - 1;
3332 fade_length = region->last_frame() - pos;
3335 _editor->begin_reversible_command (_("change fade out length"));
3337 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3339 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3345 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3346 XMLNode &before = alist->get_state();
3348 tmp->audio_region()->set_fade_out_length (fade_length);
3349 tmp->audio_region()->set_fade_out_active (true);
3351 XMLNode &after = alist->get_state();
3352 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3355 _editor->commit_reversible_command ();
3359 FadeOutDrag::aborted (bool)
3361 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3362 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3368 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3372 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3375 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3377 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3380 _points.push_back (ArdourCanvas::Duple (0, 0));
3381 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3384 MarkerDrag::~MarkerDrag ()
3386 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3391 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3393 location = new Location (*l);
3394 markers.push_back (m);
3399 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3401 Drag::start_grab (event, cursor);
3405 Location *location = _editor->find_location_from_marker (_marker, is_start);
3406 _editor->_dragging_edit_point = true;
3408 update_item (location);
3410 // _drag_line->show();
3411 // _line->raise_to_top();
3414 show_verbose_cursor_time (location->start());
3416 show_verbose_cursor_time (location->end());
3419 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3422 case Selection::Toggle:
3423 /* we toggle on the button release */
3425 case Selection::Set:
3426 if (!_editor->selection->selected (_marker)) {
3427 _editor->selection->set (_marker);
3430 case Selection::Extend:
3432 Locations::LocationList ll;
3433 list<Marker*> to_add;
3435 _editor->selection->markers.range (s, e);
3436 s = min (_marker->position(), s);
3437 e = max (_marker->position(), e);
3440 if (e < max_framepos) {
3443 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3444 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3445 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3448 to_add.push_back (lm->start);
3451 to_add.push_back (lm->end);
3455 if (!to_add.empty()) {
3456 _editor->selection->add (to_add);
3460 case Selection::Add:
3461 _editor->selection->add (_marker);
3465 /* Set up copies for us to manipulate during the drag
3468 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3470 Location* l = _editor->find_location_from_marker (*i, is_start);
3477 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3479 /* range: check that the other end of the range isn't
3482 CopiedLocationInfo::iterator x;
3483 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3484 if (*(*x).location == *l) {
3488 if (x == _copied_locations.end()) {
3489 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3491 (*x).markers.push_back (*i);
3492 (*x).move_both = true;
3500 MarkerDrag::setup_pointer_frame_offset ()
3503 Location *location = _editor->find_location_from_marker (_marker, is_start);
3504 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3508 MarkerDrag::motion (GdkEvent* event, bool)
3510 framecnt_t f_delta = 0;
3512 bool move_both = false;
3513 Location *real_location;
3514 Location *copy_location = 0;
3516 framepos_t const newframe = adjusted_current_frame (event);
3517 framepos_t next = newframe;
3519 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3523 CopiedLocationInfo::iterator x;
3525 /* find the marker we're dragging, and compute the delta */
3527 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3529 copy_location = (*x).location;
3531 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3533 /* this marker is represented by this
3534 * CopiedLocationMarkerInfo
3537 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3542 if (real_location->is_mark()) {
3543 f_delta = newframe - copy_location->start();
3547 switch (_marker->type()) {
3548 case Marker::SessionStart:
3549 case Marker::RangeStart:
3550 case Marker::LoopStart:
3551 case Marker::PunchIn:
3552 f_delta = newframe - copy_location->start();
3555 case Marker::SessionEnd:
3556 case Marker::RangeEnd:
3557 case Marker::LoopEnd:
3558 case Marker::PunchOut:
3559 f_delta = newframe - copy_location->end();
3562 /* what kind of marker is this ? */
3571 if (x == _copied_locations.end()) {
3572 /* hmm, impossible - we didn't find the dragged marker */
3576 /* now move them all */
3578 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3580 copy_location = x->location;
3582 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3586 if (real_location->locked()) {
3590 if (copy_location->is_mark()) {
3594 copy_location->set_start (copy_location->start() + f_delta);
3598 framepos_t new_start = copy_location->start() + f_delta;
3599 framepos_t new_end = copy_location->end() + f_delta;
3601 if (is_start) { // start-of-range marker
3603 if (move_both || (*x).move_both) {
3604 copy_location->set_start (new_start);
3605 copy_location->set_end (new_end);
3606 } else if (new_start < copy_location->end()) {
3607 copy_location->set_start (new_start);
3608 } else if (newframe > 0) {
3609 _editor->snap_to (next, RoundUpAlways, true);
3610 copy_location->set_end (next);
3611 copy_location->set_start (newframe);
3614 } else { // end marker
3616 if (move_both || (*x).move_both) {
3617 copy_location->set_end (new_end);
3618 copy_location->set_start (new_start);
3619 } else if (new_end > copy_location->start()) {
3620 copy_location->set_end (new_end);
3621 } else if (newframe > 0) {
3622 _editor->snap_to (next, RoundDownAlways, true);
3623 copy_location->set_start (next);
3624 copy_location->set_end (newframe);
3629 update_item (copy_location);
3631 /* now lookup the actual GUI items used to display this
3632 * location and move them to wherever the copy of the location
3633 * is now. This means that the logic in ARDOUR::Location is
3634 * still enforced, even though we are not (yet) modifying
3635 * the real Location itself.
3638 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3641 lm->set_position (copy_location->start(), copy_location->end());
3646 assert (!_copied_locations.empty());
3648 show_verbose_cursor_time (newframe);
3652 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3654 if (!movement_occurred) {
3656 if (was_double_click()) {
3657 _editor->rename_marker (_marker);
3661 /* just a click, do nothing but finish
3662 off the selection process
3665 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3668 case Selection::Set:
3669 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3670 _editor->selection->set (_marker);
3674 case Selection::Toggle:
3675 /* we toggle on the button release, click only */
3676 _editor->selection->toggle (_marker);
3679 case Selection::Extend:
3680 case Selection::Add:
3687 _editor->_dragging_edit_point = false;
3689 _editor->begin_reversible_command ( _("move marker") );
3690 XMLNode &before = _editor->session()->locations()->get_state();
3692 MarkerSelection::iterator i;
3693 CopiedLocationInfo::iterator x;
3696 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3697 x != _copied_locations.end() && i != _editor->selection->markers.end();
3700 Location * location = _editor->find_location_from_marker (*i, is_start);
3704 if (location->locked()) {
3708 if (location->is_mark()) {
3709 location->set_start (((*x).location)->start());
3711 location->set (((*x).location)->start(), ((*x).location)->end());
3716 XMLNode &after = _editor->session()->locations()->get_state();
3717 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3718 _editor->commit_reversible_command ();
3722 MarkerDrag::aborted (bool movement_occured)
3724 if (!movement_occured) {
3728 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3730 /* move all markers to their original location */
3733 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3736 Location * location = _editor->find_location_from_marker (*m, is_start);
3739 (*m)->set_position (is_start ? location->start() : location->end());
3746 MarkerDrag::update_item (Location*)
3751 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3753 _cumulative_x_drag (0),
3754 _cumulative_y_drag (0)
3756 if (_zero_gain_fraction < 0.0) {
3757 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3760 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3762 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3768 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3770 Drag::start_grab (event, _editor->cursors()->fader);
3772 // start the grab at the center of the control point so
3773 // the point doesn't 'jump' to the mouse after the first drag
3774 _fixed_grab_x = _point->get_x();
3775 _fixed_grab_y = _point->get_y();
3777 float const fraction = 1 - (_point->get_y() / _point->line().height());
3779 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3781 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3783 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3785 if (!_point->can_slide ()) {
3786 _x_constrained = true;
3791 ControlPointDrag::motion (GdkEvent* event, bool)
3793 double dx = _drags->current_pointer_x() - last_pointer_x();
3794 double dy = current_pointer_y() - last_pointer_y();
3796 if (event->button.state & Keyboard::SecondaryModifier) {
3801 /* coordinate in pixels relative to the start of the region (for region-based automation)
3802 or track (for track-based automation) */
3803 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3804 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3806 // calculate zero crossing point. back off by .01 to stay on the
3807 // positive side of zero
3808 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3810 // make sure we hit zero when passing through
3811 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3815 if (_x_constrained) {
3818 if (_y_constrained) {
3822 _cumulative_x_drag = cx - _fixed_grab_x;
3823 _cumulative_y_drag = cy - _fixed_grab_y;
3827 cy = min ((double) _point->line().height(), cy);
3829 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3831 if (!_x_constrained) {
3832 _editor->snap_to_with_modifier (cx_frames, event);
3835 cx_frames = min (cx_frames, _point->line().maximum_time());
3837 float const fraction = 1.0 - (cy / _point->line().height());
3839 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3841 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3845 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3847 if (!movement_occurred) {
3851 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3852 _editor->reset_point_selection ();
3856 motion (event, false);
3859 _point->line().end_drag (_pushing, _final_index);
3860 _editor->commit_reversible_command ();
3864 ControlPointDrag::aborted (bool)
3866 _point->line().reset ();
3870 ControlPointDrag::active (Editing::MouseMode m)
3872 if (m == Editing::MouseDraw) {
3873 /* always active in mouse draw */
3877 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3878 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3881 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3884 _cumulative_y_drag (0)
3886 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3890 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3892 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3895 _item = &_line->grab_item ();
3897 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3898 origin, and ditto for y.
3901 double cx = event->button.x;
3902 double cy = event->button.y;
3904 _line->parent_group().canvas_to_item (cx, cy);
3906 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3911 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3912 /* no adjacent points */
3916 Drag::start_grab (event, _editor->cursors()->fader);
3918 /* store grab start in parent frame */
3923 double fraction = 1.0 - (cy / _line->height());
3925 _line->start_drag_line (before, after, fraction);
3927 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3931 LineDrag::motion (GdkEvent* event, bool)
3933 double dy = current_pointer_y() - last_pointer_y();
3935 if (event->button.state & Keyboard::SecondaryModifier) {
3939 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3941 _cumulative_y_drag = cy - _fixed_grab_y;
3944 cy = min ((double) _line->height(), cy);
3946 double const fraction = 1.0 - (cy / _line->height());
3949 /* we are ignoring x position for this drag, so we can just pass in anything */
3950 _line->drag_motion (0, fraction, true, false, ignored);
3952 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3956 LineDrag::finished (GdkEvent* event, bool movement_occured)
3958 if (movement_occured) {
3959 motion (event, false);
3960 _line->end_drag (false, 0);
3962 /* add a new control point on the line */
3964 AutomationTimeAxisView* atv;
3966 _line->end_drag (false, 0);
3968 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3969 framepos_t where = _editor->window_event_sample (event, 0, 0);
3970 atv->add_automation_event (event, where, event->button.y, false);
3974 _editor->commit_reversible_command ();
3978 LineDrag::aborted (bool)
3983 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3986 _cumulative_x_drag (0)
3988 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3992 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3994 Drag::start_grab (event);
3996 _line = reinterpret_cast<Line*> (_item);
3999 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4001 double cx = event->button.x;
4002 double cy = event->button.y;
4004 _item->parent()->canvas_to_item (cx, cy);
4006 /* store grab start in parent frame */
4007 _region_view_grab_x = cx;
4009 _before = *(float*) _item->get_data ("position");
4011 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4013 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4017 FeatureLineDrag::motion (GdkEvent*, bool)
4019 double dx = _drags->current_pointer_x() - last_pointer_x();
4021 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4023 _cumulative_x_drag += dx;
4025 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4034 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4036 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4038 float *pos = new float;
4041 _line->set_data ("position", pos);
4047 FeatureLineDrag::finished (GdkEvent*, bool)
4049 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4050 _arv->update_transient(_before, _before);
4054 FeatureLineDrag::aborted (bool)
4059 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4061 , _vertical_only (false)
4063 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4067 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4069 Drag::start_grab (event);
4070 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4074 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4081 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4083 framepos_t grab = grab_frame ();
4084 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4085 _editor->snap_to_with_modifier (grab, event);
4087 grab = raw_grab_frame ();
4090 /* base start and end on initial click position */
4100 if (current_pointer_y() < grab_y()) {
4101 y1 = current_pointer_y();
4104 y2 = current_pointer_y();
4108 if (start != end || y1 != y2) {
4110 double x1 = _editor->sample_to_pixel (start);
4111 double x2 = _editor->sample_to_pixel (end);
4112 const double min_dimension = 2.0;
4114 if (_vertical_only) {
4115 /* fixed 10 pixel width */
4119 x2 = min (x1 - min_dimension, x2);
4121 x2 = max (x1 + min_dimension, x2);
4126 y2 = min (y1 - min_dimension, y2);
4128 y2 = max (y1 + min_dimension, y2);
4131 /* translate rect into item space and set */
4133 ArdourCanvas::Rect r (x1, y1, x2, y2);
4135 /* this drag is a _trackview_only == true drag, so the y1 and
4136 * y2 (computed using current_pointer_y() and grab_y()) will be
4137 * relative to the top of the trackview group). The
4138 * rubberband rect has the same parent/scroll offset as the
4139 * the trackview group, so we can use the "r" rect directly
4140 * to set the shape of the rubberband.
4143 _editor->rubberband_rect->set (r);
4144 _editor->rubberband_rect->show();
4145 _editor->rubberband_rect->raise_to_top();
4147 show_verbose_cursor_time (pf);
4149 do_select_things (event, true);
4154 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4158 framepos_t grab = grab_frame ();
4159 framepos_t lpf = last_pointer_frame ();
4161 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4162 grab = raw_grab_frame ();
4163 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4177 if (current_pointer_y() < grab_y()) {
4178 y1 = current_pointer_y();
4181 y2 = current_pointer_y();
4185 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4189 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4191 if (movement_occurred) {
4193 motion (event, false);
4194 do_select_things (event, false);
4200 bool do_deselect = true;
4201 MidiTimeAxisView* mtv;
4203 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4205 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4206 /* nothing selected */
4207 add_midi_region (mtv);
4208 do_deselect = false;
4212 /* do not deselect if Primary or Tertiary (toggle-select or
4213 * extend-select are pressed.
4216 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4217 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4224 _editor->rubberband_rect->hide();
4228 RubberbandSelectDrag::aborted (bool)
4230 _editor->rubberband_rect->hide ();
4233 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4234 : RegionDrag (e, i, p, v)
4236 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4240 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4242 Drag::start_grab (event, cursor);
4244 show_verbose_cursor_time (adjusted_current_frame (event));
4248 TimeFXDrag::motion (GdkEvent* event, bool)
4250 RegionView* rv = _primary;
4251 StreamView* cv = rv->get_time_axis_view().view ();
4253 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4254 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4255 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4257 framepos_t const pf = adjusted_current_frame (event);
4259 if (pf > rv->region()->position()) {
4260 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4263 show_verbose_cursor_time (pf);
4267 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4269 _primary->get_time_axis_view().hide_timestretch ();
4271 if (!movement_occurred) {
4275 if (last_pointer_frame() < _primary->region()->position()) {
4276 /* backwards drag of the left edge - not usable */
4280 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4282 float percentage = (double) newlen / (double) _primary->region()->length();
4284 #ifndef USE_RUBBERBAND
4285 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4286 if (_primary->region()->data_type() == DataType::AUDIO) {
4287 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4291 if (!_editor->get_selection().regions.empty()) {
4292 /* primary will already be included in the selection, and edit
4293 group shared editing will propagate selection across
4294 equivalent regions, so just use the current region
4298 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4299 error << _("An error occurred while executing time stretch operation") << endmsg;
4305 TimeFXDrag::aborted (bool)
4307 _primary->get_time_axis_view().hide_timestretch ();
4310 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4313 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4317 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4319 Drag::start_grab (event);
4323 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4325 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4329 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4331 if (movement_occurred && _editor->session()) {
4332 /* make sure we stop */
4333 _editor->session()->request_transport_speed (0.0);
4338 ScrubDrag::aborted (bool)
4343 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4347 , _original_pointer_time_axis (-1)
4348 , _time_selection_at_start (!_editor->get_selection().time.empty())
4350 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4352 if (_time_selection_at_start) {
4353 start_at_start = _editor->get_selection().time.start();
4354 end_at_start = _editor->get_selection().time.end_frame();
4359 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4361 if (_editor->session() == 0) {
4365 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4367 switch (_operation) {
4368 case CreateSelection:
4369 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4374 cursor = _editor->cursors()->selector;
4375 Drag::start_grab (event, cursor);
4378 case SelectionStartTrim:
4379 if (_editor->clicked_axisview) {
4380 _editor->clicked_axisview->order_selection_trims (_item, true);
4382 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4385 case SelectionEndTrim:
4386 if (_editor->clicked_axisview) {
4387 _editor->clicked_axisview->order_selection_trims (_item, false);
4389 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4393 Drag::start_grab (event, cursor);
4396 case SelectionExtend:
4397 Drag::start_grab (event, cursor);
4401 if (_operation == SelectionMove) {
4402 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4404 show_verbose_cursor_time (adjusted_current_frame (event));
4407 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4411 SelectionDrag::setup_pointer_frame_offset ()
4413 switch (_operation) {
4414 case CreateSelection:
4415 _pointer_frame_offset = 0;
4418 case SelectionStartTrim:
4420 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4423 case SelectionEndTrim:
4424 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4427 case SelectionExtend:
4433 SelectionDrag::motion (GdkEvent* event, bool first_move)
4435 framepos_t start = 0;
4437 framecnt_t length = 0;
4438 framecnt_t distance = 0;
4440 framepos_t const pending_position = adjusted_current_frame (event);
4442 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4446 switch (_operation) {
4447 case CreateSelection:
4449 framepos_t grab = grab_frame ();
4452 grab = adjusted_current_frame (event, false);
4453 if (grab < pending_position) {
4454 _editor->snap_to (grab, RoundDownMaybe);
4456 _editor->snap_to (grab, RoundUpMaybe);
4460 if (pending_position < grab) {
4461 start = pending_position;
4464 end = pending_position;
4468 /* first drag: Either add to the selection
4469 or create a new selection
4476 /* adding to the selection */
4477 _editor->set_selected_track_as_side_effect (Selection::Add);
4478 _editor->clicked_selection = _editor->selection->add (start, end);
4485 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4486 _editor->set_selected_track_as_side_effect (Selection::Set);
4489 _editor->clicked_selection = _editor->selection->set (start, end);
4493 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4494 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4495 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4497 _editor->selection->add (atest);
4501 /* select all tracks within the rectangle that we've marked out so far */
4502 TrackViewList new_selection;
4503 TrackViewList& all_tracks (_editor->track_views);
4505 ArdourCanvas::Coord const top = grab_y();
4506 ArdourCanvas::Coord const bottom = current_pointer_y();
4508 if (top >= 0 && bottom >= 0) {
4510 //first, find the tracks that are covered in the y range selection
4511 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4512 if ((*i)->covered_by_y_range (top, bottom)) {
4513 new_selection.push_back (*i);
4517 //now find any tracks that are GROUPED with the tracks we selected
4518 TrackViewList grouped_add = new_selection;
4519 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4520 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4521 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4522 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4523 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4524 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4525 grouped_add.push_back (*j);
4530 //now compare our list with the current selection, and add or remove as necessary
4531 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4532 TrackViewList tracks_to_add;
4533 TrackViewList tracks_to_remove;
4534 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4535 if ( !_editor->selection->tracks.contains ( *i ) )
4536 tracks_to_add.push_back ( *i );
4537 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4538 if ( !grouped_add.contains ( *i ) )
4539 tracks_to_remove.push_back ( *i );
4540 _editor->selection->add(tracks_to_add);
4541 _editor->selection->remove(tracks_to_remove);
4547 case SelectionStartTrim:
4549 start = _editor->selection->time[_editor->clicked_selection].start;
4550 end = _editor->selection->time[_editor->clicked_selection].end;
4552 if (pending_position > end) {
4555 start = pending_position;
4559 case SelectionEndTrim:
4561 start = _editor->selection->time[_editor->clicked_selection].start;
4562 end = _editor->selection->time[_editor->clicked_selection].end;
4564 if (pending_position < start) {
4567 end = pending_position;
4574 start = _editor->selection->time[_editor->clicked_selection].start;
4575 end = _editor->selection->time[_editor->clicked_selection].end;
4577 length = end - start;
4578 distance = pending_position - start;
4579 start = pending_position;
4580 _editor->snap_to (start);
4582 end = start + length;
4586 case SelectionExtend:
4591 switch (_operation) {
4593 if (_time_selection_at_start) {
4594 _editor->selection->move_time (distance);
4598 _editor->selection->replace (_editor->clicked_selection, start, end);
4602 if (_operation == SelectionMove) {
4603 show_verbose_cursor_time(start);
4605 show_verbose_cursor_time(pending_position);
4610 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4612 Session* s = _editor->session();
4614 _editor->begin_reversible_selection_op (_("Change Time Selection"));
4615 if (movement_occurred) {
4616 motion (event, false);
4617 /* XXX this is not object-oriented programming at all. ick */
4618 if (_editor->selection->time.consolidate()) {
4619 _editor->selection->TimeChanged ();
4622 /* XXX what if its a music time selection? */
4624 if ( s->get_play_range() && s->transport_rolling() ) {
4625 s->request_play_range (&_editor->selection->time, true);
4627 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4628 if (_operation == SelectionEndTrim)
4629 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4631 s->request_locate (_editor->get_selection().time.start());
4637 /* just a click, no pointer movement.
4640 if (_operation == SelectionExtend) {
4641 if (_time_selection_at_start) {
4642 framepos_t pos = adjusted_current_frame (event, false);
4643 framepos_t start = min (pos, start_at_start);
4644 framepos_t end = max (pos, end_at_start);
4645 _editor->selection->set (start, end);
4648 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4649 if (_editor->clicked_selection) {
4650 _editor->selection->remove (_editor->clicked_selection);
4653 if (!_editor->clicked_selection) {
4654 _editor->selection->clear_time();
4659 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4660 _editor->selection->set (_editor->clicked_axisview);
4663 if (s && s->get_play_range () && s->transport_rolling()) {
4664 s->request_stop (false, false);
4669 _editor->stop_canvas_autoscroll ();
4670 _editor->clicked_selection = 0;
4671 _editor->commit_reversible_selection_op ();
4675 SelectionDrag::aborted (bool)
4680 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4681 : Drag (e, i, false),
4685 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4687 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4688 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4689 physical_screen_height (_editor->get_window())));
4690 _drag_rect->hide ();
4692 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4693 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4696 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4698 /* normal canvas items will be cleaned up when their parent group is deleted. But
4699 this item is created as the child of a long-lived parent group, and so we
4700 need to explicitly delete it.
4706 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4708 if (_editor->session() == 0) {
4712 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4714 if (!_editor->temp_location) {
4715 _editor->temp_location = new Location (*_editor->session());
4718 switch (_operation) {
4719 case CreateSkipMarker:
4720 case CreateRangeMarker:
4721 case CreateTransportMarker:
4722 case CreateCDMarker:
4724 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4729 cursor = _editor->cursors()->selector;
4733 Drag::start_grab (event, cursor);
4735 show_verbose_cursor_time (adjusted_current_frame (event));
4739 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4741 framepos_t start = 0;
4743 ArdourCanvas::Rectangle *crect;
4745 switch (_operation) {
4746 case CreateSkipMarker:
4747 crect = _editor->range_bar_drag_rect;
4749 case CreateRangeMarker:
4750 crect = _editor->range_bar_drag_rect;
4752 case CreateTransportMarker:
4753 crect = _editor->transport_bar_drag_rect;
4755 case CreateCDMarker:
4756 crect = _editor->cd_marker_bar_drag_rect;
4759 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4764 framepos_t const pf = adjusted_current_frame (event);
4766 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4767 framepos_t grab = grab_frame ();
4768 _editor->snap_to (grab);
4770 if (pf < grab_frame()) {
4778 /* first drag: Either add to the selection
4779 or create a new selection.
4784 _editor->temp_location->set (start, end);
4788 update_item (_editor->temp_location);
4790 //_drag_rect->raise_to_top();
4796 _editor->temp_location->set (start, end);
4798 double x1 = _editor->sample_to_pixel (start);
4799 double x2 = _editor->sample_to_pixel (end);
4803 update_item (_editor->temp_location);
4806 show_verbose_cursor_time (pf);
4811 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4813 Location * newloc = 0;
4817 if (movement_occurred) {
4818 motion (event, false);
4821 switch (_operation) {
4822 case CreateSkipMarker:
4823 case CreateRangeMarker:
4824 case CreateCDMarker:
4826 XMLNode &before = _editor->session()->locations()->get_state();
4827 if (_operation == CreateSkipMarker) {
4828 _editor->begin_reversible_command (_("new skip marker"));
4829 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4830 flags = Location::IsRangeMarker | Location::IsSkip;
4831 _editor->range_bar_drag_rect->hide();
4832 } else if (_operation == CreateCDMarker) {
4833 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4834 _editor->begin_reversible_command (_("new CD marker"));
4835 flags = Location::IsRangeMarker | Location::IsCDMarker;
4836 _editor->cd_marker_bar_drag_rect->hide();
4838 _editor->begin_reversible_command (_("new skip marker"));
4839 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4840 flags = Location::IsRangeMarker;
4841 _editor->range_bar_drag_rect->hide();
4843 newloc = new Location (
4844 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4847 _editor->session()->locations()->add (newloc, true);
4848 XMLNode &after = _editor->session()->locations()->get_state();
4849 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4850 _editor->commit_reversible_command ();
4854 case CreateTransportMarker:
4855 // popup menu to pick loop or punch
4856 _editor->new_transport_marker_context_menu (&event->button, _item);
4862 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4864 if (_operation == CreateTransportMarker) {
4866 /* didn't drag, so just locate */
4868 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4870 } else if (_operation == CreateCDMarker) {
4872 /* didn't drag, but mark is already created so do
4875 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4881 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4883 if (end == max_framepos) {
4884 end = _editor->session()->current_end_frame ();
4887 if (start == max_framepos) {
4888 start = _editor->session()->current_start_frame ();
4891 switch (_editor->mouse_mode) {
4893 /* find the two markers on either side and then make the selection from it */
4894 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4898 /* find the two markers on either side of the click and make the range out of it */
4899 _editor->selection->set (start, end);
4908 _editor->stop_canvas_autoscroll ();
4912 RangeMarkerBarDrag::aborted (bool movement_occured)
4914 if (movement_occured) {
4915 _drag_rect->hide ();
4920 RangeMarkerBarDrag::update_item (Location* location)
4922 double const x1 = _editor->sample_to_pixel (location->start());
4923 double const x2 = _editor->sample_to_pixel (location->end());
4925 _drag_rect->set_x0 (x1);
4926 _drag_rect->set_x1 (x2);
4929 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4931 , _cumulative_dx (0)
4932 , _cumulative_dy (0)
4934 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4936 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4938 _region = &_primary->region_view ();
4939 _note_height = _region->midi_stream_view()->note_height ();
4943 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4945 Drag::start_grab (event);
4947 if (!(_was_selected = _primary->selected())) {
4949 /* tertiary-click means extend selection - we'll do that on button release,
4950 so don't add it here, because otherwise we make it hard to figure
4951 out the "extend-to" range.
4954 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4957 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4960 _region->note_selected (_primary, true);
4962 _region->unique_select (_primary);
4965 _editor->begin_reversible_selection_op(_("Select Note Press"));
4966 _editor->commit_reversible_selection_op();
4971 /** @return Current total drag x change in frames */
4973 NoteDrag::total_dx () const
4976 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4978 /* primary note time */
4979 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4981 /* new time of the primary note in session frames */
4982 frameoffset_t st = n + dx;
4984 framepos_t const rp = _region->region()->position ();
4986 /* prevent the note being dragged earlier than the region's position */
4989 /* snap and return corresponding delta */
4990 return _region->snap_frame_to_frame (st - rp) + rp - n;
4993 /** @return Current total drag y change in note number */
4995 NoteDrag::total_dy () const
4997 MidiStreamView* msv = _region->midi_stream_view ();
4998 double const y = _region->midi_view()->y_position ();
4999 /* new current note */
5000 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5002 n = max (msv->lowest_note(), n);
5003 n = min (msv->highest_note(), n);
5004 /* and work out delta */
5005 return n - msv->y_to_note (grab_y() - y);
5009 NoteDrag::motion (GdkEvent *, bool)
5011 /* Total change in x and y since the start of the drag */
5012 frameoffset_t const dx = total_dx ();
5013 int8_t const dy = total_dy ();
5015 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5016 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5017 double const tdy = -dy * _note_height - _cumulative_dy;
5020 _cumulative_dx += tdx;
5021 _cumulative_dy += tdy;
5023 int8_t note_delta = total_dy();
5025 _region->move_selection (tdx, tdy, note_delta);
5027 /* the new note value may be the same as the old one, but we
5028 * don't know what that means because the selection may have
5029 * involved more than one note and we might be doing something
5030 * odd with them. so show the note value anyway, always.
5034 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5036 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5037 (int) floor ((double)new_note));
5039 show_verbose_cursor_text (buf);
5044 NoteDrag::finished (GdkEvent* ev, bool moved)
5047 /* no motion - select note */
5049 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5050 _editor->current_mouse_mode() == Editing::MouseDraw) {
5052 bool changed = false;
5054 if (_was_selected) {
5055 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5057 _region->note_deselected (_primary);
5061 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5062 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5064 if (!extend && !add && _region->selection_size() > 1) {
5065 _region->unique_select (_primary);
5067 } else if (extend) {
5068 _region->note_selected (_primary, true, true);
5071 /* it was added during button press */
5076 _editor->begin_reversible_selection_op(_("Select Note Release"));
5077 _editor->commit_reversible_selection_op();
5081 _region->note_dropped (_primary, total_dx(), total_dy());
5086 NoteDrag::aborted (bool)
5091 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5092 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5093 : Drag (editor, atv->base_item ())
5095 , _y_origin (atv->y_position())
5096 , _nothing_to_drag (false)
5098 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5099 setup (atv->lines ());
5102 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5103 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5104 : Drag (editor, rv->get_canvas_group ())
5106 , _y_origin (rv->get_time_axis_view().y_position())
5107 , _nothing_to_drag (false)
5110 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5112 list<boost::shared_ptr<AutomationLine> > lines;
5114 AudioRegionView* audio_view;
5115 AutomationRegionView* automation_view;
5116 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5117 lines.push_back (audio_view->get_gain_line ());
5118 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5119 lines.push_back (automation_view->line ());
5122 error << _("Automation range drag created for invalid region type") << endmsg;
5128 /** @param lines AutomationLines to drag.
5129 * @param offset Offset from the session start to the points in the AutomationLines.
5132 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5134 /* find the lines that overlap the ranges being dragged */
5135 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5136 while (i != lines.end ()) {
5137 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5140 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5142 /* check this range against all the AudioRanges that we are using */
5143 list<AudioRange>::const_iterator k = _ranges.begin ();
5144 while (k != _ranges.end()) {
5145 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5151 /* add it to our list if it overlaps at all */
5152 if (k != _ranges.end()) {
5157 _lines.push_back (n);
5163 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5167 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5169 return 1.0 - ((global_y - _y_origin) / line->height());
5173 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5175 const double v = list->eval(x);
5176 return _integral ? rint(v) : v;
5180 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5182 Drag::start_grab (event, cursor);
5184 /* Get line states before we start changing things */
5185 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5186 i->state = &i->line->get_state ();
5187 i->original_fraction = y_fraction (i->line, current_pointer_y());
5190 if (_ranges.empty()) {
5192 /* No selected time ranges: drag all points */
5193 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5194 uint32_t const N = i->line->npoints ();
5195 for (uint32_t j = 0; j < N; ++j) {
5196 i->points.push_back (i->line->nth (j));
5202 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5204 framecnt_t const half = (i->start + i->end) / 2;
5206 /* find the line that this audio range starts in */
5207 list<Line>::iterator j = _lines.begin();
5208 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5212 if (j != _lines.end()) {
5213 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5215 /* j is the line that this audio range starts in; fade into it;
5216 64 samples length plucked out of thin air.
5219 framepos_t a = i->start + 64;
5224 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5225 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5227 the_list->editor_add (p, value (the_list, p));
5228 the_list->editor_add (q, value (the_list, q));
5231 /* same thing for the end */
5234 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5238 if (j != _lines.end()) {
5239 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5241 /* j is the line that this audio range starts in; fade out of it;
5242 64 samples length plucked out of thin air.
5245 framepos_t b = i->end - 64;
5250 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5251 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5253 the_list->editor_add (p, value (the_list, p));
5254 the_list->editor_add (q, value (the_list, q));
5258 _nothing_to_drag = true;
5260 /* Find all the points that should be dragged and put them in the relevant
5261 points lists in the Line structs.
5264 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5266 uint32_t const N = i->line->npoints ();
5267 for (uint32_t j = 0; j < N; ++j) {
5269 /* here's a control point on this line */
5270 ControlPoint* p = i->line->nth (j);
5271 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5273 /* see if it's inside a range */
5274 list<AudioRange>::const_iterator k = _ranges.begin ();
5275 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5279 if (k != _ranges.end()) {
5280 /* dragging this point */
5281 _nothing_to_drag = false;
5282 i->points.push_back (p);
5288 if (_nothing_to_drag) {
5292 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5293 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5298 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5300 if (_nothing_to_drag) {
5304 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5305 float const f = y_fraction (l->line, current_pointer_y());
5306 /* we are ignoring x position for this drag, so we can just pass in anything */
5308 l->line->drag_motion (0, f, true, false, ignored);
5309 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5314 AutomationRangeDrag::finished (GdkEvent* event, bool)
5316 if (_nothing_to_drag) {
5320 motion (event, false);
5321 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5322 i->line->end_drag (false, 0);
5325 _editor->commit_reversible_command ();
5329 AutomationRangeDrag::aborted (bool)
5331 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5336 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5338 , initial_time_axis_view (itav)
5340 /* note that time_axis_view may be null if the regionview was created
5341 * as part of a copy operation.
5343 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5344 layer = v->region()->layer ();
5345 initial_y = v->get_canvas_group()->position().y;
5346 initial_playlist = v->region()->playlist ();
5347 initial_position = v->region()->position ();
5348 initial_end = v->region()->position () + v->region()->length ();
5351 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5352 : Drag (e, i->canvas_item ())
5355 , _cumulative_dx (0)
5357 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5358 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5363 PatchChangeDrag::motion (GdkEvent* ev, bool)
5365 framepos_t f = adjusted_current_frame (ev);
5366 boost::shared_ptr<Region> r = _region_view->region ();
5367 f = max (f, r->position ());
5368 f = min (f, r->last_frame ());
5370 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5371 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5372 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5373 _cumulative_dx = dxu;
5377 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5379 if (!movement_occurred) {
5383 boost::shared_ptr<Region> r (_region_view->region ());
5384 framepos_t f = adjusted_current_frame (ev);
5385 f = max (f, r->position ());
5386 f = min (f, r->last_frame ());
5388 _region_view->move_patch_change (
5390 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5395 PatchChangeDrag::aborted (bool)
5397 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5401 PatchChangeDrag::setup_pointer_frame_offset ()
5403 boost::shared_ptr<Region> region = _region_view->region ();
5404 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5407 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5408 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5415 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5417 _region_view->update_drag_selection (
5419 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5423 MidiRubberbandSelectDrag::deselect_things ()
5428 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5429 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5432 _vertical_only = true;
5436 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5438 double const y = _region_view->midi_view()->y_position ();
5440 y1 = max (0.0, y1 - y);
5441 y2 = max (0.0, y2 - y);
5443 _region_view->update_vertical_drag_selection (
5446 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5451 MidiVerticalSelectDrag::deselect_things ()
5456 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5457 : RubberbandSelectDrag (e, i)
5463 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5465 if (drag_in_progress) {
5466 /* We just want to select things at the end of the drag, not during it */
5470 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5472 _editor->begin_reversible_selection_op (_("rubberband selection"));
5474 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5476 _editor->commit_reversible_selection_op ();
5480 EditorRubberbandSelectDrag::deselect_things ()
5482 _editor->begin_reversible_selection_op (_("Clear Selection (rubberband)"));
5484 _editor->selection->clear_tracks();
5485 _editor->selection->clear_regions();
5486 _editor->selection->clear_points ();
5487 _editor->selection->clear_lines ();
5488 _editor->selection->clear_midi_notes ();
5490 _editor->commit_reversible_selection_op();
5493 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5498 _note[0] = _note[1] = 0;
5501 NoteCreateDrag::~NoteCreateDrag ()
5507 NoteCreateDrag::grid_frames (framepos_t t) const
5510 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5512 grid_beats = Evoral::Beats(1);
5515 return _region_view->region_beats_to_region_frames (grid_beats);
5519 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5521 Drag::start_grab (event, cursor);
5523 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5525 framepos_t pf = _drags->current_pointer_frame ();
5526 framecnt_t const g = grid_frames (pf);
5528 /* Hack so that we always snap to the note that we are over, instead of snapping
5529 to the next one if we're more than halfway through the one we're over.
5531 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5535 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5536 _note[1] = _note[0];
5538 MidiStreamView* sv = _region_view->midi_stream_view ();
5539 double const x = _editor->sample_to_pixel (_note[0]);
5540 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5542 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5543 _drag_rect->set_outline_all ();
5544 _drag_rect->set_outline_color (0xffffff99);
5545 _drag_rect->set_fill_color (0xffffff66);
5549 NoteCreateDrag::motion (GdkEvent* event, bool)
5551 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5552 double const x0 = _editor->sample_to_pixel (_note[0]);
5553 double const x1 = _editor->sample_to_pixel (_note[1]);
5554 _drag_rect->set_x0 (std::min(x0, x1));
5555 _drag_rect->set_x1 (std::max(x0, x1));
5559 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5561 if (!had_movement) {
5565 framepos_t const start = min (_note[0], _note[1]);
5566 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5568 framecnt_t const g = grid_frames (start);
5569 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5571 if (_editor->snap_mode() == SnapNormal && length < g) {
5575 Evoral::Beats length_beats = max (
5576 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5578 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5582 NoteCreateDrag::y_to_region (double y) const
5585 _region_view->get_canvas_group()->canvas_to_item (x, y);
5590 NoteCreateDrag::aborted (bool)
5595 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5600 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5604 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5606 Drag::start_grab (event, cursor);
5610 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5616 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5619 distance = _drags->current_pointer_x() - grab_x();
5620 len = ar->fade_in()->back()->when;
5622 distance = grab_x() - _drags->current_pointer_x();
5623 len = ar->fade_out()->back()->when;
5626 /* how long should it be ? */
5628 new_length = len + _editor->pixel_to_sample (distance);
5630 /* now check with the region that this is legal */
5632 new_length = ar->verify_xfade_bounds (new_length, start);
5635 arv->reset_fade_in_shape_width (ar, new_length);
5637 arv->reset_fade_out_shape_width (ar, new_length);
5642 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5648 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5651 distance = _drags->current_pointer_x() - grab_x();
5652 len = ar->fade_in()->back()->when;
5654 distance = grab_x() - _drags->current_pointer_x();
5655 len = ar->fade_out()->back()->when;
5658 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5660 _editor->begin_reversible_command ("xfade trim");
5661 ar->playlist()->clear_owned_changes ();
5664 ar->set_fade_in_length (new_length);
5666 ar->set_fade_out_length (new_length);
5669 /* Adjusting the xfade may affect other regions in the playlist, so we need
5670 to get undo Commands from the whole playlist rather than just the
5674 vector<Command*> cmds;
5675 ar->playlist()->rdiff (cmds);
5676 _editor->session()->add_commands (cmds);
5677 _editor->commit_reversible_command ();
5682 CrossfadeEdgeDrag::aborted (bool)
5685 // arv->redraw_start_xfade ();
5687 // arv->redraw_end_xfade ();
5691 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5692 : Drag (e, item, true)
5693 , line (new EditorCursor (*e))
5695 line->set_position (pos);
5699 RegionCutDrag::~RegionCutDrag ()
5705 RegionCutDrag::motion (GdkEvent*, bool)
5707 framepos_t where = _drags->current_pointer_frame();
5708 _editor->snap_to (where);
5710 line->set_position (where);
5714 RegionCutDrag::finished (GdkEvent*, bool)
5716 _editor->get_track_canvas()->canvas()->re_enter();
5718 framepos_t pos = _drags->current_pointer_frame();
5722 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5728 _editor->split_regions_at (pos, rs);
5732 RegionCutDrag::aborted (bool)