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.
662 /* upward motion - set n to the track we would end up in if motion
663 is successful, and check validity below. */
664 n = _time_axis_views.size() + delta_track;
670 } else if (n >= int (_time_axis_views.size())) {
671 /* downward motion into drop zone. That's fine. */
674 /* target is not in the drop zone */
675 all_in_drop_zone = false;
678 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
679 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
680 /* not a track, or the wrong type */
684 double const l = i->layer + delta_layer;
686 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
687 mode to allow the user to place a region below another on layer 0.
689 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
690 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
691 If it has, the layers will be munged later anyway, so it's ok.
697 /* all regions being dragged are ok with this change */
698 return !all_in_drop_zone;
702 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
704 double delta_layer = 0;
705 int delta_time_axis_view = 0;
706 int current_pointer_time_axis_view = -1;
708 assert (!_views.empty ());
712 if (initially_vertical()) {
713 _y_constrained = false;
714 _x_constrained = true;
716 _y_constrained = true;
717 _x_constrained = false;
722 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
724 /* Find the TimeAxisView that the pointer is now over */
726 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
727 TimeAxisView* tv = r.first;
729 if (!tv && current_pointer_y() < 0) {
730 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
734 if (tv && tv->view()) {
735 double layer = r.second;
737 if (first_move && tv->view()->layer_display() == Stacked) {
738 tv->view()->set_layer_display (Expanded);
741 /* Here's the current pointer position in terms of time axis view and layer */
742 current_pointer_time_axis_view = find_time_axis_view (tv);
744 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
746 /* Work out the change in y */
748 if (_last_pointer_time_axis_view < 0) {
749 /* We can only move up, set delta to move up the correct number of
750 tracks from the bottom. This is necessary because steps may be
751 skipped if the lowest track is not a valid target, so e.g. delta
752 -2 may be the first valid value. */
753 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size();
755 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
758 delta_layer = current_pointer_layer - _last_pointer_layer;
761 /* Work out the change in x */
762 framepos_t pending_region_position;
763 double const x_delta = compute_x_delta (event, &pending_region_position);
764 _last_frame_position = pending_region_position;
766 /* Verify change in y */
767 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
768 /* this y movement is not allowed, so do no y movement this time */
769 delta_time_axis_view = 0;
773 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
774 /* haven't reached next snap point, and we're not switching
775 trackviews nor layers. nothing to do.
780 typedef pair<int,double> NewTrackIndexAndPosition;
781 typedef map<boost::shared_ptr<Playlist>,NewTrackIndexAndPosition> PlaylistDropzoneMap;
782 PlaylistDropzoneMap playlist_dropzone_map;
783 int biggest_drop_zone_offset = 0;
785 Coord last_track_bottom_edge;
787 if (_time_axis_views.empty()) {
788 last_track_bottom_edge = 0;
790 TimeAxisView* last = _time_axis_views.back();
791 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
794 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
796 RegionView* rv = i->view;
801 if (rv->region()->locked() || rv->region()->video_locked()) {
808 /* reparent the regionview into a group above all
812 ArdourCanvas::Item* rvg = rv->get_canvas_group();
813 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
814 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
815 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
816 /* move the item so that it continues to appear at the
817 same location now that its parent has changed.
819 rvg->move (rv_canvas_offset - dmg_canvas_offset);
822 /* If we have moved tracks, we'll fudge the layer delta so that the
823 region gets moved back onto layer 0 on its new track; this avoids
824 confusion when dragging regions from non-zero layers onto different
827 double this_delta_layer = delta_layer;
828 if (delta_time_axis_view != 0) {
829 this_delta_layer = - i->layer;
836 if (i->time_axis_view >= 0) {
837 track_index = i->time_axis_view + delta_time_axis_view;
839 /* delta time axis view will be at least -1, because we can only
840 be moving up if some dragged views have i->time_axis_view negative.
842 the final real time axis view has index _time_axis_views.size() - 1.
844 so for DTAV = -1, the first drop zone track (i->tav == -1) should
845 move to the final TAV.
847 the second drop zone track should move to i->tav == -1 (i.e. becomes
848 the first drop zone track). and so on.
851 int index_from_bottom = i->time_axis_view - delta_time_axis_view;
852 int const bottom = _time_axis_views.size() - 1;
854 if (index_from_bottom >= 0) {
855 track_index = bottom - index_from_bottom;
857 track_index = index_from_bottom;
861 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
865 /* The TimeAxisView that this region is now over */
866 TimeAxisView* current_tv = _time_axis_views[track_index];
868 /* Ensure it is moved from stacked -> expanded if appropriate */
869 if (current_tv->view()->layer_display() == Stacked) {
870 current_tv->view()->set_layer_display (Expanded);
873 /* We're only allowed to go -ve in layer on Expanded views */
874 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
875 this_delta_layer = - i->layer;
879 rv->set_height (current_tv->view()->child_height ());
881 /* Update show/hidden status as the region view may have come from a hidden track,
882 or have moved to one.
884 if (current_tv->hidden ()) {
885 rv->get_canvas_group()->hide ();
887 rv->get_canvas_group()->show ();
890 /* Update the DraggingView */
891 i->time_axis_view = track_index;
892 i->layer += this_delta_layer;
895 _editor->mouse_brush_insert_region (rv, pending_region_position);
899 /* Get the y coordinate of the top of the track that this region is now over */
900 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
902 /* And adjust for the layer that it should be on */
903 StreamView* cv = current_tv->view ();
904 switch (cv->layer_display ()) {
908 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
911 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
915 /* need to get the parent of the regionview
916 * canvas group and get its position in
917 * equivalent coordinate space as the trackview
918 * we are now dragging over.
921 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
930 if (current_pointer_y() >= 0) {
932 NewTrackIndexAndPosition ip;
933 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
935 /* store index of each new playlist as a negative count, starting at -1 */
937 if (pdz == playlist_dropzone_map.end()) {
939 int n = playlist_dropzone_map.size() + 1;
941 /* compute where this new track (which doesn't exist yet) will live
945 ip.first = -n; /* in time axis units, negative to signify "in drop zone " */
946 ip.second = last_track_bottom_edge; /* where to place the top edge of the regionview */
948 /* How high is this region view ? */
950 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
951 ArdourCanvas::Rect bbox;
957 last_track_bottom_edge += bbox.height();
959 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), ip));
960 i->time_axis_view = -n;
964 i->time_axis_view = ip.first;
967 /* values are zero or negative, hence the use of min() */
968 biggest_drop_zone_offset = min (biggest_drop_zone_offset, i->time_axis_view);
970 y_delta = ip.second - rv->get_canvas_group()->canvas_origin().y;
974 /* Now move the region view */
975 rv->move (x_delta, y_delta);
977 } /* foreach region */
979 _total_x_delta += x_delta;
981 if (x_delta != 0 && !_brushing) {
982 show_verbose_cursor_time (_last_frame_position);
987 /* the pointer is currently over a time axis view */
989 if (_last_pointer_time_axis_view < 0) {
991 /* last motion event was not over a time axis view */
993 if (delta_time_axis_view < 0) {
994 /* was in the drop zone, moving up */
995 _last_pointer_time_axis_view = current_pointer_time_axis_view;
997 /* was in the drop zone, moving down ... not possible */
1002 /* last motion event was also over a time axis view */
1004 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1009 /* the pointer is not over a time axis view */
1011 _last_pointer_time_axis_view = biggest_drop_zone_offset;
1014 _last_pointer_layer += delta_layer;
1018 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1020 if (_copy && first_move) {
1022 if (_x_constrained) {
1023 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1025 _editor->begin_reversible_command (Operations::region_copy);
1028 /* duplicate the regionview(s) and region(s) */
1030 list<DraggingView> new_regionviews;
1032 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1034 RegionView* rv = i->view;
1035 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1036 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1038 const boost::shared_ptr<const Region> original = rv->region();
1039 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1040 region_copy->set_position (original->position());
1041 /* need to set this so that the drop zone code can work. This doesn't
1042 actually put the region into the playlist, but just sets a weak pointer
1045 region_copy->set_playlist (original->playlist());
1049 boost::shared_ptr<AudioRegion> audioregion_copy
1050 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1052 nrv = new AudioRegionView (*arv, audioregion_copy);
1054 boost::shared_ptr<MidiRegion> midiregion_copy
1055 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1056 nrv = new MidiRegionView (*mrv, midiregion_copy);
1061 nrv->get_canvas_group()->show ();
1062 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1064 /* swap _primary to the copy */
1066 if (rv == _primary) {
1070 /* ..and deselect the one we copied */
1072 rv->set_selected (false);
1075 if (!new_regionviews.empty()) {
1077 /* reflect the fact that we are dragging the copies */
1079 _views = new_regionviews;
1081 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1084 } else if (!_copy && first_move) {
1086 if (_x_constrained) {
1087 _editor->begin_reversible_command (_("fixed time region drag"));
1089 _editor->begin_reversible_command (Operations::region_drag);
1093 RegionMotionDrag::motion (event, first_move);
1097 RegionMotionDrag::finished (GdkEvent *, bool)
1099 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1100 if (!(*i)->view()) {
1104 if ((*i)->view()->layer_display() == Expanded) {
1105 (*i)->view()->set_layer_display (Stacked);
1111 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1113 RegionMotionDrag::finished (ev, movement_occurred);
1115 if (!movement_occurred) {
1119 if (was_double_click() && !_views.empty()) {
1120 DraggingView dv = _views.front();
1121 dv.view->show_region_editor ();
1128 /* reverse this here so that we have the correct logic to finalize
1132 if (Config->get_edit_mode() == Lock) {
1133 _x_constrained = !_x_constrained;
1136 assert (!_views.empty ());
1138 /* We might have hidden region views so that they weren't visible during the drag
1139 (when they have been reparented). Now everything can be shown again, as region
1140 views are back in their track parent groups.
1142 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1143 i->view->get_canvas_group()->show ();
1146 bool const changed_position = (_last_frame_position != _primary->region()->position());
1147 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1148 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1168 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1172 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1174 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1179 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1180 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1181 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1182 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1184 rtav->set_height (original->current_height());
1188 ChanCount one_midi_port (DataType::MIDI, 1);
1189 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1190 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1191 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1193 rtav->set_height (original->current_height());
1198 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1204 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1206 RegionSelection new_views;
1207 PlaylistSet modified_playlists;
1208 RouteTimeAxisView* new_time_axis_view = 0;
1211 /* all changes were made during motion event handlers */
1213 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1217 _editor->commit_reversible_command ();
1221 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1222 PlaylistMapping playlist_mapping;
1224 /* insert the regions into their new playlists */
1225 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1227 RouteTimeAxisView* dest_rtv = 0;
1229 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1235 if (changed_position && !_x_constrained) {
1236 where = i->view->region()->position() - drag_delta;
1238 where = i->view->region()->position();
1241 if (i->time_axis_view < 0) {
1242 /* dragged to drop zone */
1244 PlaylistMapping::iterator pm;
1246 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1247 /* first region from this original playlist: create a new track */
1248 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1249 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1250 dest_rtv = new_time_axis_view;
1252 /* we already created a new track for regions from this playlist, use it */
1253 dest_rtv = pm->second;
1256 /* destination time axis view is the one we dragged to */
1257 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1260 if (dest_rtv != 0) {
1261 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1262 if (new_view != 0) {
1263 new_views.push_back (new_view);
1267 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1268 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1271 list<DraggingView>::const_iterator next = i;
1277 /* If we've created new regions either by copying or moving
1278 to a new track, we want to replace the old selection with the new ones
1281 if (new_views.size() > 0) {
1282 _editor->selection->set (new_views);
1285 /* write commands for the accumulated diffs for all our modified playlists */
1286 add_stateful_diff_commands_for_playlists (modified_playlists);
1288 _editor->commit_reversible_command ();
1292 RegionMoveDrag::finished_no_copy (
1293 bool const changed_position,
1294 bool const changed_tracks,
1295 framecnt_t const drag_delta
1298 RegionSelection new_views;
1299 PlaylistSet modified_playlists;
1300 PlaylistSet frozen_playlists;
1301 set<RouteTimeAxisView*> views_to_update;
1302 RouteTimeAxisView* new_time_axis_view = 0;
1305 /* all changes were made during motion event handlers */
1306 _editor->commit_reversible_command ();
1310 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1311 PlaylistMapping playlist_mapping;
1313 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1315 RegionView* rv = i->view;
1316 RouteTimeAxisView* dest_rtv = 0;
1318 if (rv->region()->locked() || rv->region()->video_locked()) {
1323 if (i->time_axis_view < 0) {
1324 /* dragged to drop zone */
1326 PlaylistMapping::iterator pm;
1328 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1329 /* first region from this original playlist: create a new track */
1330 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1331 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1332 dest_rtv = new_time_axis_view;
1334 /* we already created a new track for regions from this playlist, use it */
1335 dest_rtv = pm->second;
1339 /* destination time axis view is the one we dragged to */
1340 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1345 double const dest_layer = i->layer;
1347 views_to_update.insert (dest_rtv);
1351 if (changed_position && !_x_constrained) {
1352 where = rv->region()->position() - drag_delta;
1354 where = rv->region()->position();
1357 if (changed_tracks) {
1359 /* insert into new playlist */
1361 RegionView* new_view = insert_region_into_playlist (
1362 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1365 if (new_view == 0) {
1370 new_views.push_back (new_view);
1372 /* remove from old playlist */
1374 /* the region that used to be in the old playlist is not
1375 moved to the new one - we use a copy of it. as a result,
1376 any existing editor for the region should no longer be
1379 rv->hide_region_editor();
1382 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1386 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1388 /* this movement may result in a crossfade being modified, or a layering change,
1389 so we need to get undo data from the playlist as well as the region.
1392 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1394 playlist->clear_changes ();
1397 rv->region()->clear_changes ();
1400 motion on the same track. plonk the previously reparented region
1401 back to its original canvas group (its streamview).
1402 No need to do anything for copies as they are fake regions which will be deleted.
1405 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1406 rv->get_canvas_group()->set_y_position (i->initial_y);
1409 /* just change the model */
1410 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1411 playlist->set_layer (rv->region(), dest_layer);
1414 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1416 r = frozen_playlists.insert (playlist);
1419 playlist->freeze ();
1422 rv->region()->set_position (where);
1424 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1427 if (changed_tracks) {
1429 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1430 was selected in all of them, then removing it from a playlist will have removed all
1431 trace of it from _views (i.e. there were N regions selected, we removed 1,
1432 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1433 corresponding regionview, and _views is now empty).
1435 This could have invalidated any and all iterators into _views.
1437 The heuristic we use here is: if the region selection is empty, break out of the loop
1438 here. if the region selection is not empty, then restart the loop because we know that
1439 we must have removed at least the region(view) we've just been working on as well as any
1440 that we processed on previous iterations.
1442 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1443 we can just iterate.
1447 if (_views.empty()) {
1458 /* If we've created new regions either by copying or moving
1459 to a new track, we want to replace the old selection with the new ones
1462 if (new_views.size() > 0) {
1463 _editor->selection->set (new_views);
1466 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1470 /* write commands for the accumulated diffs for all our modified playlists */
1471 add_stateful_diff_commands_for_playlists (modified_playlists);
1473 _editor->commit_reversible_command ();
1475 /* We have futzed with the layering of canvas items on our streamviews.
1476 If any region changed layer, this will have resulted in the stream
1477 views being asked to set up their region views, and all will be well.
1478 If not, we might now have badly-ordered region views. Ask the StreamViews
1479 involved to sort themselves out, just in case.
1482 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1483 (*i)->view()->playlist_layered ((*i)->track ());
1487 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1488 * @param region Region to remove.
1489 * @param playlist playlist To remove from.
1490 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1491 * that clear_changes () is only called once per playlist.
1494 RegionMoveDrag::remove_region_from_playlist (
1495 boost::shared_ptr<Region> region,
1496 boost::shared_ptr<Playlist> playlist,
1497 PlaylistSet& modified_playlists
1500 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1503 playlist->clear_changes ();
1506 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1510 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1511 * clearing the playlist's diff history first if necessary.
1512 * @param region Region to insert.
1513 * @param dest_rtv Destination RouteTimeAxisView.
1514 * @param dest_layer Destination layer.
1515 * @param where Destination position.
1516 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1517 * that clear_changes () is only called once per playlist.
1518 * @return New RegionView, or 0 if no insert was performed.
1521 RegionMoveDrag::insert_region_into_playlist (
1522 boost::shared_ptr<Region> region,
1523 RouteTimeAxisView* dest_rtv,
1526 PlaylistSet& modified_playlists
1529 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1530 if (!dest_playlist) {
1534 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1535 _new_region_view = 0;
1536 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1538 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1539 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1541 dest_playlist->clear_changes ();
1544 dest_playlist->add_region (region, where);
1546 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1547 dest_playlist->set_layer (region, dest_layer);
1552 assert (_new_region_view);
1554 return _new_region_view;
1558 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1560 _new_region_view = rv;
1564 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1566 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1567 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1569 _editor->session()->add_command (c);
1578 RegionMoveDrag::aborted (bool movement_occurred)
1582 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1583 list<DraggingView>::const_iterator next = i;
1592 RegionMotionDrag::aborted (movement_occurred);
1597 RegionMotionDrag::aborted (bool)
1599 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1601 StreamView* sview = (*i)->view();
1604 if (sview->layer_display() == Expanded) {
1605 sview->set_layer_display (Stacked);
1610 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1611 RegionView* rv = i->view;
1612 TimeAxisView* tv = &(rv->get_time_axis_view ());
1613 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1615 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1616 rv->get_canvas_group()->set_y_position (0);
1618 rv->move (-_total_x_delta, 0);
1619 rv->set_height (rtv->view()->child_height ());
1623 /** @param b true to brush, otherwise false.
1624 * @param c true to make copies of the regions being moved, otherwise false.
1626 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1627 : RegionMotionDrag (e, i, p, v, b)
1630 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1633 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1634 if (rtv && rtv->is_track()) {
1635 speed = rtv->track()->speed ();
1638 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1642 RegionMoveDrag::setup_pointer_frame_offset ()
1644 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1647 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1648 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1650 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1652 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1653 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1655 _primary = v->view()->create_region_view (r, false, false);
1657 _primary->get_canvas_group()->show ();
1658 _primary->set_position (pos, 0);
1659 _views.push_back (DraggingView (_primary, this, v));
1661 _last_frame_position = pos;
1663 _item = _primary->get_canvas_group ();
1667 RegionInsertDrag::finished (GdkEvent *, bool)
1669 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1671 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1672 _primary->get_canvas_group()->set_y_position (0);
1674 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1676 _editor->begin_reversible_command (Operations::insert_region);
1677 playlist->clear_changes ();
1678 playlist->add_region (_primary->region (), _last_frame_position);
1680 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1681 if (Config->get_edit_mode() == Ripple) {
1682 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1685 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1686 _editor->commit_reversible_command ();
1694 RegionInsertDrag::aborted (bool)
1701 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1702 : RegionMoveDrag (e, i, p, v, false, false)
1704 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1707 struct RegionSelectionByPosition {
1708 bool operator() (RegionView*a, RegionView* b) {
1709 return a->region()->position () < b->region()->position();
1714 RegionSpliceDrag::motion (GdkEvent* event, bool)
1716 /* Which trackview is this ? */
1718 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1719 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1721 /* The region motion is only processed if the pointer is over
1725 if (!tv || !tv->is_track()) {
1726 /* To make sure we hide the verbose canvas cursor when the mouse is
1727 not held over an audio track.
1729 _editor->verbose_cursor()->hide ();
1732 _editor->verbose_cursor()->show ();
1737 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1743 RegionSelection copy;
1744 _editor->selection->regions.by_position(copy);
1746 framepos_t const pf = adjusted_current_frame (event);
1748 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1750 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1756 boost::shared_ptr<Playlist> playlist;
1758 if ((playlist = atv->playlist()) == 0) {
1762 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1767 if (pf < (*i)->region()->last_frame() + 1) {
1771 if (pf > (*i)->region()->first_frame()) {
1777 playlist->shuffle ((*i)->region(), dir);
1782 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1784 RegionMoveDrag::finished (event, movement_occurred);
1788 RegionSpliceDrag::aborted (bool)
1798 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1801 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1803 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1804 RegionSelection to_ripple;
1805 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1806 if ((*i)->position() >= where) {
1807 to_ripple.push_back (rtv->view()->find_view(*i));
1811 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1812 if (!exclude.contains (*i)) {
1813 // the selection has already been added to _views
1815 if (drag_in_progress) {
1816 // do the same things that RegionMotionDrag::motion does when
1817 // first_move is true, for the region views that we're adding
1818 // to _views this time
1821 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1822 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1823 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1824 rvg->reparent (_editor->_drag_motion_group);
1826 // we only need to move in the y direction
1827 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1832 _views.push_back (DraggingView (*i, this, tav));
1838 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1841 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1842 // we added all the regions after the selection
1844 std::list<DraggingView>::iterator to_erase = i++;
1845 if (!_editor->selection->regions.contains (to_erase->view)) {
1846 // restore the non-selected regions to their original playlist & positions,
1847 // and then ripple them back by the length of the regions that were dragged away
1848 // do the same things as RegionMotionDrag::aborted
1850 RegionView *rv = to_erase->view;
1851 TimeAxisView* tv = &(rv->get_time_axis_view ());
1852 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1855 // plonk them back onto their own track
1856 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1857 rv->get_canvas_group()->set_y_position (0);
1861 // move the underlying region to match the view
1862 rv->region()->set_position (rv->region()->position() + amount);
1864 // restore the view to match the underlying region's original position
1865 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1868 rv->set_height (rtv->view()->child_height ());
1869 _views.erase (to_erase);
1875 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1877 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1879 return allow_moves_across_tracks;
1887 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1888 : RegionMoveDrag (e, i, p, v, false, false)
1890 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1891 // compute length of selection
1892 RegionSelection selected_regions = _editor->selection->regions;
1893 selection_length = selected_regions.end_frame() - selected_regions.start();
1895 // we'll only allow dragging to another track in ripple mode if all the regions
1896 // being dragged start off on the same track
1897 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1900 exclude = new RegionList;
1901 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1902 exclude->push_back((*i)->region());
1905 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1906 RegionSelection copy;
1907 selected_regions.by_position(copy); // get selected regions sorted by position into copy
1909 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1910 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1912 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1913 // find ripple start point on each applicable playlist
1914 RegionView *first_selected_on_this_track = NULL;
1915 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1916 if ((*i)->region()->playlist() == (*pi)) {
1917 // region is on this playlist - it's the first, because they're sorted
1918 first_selected_on_this_track = *i;
1922 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1923 add_all_after_to_views (
1924 &first_selected_on_this_track->get_time_axis_view(),
1925 first_selected_on_this_track->region()->position(),
1926 selected_regions, false);
1929 if (allow_moves_across_tracks) {
1930 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1938 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1940 /* Which trackview is this ? */
1942 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1943 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1945 /* The region motion is only processed if the pointer is over
1949 if (!tv || !tv->is_track()) {
1950 /* To make sure we hide the verbose canvas cursor when the mouse is
1951 not held over an audiotrack.
1953 _editor->verbose_cursor()->hide ();
1957 framepos_t where = adjusted_current_frame (event);
1958 assert (where >= 0);
1960 double delta = compute_x_delta (event, &after);
1962 framecnt_t amount = _editor->pixel_to_sample (delta);
1964 if (allow_moves_across_tracks) {
1965 // all the originally selected regions were on the same track
1967 framecnt_t adjust = 0;
1968 if (prev_tav && tv != prev_tav) {
1969 // dragged onto a different track
1970 // remove the unselected regions from _views, restore them to their original positions
1971 // and add the regions after the drop point on the new playlist to _views instead.
1972 // undo the effect of rippling the previous playlist, and include the effect of removing
1973 // the dragged region(s) from this track
1975 remove_unselected_from_views (prev_amount, false);
1976 // ripple previous playlist according to the regions that have been removed onto the new playlist
1977 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1980 // move just the selected regions
1981 RegionMoveDrag::motion(event, first_move);
1983 // ensure that the ripple operation on the new playlist inserts selection_length time
1984 adjust = selection_length;
1985 // ripple the new current playlist
1986 tv->playlist()->ripple (where, amount+adjust, exclude);
1988 // add regions after point where drag entered this track to subsequent ripples
1989 add_all_after_to_views (tv, where, _editor->selection->regions, true);
1992 // motion on same track
1993 RegionMoveDrag::motion(event, first_move);
1997 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1998 prev_position = where;
2000 // selection encompasses multiple tracks - just drag
2001 // cross-track drags are forbidden
2002 RegionMoveDrag::motion(event, first_move);
2005 if (!_x_constrained) {
2006 prev_amount += amount;
2009 _last_frame_position = after;
2013 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2015 if (!movement_occurred) {
2019 if (was_double_click() && !_views.empty()) {
2020 DraggingView dv = _views.front();
2021 dv.view->show_region_editor ();
2028 _editor->begin_reversible_command(_("Ripple drag"));
2030 // remove the regions being rippled from the dragging view, updating them to
2031 // their new positions
2032 remove_unselected_from_views (prev_amount, true);
2034 if (allow_moves_across_tracks) {
2036 // if regions were dragged across tracks, we've rippled any later
2037 // regions on the track the regions were dragged off, so we need
2038 // to add the original track to the undo record
2039 orig_tav->playlist()->clear_changes();
2040 vector<Command*> cmds;
2041 orig_tav->playlist()->rdiff (cmds);
2042 _editor->session()->add_commands (cmds);
2044 if (prev_tav && prev_tav != orig_tav) {
2045 prev_tav->playlist()->clear_changes();
2046 vector<Command*> cmds;
2047 prev_tav->playlist()->rdiff (cmds);
2048 _editor->session()->add_commands (cmds);
2051 // selection spanned multiple tracks - all will need adding to undo record
2053 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2054 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2056 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2057 (*pi)->clear_changes();
2058 vector<Command*> cmds;
2059 (*pi)->rdiff (cmds);
2060 _editor->session()->add_commands (cmds);
2064 // other modified playlists are added to undo by RegionMoveDrag::finished()
2065 RegionMoveDrag::finished (event, movement_occurred);
2066 _editor->commit_reversible_command();
2070 RegionRippleDrag::aborted (bool movement_occurred)
2072 RegionMoveDrag::aborted (movement_occurred);
2077 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2079 _view (dynamic_cast<MidiTimeAxisView*> (v))
2081 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2087 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2090 _region = add_midi_region (_view);
2091 _view->playlist()->freeze ();
2094 framepos_t const f = adjusted_current_frame (event);
2095 if (f < grab_frame()) {
2096 _region->set_position (f);
2099 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2100 so that if this region is duplicated, its duplicate starts on
2101 a snap point rather than 1 frame after a snap point. Otherwise things get
2102 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2103 place snapped notes at the start of the region.
2106 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2107 _region->set_length (len < 1 ? 1 : len);
2113 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2115 if (!movement_occurred) {
2116 add_midi_region (_view);
2118 _view->playlist()->thaw ();
2123 RegionCreateDrag::aborted (bool)
2126 _view->playlist()->thaw ();
2132 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2136 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2140 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2142 Gdk::Cursor* cursor;
2143 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2145 float x_fraction = cnote->mouse_x_fraction ();
2147 if (x_fraction > 0.0 && x_fraction < 0.25) {
2148 cursor = _editor->cursors()->left_side_trim;
2151 cursor = _editor->cursors()->right_side_trim;
2155 Drag::start_grab (event, cursor);
2157 region = &cnote->region_view();
2161 if (event->motion.state & Keyboard::PrimaryModifier) {
2167 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2169 if (ms.size() > 1) {
2170 /* has to be relative, may make no sense otherwise */
2174 /* select this note; if it is already selected, preserve the existing selection,
2175 otherwise make this note the only one selected.
2177 region->note_selected (cnote, cnote->selected ());
2179 _editor->begin_reversible_command (_("resize notes"));
2181 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2182 MidiRegionSelection::iterator next;
2185 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2187 mrv->begin_resizing (at_front);
2194 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2196 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2197 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2198 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2200 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2202 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2208 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2210 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2211 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2212 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2214 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2216 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2220 _editor->commit_reversible_command ();
2224 NoteResizeDrag::aborted (bool)
2226 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2227 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2228 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2230 mrv->abort_resizing ();
2235 AVDraggingView::AVDraggingView (RegionView* v)
2238 initial_position = v->region()->position ();
2241 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2244 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2247 TrackViewList empty;
2249 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2250 std::list<RegionView*> views = rs.by_layer();
2252 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2253 RegionView* rv = (*i);
2254 if (!rv->region()->video_locked()) {
2257 _views.push_back (AVDraggingView (rv));
2262 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2264 Drag::start_grab (event);
2265 if (_editor->session() == 0) {
2269 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2270 _max_backwards_drag = (
2271 ARDOUR_UI::instance()->video_timeline->get_duration()
2272 + ARDOUR_UI::instance()->video_timeline->get_offset()
2273 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2276 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2277 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2278 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2281 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2284 Timecode::Time timecode;
2285 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2286 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);
2287 show_verbose_cursor_text (buf);
2291 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2293 if (_editor->session() == 0) {
2296 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2300 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2301 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2303 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2304 dt = - _max_backwards_drag;
2307 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2308 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2310 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2311 RegionView* rv = i->view;
2312 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2315 rv->region()->clear_changes ();
2316 rv->region()->suspend_property_changes();
2318 rv->region()->set_position(i->initial_position + dt);
2319 rv->region_changed(ARDOUR::Properties::position);
2322 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2323 Timecode::Time timecode;
2324 Timecode::Time timediff;
2326 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2327 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2328 snprintf (buf, sizeof (buf),
2329 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2330 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2331 , _("Video Start:"),
2332 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2334 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2336 show_verbose_cursor_text (buf);
2340 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2342 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2346 if (!movement_occurred || ! _editor->session()) {
2350 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2352 _editor->begin_reversible_command (_("Move Video"));
2354 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2355 ARDOUR_UI::instance()->video_timeline->save_undo();
2356 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2357 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2359 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2360 i->view->drag_end();
2361 i->view->region()->resume_property_changes ();
2363 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2366 _editor->session()->maybe_update_session_range(
2367 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2368 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2372 _editor->commit_reversible_command ();
2376 VideoTimeLineDrag::aborted (bool)
2378 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2381 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2382 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2384 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2385 i->view->region()->resume_property_changes ();
2386 i->view->region()->set_position(i->initial_position);
2390 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2391 : RegionDrag (e, i, p, v)
2392 , _preserve_fade_anchor (preserve_fade_anchor)
2393 , _jump_position_when_done (false)
2395 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2399 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2402 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2403 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2405 if (tv && tv->is_track()) {
2406 speed = tv->track()->speed();
2409 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2410 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2411 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2413 framepos_t const pf = adjusted_current_frame (event);
2415 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2416 /* Move the contents of the region around without changing the region bounds */
2417 _operation = ContentsTrim;
2418 Drag::start_grab (event, _editor->cursors()->trimmer);
2420 /* These will get overridden for a point trim.*/
2421 if (pf < (region_start + region_length/2)) {
2422 /* closer to front */
2423 _operation = StartTrim;
2425 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2426 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2428 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2432 _operation = EndTrim;
2433 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2434 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2436 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2441 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2442 _jump_position_when_done = true;
2445 switch (_operation) {
2447 show_verbose_cursor_time (region_start);
2448 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2449 i->view->trim_front_starting ();
2453 show_verbose_cursor_time (region_end);
2456 show_verbose_cursor_time (pf);
2460 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2461 i->view->region()->suspend_property_changes ();
2466 TrimDrag::motion (GdkEvent* event, bool first_move)
2468 RegionView* rv = _primary;
2471 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2472 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2473 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2474 frameoffset_t frame_delta = 0;
2476 if (tv && tv->is_track()) {
2477 speed = tv->track()->speed();
2480 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2486 switch (_operation) {
2488 trim_type = "Region start trim";
2491 trim_type = "Region end trim";
2494 trim_type = "Region content trim";
2501 _editor->begin_reversible_command (trim_type);
2503 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2504 RegionView* rv = i->view;
2505 rv->enable_display (false);
2506 rv->region()->playlist()->clear_owned_changes ();
2508 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2511 arv->temporarily_hide_envelope ();
2515 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2516 insert_result = _editor->motion_frozen_playlists.insert (pl);
2518 if (insert_result.second) {
2524 bool non_overlap_trim = false;
2526 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2527 non_overlap_trim = true;
2530 /* contstrain trim to fade length */
2531 if (_preserve_fade_anchor) {
2532 switch (_operation) {
2534 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2535 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2537 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2538 if (ar->locked()) continue;
2539 framecnt_t len = ar->fade_in()->back()->when;
2540 if (len < dt) dt = min(dt, len);
2544 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2545 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2547 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2548 if (ar->locked()) continue;
2549 framecnt_t len = ar->fade_out()->back()->when;
2550 if (len < -dt) dt = max(dt, -len);
2559 switch (_operation) {
2561 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2562 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2563 if (changed && _preserve_fade_anchor) {
2564 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2566 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2567 framecnt_t len = ar->fade_in()->back()->when;
2568 framecnt_t diff = ar->first_frame() - i->initial_position;
2569 framepos_t new_length = len - diff;
2570 i->anchored_fade_length = min (ar->length(), new_length);
2571 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2572 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2579 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2580 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2581 if (changed && _preserve_fade_anchor) {
2582 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2584 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2585 framecnt_t len = ar->fade_out()->back()->when;
2586 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2587 framepos_t new_length = len + diff;
2588 i->anchored_fade_length = min (ar->length(), new_length);
2589 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2590 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2598 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2600 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2601 i->view->move_contents (frame_delta);
2607 switch (_operation) {
2609 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2612 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2615 // show_verbose_cursor_time (frame_delta);
2622 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2624 if (movement_occurred) {
2625 motion (event, false);
2627 if (_operation == StartTrim) {
2628 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2630 /* This must happen before the region's StatefulDiffCommand is created, as it may
2631 `correct' (ahem) the region's _start from being negative to being zero. It
2632 needs to be zero in the undo record.
2634 i->view->trim_front_ending ();
2636 if (_preserve_fade_anchor) {
2637 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2639 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2640 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2641 ar->set_fade_in_length(i->anchored_fade_length);
2642 ar->set_fade_in_active(true);
2645 if (_jump_position_when_done) {
2646 i->view->region()->set_position (i->initial_position);
2649 } else if (_operation == EndTrim) {
2650 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2651 if (_preserve_fade_anchor) {
2652 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2654 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2655 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2656 ar->set_fade_out_length(i->anchored_fade_length);
2657 ar->set_fade_out_active(true);
2660 if (_jump_position_when_done) {
2661 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2666 if (!_views.empty()) {
2667 if (_operation == StartTrim) {
2668 _editor->maybe_locate_with_edit_preroll(
2669 _views.begin()->view->region()->position());
2671 if (_operation == EndTrim) {
2672 _editor->maybe_locate_with_edit_preroll(
2673 _views.begin()->view->region()->position() +
2674 _views.begin()->view->region()->length());
2678 if (!_editor->selection->selected (_primary)) {
2679 _primary->thaw_after_trim ();
2682 set<boost::shared_ptr<Playlist> > diffed_playlists;
2684 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2685 i->view->thaw_after_trim ();
2686 i->view->enable_display (true);
2688 /* Trimming one region may affect others on the playlist, so we need
2689 to get undo Commands from the whole playlist rather than just the
2690 region. Use diffed_playlists to make sure we don't diff a given
2691 playlist more than once.
2693 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2694 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2695 vector<Command*> cmds;
2697 _editor->session()->add_commands (cmds);
2698 diffed_playlists.insert (p);
2703 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2707 _editor->motion_frozen_playlists.clear ();
2708 _editor->commit_reversible_command();
2711 /* no mouse movement */
2712 _editor->point_trim (event, adjusted_current_frame (event));
2715 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2716 if (_operation == StartTrim) {
2717 i->view->trim_front_ending ();
2720 i->view->region()->resume_property_changes ();
2725 TrimDrag::aborted (bool movement_occurred)
2727 /* Our motion method is changing model state, so use the Undo system
2728 to cancel. Perhaps not ideal, as this will leave an Undo point
2729 behind which may be slightly odd from the user's point of view.
2734 if (movement_occurred) {
2738 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2739 i->view->region()->resume_property_changes ();
2744 TrimDrag::setup_pointer_frame_offset ()
2746 list<DraggingView>::iterator i = _views.begin ();
2747 while (i != _views.end() && i->view != _primary) {
2751 if (i == _views.end()) {
2755 switch (_operation) {
2757 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2760 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2767 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2771 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2772 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2777 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2779 Drag::start_grab (event, cursor);
2780 show_verbose_cursor_time (adjusted_current_frame(event));
2784 MeterMarkerDrag::setup_pointer_frame_offset ()
2786 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2790 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2792 if (!_marker->meter().movable()) {
2798 // create a dummy marker for visual representation of moving the
2799 // section, because whether its a copy or not, we're going to
2800 // leave or lose the original marker (leave if its a copy; lose if its
2801 // not, because we'll remove it from the map).
2803 MeterSection section (_marker->meter());
2805 if (!section.movable()) {
2810 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2812 _marker = new MeterMarker (
2814 *_editor->meter_group,
2815 ARDOUR_UI::config()->color ("meter marker"),
2817 *new MeterSection (_marker->meter())
2820 /* use the new marker for the grab */
2821 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2824 TempoMap& map (_editor->session()->tempo_map());
2825 /* get current state */
2826 before_state = &map.get_state();
2827 /* remove the section while we drag it */
2828 map.remove_meter (section, true);
2832 framepos_t const pf = adjusted_current_frame (event);
2834 _marker->set_position (pf);
2835 show_verbose_cursor_time (pf);
2839 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2841 if (!movement_occurred) {
2842 if (was_double_click()) {
2843 _editor->edit_meter_marker (*_marker);
2848 if (!_marker->meter().movable()) {
2852 motion (event, false);
2854 Timecode::BBT_Time when;
2856 TempoMap& map (_editor->session()->tempo_map());
2857 map.bbt_time (last_pointer_frame(), when);
2859 if (_copy == true) {
2860 _editor->begin_reversible_command (_("copy meter mark"));
2861 XMLNode &before = map.get_state();
2862 map.add_meter (_marker->meter(), when);
2863 XMLNode &after = map.get_state();
2864 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2865 _editor->commit_reversible_command ();
2868 _editor->begin_reversible_command (_("move meter mark"));
2870 /* we removed it before, so add it back now */
2872 map.add_meter (_marker->meter(), when);
2873 XMLNode &after = map.get_state();
2874 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2875 _editor->commit_reversible_command ();
2878 // delete the dummy marker we used for visual representation while moving.
2879 // a new visual marker will show up automatically.
2884 MeterMarkerDrag::aborted (bool moved)
2886 _marker->set_position (_marker->meter().frame ());
2889 TempoMap& map (_editor->session()->tempo_map());
2890 /* we removed it before, so add it back now */
2891 map.add_meter (_marker->meter(), _marker->meter().frame());
2892 // delete the dummy marker we used for visual representation while moving.
2893 // a new visual marker will show up automatically.
2898 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2902 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2904 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2909 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2911 Drag::start_grab (event, cursor);
2912 show_verbose_cursor_time (adjusted_current_frame (event));
2916 TempoMarkerDrag::setup_pointer_frame_offset ()
2918 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2922 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2924 if (!_marker->tempo().movable()) {
2930 // create a dummy marker for visual representation of moving the
2931 // section, because whether its a copy or not, we're going to
2932 // leave or lose the original marker (leave if its a copy; lose if its
2933 // not, because we'll remove it from the map).
2935 // create a dummy marker for visual representation of moving the copy.
2936 // The actual copying is not done before we reach the finish callback.
2939 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2941 TempoSection section (_marker->tempo());
2943 _marker = new TempoMarker (
2945 *_editor->tempo_group,
2946 ARDOUR_UI::config()->color ("tempo marker"),
2948 *new TempoSection (_marker->tempo())
2951 /* use the new marker for the grab */
2952 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2955 TempoMap& map (_editor->session()->tempo_map());
2956 /* get current state */
2957 before_state = &map.get_state();
2958 /* remove the section while we drag it */
2959 map.remove_tempo (section, true);
2963 framepos_t const pf = adjusted_current_frame (event);
2964 _marker->set_position (pf);
2965 show_verbose_cursor_time (pf);
2969 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2971 if (!movement_occurred) {
2972 if (was_double_click()) {
2973 _editor->edit_tempo_marker (*_marker);
2978 if (!_marker->tempo().movable()) {
2982 motion (event, false);
2984 TempoMap& map (_editor->session()->tempo_map());
2985 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
2986 Timecode::BBT_Time when;
2988 map.bbt_time (beat_time, when);
2990 if (_copy == true) {
2991 _editor->begin_reversible_command (_("copy tempo mark"));
2992 XMLNode &before = map.get_state();
2993 map.add_tempo (_marker->tempo(), when);
2994 XMLNode &after = map.get_state();
2995 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2996 _editor->commit_reversible_command ();
2999 _editor->begin_reversible_command (_("move tempo mark"));
3000 /* we removed it before, so add it back now */
3001 map.add_tempo (_marker->tempo(), when);
3002 XMLNode &after = map.get_state();
3003 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3004 _editor->commit_reversible_command ();
3007 // delete the dummy marker we used for visual representation while moving.
3008 // a new visual marker will show up automatically.
3013 TempoMarkerDrag::aborted (bool moved)
3015 _marker->set_position (_marker->tempo().frame());
3017 TempoMap& map (_editor->session()->tempo_map());
3018 /* we removed it before, so add it back now */
3019 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3020 // delete the dummy marker we used for visual representation while moving.
3021 // a new visual marker will show up automatically.
3026 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3027 : Drag (e, &c.track_canvas_item(), false)
3031 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3034 /** Do all the things we do when dragging the playhead to make it look as though
3035 * we have located, without actually doing the locate (because that would cause
3036 * the diskstream buffers to be refilled, which is too slow).
3039 CursorDrag::fake_locate (framepos_t t)
3041 _editor->playhead_cursor->set_position (t);
3043 Session* s = _editor->session ();
3044 if (s->timecode_transmission_suspended ()) {
3045 framepos_t const f = _editor->playhead_cursor->current_frame ();
3046 /* This is asynchronous so it will be sent "now"
3048 s->send_mmc_locate (f);
3049 /* These are synchronous and will be sent during the next
3052 s->queue_full_time_code ();
3053 s->queue_song_position_pointer ();
3056 show_verbose_cursor_time (t);
3057 _editor->UpdateAllTransportClocks (t);
3061 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3063 Drag::start_grab (event, c);
3065 _grab_zoom = _editor->samples_per_pixel;
3067 framepos_t where = _editor->canvas_event_sample (event);
3069 _editor->snap_to_with_modifier (where, event);
3071 _editor->_dragging_playhead = true;
3073 Session* s = _editor->session ();
3075 /* grab the track canvas item as well */
3077 _cursor.track_canvas_item().grab();
3080 if (_was_rolling && _stop) {
3084 if (s->is_auditioning()) {
3085 s->cancel_audition ();
3089 if (AudioEngine::instance()->connected()) {
3091 /* do this only if we're the engine is connected
3092 * because otherwise this request will never be
3093 * serviced and we'll busy wait forever. likewise,
3094 * notice if we are disconnected while waiting for the
3095 * request to be serviced.
3098 s->request_suspend_timecode_transmission ();
3099 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3100 /* twiddle our thumbs */
3105 fake_locate (where);
3109 CursorDrag::motion (GdkEvent* event, bool)
3111 framepos_t const adjusted_frame = adjusted_current_frame (event);
3112 if (adjusted_frame != last_pointer_frame()) {
3113 fake_locate (adjusted_frame);
3118 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3120 _editor->_dragging_playhead = false;
3122 _cursor.track_canvas_item().ungrab();
3124 if (!movement_occurred && _stop) {
3128 motion (event, false);
3130 Session* s = _editor->session ();
3132 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3133 _editor->_pending_locate_request = true;
3134 s->request_resume_timecode_transmission ();
3139 CursorDrag::aborted (bool)
3141 _cursor.track_canvas_item().ungrab();
3143 if (_editor->_dragging_playhead) {
3144 _editor->session()->request_resume_timecode_transmission ();
3145 _editor->_dragging_playhead = false;
3148 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3151 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3152 : RegionDrag (e, i, p, v)
3154 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3158 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3160 Drag::start_grab (event, cursor);
3162 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3163 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3165 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3169 FadeInDrag::setup_pointer_frame_offset ()
3171 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3172 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3173 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3177 FadeInDrag::motion (GdkEvent* event, bool)
3179 framecnt_t fade_length;
3180 framepos_t const pos = adjusted_current_frame (event);
3181 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3183 if (pos < (region->position() + 64)) {
3184 fade_length = 64; // this should be a minimum defined somewhere
3185 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3186 fade_length = region->length() - region->fade_out()->back()->when - 1;
3188 fade_length = pos - region->position();
3191 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3193 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3199 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3202 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3206 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3208 if (!movement_occurred) {
3212 framecnt_t fade_length;
3214 framepos_t const pos = adjusted_current_frame (event);
3216 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3218 if (pos < (region->position() + 64)) {
3219 fade_length = 64; // this should be a minimum defined somewhere
3220 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3221 fade_length = region->length() - region->fade_out()->back()->when - 1;
3223 fade_length = pos - region->position();
3226 _editor->begin_reversible_command (_("change fade in length"));
3228 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3230 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3236 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3237 XMLNode &before = alist->get_state();
3239 tmp->audio_region()->set_fade_in_length (fade_length);
3240 tmp->audio_region()->set_fade_in_active (true);
3242 XMLNode &after = alist->get_state();
3243 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3246 _editor->commit_reversible_command ();
3250 FadeInDrag::aborted (bool)
3252 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3253 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3259 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3263 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3264 : RegionDrag (e, i, p, v)
3266 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3270 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3272 Drag::start_grab (event, cursor);
3274 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3275 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3277 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3281 FadeOutDrag::setup_pointer_frame_offset ()
3283 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3284 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3285 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3289 FadeOutDrag::motion (GdkEvent* event, bool)
3291 framecnt_t fade_length;
3293 framepos_t const pos = adjusted_current_frame (event);
3295 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3297 if (pos > (region->last_frame() - 64)) {
3298 fade_length = 64; // this should really be a minimum fade defined somewhere
3299 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3300 fade_length = region->length() - region->fade_in()->back()->when - 1;
3302 fade_length = region->last_frame() - pos;
3305 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3307 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3313 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3316 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3320 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3322 if (!movement_occurred) {
3326 framecnt_t fade_length;
3328 framepos_t const pos = adjusted_current_frame (event);
3330 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3332 if (pos > (region->last_frame() - 64)) {
3333 fade_length = 64; // this should really be a minimum fade defined somewhere
3334 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3335 fade_length = region->length() - region->fade_in()->back()->when - 1;
3337 fade_length = region->last_frame() - pos;
3340 _editor->begin_reversible_command (_("change fade out length"));
3342 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3344 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3350 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3351 XMLNode &before = alist->get_state();
3353 tmp->audio_region()->set_fade_out_length (fade_length);
3354 tmp->audio_region()->set_fade_out_active (true);
3356 XMLNode &after = alist->get_state();
3357 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3360 _editor->commit_reversible_command ();
3364 FadeOutDrag::aborted (bool)
3366 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3367 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3373 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3377 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3380 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3382 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3385 _points.push_back (ArdourCanvas::Duple (0, 0));
3386 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3389 MarkerDrag::~MarkerDrag ()
3391 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3396 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3398 location = new Location (*l);
3399 markers.push_back (m);
3404 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3406 Drag::start_grab (event, cursor);
3410 Location *location = _editor->find_location_from_marker (_marker, is_start);
3411 _editor->_dragging_edit_point = true;
3413 update_item (location);
3415 // _drag_line->show();
3416 // _line->raise_to_top();
3419 show_verbose_cursor_time (location->start());
3421 show_verbose_cursor_time (location->end());
3424 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3427 case Selection::Toggle:
3428 /* we toggle on the button release */
3430 case Selection::Set:
3431 if (!_editor->selection->selected (_marker)) {
3432 _editor->selection->set (_marker);
3435 case Selection::Extend:
3437 Locations::LocationList ll;
3438 list<Marker*> to_add;
3440 _editor->selection->markers.range (s, e);
3441 s = min (_marker->position(), s);
3442 e = max (_marker->position(), e);
3445 if (e < max_framepos) {
3448 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3449 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3450 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3453 to_add.push_back (lm->start);
3456 to_add.push_back (lm->end);
3460 if (!to_add.empty()) {
3461 _editor->selection->add (to_add);
3465 case Selection::Add:
3466 _editor->selection->add (_marker);
3470 /* Set up copies for us to manipulate during the drag
3473 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3475 Location* l = _editor->find_location_from_marker (*i, is_start);
3482 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3484 /* range: check that the other end of the range isn't
3487 CopiedLocationInfo::iterator x;
3488 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3489 if (*(*x).location == *l) {
3493 if (x == _copied_locations.end()) {
3494 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3496 (*x).markers.push_back (*i);
3497 (*x).move_both = true;
3505 MarkerDrag::setup_pointer_frame_offset ()
3508 Location *location = _editor->find_location_from_marker (_marker, is_start);
3509 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3513 MarkerDrag::motion (GdkEvent* event, bool)
3515 framecnt_t f_delta = 0;
3517 bool move_both = false;
3518 Location *real_location;
3519 Location *copy_location = 0;
3521 framepos_t const newframe = adjusted_current_frame (event);
3522 framepos_t next = newframe;
3524 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3528 CopiedLocationInfo::iterator x;
3530 /* find the marker we're dragging, and compute the delta */
3532 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3534 copy_location = (*x).location;
3536 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3538 /* this marker is represented by this
3539 * CopiedLocationMarkerInfo
3542 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3547 if (real_location->is_mark()) {
3548 f_delta = newframe - copy_location->start();
3552 switch (_marker->type()) {
3553 case Marker::SessionStart:
3554 case Marker::RangeStart:
3555 case Marker::LoopStart:
3556 case Marker::PunchIn:
3557 f_delta = newframe - copy_location->start();
3560 case Marker::SessionEnd:
3561 case Marker::RangeEnd:
3562 case Marker::LoopEnd:
3563 case Marker::PunchOut:
3564 f_delta = newframe - copy_location->end();
3567 /* what kind of marker is this ? */
3576 if (x == _copied_locations.end()) {
3577 /* hmm, impossible - we didn't find the dragged marker */
3581 /* now move them all */
3583 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3585 copy_location = x->location;
3587 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3591 if (real_location->locked()) {
3595 if (copy_location->is_mark()) {
3599 copy_location->set_start (copy_location->start() + f_delta);
3603 framepos_t new_start = copy_location->start() + f_delta;
3604 framepos_t new_end = copy_location->end() + f_delta;
3606 if (is_start) { // start-of-range marker
3608 if (move_both || (*x).move_both) {
3609 copy_location->set_start (new_start);
3610 copy_location->set_end (new_end);
3611 } else if (new_start < copy_location->end()) {
3612 copy_location->set_start (new_start);
3613 } else if (newframe > 0) {
3614 _editor->snap_to (next, RoundUpAlways, true);
3615 copy_location->set_end (next);
3616 copy_location->set_start (newframe);
3619 } else { // end marker
3621 if (move_both || (*x).move_both) {
3622 copy_location->set_end (new_end);
3623 copy_location->set_start (new_start);
3624 } else if (new_end > copy_location->start()) {
3625 copy_location->set_end (new_end);
3626 } else if (newframe > 0) {
3627 _editor->snap_to (next, RoundDownAlways, true);
3628 copy_location->set_start (next);
3629 copy_location->set_end (newframe);
3634 update_item (copy_location);
3636 /* now lookup the actual GUI items used to display this
3637 * location and move them to wherever the copy of the location
3638 * is now. This means that the logic in ARDOUR::Location is
3639 * still enforced, even though we are not (yet) modifying
3640 * the real Location itself.
3643 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3646 lm->set_position (copy_location->start(), copy_location->end());
3651 assert (!_copied_locations.empty());
3653 show_verbose_cursor_time (newframe);
3657 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3659 if (!movement_occurred) {
3661 if (was_double_click()) {
3662 _editor->rename_marker (_marker);
3666 /* just a click, do nothing but finish
3667 off the selection process
3670 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3673 case Selection::Set:
3674 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3675 _editor->selection->set (_marker);
3679 case Selection::Toggle:
3680 /* we toggle on the button release, click only */
3681 _editor->selection->toggle (_marker);
3684 case Selection::Extend:
3685 case Selection::Add:
3692 _editor->_dragging_edit_point = false;
3694 _editor->begin_reversible_command ( _("move marker") );
3695 XMLNode &before = _editor->session()->locations()->get_state();
3697 MarkerSelection::iterator i;
3698 CopiedLocationInfo::iterator x;
3701 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3702 x != _copied_locations.end() && i != _editor->selection->markers.end();
3705 Location * location = _editor->find_location_from_marker (*i, is_start);
3709 if (location->locked()) {
3713 if (location->is_mark()) {
3714 location->set_start (((*x).location)->start());
3716 location->set (((*x).location)->start(), ((*x).location)->end());
3721 XMLNode &after = _editor->session()->locations()->get_state();
3722 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3723 _editor->commit_reversible_command ();
3727 MarkerDrag::aborted (bool movement_occured)
3729 if (!movement_occured) {
3733 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3735 /* move all markers to their original location */
3738 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3741 Location * location = _editor->find_location_from_marker (*m, is_start);
3744 (*m)->set_position (is_start ? location->start() : location->end());
3751 MarkerDrag::update_item (Location*)
3756 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3758 _cumulative_x_drag (0),
3759 _cumulative_y_drag (0)
3761 if (_zero_gain_fraction < 0.0) {
3762 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3765 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3767 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3773 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3775 Drag::start_grab (event, _editor->cursors()->fader);
3777 // start the grab at the center of the control point so
3778 // the point doesn't 'jump' to the mouse after the first drag
3779 _fixed_grab_x = _point->get_x();
3780 _fixed_grab_y = _point->get_y();
3782 float const fraction = 1 - (_point->get_y() / _point->line().height());
3784 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3786 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3788 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3790 if (!_point->can_slide ()) {
3791 _x_constrained = true;
3796 ControlPointDrag::motion (GdkEvent* event, bool)
3798 double dx = _drags->current_pointer_x() - last_pointer_x();
3799 double dy = current_pointer_y() - last_pointer_y();
3801 if (event->button.state & Keyboard::SecondaryModifier) {
3806 /* coordinate in pixels relative to the start of the region (for region-based automation)
3807 or track (for track-based automation) */
3808 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3809 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3811 // calculate zero crossing point. back off by .01 to stay on the
3812 // positive side of zero
3813 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3815 // make sure we hit zero when passing through
3816 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3820 if (_x_constrained) {
3823 if (_y_constrained) {
3827 _cumulative_x_drag = cx - _fixed_grab_x;
3828 _cumulative_y_drag = cy - _fixed_grab_y;
3832 cy = min ((double) _point->line().height(), cy);
3834 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3836 if (!_x_constrained) {
3837 _editor->snap_to_with_modifier (cx_frames, event);
3840 cx_frames = min (cx_frames, _point->line().maximum_time());
3842 float const fraction = 1.0 - (cy / _point->line().height());
3844 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3846 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3850 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3852 if (!movement_occurred) {
3856 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3857 _editor->reset_point_selection ();
3861 motion (event, false);
3864 _point->line().end_drag (_pushing, _final_index);
3865 _editor->commit_reversible_command ();
3869 ControlPointDrag::aborted (bool)
3871 _point->line().reset ();
3875 ControlPointDrag::active (Editing::MouseMode m)
3877 if (m == Editing::MouseDraw) {
3878 /* always active in mouse draw */
3882 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3883 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3886 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3889 _cumulative_y_drag (0)
3891 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3895 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3897 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3900 _item = &_line->grab_item ();
3902 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3903 origin, and ditto for y.
3906 double cx = event->button.x;
3907 double cy = event->button.y;
3909 _line->parent_group().canvas_to_item (cx, cy);
3911 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3916 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3917 /* no adjacent points */
3921 Drag::start_grab (event, _editor->cursors()->fader);
3923 /* store grab start in parent frame */
3928 double fraction = 1.0 - (cy / _line->height());
3930 _line->start_drag_line (before, after, fraction);
3932 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3936 LineDrag::motion (GdkEvent* event, bool)
3938 double dy = current_pointer_y() - last_pointer_y();
3940 if (event->button.state & Keyboard::SecondaryModifier) {
3944 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3946 _cumulative_y_drag = cy - _fixed_grab_y;
3949 cy = min ((double) _line->height(), cy);
3951 double const fraction = 1.0 - (cy / _line->height());
3954 /* we are ignoring x position for this drag, so we can just pass in anything */
3955 _line->drag_motion (0, fraction, true, false, ignored);
3957 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3961 LineDrag::finished (GdkEvent* event, bool movement_occured)
3963 if (movement_occured) {
3964 motion (event, false);
3965 _line->end_drag (false, 0);
3967 /* add a new control point on the line */
3969 AutomationTimeAxisView* atv;
3971 _line->end_drag (false, 0);
3973 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3974 framepos_t where = _editor->window_event_sample (event, 0, 0);
3975 atv->add_automation_event (event, where, event->button.y, false);
3979 _editor->commit_reversible_command ();
3983 LineDrag::aborted (bool)
3988 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3991 _cumulative_x_drag (0)
3993 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3997 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3999 Drag::start_grab (event);
4001 _line = reinterpret_cast<Line*> (_item);
4004 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4006 double cx = event->button.x;
4007 double cy = event->button.y;
4009 _item->parent()->canvas_to_item (cx, cy);
4011 /* store grab start in parent frame */
4012 _region_view_grab_x = cx;
4014 _before = *(float*) _item->get_data ("position");
4016 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4018 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4022 FeatureLineDrag::motion (GdkEvent*, bool)
4024 double dx = _drags->current_pointer_x() - last_pointer_x();
4026 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4028 _cumulative_x_drag += dx;
4030 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4039 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4041 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4043 float *pos = new float;
4046 _line->set_data ("position", pos);
4052 FeatureLineDrag::finished (GdkEvent*, bool)
4054 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4055 _arv->update_transient(_before, _before);
4059 FeatureLineDrag::aborted (bool)
4064 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4066 , _vertical_only (false)
4068 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4072 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4074 Drag::start_grab (event);
4075 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4079 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4086 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4088 framepos_t grab = grab_frame ();
4089 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4090 _editor->snap_to_with_modifier (grab, event);
4092 grab = raw_grab_frame ();
4095 /* base start and end on initial click position */
4105 if (current_pointer_y() < grab_y()) {
4106 y1 = current_pointer_y();
4109 y2 = current_pointer_y();
4113 if (start != end || y1 != y2) {
4115 double x1 = _editor->sample_to_pixel (start);
4116 double x2 = _editor->sample_to_pixel (end);
4117 const double min_dimension = 2.0;
4119 if (_vertical_only) {
4120 /* fixed 10 pixel width */
4124 x2 = min (x1 - min_dimension, x2);
4126 x2 = max (x1 + min_dimension, x2);
4131 y2 = min (y1 - min_dimension, y2);
4133 y2 = max (y1 + min_dimension, y2);
4136 /* translate rect into item space and set */
4138 ArdourCanvas::Rect r (x1, y1, x2, y2);
4140 /* this drag is a _trackview_only == true drag, so the y1 and
4141 * y2 (computed using current_pointer_y() and grab_y()) will be
4142 * relative to the top of the trackview group). The
4143 * rubberband rect has the same parent/scroll offset as the
4144 * the trackview group, so we can use the "r" rect directly
4145 * to set the shape of the rubberband.
4148 _editor->rubberband_rect->set (r);
4149 _editor->rubberband_rect->show();
4150 _editor->rubberband_rect->raise_to_top();
4152 show_verbose_cursor_time (pf);
4154 do_select_things (event, true);
4159 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4163 framepos_t grab = grab_frame ();
4164 framepos_t lpf = last_pointer_frame ();
4166 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4167 grab = raw_grab_frame ();
4168 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4182 if (current_pointer_y() < grab_y()) {
4183 y1 = current_pointer_y();
4186 y2 = current_pointer_y();
4190 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4194 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4196 if (movement_occurred) {
4198 motion (event, false);
4199 do_select_things (event, false);
4205 bool do_deselect = true;
4206 MidiTimeAxisView* mtv;
4208 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4210 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4211 /* nothing selected */
4212 add_midi_region (mtv);
4213 do_deselect = false;
4217 /* do not deselect if Primary or Tertiary (toggle-select or
4218 * extend-select are pressed.
4221 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4222 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4229 _editor->rubberband_rect->hide();
4233 RubberbandSelectDrag::aborted (bool)
4235 _editor->rubberband_rect->hide ();
4238 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4239 : RegionDrag (e, i, p, v)
4241 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4245 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4247 Drag::start_grab (event, cursor);
4249 show_verbose_cursor_time (adjusted_current_frame (event));
4253 TimeFXDrag::motion (GdkEvent* event, bool)
4255 RegionView* rv = _primary;
4256 StreamView* cv = rv->get_time_axis_view().view ();
4258 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4259 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4260 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4262 framepos_t const pf = adjusted_current_frame (event);
4264 if (pf > rv->region()->position()) {
4265 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4268 show_verbose_cursor_time (pf);
4272 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4274 _primary->get_time_axis_view().hide_timestretch ();
4276 if (!movement_occurred) {
4280 if (last_pointer_frame() < _primary->region()->position()) {
4281 /* backwards drag of the left edge - not usable */
4285 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4287 float percentage = (double) newlen / (double) _primary->region()->length();
4289 #ifndef USE_RUBBERBAND
4290 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4291 if (_primary->region()->data_type() == DataType::AUDIO) {
4292 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4296 if (!_editor->get_selection().regions.empty()) {
4297 /* primary will already be included in the selection, and edit
4298 group shared editing will propagate selection across
4299 equivalent regions, so just use the current region
4303 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4304 error << _("An error occurred while executing time stretch operation") << endmsg;
4310 TimeFXDrag::aborted (bool)
4312 _primary->get_time_axis_view().hide_timestretch ();
4315 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4318 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4322 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4324 Drag::start_grab (event);
4328 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4330 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4334 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4336 if (movement_occurred && _editor->session()) {
4337 /* make sure we stop */
4338 _editor->session()->request_transport_speed (0.0);
4343 ScrubDrag::aborted (bool)
4348 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4352 , _original_pointer_time_axis (-1)
4353 , _time_selection_at_start (!_editor->get_selection().time.empty())
4355 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4357 if (_time_selection_at_start) {
4358 start_at_start = _editor->get_selection().time.start();
4359 end_at_start = _editor->get_selection().time.end_frame();
4364 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4366 if (_editor->session() == 0) {
4370 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4372 switch (_operation) {
4373 case CreateSelection:
4374 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4379 cursor = _editor->cursors()->selector;
4380 Drag::start_grab (event, cursor);
4383 case SelectionStartTrim:
4384 if (_editor->clicked_axisview) {
4385 _editor->clicked_axisview->order_selection_trims (_item, true);
4387 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4390 case SelectionEndTrim:
4391 if (_editor->clicked_axisview) {
4392 _editor->clicked_axisview->order_selection_trims (_item, false);
4394 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4398 Drag::start_grab (event, cursor);
4401 case SelectionExtend:
4402 Drag::start_grab (event, cursor);
4406 if (_operation == SelectionMove) {
4407 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4409 show_verbose_cursor_time (adjusted_current_frame (event));
4412 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4416 SelectionDrag::setup_pointer_frame_offset ()
4418 switch (_operation) {
4419 case CreateSelection:
4420 _pointer_frame_offset = 0;
4423 case SelectionStartTrim:
4425 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4428 case SelectionEndTrim:
4429 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4432 case SelectionExtend:
4438 SelectionDrag::motion (GdkEvent* event, bool first_move)
4440 framepos_t start = 0;
4442 framecnt_t length = 0;
4443 framecnt_t distance = 0;
4445 framepos_t const pending_position = adjusted_current_frame (event);
4447 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4451 switch (_operation) {
4452 case CreateSelection:
4454 framepos_t grab = grab_frame ();
4457 grab = adjusted_current_frame (event, false);
4458 if (grab < pending_position) {
4459 _editor->snap_to (grab, RoundDownMaybe);
4461 _editor->snap_to (grab, RoundUpMaybe);
4465 if (pending_position < grab) {
4466 start = pending_position;
4469 end = pending_position;
4473 /* first drag: Either add to the selection
4474 or create a new selection
4481 /* adding to the selection */
4482 _editor->set_selected_track_as_side_effect (Selection::Add);
4483 _editor->clicked_selection = _editor->selection->add (start, end);
4490 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4491 _editor->set_selected_track_as_side_effect (Selection::Set);
4494 _editor->clicked_selection = _editor->selection->set (start, end);
4498 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4499 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4500 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4502 _editor->selection->add (atest);
4506 /* select all tracks within the rectangle that we've marked out so far */
4507 TrackViewList new_selection;
4508 TrackViewList& all_tracks (_editor->track_views);
4510 ArdourCanvas::Coord const top = grab_y();
4511 ArdourCanvas::Coord const bottom = current_pointer_y();
4513 if (top >= 0 && bottom >= 0) {
4515 //first, find the tracks that are covered in the y range selection
4516 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4517 if ((*i)->covered_by_y_range (top, bottom)) {
4518 new_selection.push_back (*i);
4522 //now find any tracks that are GROUPED with the tracks we selected
4523 TrackViewList grouped_add = new_selection;
4524 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4525 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4526 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4527 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4528 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4529 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4530 grouped_add.push_back (*j);
4535 //now compare our list with the current selection, and add or remove as necessary
4536 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4537 TrackViewList tracks_to_add;
4538 TrackViewList tracks_to_remove;
4539 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4540 if ( !_editor->selection->tracks.contains ( *i ) )
4541 tracks_to_add.push_back ( *i );
4542 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4543 if ( !grouped_add.contains ( *i ) )
4544 tracks_to_remove.push_back ( *i );
4545 _editor->selection->add(tracks_to_add);
4546 _editor->selection->remove(tracks_to_remove);
4552 case SelectionStartTrim:
4554 start = _editor->selection->time[_editor->clicked_selection].start;
4555 end = _editor->selection->time[_editor->clicked_selection].end;
4557 if (pending_position > end) {
4560 start = pending_position;
4564 case SelectionEndTrim:
4566 start = _editor->selection->time[_editor->clicked_selection].start;
4567 end = _editor->selection->time[_editor->clicked_selection].end;
4569 if (pending_position < start) {
4572 end = pending_position;
4579 start = _editor->selection->time[_editor->clicked_selection].start;
4580 end = _editor->selection->time[_editor->clicked_selection].end;
4582 length = end - start;
4583 distance = pending_position - start;
4584 start = pending_position;
4585 _editor->snap_to (start);
4587 end = start + length;
4591 case SelectionExtend:
4596 switch (_operation) {
4598 if (_time_selection_at_start) {
4599 _editor->selection->move_time (distance);
4603 _editor->selection->replace (_editor->clicked_selection, start, end);
4607 if (_operation == SelectionMove) {
4608 show_verbose_cursor_time(start);
4610 show_verbose_cursor_time(pending_position);
4615 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4617 Session* s = _editor->session();
4619 _editor->begin_reversible_selection_op (_("Change Time Selection"));
4620 if (movement_occurred) {
4621 motion (event, false);
4622 /* XXX this is not object-oriented programming at all. ick */
4623 if (_editor->selection->time.consolidate()) {
4624 _editor->selection->TimeChanged ();
4627 /* XXX what if its a music time selection? */
4629 if ( s->get_play_range() && s->transport_rolling() ) {
4630 s->request_play_range (&_editor->selection->time, true);
4632 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4633 if (_operation == SelectionEndTrim)
4634 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4636 s->request_locate (_editor->get_selection().time.start());
4642 /* just a click, no pointer movement.
4645 if (_operation == SelectionExtend) {
4646 if (_time_selection_at_start) {
4647 framepos_t pos = adjusted_current_frame (event, false);
4648 framepos_t start = min (pos, start_at_start);
4649 framepos_t end = max (pos, end_at_start);
4650 _editor->selection->set (start, end);
4653 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4654 if (_editor->clicked_selection) {
4655 _editor->selection->remove (_editor->clicked_selection);
4658 if (!_editor->clicked_selection) {
4659 _editor->selection->clear_time();
4664 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4665 _editor->selection->set (_editor->clicked_axisview);
4668 if (s && s->get_play_range () && s->transport_rolling()) {
4669 s->request_stop (false, false);
4674 _editor->stop_canvas_autoscroll ();
4675 _editor->clicked_selection = 0;
4676 _editor->commit_reversible_selection_op ();
4680 SelectionDrag::aborted (bool)
4685 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4686 : Drag (e, i, false),
4690 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4692 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4693 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4694 physical_screen_height (_editor->get_window())));
4695 _drag_rect->hide ();
4697 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4698 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4701 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4703 /* normal canvas items will be cleaned up when their parent group is deleted. But
4704 this item is created as the child of a long-lived parent group, and so we
4705 need to explicitly delete it.
4711 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4713 if (_editor->session() == 0) {
4717 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4719 if (!_editor->temp_location) {
4720 _editor->temp_location = new Location (*_editor->session());
4723 switch (_operation) {
4724 case CreateSkipMarker:
4725 case CreateRangeMarker:
4726 case CreateTransportMarker:
4727 case CreateCDMarker:
4729 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4734 cursor = _editor->cursors()->selector;
4738 Drag::start_grab (event, cursor);
4740 show_verbose_cursor_time (adjusted_current_frame (event));
4744 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4746 framepos_t start = 0;
4748 ArdourCanvas::Rectangle *crect;
4750 switch (_operation) {
4751 case CreateSkipMarker:
4752 crect = _editor->range_bar_drag_rect;
4754 case CreateRangeMarker:
4755 crect = _editor->range_bar_drag_rect;
4757 case CreateTransportMarker:
4758 crect = _editor->transport_bar_drag_rect;
4760 case CreateCDMarker:
4761 crect = _editor->cd_marker_bar_drag_rect;
4764 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4769 framepos_t const pf = adjusted_current_frame (event);
4771 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4772 framepos_t grab = grab_frame ();
4773 _editor->snap_to (grab);
4775 if (pf < grab_frame()) {
4783 /* first drag: Either add to the selection
4784 or create a new selection.
4789 _editor->temp_location->set (start, end);
4793 update_item (_editor->temp_location);
4795 //_drag_rect->raise_to_top();
4801 _editor->temp_location->set (start, end);
4803 double x1 = _editor->sample_to_pixel (start);
4804 double x2 = _editor->sample_to_pixel (end);
4808 update_item (_editor->temp_location);
4811 show_verbose_cursor_time (pf);
4816 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4818 Location * newloc = 0;
4822 if (movement_occurred) {
4823 motion (event, false);
4826 switch (_operation) {
4827 case CreateSkipMarker:
4828 case CreateRangeMarker:
4829 case CreateCDMarker:
4831 XMLNode &before = _editor->session()->locations()->get_state();
4832 if (_operation == CreateSkipMarker) {
4833 _editor->begin_reversible_command (_("new skip marker"));
4834 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4835 flags = Location::IsRangeMarker | Location::IsSkip;
4836 _editor->range_bar_drag_rect->hide();
4837 } else if (_operation == CreateCDMarker) {
4838 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4839 _editor->begin_reversible_command (_("new CD marker"));
4840 flags = Location::IsRangeMarker | Location::IsCDMarker;
4841 _editor->cd_marker_bar_drag_rect->hide();
4843 _editor->begin_reversible_command (_("new skip marker"));
4844 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4845 flags = Location::IsRangeMarker;
4846 _editor->range_bar_drag_rect->hide();
4848 newloc = new Location (
4849 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4852 _editor->session()->locations()->add (newloc, true);
4853 XMLNode &after = _editor->session()->locations()->get_state();
4854 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4855 _editor->commit_reversible_command ();
4859 case CreateTransportMarker:
4860 // popup menu to pick loop or punch
4861 _editor->new_transport_marker_context_menu (&event->button, _item);
4867 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4869 if (_operation == CreateTransportMarker) {
4871 /* didn't drag, so just locate */
4873 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4875 } else if (_operation == CreateCDMarker) {
4877 /* didn't drag, but mark is already created so do
4880 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4886 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4888 if (end == max_framepos) {
4889 end = _editor->session()->current_end_frame ();
4892 if (start == max_framepos) {
4893 start = _editor->session()->current_start_frame ();
4896 switch (_editor->mouse_mode) {
4898 /* find the two markers on either side and then make the selection from it */
4899 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4903 /* find the two markers on either side of the click and make the range out of it */
4904 _editor->selection->set (start, end);
4913 _editor->stop_canvas_autoscroll ();
4917 RangeMarkerBarDrag::aborted (bool movement_occured)
4919 if (movement_occured) {
4920 _drag_rect->hide ();
4925 RangeMarkerBarDrag::update_item (Location* location)
4927 double const x1 = _editor->sample_to_pixel (location->start());
4928 double const x2 = _editor->sample_to_pixel (location->end());
4930 _drag_rect->set_x0 (x1);
4931 _drag_rect->set_x1 (x2);
4934 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4936 , _cumulative_dx (0)
4937 , _cumulative_dy (0)
4939 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4941 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4943 _region = &_primary->region_view ();
4944 _note_height = _region->midi_stream_view()->note_height ();
4948 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4950 Drag::start_grab (event);
4952 if (!(_was_selected = _primary->selected())) {
4954 /* tertiary-click means extend selection - we'll do that on button release,
4955 so don't add it here, because otherwise we make it hard to figure
4956 out the "extend-to" range.
4959 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4962 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4965 _region->note_selected (_primary, true);
4967 _region->unique_select (_primary);
4970 _editor->begin_reversible_selection_op(_("Select Note Press"));
4971 _editor->commit_reversible_selection_op();
4976 /** @return Current total drag x change in frames */
4978 NoteDrag::total_dx () const
4981 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4983 /* primary note time */
4984 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4986 /* new time of the primary note in session frames */
4987 frameoffset_t st = n + dx;
4989 framepos_t const rp = _region->region()->position ();
4991 /* prevent the note being dragged earlier than the region's position */
4994 /* snap and return corresponding delta */
4995 return _region->snap_frame_to_frame (st - rp) + rp - n;
4998 /** @return Current total drag y change in note number */
5000 NoteDrag::total_dy () const
5002 MidiStreamView* msv = _region->midi_stream_view ();
5003 double const y = _region->midi_view()->y_position ();
5004 /* new current note */
5005 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5007 n = max (msv->lowest_note(), n);
5008 n = min (msv->highest_note(), n);
5009 /* and work out delta */
5010 return n - msv->y_to_note (grab_y() - y);
5014 NoteDrag::motion (GdkEvent *, bool)
5016 /* Total change in x and y since the start of the drag */
5017 frameoffset_t const dx = total_dx ();
5018 int8_t const dy = total_dy ();
5020 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5021 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5022 double const tdy = -dy * _note_height - _cumulative_dy;
5025 _cumulative_dx += tdx;
5026 _cumulative_dy += tdy;
5028 int8_t note_delta = total_dy();
5030 _region->move_selection (tdx, tdy, note_delta);
5032 /* the new note value may be the same as the old one, but we
5033 * don't know what that means because the selection may have
5034 * involved more than one note and we might be doing something
5035 * odd with them. so show the note value anyway, always.
5039 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5041 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5042 (int) floor ((double)new_note));
5044 show_verbose_cursor_text (buf);
5049 NoteDrag::finished (GdkEvent* ev, bool moved)
5052 /* no motion - select note */
5054 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5055 _editor->current_mouse_mode() == Editing::MouseDraw) {
5057 bool changed = false;
5059 if (_was_selected) {
5060 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5062 _region->note_deselected (_primary);
5066 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5067 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5069 if (!extend && !add && _region->selection_size() > 1) {
5070 _region->unique_select (_primary);
5072 } else if (extend) {
5073 _region->note_selected (_primary, true, true);
5076 /* it was added during button press */
5081 _editor->begin_reversible_selection_op(_("Select Note Release"));
5082 _editor->commit_reversible_selection_op();
5086 _region->note_dropped (_primary, total_dx(), total_dy());
5091 NoteDrag::aborted (bool)
5096 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5097 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5098 : Drag (editor, atv->base_item ())
5100 , _y_origin (atv->y_position())
5101 , _nothing_to_drag (false)
5103 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5104 setup (atv->lines ());
5107 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5108 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5109 : Drag (editor, rv->get_canvas_group ())
5111 , _y_origin (rv->get_time_axis_view().y_position())
5112 , _nothing_to_drag (false)
5115 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5117 list<boost::shared_ptr<AutomationLine> > lines;
5119 AudioRegionView* audio_view;
5120 AutomationRegionView* automation_view;
5121 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5122 lines.push_back (audio_view->get_gain_line ());
5123 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5124 lines.push_back (automation_view->line ());
5127 error << _("Automation range drag created for invalid region type") << endmsg;
5133 /** @param lines AutomationLines to drag.
5134 * @param offset Offset from the session start to the points in the AutomationLines.
5137 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5139 /* find the lines that overlap the ranges being dragged */
5140 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5141 while (i != lines.end ()) {
5142 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5145 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5147 /* check this range against all the AudioRanges that we are using */
5148 list<AudioRange>::const_iterator k = _ranges.begin ();
5149 while (k != _ranges.end()) {
5150 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5156 /* add it to our list if it overlaps at all */
5157 if (k != _ranges.end()) {
5162 _lines.push_back (n);
5168 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5172 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5174 return 1.0 - ((global_y - _y_origin) / line->height());
5178 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5180 const double v = list->eval(x);
5181 return _integral ? rint(v) : v;
5185 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5187 Drag::start_grab (event, cursor);
5189 /* Get line states before we start changing things */
5190 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5191 i->state = &i->line->get_state ();
5192 i->original_fraction = y_fraction (i->line, current_pointer_y());
5195 if (_ranges.empty()) {
5197 /* No selected time ranges: drag all points */
5198 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5199 uint32_t const N = i->line->npoints ();
5200 for (uint32_t j = 0; j < N; ++j) {
5201 i->points.push_back (i->line->nth (j));
5207 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5209 framecnt_t const half = (i->start + i->end) / 2;
5211 /* find the line that this audio range starts in */
5212 list<Line>::iterator j = _lines.begin();
5213 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5217 if (j != _lines.end()) {
5218 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5220 /* j is the line that this audio range starts in; fade into it;
5221 64 samples length plucked out of thin air.
5224 framepos_t a = i->start + 64;
5229 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5230 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5232 the_list->editor_add (p, value (the_list, p));
5233 the_list->editor_add (q, value (the_list, q));
5236 /* same thing for the end */
5239 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5243 if (j != _lines.end()) {
5244 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5246 /* j is the line that this audio range starts in; fade out of it;
5247 64 samples length plucked out of thin air.
5250 framepos_t b = i->end - 64;
5255 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5256 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5258 the_list->editor_add (p, value (the_list, p));
5259 the_list->editor_add (q, value (the_list, q));
5263 _nothing_to_drag = true;
5265 /* Find all the points that should be dragged and put them in the relevant
5266 points lists in the Line structs.
5269 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5271 uint32_t const N = i->line->npoints ();
5272 for (uint32_t j = 0; j < N; ++j) {
5274 /* here's a control point on this line */
5275 ControlPoint* p = i->line->nth (j);
5276 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5278 /* see if it's inside a range */
5279 list<AudioRange>::const_iterator k = _ranges.begin ();
5280 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5284 if (k != _ranges.end()) {
5285 /* dragging this point */
5286 _nothing_to_drag = false;
5287 i->points.push_back (p);
5293 if (_nothing_to_drag) {
5297 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5298 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5303 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5305 if (_nothing_to_drag) {
5309 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5310 float const f = y_fraction (l->line, current_pointer_y());
5311 /* we are ignoring x position for this drag, so we can just pass in anything */
5313 l->line->drag_motion (0, f, true, false, ignored);
5314 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5319 AutomationRangeDrag::finished (GdkEvent* event, bool)
5321 if (_nothing_to_drag) {
5325 motion (event, false);
5326 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5327 i->line->end_drag (false, 0);
5330 _editor->commit_reversible_command ();
5334 AutomationRangeDrag::aborted (bool)
5336 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5341 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5343 , initial_time_axis_view (itav)
5345 /* note that time_axis_view may be null if the regionview was created
5346 * as part of a copy operation.
5348 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5349 layer = v->region()->layer ();
5350 initial_y = v->get_canvas_group()->position().y;
5351 initial_playlist = v->region()->playlist ();
5352 initial_position = v->region()->position ();
5353 initial_end = v->region()->position () + v->region()->length ();
5356 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5357 : Drag (e, i->canvas_item ())
5360 , _cumulative_dx (0)
5362 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5363 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5368 PatchChangeDrag::motion (GdkEvent* ev, bool)
5370 framepos_t f = adjusted_current_frame (ev);
5371 boost::shared_ptr<Region> r = _region_view->region ();
5372 f = max (f, r->position ());
5373 f = min (f, r->last_frame ());
5375 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5376 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5377 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5378 _cumulative_dx = dxu;
5382 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5384 if (!movement_occurred) {
5388 boost::shared_ptr<Region> r (_region_view->region ());
5389 framepos_t f = adjusted_current_frame (ev);
5390 f = max (f, r->position ());
5391 f = min (f, r->last_frame ());
5393 _region_view->move_patch_change (
5395 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5400 PatchChangeDrag::aborted (bool)
5402 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5406 PatchChangeDrag::setup_pointer_frame_offset ()
5408 boost::shared_ptr<Region> region = _region_view->region ();
5409 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5412 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5413 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5420 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5422 _region_view->update_drag_selection (
5424 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5428 MidiRubberbandSelectDrag::deselect_things ()
5433 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5434 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5437 _vertical_only = true;
5441 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5443 double const y = _region_view->midi_view()->y_position ();
5445 y1 = max (0.0, y1 - y);
5446 y2 = max (0.0, y2 - y);
5448 _region_view->update_vertical_drag_selection (
5451 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5456 MidiVerticalSelectDrag::deselect_things ()
5461 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5462 : RubberbandSelectDrag (e, i)
5468 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5470 if (drag_in_progress) {
5471 /* We just want to select things at the end of the drag, not during it */
5475 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5477 _editor->begin_reversible_selection_op (_("rubberband selection"));
5479 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5481 _editor->commit_reversible_selection_op ();
5485 EditorRubberbandSelectDrag::deselect_things ()
5487 _editor->begin_reversible_selection_op (_("Clear Selection (rubberband)"));
5489 _editor->selection->clear_tracks();
5490 _editor->selection->clear_regions();
5491 _editor->selection->clear_points ();
5492 _editor->selection->clear_lines ();
5493 _editor->selection->clear_midi_notes ();
5495 _editor->commit_reversible_selection_op();
5498 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5503 _note[0] = _note[1] = 0;
5506 NoteCreateDrag::~NoteCreateDrag ()
5512 NoteCreateDrag::grid_frames (framepos_t t) const
5515 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5517 grid_beats = Evoral::Beats(1);
5520 return _region_view->region_beats_to_region_frames (grid_beats);
5524 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5526 Drag::start_grab (event, cursor);
5528 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5530 framepos_t pf = _drags->current_pointer_frame ();
5531 framecnt_t const g = grid_frames (pf);
5533 /* Hack so that we always snap to the note that we are over, instead of snapping
5534 to the next one if we're more than halfway through the one we're over.
5536 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5540 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5541 _note[1] = _note[0];
5543 MidiStreamView* sv = _region_view->midi_stream_view ();
5544 double const x = _editor->sample_to_pixel (_note[0]);
5545 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5547 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5548 _drag_rect->set_outline_all ();
5549 _drag_rect->set_outline_color (0xffffff99);
5550 _drag_rect->set_fill_color (0xffffff66);
5554 NoteCreateDrag::motion (GdkEvent* event, bool)
5556 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5557 double const x0 = _editor->sample_to_pixel (_note[0]);
5558 double const x1 = _editor->sample_to_pixel (_note[1]);
5559 _drag_rect->set_x0 (std::min(x0, x1));
5560 _drag_rect->set_x1 (std::max(x0, x1));
5564 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5566 if (!had_movement) {
5570 framepos_t const start = min (_note[0], _note[1]);
5571 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5573 framecnt_t const g = grid_frames (start);
5574 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5576 if (_editor->snap_mode() == SnapNormal && length < g) {
5580 Evoral::Beats length_beats = max (
5581 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5583 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5587 NoteCreateDrag::y_to_region (double y) const
5590 _region_view->get_canvas_group()->canvas_to_item (x, y);
5595 NoteCreateDrag::aborted (bool)
5600 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5605 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5609 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5611 Drag::start_grab (event, cursor);
5615 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5621 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5624 distance = _drags->current_pointer_x() - grab_x();
5625 len = ar->fade_in()->back()->when;
5627 distance = grab_x() - _drags->current_pointer_x();
5628 len = ar->fade_out()->back()->when;
5631 /* how long should it be ? */
5633 new_length = len + _editor->pixel_to_sample (distance);
5635 /* now check with the region that this is legal */
5637 new_length = ar->verify_xfade_bounds (new_length, start);
5640 arv->reset_fade_in_shape_width (ar, new_length);
5642 arv->reset_fade_out_shape_width (ar, new_length);
5647 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5653 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5656 distance = _drags->current_pointer_x() - grab_x();
5657 len = ar->fade_in()->back()->when;
5659 distance = grab_x() - _drags->current_pointer_x();
5660 len = ar->fade_out()->back()->when;
5663 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5665 _editor->begin_reversible_command ("xfade trim");
5666 ar->playlist()->clear_owned_changes ();
5669 ar->set_fade_in_length (new_length);
5671 ar->set_fade_out_length (new_length);
5674 /* Adjusting the xfade may affect other regions in the playlist, so we need
5675 to get undo Commands from the whole playlist rather than just the
5679 vector<Command*> cmds;
5680 ar->playlist()->rdiff (cmds);
5681 _editor->session()->add_commands (cmds);
5682 _editor->commit_reversible_command ();
5687 CrossfadeEdgeDrag::aborted (bool)
5690 // arv->redraw_start_xfade ();
5692 // arv->redraw_end_xfade ();
5696 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5697 : Drag (e, item, true)
5698 , line (new EditorCursor (*e))
5700 line->set_position (pos);
5704 RegionCutDrag::~RegionCutDrag ()
5710 RegionCutDrag::motion (GdkEvent*, bool)
5712 framepos_t where = _drags->current_pointer_frame();
5713 _editor->snap_to (where);
5715 line->set_position (where);
5719 RegionCutDrag::finished (GdkEvent*, bool)
5721 _editor->get_track_canvas()->canvas()->re_enter();
5723 framepos_t pos = _drags->current_pointer_frame();
5727 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5733 _editor->split_regions_at (pos, rs);
5737 RegionCutDrag::aborted (bool)