2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "verbose_cursor.h"
70 using namespace ARDOUR;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75 using namespace ArdourCanvas;
77 using Gtkmm2ext::Keyboard;
79 double ControlPointDrag::_zero_gain_fraction = -1.0;
81 DragManager::DragManager (Editor* e)
84 , _current_pointer_frame (0)
88 DragManager::~DragManager ()
93 /** Call abort for each active drag */
99 cerr << "Aborting drag\n";
101 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
106 if (!_drags.empty ()) {
107 _editor->set_follow_playhead (_old_follow_playhead, false);
111 _editor->abort_reversible_command();
117 DragManager::add (Drag* d)
119 d->set_manager (this);
120 _drags.push_back (d);
124 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
126 d->set_manager (this);
127 _drags.push_back (d);
132 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
134 /* Prevent follow playhead during the drag to be nice to the user */
135 _old_follow_playhead = _editor->follow_playhead ();
136 _editor->set_follow_playhead (false);
138 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
140 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
141 (*i)->start_grab (e, c);
145 /** Call end_grab for each active drag.
146 * @return true if any drag reported movement having occurred.
149 DragManager::end_grab (GdkEvent* e)
154 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
155 bool const t = (*i)->end_grab (e);
166 _editor->set_follow_playhead (_old_follow_playhead, false);
172 DragManager::mark_double_click ()
174 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
175 (*i)->set_double_click (true);
180 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
184 /* calling this implies that we expect the event to have canvas
187 * Can we guarantee that this is true?
190 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
192 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
193 bool const t = (*i)->motion_handler (e, from_autoscroll);
194 /* run all handlers; return true if at least one of them
195 returns true (indicating that the event has been handled).
207 DragManager::have_item (ArdourCanvas::Item* i) const
209 list<Drag*>::const_iterator j = _drags.begin ();
210 while (j != _drags.end() && (*j)->item () != i) {
214 return j != _drags.end ();
217 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
220 , _pointer_frame_offset (0)
221 , _trackview_only (trackview_only)
222 , _move_threshold_passed (false)
223 , _starting_point_passed (false)
224 , _initially_vertical (false)
225 , _was_double_click (false)
226 , _raw_grab_frame (0)
228 , _last_pointer_frame (0)
234 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
240 _cursor_ctx = CursorContext::create (*_editor, cursor);
242 _cursor_ctx->change (cursor);
249 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
251 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
253 if (Keyboard::is_button2_event (&event->button)) {
254 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
255 _y_constrained = true;
256 _x_constrained = false;
258 _y_constrained = false;
259 _x_constrained = true;
262 _x_constrained = false;
263 _y_constrained = false;
266 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
267 setup_pointer_frame_offset ();
268 _grab_frame = adjusted_frame (_raw_grab_frame, event);
269 _last_pointer_frame = _grab_frame;
270 _last_pointer_x = _grab_x;
272 if (_trackview_only) {
273 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
276 _last_pointer_y = _grab_y;
280 if (!_editor->cursors()->is_invalid (cursor)) {
281 /* CAIROCANVAS need a variant here that passes *cursor */
282 _cursor_ctx = CursorContext::create (*_editor, cursor);
285 if (_editor->session() && _editor->session()->transport_rolling()) {
288 _was_rolling = false;
291 switch (_editor->snap_type()) {
292 case SnapToRegionStart:
293 case SnapToRegionEnd:
294 case SnapToRegionSync:
295 case SnapToRegionBoundary:
296 _editor->build_region_boundary_cache ();
303 /** Call to end a drag `successfully'. Ungrabs item and calls
304 * subclass' finished() method.
306 * @param event GDK event, or 0.
307 * @return true if some movement occurred, otherwise false.
310 Drag::end_grab (GdkEvent* event)
312 _editor->stop_canvas_autoscroll ();
316 finished (event, _move_threshold_passed);
318 _editor->verbose_cursor()->hide ();
321 return _move_threshold_passed;
325 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
329 if (f > _pointer_frame_offset) {
330 pos = f - _pointer_frame_offset;
334 _editor->snap_to_with_modifier (pos, event);
341 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
343 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
347 Drag::current_pointer_x() const
349 return _drags->current_pointer_x ();
353 Drag::current_pointer_y () const
355 if (!_trackview_only) {
356 return _drags->current_pointer_y ();
359 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
363 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
365 /* check to see if we have moved in any way that matters since the last motion event */
366 if (_move_threshold_passed &&
367 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
368 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
372 pair<framecnt_t, int> const threshold = move_threshold ();
374 bool const old_move_threshold_passed = _move_threshold_passed;
376 if (!_move_threshold_passed) {
378 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
379 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
381 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
384 if (active (_editor->mouse_mode) && _move_threshold_passed) {
386 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
388 if (old_move_threshold_passed != _move_threshold_passed) {
392 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
393 _initially_vertical = true;
395 _initially_vertical = false;
399 if (!from_autoscroll) {
400 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
403 if (!_editor->autoscroll_active() || from_autoscroll) {
406 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
408 motion (event, first_move && !_starting_point_passed);
410 if (first_move && !_starting_point_passed) {
411 _starting_point_passed = true;
414 _last_pointer_x = _drags->current_pointer_x ();
415 _last_pointer_y = current_pointer_y ();
416 _last_pointer_frame = adjusted_current_frame (event);
426 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
434 aborted (_move_threshold_passed);
436 _editor->stop_canvas_autoscroll ();
437 _editor->verbose_cursor()->hide ();
441 Drag::show_verbose_cursor_time (framepos_t frame)
443 _editor->verbose_cursor()->set_time (frame);
444 _editor->verbose_cursor()->show ();
448 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
450 _editor->verbose_cursor()->set_duration (start, end);
451 _editor->verbose_cursor()->show ();
455 Drag::show_verbose_cursor_text (string const & text)
457 _editor->verbose_cursor()->set (text);
458 _editor->verbose_cursor()->show ();
461 boost::shared_ptr<Region>
462 Drag::add_midi_region (MidiTimeAxisView* view)
464 if (_editor->session()) {
465 const TempoMap& map (_editor->session()->tempo_map());
466 framecnt_t pos = grab_frame();
467 const Meter& m = map.meter_at (pos);
468 /* not that the frame rate used here can be affected by pull up/down which
471 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
472 return view->add_region (grab_frame(), len, true);
475 return boost::shared_ptr<Region>();
478 struct EditorOrderTimeAxisViewSorter {
479 bool operator() (TimeAxisView* a, TimeAxisView* b) {
480 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
481 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
483 return ra->route()->order_key () < rb->route()->order_key ();
487 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
491 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
493 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
494 as some of the regions we are dragging may be on such tracks.
497 TrackViewList track_views = _editor->track_views;
498 track_views.sort (EditorOrderTimeAxisViewSorter ());
500 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
501 _time_axis_views.push_back (*i);
503 TimeAxisView::Children children_list = (*i)->get_child_list ();
504 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
505 _time_axis_views.push_back (j->get());
509 /* the list of views can be empty at this point if this is a region list-insert drag
512 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
513 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
516 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
520 RegionDrag::region_going_away (RegionView* v)
522 list<DraggingView>::iterator i = _views.begin ();
523 while (i != _views.end() && i->view != v) {
527 if (i != _views.end()) {
532 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
533 * or -1 if it is not found.
536 RegionDrag::find_time_axis_view (TimeAxisView* t) const
539 int const N = _time_axis_views.size ();
540 while (i < N && _time_axis_views[i] != t) {
544 if (_time_axis_views[i] != t) {
551 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
552 : RegionDrag (e, i, p, v)
555 , _last_pointer_time_axis_view (0)
556 , _last_pointer_layer (0)
557 , _single_axis (false)
559 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
563 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
565 Drag::start_grab (event, cursor);
567 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
571 show_verbose_cursor_time (_last_frame_position);
573 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
575 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
576 assert(_last_pointer_time_axis_view >= 0);
577 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
582 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
584 /* compute the amount of pointer motion in frames, and where
585 the region would be if we moved it by that much.
587 *pending_region_position = adjusted_current_frame (event);
589 framepos_t sync_frame;
590 framecnt_t sync_offset;
593 sync_offset = _primary->region()->sync_offset (sync_dir);
595 /* we don't handle a sync point that lies before zero.
597 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
599 sync_frame = *pending_region_position + (sync_dir*sync_offset);
601 _editor->snap_to_with_modifier (sync_frame, event);
603 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
606 *pending_region_position = _last_frame_position;
609 if (*pending_region_position > max_framepos - _primary->region()->length()) {
610 *pending_region_position = _last_frame_position;
615 /* in locked edit mode, reverse the usual meaning of _x_constrained */
616 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
618 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
620 /* x movement since last time (in pixels) */
621 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
623 /* total x movement */
624 framecnt_t total_dx = *pending_region_position;
625 if (regions_came_from_canvas()) {
626 total_dx = total_dx - grab_frame ();
629 /* check that no regions have gone off the start of the session */
630 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
631 if ((i->view->region()->position() + total_dx) < 0) {
633 *pending_region_position = _last_frame_position;
644 RegionDrag::apply_track_delta (const int start, const int delta, const int skip) const
650 const int dt = delta > 0 ? +1 : -1;
652 int target = start + delta - skip;
654 assert (current < 0 || current >= _time_axis_views.size() || !_time_axis_views[current]->hidden());
655 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
657 #ifdef DEBUG_DROPZONEDRAG
658 if (current >= _time_axis_views.size() && target >= 0 && target < _time_axis_views.size()) {
659 printf("MOVE OUT OF THE ZONE cur: %d d: %d s: %d\n", start, delta, skip);
663 while (current >= 0 && current != target) {
665 if (current < 0 || current >= _time_axis_views.size()) {
668 if (_time_axis_views[current]->hidden()) {
676 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
678 if (_y_constrained) {
682 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
683 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
684 #ifdef DEBUG_DROPZONEDRAG
685 printf("Y MOVEMENT CHECK: from %d to %d skip: %d\n", i->time_axis_view, i->time_axis_view + delta_track, skip_invisible);
687 assert (n < 0 || n >= _time_axis_views.size() || !_time_axis_views[n]->hidden());
689 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
690 /* already in the drop zone */
691 if (delta_track >= 0) {
692 /* downward motion - might be OK if others are still not in the dropzone,
693 so check at the end of the loop if that is the case.
698 /* upward motion - set n to the track we would end up in if motion
699 is successful, and check validity below. */
700 n = _time_axis_views.size() + delta_track;
706 } else if (n >= int (_time_axis_views.size())) {
707 /* downward motion into drop zone. That's fine. */
711 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
712 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
713 /* not a track, or the wrong type */
717 double const l = i->layer + delta_layer;
719 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
720 mode to allow the user to place a region below another on layer 0.
722 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
723 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
724 If it has, the layers will be munged later anyway, so it's ok.
730 /* all regions being dragged are ok with this change */
735 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
737 double delta_layer = 0;
738 int delta_time_axis_view = 0;
739 int current_pointer_time_axis_view = -1;
741 assert (!_views.empty ());
745 if (initially_vertical()) {
746 _y_constrained = false;
747 _x_constrained = true;
749 _y_constrained = true;
750 _x_constrained = false;
755 #ifdef DEBUG_DROPZONEDRAG
756 printf("--------- LAST AXIS: %d\n", _last_pointer_time_axis_view);
758 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
760 /* Find the TimeAxisView that the pointer is now over */
762 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
763 TimeAxisView* tv = r.first;
765 if (!tv && current_pointer_y() < 0) {
766 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
770 if (tv && tv->view()) {
771 double layer = r.second;
773 if (first_move && tv->view()->layer_display() == Stacked) {
774 tv->view()->set_layer_display (Expanded);
777 /* Here's the current pointer position in terms of time axis view and layer */
778 current_pointer_time_axis_view = find_time_axis_view (tv);
779 assert(current_pointer_time_axis_view >= 0);
781 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
783 /* Work out the change in y */
785 if (_last_pointer_time_axis_view < 0) {
786 /* Was in the drop-zone, now over a track.
787 * Hence it must be an upward move (from the bottom)
789 * track_index is still -1, so delta must be set to
790 * move up the correct number of tracks from the bottom.
792 * This is necessary because steps may be skipped if
793 * the bottom-most track is not a valid target,
795 #ifdef DEBUG_DROPZONEDRAG
796 printf("MOVE OUT OF THE ZONE...\n");
798 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size ();
800 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
803 /* TODO needs adjustment per DraggingView,
805 * e.g. select one region on the top-layer of a track
806 * and one region which is at the bottom-layer of another track
809 * Indicated drop-zones and layering is wrong.
810 * and may infer additional layers on the target-track
811 * (depending how many layers the original track had).
813 * Or select two regions (different layers) on a same track,
814 * move across a non-layer track.. -> layering info is lost.
815 * on drop either of the regions may be on top.
817 * Proposed solution: screw it :) well,
818 * don't use delta_layer, use an absolute value
819 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
820 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
821 * 3) iterate over all DraggingView, find the one that is over the track with most layers
822 * 4) proportionally scale layer to layers available on target
824 delta_layer = current_pointer_layer - _last_pointer_layer;
827 /* for automation lanes, there is a TimeAxisView but no ->view() */
828 else if (!tv && current_pointer_y() >= 0 && _last_pointer_time_axis_view >= 0) {
829 /* Moving into the drop-zone..
831 * TODO allow moving further down in drop-zone:
832 * e.g. 2 Tracks, select a region on both of them.
834 * A) grab the upper, drag 2 down, both regions are in the dropzone: all fine (works)
836 * B) grab the lower, drag 1 down, region (and mouse) are in dropzone, The End.
837 * upper region is only down one track and cannot be moved into the zone.
839 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
840 #ifdef DEBUG_DROPZONEDRAG
841 printf("INTO THE ZONE DELTA: %d\n", delta_time_axis_view);
845 /* Work out the change in x */
846 framepos_t pending_region_position;
847 double const x_delta = compute_x_delta (event, &pending_region_position);
848 _last_frame_position = pending_region_position;
850 /* calculate hidden tracks in current delta */
852 if (_last_pointer_time_axis_view < 0) {
853 // Moving out of the zone, check for hidden tracks at the bottom.
854 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0)
855 -_time_axis_views.size() - delta_time_axis_view;
856 #ifdef DEBUG_DROPZONEDRAG
857 printf("NOW WHAT?? last: %d delta %d || skip %d\n", _last_pointer_time_axis_view, delta_time_axis_view, delta_skip);
860 // calculate hidden tracks that are skipped by the pointer movement
861 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0)
862 - _last_pointer_time_axis_view
863 - delta_time_axis_view;
864 #ifdef DEBUG_DROPZONEDRAG
865 printf("Drag from %d to %d || skip %d\n",
866 _last_pointer_time_axis_view,
867 _last_pointer_time_axis_view + delta_time_axis_view,
872 /* Verify change in y */
873 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
874 /* this y movement is not allowed, so do no y movement this time */
875 delta_time_axis_view = 0;
878 #ifdef DEBUG_DROPZONEDRAG
879 printf(" ** NOT ALLOWED\n");
883 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
884 /* haven't reached next snap point, and we're not switching
885 trackviews nor layers. nothing to do.
890 typedef pair<int,double> NewTrackIndexAndPosition;
891 typedef map<boost::shared_ptr<Playlist>,NewTrackIndexAndPosition> PlaylistDropzoneMap;
892 PlaylistDropzoneMap playlist_dropzone_map;
893 int biggest_drop_zone_offset = 0;
895 /* find drop-zone y-position */
896 Coord last_track_bottom_edge;
897 last_track_bottom_edge = 0;
898 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
899 if (!(*t)->hidden()) {
900 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
905 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
907 RegionView* rv = i->view;
912 if (rv->region()->locked() || rv->region()->video_locked()) {
919 /* reparent the regionview into a group above all
923 ArdourCanvas::Item* rvg = rv->get_canvas_group();
924 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
925 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
926 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
927 /* move the item so that it continues to appear at the
928 same location now that its parent has changed.
930 rvg->move (rv_canvas_offset - dmg_canvas_offset);
933 /* If we have moved tracks, we'll fudge the layer delta so that the
934 region gets moved back onto layer 0 on its new track; this avoids
935 confusion when dragging regions from non-zero layers onto different
938 double this_delta_layer = delta_layer;
939 if (delta_time_axis_view != 0) {
940 this_delta_layer = - i->layer;
943 int this_delta_time_axis_view = delta_time_axis_view;
944 this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
946 int track_index = i->time_axis_view + this_delta_time_axis_view;
947 assert(track_index >= 0);
949 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
950 i->time_axis_view = track_index;
951 #ifdef DEBUG_DROPZONEDRAG
952 printf("IN THE ZONE\n");
954 assert(i->time_axis_view >= _time_axis_views.size());
955 if (current_pointer_y() >= 0) {
958 NewTrackIndexAndPosition ip;
959 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
961 /* store index of each new playlist as a negative count, starting at -1 */
963 if (pdz == playlist_dropzone_map.end()) {
966 * retain the ordering top -> bottom in the drop-zone
967 * this can be done by sorting the regions according to
968 * i->time_axis_view Y, prior to iterating over DraggingView
971 int n = playlist_dropzone_map.size() + 1;
973 /* compute where this new track (which doesn't exist yet) will live
977 ip.first = -n; /* in time axis units, negative to signify "in drop zone " */
978 ip.second = last_track_bottom_edge; /* where to place the top edge of the regionview */
980 /* How high is this region view ? */
982 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
983 ArdourCanvas::Rect bbox;
989 last_track_bottom_edge += bbox.height();
991 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), ip));
999 /* values are zero or negative, hence the use of min() */
1000 biggest_drop_zone_offset = min (biggest_drop_zone_offset, dzoffset);
1001 y_delta = ip.second - rv->get_canvas_group()->canvas_origin().y;
1006 /* The TimeAxisView that this region is now over */
1007 TimeAxisView* current_tv = _time_axis_views[track_index];
1009 /* Ensure it is moved from stacked -> expanded if appropriate */
1010 if (current_tv->view()->layer_display() == Stacked) {
1011 current_tv->view()->set_layer_display (Expanded);
1014 /* We're only allowed to go -ve in layer on Expanded views */
1015 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1016 this_delta_layer = - i->layer;
1020 rv->set_height (current_tv->view()->child_height ());
1022 /* Update show/hidden status as the region view may have come from a hidden track,
1023 or have moved to one.
1025 if (current_tv->hidden ()) {
1026 rv->get_canvas_group()->hide ();
1028 rv->get_canvas_group()->show ();
1031 /* Update the DraggingView */
1032 i->time_axis_view = track_index;
1033 i->layer += this_delta_layer;
1036 _editor->mouse_brush_insert_region (rv, pending_region_position);
1040 /* Get the y coordinate of the top of the track that this region is now over */
1041 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1043 /* And adjust for the layer that it should be on */
1044 StreamView* cv = current_tv->view ();
1045 switch (cv->layer_display ()) {
1049 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1052 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1056 /* need to get the parent of the regionview
1057 * canvas group and get its position in
1058 * equivalent coordinate space as the trackview
1059 * we are now dragging over.
1062 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1067 /* Now move the region view */
1068 rv->move (x_delta, y_delta);
1070 } /* foreach region */
1072 _total_x_delta += x_delta;
1074 if (x_delta != 0 && !_brushing) {
1075 show_verbose_cursor_time (_last_frame_position);
1080 /* the pointer is currently over a time axis view */
1082 if (_last_pointer_time_axis_view < 0) {
1084 /* last motion event was not over a time axis view */
1086 if (delta_time_axis_view < 0) {
1087 /* was in the drop zone, moving up */
1088 assert(current_pointer_time_axis_view >= 0);
1089 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1091 /* was in the drop zone, moving down ... not possible */
1096 /* last motion event was also over a time axis view */
1098 _last_pointer_time_axis_view += delta_time_axis_view;
1099 assert(_last_pointer_time_axis_view >= 0);
1104 /* the pointer is not over a time axis view */
1106 _last_pointer_time_axis_view = biggest_drop_zone_offset;
1109 _last_pointer_layer += delta_layer;
1113 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1115 if (_copy && first_move) {
1117 if (_x_constrained) {
1118 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1120 _editor->begin_reversible_command (Operations::region_copy);
1123 /* duplicate the regionview(s) and region(s) */
1125 list<DraggingView> new_regionviews;
1127 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1129 RegionView* rv = i->view;
1130 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1131 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1133 const boost::shared_ptr<const Region> original = rv->region();
1134 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1135 region_copy->set_position (original->position());
1136 /* need to set this so that the drop zone code can work. This doesn't
1137 actually put the region into the playlist, but just sets a weak pointer
1140 region_copy->set_playlist (original->playlist());
1144 boost::shared_ptr<AudioRegion> audioregion_copy
1145 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1147 nrv = new AudioRegionView (*arv, audioregion_copy);
1149 boost::shared_ptr<MidiRegion> midiregion_copy
1150 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1151 nrv = new MidiRegionView (*mrv, midiregion_copy);
1156 nrv->get_canvas_group()->show ();
1157 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1159 /* swap _primary to the copy */
1161 if (rv == _primary) {
1165 /* ..and deselect the one we copied */
1167 rv->set_selected (false);
1170 if (!new_regionviews.empty()) {
1172 /* reflect the fact that we are dragging the copies */
1174 _views = new_regionviews;
1176 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1179 } else if (!_copy && first_move) {
1181 if (_x_constrained) {
1182 _editor->begin_reversible_command (_("fixed time region drag"));
1184 _editor->begin_reversible_command (Operations::region_drag);
1188 RegionMotionDrag::motion (event, first_move);
1192 RegionMotionDrag::finished (GdkEvent *, bool)
1194 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1195 if (!(*i)->view()) {
1199 if ((*i)->view()->layer_display() == Expanded) {
1200 (*i)->view()->set_layer_display (Stacked);
1206 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1208 RegionMotionDrag::finished (ev, movement_occurred);
1210 if (!movement_occurred) {
1214 if (was_double_click() && !_views.empty()) {
1215 DraggingView dv = _views.front();
1216 dv.view->show_region_editor ();
1223 /* reverse this here so that we have the correct logic to finalize
1227 if (Config->get_edit_mode() == Lock) {
1228 _x_constrained = !_x_constrained;
1231 assert (!_views.empty ());
1233 /* We might have hidden region views so that they weren't visible during the drag
1234 (when they have been reparented). Now everything can be shown again, as region
1235 views are back in their track parent groups.
1237 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1238 i->view->get_canvas_group()->show ();
1241 bool const changed_position = (_last_frame_position != _primary->region()->position());
1242 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1243 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1263 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1267 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1269 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1274 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1275 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1276 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1277 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1279 rtav->set_height (original->current_height());
1283 ChanCount one_midi_port (DataType::MIDI, 1);
1284 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1285 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1286 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1288 rtav->set_height (original->current_height());
1293 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1299 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1301 RegionSelection new_views;
1302 PlaylistSet modified_playlists;
1303 RouteTimeAxisView* new_time_axis_view = 0;
1306 /* all changes were made during motion event handlers */
1308 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1312 _editor->commit_reversible_command ();
1316 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1317 PlaylistMapping playlist_mapping;
1319 /* insert the regions into their new playlists */
1320 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1322 RouteTimeAxisView* dest_rtv = 0;
1324 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1330 if (changed_position && !_x_constrained) {
1331 where = i->view->region()->position() - drag_delta;
1333 where = i->view->region()->position();
1336 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
1337 /* dragged to drop zone */
1339 PlaylistMapping::iterator pm;
1341 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1342 /* first region from this original playlist: create a new track */
1343 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1344 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1345 dest_rtv = new_time_axis_view;
1347 /* we already created a new track for regions from this playlist, use it */
1348 dest_rtv = pm->second;
1351 /* destination time axis view is the one we dragged to */
1352 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1355 if (dest_rtv != 0) {
1356 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1357 if (new_view != 0) {
1358 new_views.push_back (new_view);
1362 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1363 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1366 list<DraggingView>::const_iterator next = i;
1372 /* If we've created new regions either by copying or moving
1373 to a new track, we want to replace the old selection with the new ones
1376 if (new_views.size() > 0) {
1377 _editor->selection->set (new_views);
1380 /* write commands for the accumulated diffs for all our modified playlists */
1381 add_stateful_diff_commands_for_playlists (modified_playlists);
1383 _editor->commit_reversible_command ();
1387 RegionMoveDrag::finished_no_copy (
1388 bool const changed_position,
1389 bool const changed_tracks,
1390 framecnt_t const drag_delta
1393 RegionSelection new_views;
1394 PlaylistSet modified_playlists;
1395 PlaylistSet frozen_playlists;
1396 set<RouteTimeAxisView*> views_to_update;
1397 RouteTimeAxisView* new_time_axis_view = 0;
1400 /* all changes were made during motion event handlers */
1401 _editor->commit_reversible_command ();
1405 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1406 PlaylistMapping playlist_mapping;
1408 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1410 RegionView* rv = i->view;
1411 RouteTimeAxisView* dest_rtv = 0;
1413 if (rv->region()->locked() || rv->region()->video_locked()) {
1418 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
1419 /* dragged to drop zone */
1421 PlaylistMapping::iterator pm;
1423 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1424 /* first region from this original playlist: create a new track */
1425 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1426 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1427 dest_rtv = new_time_axis_view;
1429 /* we already created a new track for regions from this playlist, use it */
1430 dest_rtv = pm->second;
1434 /* destination time axis view is the one we dragged to */
1435 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1440 double const dest_layer = i->layer;
1442 views_to_update.insert (dest_rtv);
1446 if (changed_position && !_x_constrained) {
1447 where = rv->region()->position() - drag_delta;
1449 where = rv->region()->position();
1452 if (changed_tracks) {
1454 /* insert into new playlist */
1456 RegionView* new_view = insert_region_into_playlist (
1457 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1460 if (new_view == 0) {
1465 new_views.push_back (new_view);
1467 /* remove from old playlist */
1469 /* the region that used to be in the old playlist is not
1470 moved to the new one - we use a copy of it. as a result,
1471 any existing editor for the region should no longer be
1474 rv->hide_region_editor();
1477 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1481 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1483 /* this movement may result in a crossfade being modified, or a layering change,
1484 so we need to get undo data from the playlist as well as the region.
1487 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1489 playlist->clear_changes ();
1492 rv->region()->clear_changes ();
1495 motion on the same track. plonk the previously reparented region
1496 back to its original canvas group (its streamview).
1497 No need to do anything for copies as they are fake regions which will be deleted.
1500 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1501 rv->get_canvas_group()->set_y_position (i->initial_y);
1504 /* just change the model */
1505 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1506 playlist->set_layer (rv->region(), dest_layer);
1509 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1511 r = frozen_playlists.insert (playlist);
1514 playlist->freeze ();
1517 rv->region()->set_position (where);
1519 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1522 if (changed_tracks) {
1524 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1525 was selected in all of them, then removing it from a playlist will have removed all
1526 trace of it from _views (i.e. there were N regions selected, we removed 1,
1527 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1528 corresponding regionview, and _views is now empty).
1530 This could have invalidated any and all iterators into _views.
1532 The heuristic we use here is: if the region selection is empty, break out of the loop
1533 here. if the region selection is not empty, then restart the loop because we know that
1534 we must have removed at least the region(view) we've just been working on as well as any
1535 that we processed on previous iterations.
1537 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1538 we can just iterate.
1542 if (_views.empty()) {
1553 /* If we've created new regions either by copying or moving
1554 to a new track, we want to replace the old selection with the new ones
1557 if (new_views.size() > 0) {
1558 _editor->selection->set (new_views);
1561 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1565 /* write commands for the accumulated diffs for all our modified playlists */
1566 add_stateful_diff_commands_for_playlists (modified_playlists);
1568 _editor->commit_reversible_command ();
1570 /* We have futzed with the layering of canvas items on our streamviews.
1571 If any region changed layer, this will have resulted in the stream
1572 views being asked to set up their region views, and all will be well.
1573 If not, we might now have badly-ordered region views. Ask the StreamViews
1574 involved to sort themselves out, just in case.
1577 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1578 (*i)->view()->playlist_layered ((*i)->track ());
1582 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1583 * @param region Region to remove.
1584 * @param playlist playlist To remove from.
1585 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1586 * that clear_changes () is only called once per playlist.
1589 RegionMoveDrag::remove_region_from_playlist (
1590 boost::shared_ptr<Region> region,
1591 boost::shared_ptr<Playlist> playlist,
1592 PlaylistSet& modified_playlists
1595 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1598 playlist->clear_changes ();
1601 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1605 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1606 * clearing the playlist's diff history first if necessary.
1607 * @param region Region to insert.
1608 * @param dest_rtv Destination RouteTimeAxisView.
1609 * @param dest_layer Destination layer.
1610 * @param where Destination position.
1611 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1612 * that clear_changes () is only called once per playlist.
1613 * @return New RegionView, or 0 if no insert was performed.
1616 RegionMoveDrag::insert_region_into_playlist (
1617 boost::shared_ptr<Region> region,
1618 RouteTimeAxisView* dest_rtv,
1621 PlaylistSet& modified_playlists
1624 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1625 if (!dest_playlist) {
1629 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1630 _new_region_view = 0;
1631 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1633 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1634 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1636 dest_playlist->clear_changes ();
1639 dest_playlist->add_region (region, where);
1641 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1642 dest_playlist->set_layer (region, dest_layer);
1647 assert (_new_region_view);
1649 return _new_region_view;
1653 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1655 _new_region_view = rv;
1659 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1661 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1662 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1664 _editor->session()->add_command (c);
1673 RegionMoveDrag::aborted (bool movement_occurred)
1677 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1678 list<DraggingView>::const_iterator next = i;
1687 RegionMotionDrag::aborted (movement_occurred);
1692 RegionMotionDrag::aborted (bool)
1694 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1696 StreamView* sview = (*i)->view();
1699 if (sview->layer_display() == Expanded) {
1700 sview->set_layer_display (Stacked);
1705 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1706 RegionView* rv = i->view;
1707 TimeAxisView* tv = &(rv->get_time_axis_view ());
1708 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1710 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1711 rv->get_canvas_group()->set_y_position (0);
1713 rv->move (-_total_x_delta, 0);
1714 rv->set_height (rtv->view()->child_height ());
1718 /** @param b true to brush, otherwise false.
1719 * @param c true to make copies of the regions being moved, otherwise false.
1721 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1722 : RegionMotionDrag (e, i, p, v, b)
1725 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1728 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1729 if (rtv && rtv->is_track()) {
1730 speed = rtv->track()->speed ();
1733 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1737 RegionMoveDrag::setup_pointer_frame_offset ()
1739 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1742 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1743 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1745 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1747 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1748 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1750 _primary = v->view()->create_region_view (r, false, false);
1752 _primary->get_canvas_group()->show ();
1753 _primary->set_position (pos, 0);
1754 _views.push_back (DraggingView (_primary, this, v));
1756 _last_frame_position = pos;
1758 _item = _primary->get_canvas_group ();
1762 RegionInsertDrag::finished (GdkEvent *, bool)
1764 int pos = _views.front().time_axis_view;
1765 assert(pos >= 0 && pos < _time_axis_views.size());
1767 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1769 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1770 _primary->get_canvas_group()->set_y_position (0);
1772 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1774 _editor->begin_reversible_command (Operations::insert_region);
1775 playlist->clear_changes ();
1776 playlist->add_region (_primary->region (), _last_frame_position);
1778 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1779 if (Config->get_edit_mode() == Ripple) {
1780 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1783 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1784 _editor->commit_reversible_command ();
1792 RegionInsertDrag::aborted (bool)
1799 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1800 : RegionMoveDrag (e, i, p, v, false, false)
1802 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1805 struct RegionSelectionByPosition {
1806 bool operator() (RegionView*a, RegionView* b) {
1807 return a->region()->position () < b->region()->position();
1812 RegionSpliceDrag::motion (GdkEvent* event, bool)
1814 /* Which trackview is this ? */
1816 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1817 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1819 /* The region motion is only processed if the pointer is over
1823 if (!tv || !tv->is_track()) {
1824 /* To make sure we hide the verbose canvas cursor when the mouse is
1825 not held over an audio track.
1827 _editor->verbose_cursor()->hide ();
1830 _editor->verbose_cursor()->show ();
1835 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1841 RegionSelection copy;
1842 _editor->selection->regions.by_position(copy);
1844 framepos_t const pf = adjusted_current_frame (event);
1846 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1848 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1854 boost::shared_ptr<Playlist> playlist;
1856 if ((playlist = atv->playlist()) == 0) {
1860 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1865 if (pf < (*i)->region()->last_frame() + 1) {
1869 if (pf > (*i)->region()->first_frame()) {
1875 playlist->shuffle ((*i)->region(), dir);
1880 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1882 RegionMoveDrag::finished (event, movement_occurred);
1886 RegionSpliceDrag::aborted (bool)
1896 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1899 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1901 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1902 RegionSelection to_ripple;
1903 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1904 if ((*i)->position() >= where) {
1905 to_ripple.push_back (rtv->view()->find_view(*i));
1909 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1910 if (!exclude.contains (*i)) {
1911 // the selection has already been added to _views
1913 if (drag_in_progress) {
1914 // do the same things that RegionMotionDrag::motion does when
1915 // first_move is true, for the region views that we're adding
1916 // to _views this time
1919 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1920 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1921 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1922 rvg->reparent (_editor->_drag_motion_group);
1924 // we only need to move in the y direction
1925 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1930 _views.push_back (DraggingView (*i, this, tav));
1936 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1939 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1940 // we added all the regions after the selection
1942 std::list<DraggingView>::iterator to_erase = i++;
1943 if (!_editor->selection->regions.contains (to_erase->view)) {
1944 // restore the non-selected regions to their original playlist & positions,
1945 // and then ripple them back by the length of the regions that were dragged away
1946 // do the same things as RegionMotionDrag::aborted
1948 RegionView *rv = to_erase->view;
1949 TimeAxisView* tv = &(rv->get_time_axis_view ());
1950 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1953 // plonk them back onto their own track
1954 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1955 rv->get_canvas_group()->set_y_position (0);
1959 // move the underlying region to match the view
1960 rv->region()->set_position (rv->region()->position() + amount);
1962 // restore the view to match the underlying region's original position
1963 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1966 rv->set_height (rtv->view()->child_height ());
1967 _views.erase (to_erase);
1973 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
1975 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
1977 return allow_moves_across_tracks;
1985 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1986 : RegionMoveDrag (e, i, p, v, false, false)
1988 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1989 // compute length of selection
1990 RegionSelection selected_regions = _editor->selection->regions;
1991 selection_length = selected_regions.end_frame() - selected_regions.start();
1993 // we'll only allow dragging to another track in ripple mode if all the regions
1994 // being dragged start off on the same track
1995 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1998 exclude = new RegionList;
1999 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2000 exclude->push_back((*i)->region());
2003 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2004 RegionSelection copy;
2005 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2007 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2008 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2010 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2011 // find ripple start point on each applicable playlist
2012 RegionView *first_selected_on_this_track = NULL;
2013 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2014 if ((*i)->region()->playlist() == (*pi)) {
2015 // region is on this playlist - it's the first, because they're sorted
2016 first_selected_on_this_track = *i;
2020 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2021 add_all_after_to_views (
2022 &first_selected_on_this_track->get_time_axis_view(),
2023 first_selected_on_this_track->region()->position(),
2024 selected_regions, false);
2027 if (allow_moves_across_tracks) {
2028 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2036 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2038 /* Which trackview is this ? */
2040 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2041 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2043 /* The region motion is only processed if the pointer is over
2047 if (!tv || !tv->is_track()) {
2048 /* To make sure we hide the verbose canvas cursor when the mouse is
2049 not held over an audiotrack.
2051 _editor->verbose_cursor()->hide ();
2055 framepos_t where = adjusted_current_frame (event);
2056 assert (where >= 0);
2058 double delta = compute_x_delta (event, &after);
2060 framecnt_t amount = _editor->pixel_to_sample (delta);
2062 if (allow_moves_across_tracks) {
2063 // all the originally selected regions were on the same track
2065 framecnt_t adjust = 0;
2066 if (prev_tav && tv != prev_tav) {
2067 // dragged onto a different track
2068 // remove the unselected regions from _views, restore them to their original positions
2069 // and add the regions after the drop point on the new playlist to _views instead.
2070 // undo the effect of rippling the previous playlist, and include the effect of removing
2071 // the dragged region(s) from this track
2073 remove_unselected_from_views (prev_amount, false);
2074 // ripple previous playlist according to the regions that have been removed onto the new playlist
2075 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2078 // move just the selected regions
2079 RegionMoveDrag::motion(event, first_move);
2081 // ensure that the ripple operation on the new playlist inserts selection_length time
2082 adjust = selection_length;
2083 // ripple the new current playlist
2084 tv->playlist()->ripple (where, amount+adjust, exclude);
2086 // add regions after point where drag entered this track to subsequent ripples
2087 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2090 // motion on same track
2091 RegionMoveDrag::motion(event, first_move);
2095 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2096 prev_position = where;
2098 // selection encompasses multiple tracks - just drag
2099 // cross-track drags are forbidden
2100 RegionMoveDrag::motion(event, first_move);
2103 if (!_x_constrained) {
2104 prev_amount += amount;
2107 _last_frame_position = after;
2111 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2113 if (!movement_occurred) {
2117 if (was_double_click() && !_views.empty()) {
2118 DraggingView dv = _views.front();
2119 dv.view->show_region_editor ();
2126 _editor->begin_reversible_command(_("Ripple drag"));
2128 // remove the regions being rippled from the dragging view, updating them to
2129 // their new positions
2130 remove_unselected_from_views (prev_amount, true);
2132 if (allow_moves_across_tracks) {
2134 // if regions were dragged across tracks, we've rippled any later
2135 // regions on the track the regions were dragged off, so we need
2136 // to add the original track to the undo record
2137 orig_tav->playlist()->clear_changes();
2138 vector<Command*> cmds;
2139 orig_tav->playlist()->rdiff (cmds);
2140 _editor->session()->add_commands (cmds);
2142 if (prev_tav && prev_tav != orig_tav) {
2143 prev_tav->playlist()->clear_changes();
2144 vector<Command*> cmds;
2145 prev_tav->playlist()->rdiff (cmds);
2146 _editor->session()->add_commands (cmds);
2149 // selection spanned multiple tracks - all will need adding to undo record
2151 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2152 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2154 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2155 (*pi)->clear_changes();
2156 vector<Command*> cmds;
2157 (*pi)->rdiff (cmds);
2158 _editor->session()->add_commands (cmds);
2162 // other modified playlists are added to undo by RegionMoveDrag::finished()
2163 RegionMoveDrag::finished (event, movement_occurred);
2164 _editor->commit_reversible_command();
2168 RegionRippleDrag::aborted (bool movement_occurred)
2170 RegionMoveDrag::aborted (movement_occurred);
2175 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2177 _view (dynamic_cast<MidiTimeAxisView*> (v))
2179 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2185 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2188 _region = add_midi_region (_view);
2189 _view->playlist()->freeze ();
2192 framepos_t const f = adjusted_current_frame (event);
2193 if (f < grab_frame()) {
2194 _region->set_position (f);
2197 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2198 so that if this region is duplicated, its duplicate starts on
2199 a snap point rather than 1 frame after a snap point. Otherwise things get
2200 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2201 place snapped notes at the start of the region.
2204 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2205 _region->set_length (len < 1 ? 1 : len);
2211 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2213 if (!movement_occurred) {
2214 add_midi_region (_view);
2216 _view->playlist()->thaw ();
2221 RegionCreateDrag::aborted (bool)
2224 _view->playlist()->thaw ();
2230 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2234 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2238 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2240 Gdk::Cursor* cursor;
2241 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2243 float x_fraction = cnote->mouse_x_fraction ();
2245 if (x_fraction > 0.0 && x_fraction < 0.25) {
2246 cursor = _editor->cursors()->left_side_trim;
2249 cursor = _editor->cursors()->right_side_trim;
2253 Drag::start_grab (event, cursor);
2255 region = &cnote->region_view();
2259 if (event->motion.state & Keyboard::PrimaryModifier) {
2265 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2267 if (ms.size() > 1) {
2268 /* has to be relative, may make no sense otherwise */
2272 /* select this note; if it is already selected, preserve the existing selection,
2273 otherwise make this note the only one selected.
2275 region->note_selected (cnote, cnote->selected ());
2277 _editor->begin_reversible_command (_("resize notes"));
2279 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2280 MidiRegionSelection::iterator next;
2283 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2285 mrv->begin_resizing (at_front);
2292 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2294 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2295 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2296 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2298 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2300 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2306 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2308 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2309 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2310 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2312 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2314 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2318 _editor->commit_reversible_command ();
2322 NoteResizeDrag::aborted (bool)
2324 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2325 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2326 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2328 mrv->abort_resizing ();
2333 AVDraggingView::AVDraggingView (RegionView* v)
2336 initial_position = v->region()->position ();
2339 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2342 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2345 TrackViewList empty;
2347 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2348 std::list<RegionView*> views = rs.by_layer();
2350 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2351 RegionView* rv = (*i);
2352 if (!rv->region()->video_locked()) {
2355 _views.push_back (AVDraggingView (rv));
2360 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2362 Drag::start_grab (event);
2363 if (_editor->session() == 0) {
2367 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2368 _max_backwards_drag = (
2369 ARDOUR_UI::instance()->video_timeline->get_duration()
2370 + ARDOUR_UI::instance()->video_timeline->get_offset()
2371 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2374 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2375 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2376 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2379 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2382 Timecode::Time timecode;
2383 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2384 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);
2385 show_verbose_cursor_text (buf);
2389 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2391 if (_editor->session() == 0) {
2394 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2398 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2399 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2401 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2402 dt = - _max_backwards_drag;
2405 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2406 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2408 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2409 RegionView* rv = i->view;
2410 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2413 rv->region()->clear_changes ();
2414 rv->region()->suspend_property_changes();
2416 rv->region()->set_position(i->initial_position + dt);
2417 rv->region_changed(ARDOUR::Properties::position);
2420 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2421 Timecode::Time timecode;
2422 Timecode::Time timediff;
2424 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2425 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2426 snprintf (buf, sizeof (buf),
2427 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2428 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2429 , _("Video Start:"),
2430 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2432 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2434 show_verbose_cursor_text (buf);
2438 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2440 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2444 if (!movement_occurred || ! _editor->session()) {
2448 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2450 _editor->begin_reversible_command (_("Move Video"));
2452 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2453 ARDOUR_UI::instance()->video_timeline->save_undo();
2454 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2455 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2457 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2458 i->view->drag_end();
2459 i->view->region()->resume_property_changes ();
2461 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2464 _editor->session()->maybe_update_session_range(
2465 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2466 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2470 _editor->commit_reversible_command ();
2474 VideoTimeLineDrag::aborted (bool)
2476 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2479 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2480 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2482 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2483 i->view->region()->resume_property_changes ();
2484 i->view->region()->set_position(i->initial_position);
2488 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2489 : RegionDrag (e, i, p, v)
2490 , _preserve_fade_anchor (preserve_fade_anchor)
2491 , _jump_position_when_done (false)
2493 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2497 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2500 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2501 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2503 if (tv && tv->is_track()) {
2504 speed = tv->track()->speed();
2507 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2508 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2509 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2511 framepos_t const pf = adjusted_current_frame (event);
2513 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2514 /* Move the contents of the region around without changing the region bounds */
2515 _operation = ContentsTrim;
2516 Drag::start_grab (event, _editor->cursors()->trimmer);
2518 /* These will get overridden for a point trim.*/
2519 if (pf < (region_start + region_length/2)) {
2520 /* closer to front */
2521 _operation = StartTrim;
2523 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2524 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2526 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2530 _operation = EndTrim;
2531 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2532 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2534 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2539 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2540 _jump_position_when_done = true;
2543 switch (_operation) {
2545 show_verbose_cursor_time (region_start);
2546 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2547 i->view->trim_front_starting ();
2551 show_verbose_cursor_time (region_end);
2554 show_verbose_cursor_time (pf);
2558 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2559 i->view->region()->suspend_property_changes ();
2564 TrimDrag::motion (GdkEvent* event, bool first_move)
2566 RegionView* rv = _primary;
2569 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2570 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2571 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2572 frameoffset_t frame_delta = 0;
2574 if (tv && tv->is_track()) {
2575 speed = tv->track()->speed();
2578 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2584 switch (_operation) {
2586 trim_type = "Region start trim";
2589 trim_type = "Region end trim";
2592 trim_type = "Region content trim";
2599 _editor->begin_reversible_command (trim_type);
2601 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2602 RegionView* rv = i->view;
2603 rv->enable_display (false);
2604 rv->region()->playlist()->clear_owned_changes ();
2606 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2609 arv->temporarily_hide_envelope ();
2613 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2614 insert_result = _editor->motion_frozen_playlists.insert (pl);
2616 if (insert_result.second) {
2622 bool non_overlap_trim = false;
2624 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2625 non_overlap_trim = true;
2628 /* contstrain trim to fade length */
2629 if (_preserve_fade_anchor) {
2630 switch (_operation) {
2632 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2633 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2635 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2636 if (ar->locked()) continue;
2637 framecnt_t len = ar->fade_in()->back()->when;
2638 if (len < dt) dt = min(dt, len);
2642 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2643 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2645 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2646 if (ar->locked()) continue;
2647 framecnt_t len = ar->fade_out()->back()->when;
2648 if (len < -dt) dt = max(dt, -len);
2657 switch (_operation) {
2659 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2660 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2661 if (changed && _preserve_fade_anchor) {
2662 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2664 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2665 framecnt_t len = ar->fade_in()->back()->when;
2666 framecnt_t diff = ar->first_frame() - i->initial_position;
2667 framepos_t new_length = len - diff;
2668 i->anchored_fade_length = min (ar->length(), new_length);
2669 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2670 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2677 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2678 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2679 if (changed && _preserve_fade_anchor) {
2680 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2682 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2683 framecnt_t len = ar->fade_out()->back()->when;
2684 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2685 framepos_t new_length = len + diff;
2686 i->anchored_fade_length = min (ar->length(), new_length);
2687 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2688 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2696 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2698 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2699 i->view->move_contents (frame_delta);
2705 switch (_operation) {
2707 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2710 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2713 // show_verbose_cursor_time (frame_delta);
2720 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2722 if (movement_occurred) {
2723 motion (event, false);
2725 if (_operation == StartTrim) {
2726 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2728 /* This must happen before the region's StatefulDiffCommand is created, as it may
2729 `correct' (ahem) the region's _start from being negative to being zero. It
2730 needs to be zero in the undo record.
2732 i->view->trim_front_ending ();
2734 if (_preserve_fade_anchor) {
2735 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2737 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2738 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2739 ar->set_fade_in_length(i->anchored_fade_length);
2740 ar->set_fade_in_active(true);
2743 if (_jump_position_when_done) {
2744 i->view->region()->set_position (i->initial_position);
2747 } else if (_operation == EndTrim) {
2748 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2749 if (_preserve_fade_anchor) {
2750 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2752 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2753 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2754 ar->set_fade_out_length(i->anchored_fade_length);
2755 ar->set_fade_out_active(true);
2758 if (_jump_position_when_done) {
2759 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2764 if (!_views.empty()) {
2765 if (_operation == StartTrim) {
2766 _editor->maybe_locate_with_edit_preroll(
2767 _views.begin()->view->region()->position());
2769 if (_operation == EndTrim) {
2770 _editor->maybe_locate_with_edit_preroll(
2771 _views.begin()->view->region()->position() +
2772 _views.begin()->view->region()->length());
2776 if (!_editor->selection->selected (_primary)) {
2777 _primary->thaw_after_trim ();
2780 set<boost::shared_ptr<Playlist> > diffed_playlists;
2782 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2783 i->view->thaw_after_trim ();
2784 i->view->enable_display (true);
2786 /* Trimming one region may affect others on the playlist, so we need
2787 to get undo Commands from the whole playlist rather than just the
2788 region. Use diffed_playlists to make sure we don't diff a given
2789 playlist more than once.
2791 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2792 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2793 vector<Command*> cmds;
2795 _editor->session()->add_commands (cmds);
2796 diffed_playlists.insert (p);
2801 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2805 _editor->motion_frozen_playlists.clear ();
2806 _editor->commit_reversible_command();
2809 /* no mouse movement */
2810 _editor->point_trim (event, adjusted_current_frame (event));
2813 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2814 if (_operation == StartTrim) {
2815 i->view->trim_front_ending ();
2818 i->view->region()->resume_property_changes ();
2823 TrimDrag::aborted (bool movement_occurred)
2825 /* Our motion method is changing model state, so use the Undo system
2826 to cancel. Perhaps not ideal, as this will leave an Undo point
2827 behind which may be slightly odd from the user's point of view.
2832 if (movement_occurred) {
2836 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2837 i->view->region()->resume_property_changes ();
2842 TrimDrag::setup_pointer_frame_offset ()
2844 list<DraggingView>::iterator i = _views.begin ();
2845 while (i != _views.end() && i->view != _primary) {
2849 if (i == _views.end()) {
2853 switch (_operation) {
2855 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2858 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2865 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2869 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2870 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2875 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2877 Drag::start_grab (event, cursor);
2878 show_verbose_cursor_time (adjusted_current_frame(event));
2882 MeterMarkerDrag::setup_pointer_frame_offset ()
2884 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2888 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2890 if (!_marker->meter().movable()) {
2896 // create a dummy marker for visual representation of moving the
2897 // section, because whether its a copy or not, we're going to
2898 // leave or lose the original marker (leave if its a copy; lose if its
2899 // not, because we'll remove it from the map).
2901 MeterSection section (_marker->meter());
2903 if (!section.movable()) {
2908 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2910 _marker = new MeterMarker (
2912 *_editor->meter_group,
2913 ARDOUR_UI::config()->color ("meter marker"),
2915 *new MeterSection (_marker->meter())
2918 /* use the new marker for the grab */
2919 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2922 TempoMap& map (_editor->session()->tempo_map());
2923 /* get current state */
2924 before_state = &map.get_state();
2925 /* remove the section while we drag it */
2926 map.remove_meter (section, true);
2930 framepos_t const pf = adjusted_current_frame (event);
2932 _marker->set_position (pf);
2933 show_verbose_cursor_time (pf);
2937 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2939 if (!movement_occurred) {
2940 if (was_double_click()) {
2941 _editor->edit_meter_marker (*_marker);
2946 if (!_marker->meter().movable()) {
2950 motion (event, false);
2952 Timecode::BBT_Time when;
2954 TempoMap& map (_editor->session()->tempo_map());
2955 map.bbt_time (last_pointer_frame(), when);
2957 if (_copy == true) {
2958 _editor->begin_reversible_command (_("copy meter mark"));
2959 XMLNode &before = map.get_state();
2960 map.add_meter (_marker->meter(), when);
2961 XMLNode &after = map.get_state();
2962 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2963 _editor->commit_reversible_command ();
2966 _editor->begin_reversible_command (_("move meter mark"));
2968 /* we removed it before, so add it back now */
2970 map.add_meter (_marker->meter(), when);
2971 XMLNode &after = map.get_state();
2972 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2973 _editor->commit_reversible_command ();
2976 // delete the dummy marker we used for visual representation while moving.
2977 // a new visual marker will show up automatically.
2982 MeterMarkerDrag::aborted (bool moved)
2984 _marker->set_position (_marker->meter().frame ());
2987 TempoMap& map (_editor->session()->tempo_map());
2988 /* we removed it before, so add it back now */
2989 map.add_meter (_marker->meter(), _marker->meter().frame());
2990 // delete the dummy marker we used for visual representation while moving.
2991 // a new visual marker will show up automatically.
2996 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3000 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3002 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3007 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3009 Drag::start_grab (event, cursor);
3010 show_verbose_cursor_time (adjusted_current_frame (event));
3014 TempoMarkerDrag::setup_pointer_frame_offset ()
3016 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3020 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3022 if (!_marker->tempo().movable()) {
3028 // create a dummy marker for visual representation of moving the
3029 // section, because whether its a copy or not, we're going to
3030 // leave or lose the original marker (leave if its a copy; lose if its
3031 // not, because we'll remove it from the map).
3033 // create a dummy marker for visual representation of moving the copy.
3034 // The actual copying is not done before we reach the finish callback.
3037 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3039 TempoSection section (_marker->tempo());
3041 _marker = new TempoMarker (
3043 *_editor->tempo_group,
3044 ARDOUR_UI::config()->color ("tempo marker"),
3046 *new TempoSection (_marker->tempo())
3049 /* use the new marker for the grab */
3050 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3053 TempoMap& map (_editor->session()->tempo_map());
3054 /* get current state */
3055 before_state = &map.get_state();
3056 /* remove the section while we drag it */
3057 map.remove_tempo (section, true);
3061 framepos_t const pf = adjusted_current_frame (event);
3062 _marker->set_position (pf);
3063 show_verbose_cursor_time (pf);
3067 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3069 if (!movement_occurred) {
3070 if (was_double_click()) {
3071 _editor->edit_tempo_marker (*_marker);
3076 if (!_marker->tempo().movable()) {
3080 motion (event, false);
3082 TempoMap& map (_editor->session()->tempo_map());
3083 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3084 Timecode::BBT_Time when;
3086 map.bbt_time (beat_time, when);
3088 if (_copy == true) {
3089 _editor->begin_reversible_command (_("copy tempo mark"));
3090 XMLNode &before = map.get_state();
3091 map.add_tempo (_marker->tempo(), when);
3092 XMLNode &after = map.get_state();
3093 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3094 _editor->commit_reversible_command ();
3097 _editor->begin_reversible_command (_("move tempo mark"));
3098 /* we removed it before, so add it back now */
3099 map.add_tempo (_marker->tempo(), when);
3100 XMLNode &after = map.get_state();
3101 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3102 _editor->commit_reversible_command ();
3105 // delete the dummy marker we used for visual representation while moving.
3106 // a new visual marker will show up automatically.
3111 TempoMarkerDrag::aborted (bool moved)
3113 _marker->set_position (_marker->tempo().frame());
3115 TempoMap& map (_editor->session()->tempo_map());
3116 /* we removed it before, so add it back now */
3117 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3118 // delete the dummy marker we used for visual representation while moving.
3119 // a new visual marker will show up automatically.
3124 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3125 : Drag (e, &c.track_canvas_item(), false)
3129 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3132 /** Do all the things we do when dragging the playhead to make it look as though
3133 * we have located, without actually doing the locate (because that would cause
3134 * the diskstream buffers to be refilled, which is too slow).
3137 CursorDrag::fake_locate (framepos_t t)
3139 _editor->playhead_cursor->set_position (t);
3141 Session* s = _editor->session ();
3142 if (s->timecode_transmission_suspended ()) {
3143 framepos_t const f = _editor->playhead_cursor->current_frame ();
3144 /* This is asynchronous so it will be sent "now"
3146 s->send_mmc_locate (f);
3147 /* These are synchronous and will be sent during the next
3150 s->queue_full_time_code ();
3151 s->queue_song_position_pointer ();
3154 show_verbose_cursor_time (t);
3155 _editor->UpdateAllTransportClocks (t);
3159 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3161 Drag::start_grab (event, c);
3163 _grab_zoom = _editor->samples_per_pixel;
3165 framepos_t where = _editor->canvas_event_sample (event);
3167 _editor->snap_to_with_modifier (where, event);
3169 _editor->_dragging_playhead = true;
3171 Session* s = _editor->session ();
3173 /* grab the track canvas item as well */
3175 _cursor.track_canvas_item().grab();
3178 if (_was_rolling && _stop) {
3182 if (s->is_auditioning()) {
3183 s->cancel_audition ();
3187 if (AudioEngine::instance()->connected()) {
3189 /* do this only if we're the engine is connected
3190 * because otherwise this request will never be
3191 * serviced and we'll busy wait forever. likewise,
3192 * notice if we are disconnected while waiting for the
3193 * request to be serviced.
3196 s->request_suspend_timecode_transmission ();
3197 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3198 /* twiddle our thumbs */
3203 fake_locate (where);
3207 CursorDrag::motion (GdkEvent* event, bool)
3209 framepos_t const adjusted_frame = adjusted_current_frame (event);
3210 if (adjusted_frame != last_pointer_frame()) {
3211 fake_locate (adjusted_frame);
3216 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3218 _editor->_dragging_playhead = false;
3220 _cursor.track_canvas_item().ungrab();
3222 if (!movement_occurred && _stop) {
3226 motion (event, false);
3228 Session* s = _editor->session ();
3230 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3231 _editor->_pending_locate_request = true;
3232 s->request_resume_timecode_transmission ();
3237 CursorDrag::aborted (bool)
3239 _cursor.track_canvas_item().ungrab();
3241 if (_editor->_dragging_playhead) {
3242 _editor->session()->request_resume_timecode_transmission ();
3243 _editor->_dragging_playhead = false;
3246 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3249 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3250 : RegionDrag (e, i, p, v)
3252 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3256 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3258 Drag::start_grab (event, cursor);
3260 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3261 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3263 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3267 FadeInDrag::setup_pointer_frame_offset ()
3269 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3270 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3271 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3275 FadeInDrag::motion (GdkEvent* event, bool)
3277 framecnt_t fade_length;
3278 framepos_t const pos = adjusted_current_frame (event);
3279 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3281 if (pos < (region->position() + 64)) {
3282 fade_length = 64; // this should be a minimum defined somewhere
3283 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3284 fade_length = region->length() - region->fade_out()->back()->when - 1;
3286 fade_length = pos - region->position();
3289 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3291 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3297 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3300 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3304 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3306 if (!movement_occurred) {
3310 framecnt_t fade_length;
3312 framepos_t const pos = adjusted_current_frame (event);
3314 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3316 if (pos < (region->position() + 64)) {
3317 fade_length = 64; // this should be a minimum defined somewhere
3318 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3319 fade_length = region->length() - region->fade_out()->back()->when - 1;
3321 fade_length = pos - region->position();
3324 _editor->begin_reversible_command (_("change fade in length"));
3326 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3328 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3334 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3335 XMLNode &before = alist->get_state();
3337 tmp->audio_region()->set_fade_in_length (fade_length);
3338 tmp->audio_region()->set_fade_in_active (true);
3340 XMLNode &after = alist->get_state();
3341 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3344 _editor->commit_reversible_command ();
3348 FadeInDrag::aborted (bool)
3350 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3351 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3357 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3361 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3362 : RegionDrag (e, i, p, v)
3364 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3368 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3370 Drag::start_grab (event, cursor);
3372 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3373 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3375 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3379 FadeOutDrag::setup_pointer_frame_offset ()
3381 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3382 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3383 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3387 FadeOutDrag::motion (GdkEvent* event, bool)
3389 framecnt_t fade_length;
3391 framepos_t const pos = adjusted_current_frame (event);
3393 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3395 if (pos > (region->last_frame() - 64)) {
3396 fade_length = 64; // this should really be a minimum fade defined somewhere
3397 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3398 fade_length = region->length() - region->fade_in()->back()->when - 1;
3400 fade_length = region->last_frame() - pos;
3403 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3405 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3411 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3414 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3418 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3420 if (!movement_occurred) {
3424 framecnt_t fade_length;
3426 framepos_t const pos = adjusted_current_frame (event);
3428 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3430 if (pos > (region->last_frame() - 64)) {
3431 fade_length = 64; // this should really be a minimum fade defined somewhere
3432 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3433 fade_length = region->length() - region->fade_in()->back()->when - 1;
3435 fade_length = region->last_frame() - pos;
3438 _editor->begin_reversible_command (_("change fade out length"));
3440 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3442 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3448 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3449 XMLNode &before = alist->get_state();
3451 tmp->audio_region()->set_fade_out_length (fade_length);
3452 tmp->audio_region()->set_fade_out_active (true);
3454 XMLNode &after = alist->get_state();
3455 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3458 _editor->commit_reversible_command ();
3462 FadeOutDrag::aborted (bool)
3464 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3465 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3471 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3475 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3478 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3480 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3483 _points.push_back (ArdourCanvas::Duple (0, 0));
3484 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3487 MarkerDrag::~MarkerDrag ()
3489 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3494 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3496 location = new Location (*l);
3497 markers.push_back (m);
3502 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3504 Drag::start_grab (event, cursor);
3508 Location *location = _editor->find_location_from_marker (_marker, is_start);
3509 _editor->_dragging_edit_point = true;
3511 update_item (location);
3513 // _drag_line->show();
3514 // _line->raise_to_top();
3517 show_verbose_cursor_time (location->start());
3519 show_verbose_cursor_time (location->end());
3522 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3525 case Selection::Toggle:
3526 /* we toggle on the button release */
3528 case Selection::Set:
3529 if (!_editor->selection->selected (_marker)) {
3530 _editor->selection->set (_marker);
3533 case Selection::Extend:
3535 Locations::LocationList ll;
3536 list<Marker*> to_add;
3538 _editor->selection->markers.range (s, e);
3539 s = min (_marker->position(), s);
3540 e = max (_marker->position(), e);
3543 if (e < max_framepos) {
3546 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3547 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3548 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3551 to_add.push_back (lm->start);
3554 to_add.push_back (lm->end);
3558 if (!to_add.empty()) {
3559 _editor->selection->add (to_add);
3563 case Selection::Add:
3564 _editor->selection->add (_marker);
3568 /* Set up copies for us to manipulate during the drag
3571 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3573 Location* l = _editor->find_location_from_marker (*i, is_start);
3580 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3582 /* range: check that the other end of the range isn't
3585 CopiedLocationInfo::iterator x;
3586 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3587 if (*(*x).location == *l) {
3591 if (x == _copied_locations.end()) {
3592 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3594 (*x).markers.push_back (*i);
3595 (*x).move_both = true;
3603 MarkerDrag::setup_pointer_frame_offset ()
3606 Location *location = _editor->find_location_from_marker (_marker, is_start);
3607 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3611 MarkerDrag::motion (GdkEvent* event, bool)
3613 framecnt_t f_delta = 0;
3615 bool move_both = false;
3616 Location *real_location;
3617 Location *copy_location = 0;
3619 framepos_t const newframe = adjusted_current_frame (event);
3620 framepos_t next = newframe;
3622 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3626 CopiedLocationInfo::iterator x;
3628 /* find the marker we're dragging, and compute the delta */
3630 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3632 copy_location = (*x).location;
3634 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3636 /* this marker is represented by this
3637 * CopiedLocationMarkerInfo
3640 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3645 if (real_location->is_mark()) {
3646 f_delta = newframe - copy_location->start();
3650 switch (_marker->type()) {
3651 case Marker::SessionStart:
3652 case Marker::RangeStart:
3653 case Marker::LoopStart:
3654 case Marker::PunchIn:
3655 f_delta = newframe - copy_location->start();
3658 case Marker::SessionEnd:
3659 case Marker::RangeEnd:
3660 case Marker::LoopEnd:
3661 case Marker::PunchOut:
3662 f_delta = newframe - copy_location->end();
3665 /* what kind of marker is this ? */
3674 if (x == _copied_locations.end()) {
3675 /* hmm, impossible - we didn't find the dragged marker */
3679 /* now move them all */
3681 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3683 copy_location = x->location;
3685 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3689 if (real_location->locked()) {
3693 if (copy_location->is_mark()) {
3697 copy_location->set_start (copy_location->start() + f_delta);
3701 framepos_t new_start = copy_location->start() + f_delta;
3702 framepos_t new_end = copy_location->end() + f_delta;
3704 if (is_start) { // start-of-range marker
3706 if (move_both || (*x).move_both) {
3707 copy_location->set_start (new_start);
3708 copy_location->set_end (new_end);
3709 } else if (new_start < copy_location->end()) {
3710 copy_location->set_start (new_start);
3711 } else if (newframe > 0) {
3712 _editor->snap_to (next, RoundUpAlways, true);
3713 copy_location->set_end (next);
3714 copy_location->set_start (newframe);
3717 } else { // end marker
3719 if (move_both || (*x).move_both) {
3720 copy_location->set_end (new_end);
3721 copy_location->set_start (new_start);
3722 } else if (new_end > copy_location->start()) {
3723 copy_location->set_end (new_end);
3724 } else if (newframe > 0) {
3725 _editor->snap_to (next, RoundDownAlways, true);
3726 copy_location->set_start (next);
3727 copy_location->set_end (newframe);
3732 update_item (copy_location);
3734 /* now lookup the actual GUI items used to display this
3735 * location and move them to wherever the copy of the location
3736 * is now. This means that the logic in ARDOUR::Location is
3737 * still enforced, even though we are not (yet) modifying
3738 * the real Location itself.
3741 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3744 lm->set_position (copy_location->start(), copy_location->end());
3749 assert (!_copied_locations.empty());
3751 show_verbose_cursor_time (newframe);
3755 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3757 if (!movement_occurred) {
3759 if (was_double_click()) {
3760 _editor->rename_marker (_marker);
3764 /* just a click, do nothing but finish
3765 off the selection process
3768 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3771 case Selection::Set:
3772 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3773 _editor->selection->set (_marker);
3777 case Selection::Toggle:
3778 /* we toggle on the button release, click only */
3779 _editor->selection->toggle (_marker);
3782 case Selection::Extend:
3783 case Selection::Add:
3790 _editor->_dragging_edit_point = false;
3792 _editor->begin_reversible_command ( _("move marker") );
3793 XMLNode &before = _editor->session()->locations()->get_state();
3795 MarkerSelection::iterator i;
3796 CopiedLocationInfo::iterator x;
3799 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3800 x != _copied_locations.end() && i != _editor->selection->markers.end();
3803 Location * location = _editor->find_location_from_marker (*i, is_start);
3807 if (location->locked()) {
3811 if (location->is_mark()) {
3812 location->set_start (((*x).location)->start());
3814 location->set (((*x).location)->start(), ((*x).location)->end());
3819 XMLNode &after = _editor->session()->locations()->get_state();
3820 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3821 _editor->commit_reversible_command ();
3825 MarkerDrag::aborted (bool movement_occured)
3827 if (!movement_occured) {
3831 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3833 /* move all markers to their original location */
3836 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3839 Location * location = _editor->find_location_from_marker (*m, is_start);
3842 (*m)->set_position (is_start ? location->start() : location->end());
3849 MarkerDrag::update_item (Location*)
3854 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3856 _cumulative_x_drag (0),
3857 _cumulative_y_drag (0)
3859 if (_zero_gain_fraction < 0.0) {
3860 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3863 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3865 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3871 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3873 Drag::start_grab (event, _editor->cursors()->fader);
3875 // start the grab at the center of the control point so
3876 // the point doesn't 'jump' to the mouse after the first drag
3877 _fixed_grab_x = _point->get_x();
3878 _fixed_grab_y = _point->get_y();
3880 float const fraction = 1 - (_point->get_y() / _point->line().height());
3882 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3884 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3886 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3888 if (!_point->can_slide ()) {
3889 _x_constrained = true;
3894 ControlPointDrag::motion (GdkEvent* event, bool)
3896 double dx = _drags->current_pointer_x() - last_pointer_x();
3897 double dy = current_pointer_y() - last_pointer_y();
3899 if (event->button.state & Keyboard::SecondaryModifier) {
3904 /* coordinate in pixels relative to the start of the region (for region-based automation)
3905 or track (for track-based automation) */
3906 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3907 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3909 // calculate zero crossing point. back off by .01 to stay on the
3910 // positive side of zero
3911 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3913 // make sure we hit zero when passing through
3914 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3918 if (_x_constrained) {
3921 if (_y_constrained) {
3925 _cumulative_x_drag = cx - _fixed_grab_x;
3926 _cumulative_y_drag = cy - _fixed_grab_y;
3930 cy = min ((double) _point->line().height(), cy);
3932 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3934 if (!_x_constrained) {
3935 _editor->snap_to_with_modifier (cx_frames, event);
3938 cx_frames = min (cx_frames, _point->line().maximum_time());
3940 float const fraction = 1.0 - (cy / _point->line().height());
3942 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3944 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3948 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3950 if (!movement_occurred) {
3954 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3955 _editor->reset_point_selection ();
3959 motion (event, false);
3962 _point->line().end_drag (_pushing, _final_index);
3963 _editor->commit_reversible_command ();
3967 ControlPointDrag::aborted (bool)
3969 _point->line().reset ();
3973 ControlPointDrag::active (Editing::MouseMode m)
3975 if (m == Editing::MouseDraw) {
3976 /* always active in mouse draw */
3980 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3981 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3984 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3987 _cumulative_y_drag (0)
3989 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3993 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3995 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3998 _item = &_line->grab_item ();
4000 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4001 origin, and ditto for y.
4004 double cx = event->button.x;
4005 double cy = event->button.y;
4007 _line->parent_group().canvas_to_item (cx, cy);
4009 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4014 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4015 /* no adjacent points */
4019 Drag::start_grab (event, _editor->cursors()->fader);
4021 /* store grab start in parent frame */
4026 double fraction = 1.0 - (cy / _line->height());
4028 _line->start_drag_line (before, after, fraction);
4030 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4034 LineDrag::motion (GdkEvent* event, bool)
4036 double dy = current_pointer_y() - last_pointer_y();
4038 if (event->button.state & Keyboard::SecondaryModifier) {
4042 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4044 _cumulative_y_drag = cy - _fixed_grab_y;
4047 cy = min ((double) _line->height(), cy);
4049 double const fraction = 1.0 - (cy / _line->height());
4052 /* we are ignoring x position for this drag, so we can just pass in anything */
4053 _line->drag_motion (0, fraction, true, false, ignored);
4055 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4059 LineDrag::finished (GdkEvent* event, bool movement_occured)
4061 if (movement_occured) {
4062 motion (event, false);
4063 _line->end_drag (false, 0);
4065 /* add a new control point on the line */
4067 AutomationTimeAxisView* atv;
4069 _line->end_drag (false, 0);
4071 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4072 framepos_t where = _editor->window_event_sample (event, 0, 0);
4073 atv->add_automation_event (event, where, event->button.y, false);
4077 _editor->commit_reversible_command ();
4081 LineDrag::aborted (bool)
4086 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4089 _cumulative_x_drag (0)
4091 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4095 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4097 Drag::start_grab (event);
4099 _line = reinterpret_cast<Line*> (_item);
4102 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4104 double cx = event->button.x;
4105 double cy = event->button.y;
4107 _item->parent()->canvas_to_item (cx, cy);
4109 /* store grab start in parent frame */
4110 _region_view_grab_x = cx;
4112 _before = *(float*) _item->get_data ("position");
4114 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4116 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4120 FeatureLineDrag::motion (GdkEvent*, bool)
4122 double dx = _drags->current_pointer_x() - last_pointer_x();
4124 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4126 _cumulative_x_drag += dx;
4128 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4137 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4139 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4141 float *pos = new float;
4144 _line->set_data ("position", pos);
4150 FeatureLineDrag::finished (GdkEvent*, bool)
4152 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4153 _arv->update_transient(_before, _before);
4157 FeatureLineDrag::aborted (bool)
4162 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4164 , _vertical_only (false)
4166 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4170 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4172 Drag::start_grab (event);
4173 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4177 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4184 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4186 framepos_t grab = grab_frame ();
4187 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4188 _editor->snap_to_with_modifier (grab, event);
4190 grab = raw_grab_frame ();
4193 /* base start and end on initial click position */
4203 if (current_pointer_y() < grab_y()) {
4204 y1 = current_pointer_y();
4207 y2 = current_pointer_y();
4211 if (start != end || y1 != y2) {
4213 double x1 = _editor->sample_to_pixel (start);
4214 double x2 = _editor->sample_to_pixel (end);
4215 const double min_dimension = 2.0;
4217 if (_vertical_only) {
4218 /* fixed 10 pixel width */
4222 x2 = min (x1 - min_dimension, x2);
4224 x2 = max (x1 + min_dimension, x2);
4229 y2 = min (y1 - min_dimension, y2);
4231 y2 = max (y1 + min_dimension, y2);
4234 /* translate rect into item space and set */
4236 ArdourCanvas::Rect r (x1, y1, x2, y2);
4238 /* this drag is a _trackview_only == true drag, so the y1 and
4239 * y2 (computed using current_pointer_y() and grab_y()) will be
4240 * relative to the top of the trackview group). The
4241 * rubberband rect has the same parent/scroll offset as the
4242 * the trackview group, so we can use the "r" rect directly
4243 * to set the shape of the rubberband.
4246 _editor->rubberband_rect->set (r);
4247 _editor->rubberband_rect->show();
4248 _editor->rubberband_rect->raise_to_top();
4250 show_verbose_cursor_time (pf);
4252 do_select_things (event, true);
4257 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4261 framepos_t grab = grab_frame ();
4262 framepos_t lpf = last_pointer_frame ();
4264 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4265 grab = raw_grab_frame ();
4266 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4280 if (current_pointer_y() < grab_y()) {
4281 y1 = current_pointer_y();
4284 y2 = current_pointer_y();
4288 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4292 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4294 if (movement_occurred) {
4296 motion (event, false);
4297 do_select_things (event, false);
4303 bool do_deselect = true;
4304 MidiTimeAxisView* mtv;
4306 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4308 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4309 /* nothing selected */
4310 add_midi_region (mtv);
4311 do_deselect = false;
4315 /* do not deselect if Primary or Tertiary (toggle-select or
4316 * extend-select are pressed.
4319 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4320 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4327 _editor->rubberband_rect->hide();
4331 RubberbandSelectDrag::aborted (bool)
4333 _editor->rubberband_rect->hide ();
4336 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4337 : RegionDrag (e, i, p, v)
4339 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4343 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4345 Drag::start_grab (event, cursor);
4347 show_verbose_cursor_time (adjusted_current_frame (event));
4351 TimeFXDrag::motion (GdkEvent* event, bool)
4353 RegionView* rv = _primary;
4354 StreamView* cv = rv->get_time_axis_view().view ();
4356 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4357 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4358 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4360 framepos_t const pf = adjusted_current_frame (event);
4362 if (pf > rv->region()->position()) {
4363 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4366 show_verbose_cursor_time (pf);
4370 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4372 _primary->get_time_axis_view().hide_timestretch ();
4374 if (!movement_occurred) {
4378 if (last_pointer_frame() < _primary->region()->position()) {
4379 /* backwards drag of the left edge - not usable */
4383 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4385 float percentage = (double) newlen / (double) _primary->region()->length();
4387 #ifndef USE_RUBBERBAND
4388 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4389 if (_primary->region()->data_type() == DataType::AUDIO) {
4390 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4394 if (!_editor->get_selection().regions.empty()) {
4395 /* primary will already be included in the selection, and edit
4396 group shared editing will propagate selection across
4397 equivalent regions, so just use the current region
4401 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4402 error << _("An error occurred while executing time stretch operation") << endmsg;
4408 TimeFXDrag::aborted (bool)
4410 _primary->get_time_axis_view().hide_timestretch ();
4413 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4416 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4420 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4422 Drag::start_grab (event);
4426 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4428 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4432 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4434 if (movement_occurred && _editor->session()) {
4435 /* make sure we stop */
4436 _editor->session()->request_transport_speed (0.0);
4441 ScrubDrag::aborted (bool)
4446 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4450 , _original_pointer_time_axis (-1)
4451 , _time_selection_at_start (!_editor->get_selection().time.empty())
4453 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4455 if (_time_selection_at_start) {
4456 start_at_start = _editor->get_selection().time.start();
4457 end_at_start = _editor->get_selection().time.end_frame();
4462 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4464 if (_editor->session() == 0) {
4468 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4470 switch (_operation) {
4471 case CreateSelection:
4472 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4477 cursor = _editor->cursors()->selector;
4478 Drag::start_grab (event, cursor);
4481 case SelectionStartTrim:
4482 if (_editor->clicked_axisview) {
4483 _editor->clicked_axisview->order_selection_trims (_item, true);
4485 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4488 case SelectionEndTrim:
4489 if (_editor->clicked_axisview) {
4490 _editor->clicked_axisview->order_selection_trims (_item, false);
4492 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4496 Drag::start_grab (event, cursor);
4499 case SelectionExtend:
4500 Drag::start_grab (event, cursor);
4504 if (_operation == SelectionMove) {
4505 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4507 show_verbose_cursor_time (adjusted_current_frame (event));
4510 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4514 SelectionDrag::setup_pointer_frame_offset ()
4516 switch (_operation) {
4517 case CreateSelection:
4518 _pointer_frame_offset = 0;
4521 case SelectionStartTrim:
4523 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4526 case SelectionEndTrim:
4527 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4530 case SelectionExtend:
4536 SelectionDrag::motion (GdkEvent* event, bool first_move)
4538 framepos_t start = 0;
4540 framecnt_t length = 0;
4541 framecnt_t distance = 0;
4543 framepos_t const pending_position = adjusted_current_frame (event);
4545 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4549 switch (_operation) {
4550 case CreateSelection:
4552 framepos_t grab = grab_frame ();
4555 grab = adjusted_current_frame (event, false);
4556 if (grab < pending_position) {
4557 _editor->snap_to (grab, RoundDownMaybe);
4559 _editor->snap_to (grab, RoundUpMaybe);
4563 if (pending_position < grab) {
4564 start = pending_position;
4567 end = pending_position;
4571 /* first drag: Either add to the selection
4572 or create a new selection
4579 /* adding to the selection */
4580 _editor->set_selected_track_as_side_effect (Selection::Add);
4581 _editor->clicked_selection = _editor->selection->add (start, end);
4588 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4589 _editor->set_selected_track_as_side_effect (Selection::Set);
4592 _editor->clicked_selection = _editor->selection->set (start, end);
4596 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4597 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4598 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4600 _editor->selection->add (atest);
4604 /* select all tracks within the rectangle that we've marked out so far */
4605 TrackViewList new_selection;
4606 TrackViewList& all_tracks (_editor->track_views);
4608 ArdourCanvas::Coord const top = grab_y();
4609 ArdourCanvas::Coord const bottom = current_pointer_y();
4611 if (top >= 0 && bottom >= 0) {
4613 //first, find the tracks that are covered in the y range selection
4614 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4615 if ((*i)->covered_by_y_range (top, bottom)) {
4616 new_selection.push_back (*i);
4620 //now find any tracks that are GROUPED with the tracks we selected
4621 TrackViewList grouped_add = new_selection;
4622 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4623 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4624 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4625 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4626 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4627 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4628 grouped_add.push_back (*j);
4633 //now compare our list with the current selection, and add or remove as necessary
4634 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4635 TrackViewList tracks_to_add;
4636 TrackViewList tracks_to_remove;
4637 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4638 if ( !_editor->selection->tracks.contains ( *i ) )
4639 tracks_to_add.push_back ( *i );
4640 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4641 if ( !grouped_add.contains ( *i ) )
4642 tracks_to_remove.push_back ( *i );
4643 _editor->selection->add(tracks_to_add);
4644 _editor->selection->remove(tracks_to_remove);
4650 case SelectionStartTrim:
4652 start = _editor->selection->time[_editor->clicked_selection].start;
4653 end = _editor->selection->time[_editor->clicked_selection].end;
4655 if (pending_position > end) {
4658 start = pending_position;
4662 case SelectionEndTrim:
4664 start = _editor->selection->time[_editor->clicked_selection].start;
4665 end = _editor->selection->time[_editor->clicked_selection].end;
4667 if (pending_position < start) {
4670 end = pending_position;
4677 start = _editor->selection->time[_editor->clicked_selection].start;
4678 end = _editor->selection->time[_editor->clicked_selection].end;
4680 length = end - start;
4681 distance = pending_position - start;
4682 start = pending_position;
4683 _editor->snap_to (start);
4685 end = start + length;
4689 case SelectionExtend:
4694 switch (_operation) {
4696 if (_time_selection_at_start) {
4697 _editor->selection->move_time (distance);
4701 _editor->selection->replace (_editor->clicked_selection, start, end);
4705 if (_operation == SelectionMove) {
4706 show_verbose_cursor_time(start);
4708 show_verbose_cursor_time(pending_position);
4713 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4715 Session* s = _editor->session();
4717 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4718 if (movement_occurred) {
4719 motion (event, false);
4720 /* XXX this is not object-oriented programming at all. ick */
4721 if (_editor->selection->time.consolidate()) {
4722 _editor->selection->TimeChanged ();
4725 /* XXX what if its a music time selection? */
4727 if ( s->get_play_range() && s->transport_rolling() ) {
4728 s->request_play_range (&_editor->selection->time, true);
4730 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4731 if (_operation == SelectionEndTrim)
4732 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4734 s->request_locate (_editor->get_selection().time.start());
4740 /* just a click, no pointer movement.
4743 if (_operation == SelectionExtend) {
4744 if (_time_selection_at_start) {
4745 framepos_t pos = adjusted_current_frame (event, false);
4746 framepos_t start = min (pos, start_at_start);
4747 framepos_t end = max (pos, end_at_start);
4748 _editor->selection->set (start, end);
4751 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4752 if (_editor->clicked_selection) {
4753 _editor->selection->remove (_editor->clicked_selection);
4756 if (!_editor->clicked_selection) {
4757 _editor->selection->clear_time();
4762 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4763 _editor->selection->set (_editor->clicked_axisview);
4766 if (s && s->get_play_range () && s->transport_rolling()) {
4767 s->request_stop (false, false);
4772 _editor->stop_canvas_autoscroll ();
4773 _editor->clicked_selection = 0;
4774 _editor->commit_reversible_selection_op ();
4778 SelectionDrag::aborted (bool)
4783 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4784 : Drag (e, i, false),
4788 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4790 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4791 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4792 physical_screen_height (_editor->get_window())));
4793 _drag_rect->hide ();
4795 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4796 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4799 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4801 /* normal canvas items will be cleaned up when their parent group is deleted. But
4802 this item is created as the child of a long-lived parent group, and so we
4803 need to explicitly delete it.
4809 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4811 if (_editor->session() == 0) {
4815 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4817 if (!_editor->temp_location) {
4818 _editor->temp_location = new Location (*_editor->session());
4821 switch (_operation) {
4822 case CreateSkipMarker:
4823 case CreateRangeMarker:
4824 case CreateTransportMarker:
4825 case CreateCDMarker:
4827 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4832 cursor = _editor->cursors()->selector;
4836 Drag::start_grab (event, cursor);
4838 show_verbose_cursor_time (adjusted_current_frame (event));
4842 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4844 framepos_t start = 0;
4846 ArdourCanvas::Rectangle *crect;
4848 switch (_operation) {
4849 case CreateSkipMarker:
4850 crect = _editor->range_bar_drag_rect;
4852 case CreateRangeMarker:
4853 crect = _editor->range_bar_drag_rect;
4855 case CreateTransportMarker:
4856 crect = _editor->transport_bar_drag_rect;
4858 case CreateCDMarker:
4859 crect = _editor->cd_marker_bar_drag_rect;
4862 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4867 framepos_t const pf = adjusted_current_frame (event);
4869 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4870 framepos_t grab = grab_frame ();
4871 _editor->snap_to (grab);
4873 if (pf < grab_frame()) {
4881 /* first drag: Either add to the selection
4882 or create a new selection.
4887 _editor->temp_location->set (start, end);
4891 update_item (_editor->temp_location);
4893 //_drag_rect->raise_to_top();
4899 _editor->temp_location->set (start, end);
4901 double x1 = _editor->sample_to_pixel (start);
4902 double x2 = _editor->sample_to_pixel (end);
4906 update_item (_editor->temp_location);
4909 show_verbose_cursor_time (pf);
4914 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4916 Location * newloc = 0;
4920 if (movement_occurred) {
4921 motion (event, false);
4924 switch (_operation) {
4925 case CreateSkipMarker:
4926 case CreateRangeMarker:
4927 case CreateCDMarker:
4929 XMLNode &before = _editor->session()->locations()->get_state();
4930 if (_operation == CreateSkipMarker) {
4931 _editor->begin_reversible_command (_("new skip marker"));
4932 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4933 flags = Location::IsRangeMarker | Location::IsSkip;
4934 _editor->range_bar_drag_rect->hide();
4935 } else if (_operation == CreateCDMarker) {
4936 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4937 _editor->begin_reversible_command (_("new CD marker"));
4938 flags = Location::IsRangeMarker | Location::IsCDMarker;
4939 _editor->cd_marker_bar_drag_rect->hide();
4941 _editor->begin_reversible_command (_("new skip marker"));
4942 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4943 flags = Location::IsRangeMarker;
4944 _editor->range_bar_drag_rect->hide();
4946 newloc = new Location (
4947 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4950 _editor->session()->locations()->add (newloc, true);
4951 XMLNode &after = _editor->session()->locations()->get_state();
4952 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4953 _editor->commit_reversible_command ();
4957 case CreateTransportMarker:
4958 // popup menu to pick loop or punch
4959 _editor->new_transport_marker_context_menu (&event->button, _item);
4965 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4967 if (_operation == CreateTransportMarker) {
4969 /* didn't drag, so just locate */
4971 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4973 } else if (_operation == CreateCDMarker) {
4975 /* didn't drag, but mark is already created so do
4978 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4983 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4985 if (end == max_framepos) {
4986 end = _editor->session()->current_end_frame ();
4989 if (start == max_framepos) {
4990 start = _editor->session()->current_start_frame ();
4993 switch (_editor->mouse_mode) {
4995 /* find the two markers on either side and then make the selection from it */
4996 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5000 /* find the two markers on either side of the click and make the range out of it */
5001 _editor->selection->set (start, end);
5010 _editor->stop_canvas_autoscroll ();
5014 RangeMarkerBarDrag::aborted (bool movement_occured)
5016 if (movement_occured) {
5017 _drag_rect->hide ();
5022 RangeMarkerBarDrag::update_item (Location* location)
5024 double const x1 = _editor->sample_to_pixel (location->start());
5025 double const x2 = _editor->sample_to_pixel (location->end());
5027 _drag_rect->set_x0 (x1);
5028 _drag_rect->set_x1 (x2);
5031 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5033 , _cumulative_dx (0)
5034 , _cumulative_dy (0)
5036 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5038 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5040 _region = &_primary->region_view ();
5041 _note_height = _region->midi_stream_view()->note_height ();
5045 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5047 Drag::start_grab (event);
5049 if (!(_was_selected = _primary->selected())) {
5051 /* tertiary-click means extend selection - we'll do that on button release,
5052 so don't add it here, because otherwise we make it hard to figure
5053 out the "extend-to" range.
5056 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5059 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5062 _region->note_selected (_primary, true);
5064 _region->unique_select (_primary);
5067 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5068 _editor->commit_reversible_selection_op();
5073 /** @return Current total drag x change in frames */
5075 NoteDrag::total_dx () const
5078 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5080 /* primary note time */
5081 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5083 /* new time of the primary note in session frames */
5084 frameoffset_t st = n + dx;
5086 framepos_t const rp = _region->region()->position ();
5088 /* prevent the note being dragged earlier than the region's position */
5091 /* snap and return corresponding delta */
5092 return _region->snap_frame_to_frame (st - rp) + rp - n;
5095 /** @return Current total drag y change in note number */
5097 NoteDrag::total_dy () const
5099 MidiStreamView* msv = _region->midi_stream_view ();
5100 double const y = _region->midi_view()->y_position ();
5101 /* new current note */
5102 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5104 n = max (msv->lowest_note(), n);
5105 n = min (msv->highest_note(), n);
5106 /* and work out delta */
5107 return n - msv->y_to_note (grab_y() - y);
5111 NoteDrag::motion (GdkEvent *, bool)
5113 /* Total change in x and y since the start of the drag */
5114 frameoffset_t const dx = total_dx ();
5115 int8_t const dy = total_dy ();
5117 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5118 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5119 double const tdy = -dy * _note_height - _cumulative_dy;
5122 _cumulative_dx += tdx;
5123 _cumulative_dy += tdy;
5125 int8_t note_delta = total_dy();
5127 _region->move_selection (tdx, tdy, note_delta);
5129 /* the new note value may be the same as the old one, but we
5130 * don't know what that means because the selection may have
5131 * involved more than one note and we might be doing something
5132 * odd with them. so show the note value anyway, always.
5136 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5138 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5139 (int) floor ((double)new_note));
5141 show_verbose_cursor_text (buf);
5146 NoteDrag::finished (GdkEvent* ev, bool moved)
5149 /* no motion - select note */
5151 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5152 _editor->current_mouse_mode() == Editing::MouseDraw) {
5154 bool changed = false;
5156 if (_was_selected) {
5157 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5159 _region->note_deselected (_primary);
5163 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5164 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5166 if (!extend && !add && _region->selection_size() > 1) {
5167 _region->unique_select (_primary);
5169 } else if (extend) {
5170 _region->note_selected (_primary, true, true);
5173 /* it was added during button press */
5178 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5179 _editor->commit_reversible_selection_op();
5183 _region->note_dropped (_primary, total_dx(), total_dy());
5188 NoteDrag::aborted (bool)
5193 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5194 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5195 : Drag (editor, atv->base_item ())
5197 , _y_origin (atv->y_position())
5198 , _nothing_to_drag (false)
5200 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5201 setup (atv->lines ());
5204 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5205 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5206 : Drag (editor, rv->get_canvas_group ())
5208 , _y_origin (rv->get_time_axis_view().y_position())
5209 , _nothing_to_drag (false)
5212 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5214 list<boost::shared_ptr<AutomationLine> > lines;
5216 AudioRegionView* audio_view;
5217 AutomationRegionView* automation_view;
5218 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5219 lines.push_back (audio_view->get_gain_line ());
5220 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5221 lines.push_back (automation_view->line ());
5224 error << _("Automation range drag created for invalid region type") << endmsg;
5230 /** @param lines AutomationLines to drag.
5231 * @param offset Offset from the session start to the points in the AutomationLines.
5234 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5236 /* find the lines that overlap the ranges being dragged */
5237 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5238 while (i != lines.end ()) {
5239 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5242 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5244 /* check this range against all the AudioRanges that we are using */
5245 list<AudioRange>::const_iterator k = _ranges.begin ();
5246 while (k != _ranges.end()) {
5247 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5253 /* add it to our list if it overlaps at all */
5254 if (k != _ranges.end()) {
5259 _lines.push_back (n);
5265 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5269 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5271 return 1.0 - ((global_y - _y_origin) / line->height());
5275 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5277 const double v = list->eval(x);
5278 return _integral ? rint(v) : v;
5282 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5284 Drag::start_grab (event, cursor);
5286 /* Get line states before we start changing things */
5287 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5288 i->state = &i->line->get_state ();
5289 i->original_fraction = y_fraction (i->line, current_pointer_y());
5292 if (_ranges.empty()) {
5294 /* No selected time ranges: drag all points */
5295 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5296 uint32_t const N = i->line->npoints ();
5297 for (uint32_t j = 0; j < N; ++j) {
5298 i->points.push_back (i->line->nth (j));
5304 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5306 framecnt_t const half = (i->start + i->end) / 2;
5308 /* find the line that this audio range starts in */
5309 list<Line>::iterator j = _lines.begin();
5310 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5314 if (j != _lines.end()) {
5315 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5317 /* j is the line that this audio range starts in; fade into it;
5318 64 samples length plucked out of thin air.
5321 framepos_t a = i->start + 64;
5326 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5327 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5329 the_list->editor_add (p, value (the_list, p));
5330 the_list->editor_add (q, value (the_list, q));
5333 /* same thing for the end */
5336 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5340 if (j != _lines.end()) {
5341 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5343 /* j is the line that this audio range starts in; fade out of it;
5344 64 samples length plucked out of thin air.
5347 framepos_t b = i->end - 64;
5352 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5353 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5355 the_list->editor_add (p, value (the_list, p));
5356 the_list->editor_add (q, value (the_list, q));
5360 _nothing_to_drag = true;
5362 /* Find all the points that should be dragged and put them in the relevant
5363 points lists in the Line structs.
5366 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5368 uint32_t const N = i->line->npoints ();
5369 for (uint32_t j = 0; j < N; ++j) {
5371 /* here's a control point on this line */
5372 ControlPoint* p = i->line->nth (j);
5373 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5375 /* see if it's inside a range */
5376 list<AudioRange>::const_iterator k = _ranges.begin ();
5377 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5381 if (k != _ranges.end()) {
5382 /* dragging this point */
5383 _nothing_to_drag = false;
5384 i->points.push_back (p);
5390 if (_nothing_to_drag) {
5394 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5395 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5400 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5402 if (_nothing_to_drag) {
5406 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5407 float const f = y_fraction (l->line, current_pointer_y());
5408 /* we are ignoring x position for this drag, so we can just pass in anything */
5410 l->line->drag_motion (0, f, true, false, ignored);
5411 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5416 AutomationRangeDrag::finished (GdkEvent* event, bool)
5418 if (_nothing_to_drag) {
5422 motion (event, false);
5423 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5424 i->line->end_drag (false, 0);
5427 _editor->commit_reversible_command ();
5431 AutomationRangeDrag::aborted (bool)
5433 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5438 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5440 , initial_time_axis_view (itav)
5442 /* note that time_axis_view may be null if the regionview was created
5443 * as part of a copy operation.
5445 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5446 layer = v->region()->layer ();
5447 initial_y = v->get_canvas_group()->position().y;
5448 initial_playlist = v->region()->playlist ();
5449 initial_position = v->region()->position ();
5450 initial_end = v->region()->position () + v->region()->length ();
5453 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5454 : Drag (e, i->canvas_item ())
5457 , _cumulative_dx (0)
5459 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5460 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5465 PatchChangeDrag::motion (GdkEvent* ev, bool)
5467 framepos_t f = adjusted_current_frame (ev);
5468 boost::shared_ptr<Region> r = _region_view->region ();
5469 f = max (f, r->position ());
5470 f = min (f, r->last_frame ());
5472 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5473 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5474 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5475 _cumulative_dx = dxu;
5479 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5481 if (!movement_occurred) {
5485 boost::shared_ptr<Region> r (_region_view->region ());
5486 framepos_t f = adjusted_current_frame (ev);
5487 f = max (f, r->position ());
5488 f = min (f, r->last_frame ());
5490 _region_view->move_patch_change (
5492 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5497 PatchChangeDrag::aborted (bool)
5499 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5503 PatchChangeDrag::setup_pointer_frame_offset ()
5505 boost::shared_ptr<Region> region = _region_view->region ();
5506 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5509 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5510 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5517 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5519 _region_view->update_drag_selection (
5521 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5525 MidiRubberbandSelectDrag::deselect_things ()
5530 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5531 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5534 _vertical_only = true;
5538 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5540 double const y = _region_view->midi_view()->y_position ();
5542 y1 = max (0.0, y1 - y);
5543 y2 = max (0.0, y2 - y);
5545 _region_view->update_vertical_drag_selection (
5548 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5553 MidiVerticalSelectDrag::deselect_things ()
5558 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5559 : RubberbandSelectDrag (e, i)
5565 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5567 if (drag_in_progress) {
5568 /* We just want to select things at the end of the drag, not during it */
5572 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5574 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5576 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5578 _editor->commit_reversible_selection_op ();
5582 EditorRubberbandSelectDrag::deselect_things ()
5584 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5586 _editor->selection->clear_tracks();
5587 _editor->selection->clear_regions();
5588 _editor->selection->clear_points ();
5589 _editor->selection->clear_lines ();
5590 _editor->selection->clear_midi_notes ();
5592 _editor->commit_reversible_selection_op();
5595 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5600 _note[0] = _note[1] = 0;
5603 NoteCreateDrag::~NoteCreateDrag ()
5609 NoteCreateDrag::grid_frames (framepos_t t) const
5612 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5614 grid_beats = Evoral::Beats(1);
5617 return _region_view->region_beats_to_region_frames (grid_beats);
5621 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5623 Drag::start_grab (event, cursor);
5625 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5627 framepos_t pf = _drags->current_pointer_frame ();
5628 framecnt_t const g = grid_frames (pf);
5630 /* Hack so that we always snap to the note that we are over, instead of snapping
5631 to the next one if we're more than halfway through the one we're over.
5633 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5637 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5638 _note[1] = _note[0];
5640 MidiStreamView* sv = _region_view->midi_stream_view ();
5641 double const x = _editor->sample_to_pixel (_note[0]);
5642 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5644 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5645 _drag_rect->set_outline_all ();
5646 _drag_rect->set_outline_color (0xffffff99);
5647 _drag_rect->set_fill_color (0xffffff66);
5651 NoteCreateDrag::motion (GdkEvent* event, bool)
5653 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5654 double const x0 = _editor->sample_to_pixel (_note[0]);
5655 double const x1 = _editor->sample_to_pixel (_note[1]);
5656 _drag_rect->set_x0 (std::min(x0, x1));
5657 _drag_rect->set_x1 (std::max(x0, x1));
5661 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5663 if (!had_movement) {
5667 framepos_t const start = min (_note[0], _note[1]);
5668 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5670 framecnt_t const g = grid_frames (start);
5671 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5673 if (_editor->snap_mode() == SnapNormal && length < g) {
5677 Evoral::Beats length_beats = max (
5678 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5680 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5684 NoteCreateDrag::y_to_region (double y) const
5687 _region_view->get_canvas_group()->canvas_to_item (x, y);
5692 NoteCreateDrag::aborted (bool)
5697 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5702 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5706 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5708 Drag::start_grab (event, cursor);
5712 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5718 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5721 distance = _drags->current_pointer_x() - grab_x();
5722 len = ar->fade_in()->back()->when;
5724 distance = grab_x() - _drags->current_pointer_x();
5725 len = ar->fade_out()->back()->when;
5728 /* how long should it be ? */
5730 new_length = len + _editor->pixel_to_sample (distance);
5732 /* now check with the region that this is legal */
5734 new_length = ar->verify_xfade_bounds (new_length, start);
5737 arv->reset_fade_in_shape_width (ar, new_length);
5739 arv->reset_fade_out_shape_width (ar, new_length);
5744 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5750 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5753 distance = _drags->current_pointer_x() - grab_x();
5754 len = ar->fade_in()->back()->when;
5756 distance = grab_x() - _drags->current_pointer_x();
5757 len = ar->fade_out()->back()->when;
5760 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5762 _editor->begin_reversible_command ("xfade trim");
5763 ar->playlist()->clear_owned_changes ();
5766 ar->set_fade_in_length (new_length);
5768 ar->set_fade_out_length (new_length);
5771 /* Adjusting the xfade may affect other regions in the playlist, so we need
5772 to get undo Commands from the whole playlist rather than just the
5776 vector<Command*> cmds;
5777 ar->playlist()->rdiff (cmds);
5778 _editor->session()->add_commands (cmds);
5779 _editor->commit_reversible_command ();
5784 CrossfadeEdgeDrag::aborted (bool)
5787 // arv->redraw_start_xfade ();
5789 // arv->redraw_end_xfade ();
5793 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5794 : Drag (e, item, true)
5795 , line (new EditorCursor (*e))
5797 line->set_position (pos);
5801 RegionCutDrag::~RegionCutDrag ()
5807 RegionCutDrag::motion (GdkEvent*, bool)
5809 framepos_t where = _drags->current_pointer_frame();
5810 _editor->snap_to (where);
5812 line->set_position (where);
5816 RegionCutDrag::finished (GdkEvent*, bool)
5818 _editor->get_track_canvas()->canvas()->re_enter();
5820 framepos_t pos = _drags->current_pointer_frame();
5824 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5830 _editor->split_regions_at (pos, rs);
5834 RegionCutDrag::aborted (bool)