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;
826 } else if (current_pointer_y() >= 0 && _last_pointer_time_axis_view >= 0) {
827 /* Moving into the drop-zone..
829 * TODO allow moving further down in drop-zone:
830 * e.g. 2 Tracks, select a region on both of them.
832 * A) grab the upper, drag 2 down, both regions are in the dropzone: all fine (works)
834 * B) grab the lower, drag 1 down, region (and mouse) are in dropzone, The End.
835 * upper region is only down one track and cannot be moved into the zone.
837 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
838 #ifdef DEBUG_DROPZONEDRAG
839 printf("INTO THE ZONE DELTA: %d\n", delta_time_axis_view);
843 /* Work out the change in x */
844 framepos_t pending_region_position;
845 double const x_delta = compute_x_delta (event, &pending_region_position);
846 _last_frame_position = pending_region_position;
848 /* calculate hidden tracks in current delta */
850 if (_last_pointer_time_axis_view < 0) {
851 // Moving out of the zone, check for hidden tracks at the bottom.
852 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0)
853 -_time_axis_views.size() - delta_time_axis_view;
854 #ifdef DEBUG_DROPZONEDRAG
855 printf("NOW WHAT?? last: %d delta %d || skip %d\n", _last_pointer_time_axis_view, delta_time_axis_view, delta_skip);
858 // calculate hidden tracks that are skipped by the pointer movement
859 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0)
860 - _last_pointer_time_axis_view
861 - delta_time_axis_view;
862 #ifdef DEBUG_DROPZONEDRAG
863 printf("Drag from %d to %d || skip %d\n",
864 _last_pointer_time_axis_view,
865 _last_pointer_time_axis_view + delta_time_axis_view,
870 /* Verify change in y */
871 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
872 /* this y movement is not allowed, so do no y movement this time */
873 delta_time_axis_view = 0;
876 #ifdef DEBUG_DROPZONEDRAG
877 printf(" ** NOT ALLOWED\n");
881 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
882 /* haven't reached next snap point, and we're not switching
883 trackviews nor layers. nothing to do.
888 typedef pair<int,double> NewTrackIndexAndPosition;
889 typedef map<boost::shared_ptr<Playlist>,NewTrackIndexAndPosition> PlaylistDropzoneMap;
890 PlaylistDropzoneMap playlist_dropzone_map;
891 int biggest_drop_zone_offset = 0;
893 /* find drop-zone y-position */
894 Coord last_track_bottom_edge;
895 last_track_bottom_edge = 0;
896 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
897 if (!(*t)->hidden()) {
898 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
903 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
905 RegionView* rv = i->view;
910 if (rv->region()->locked() || rv->region()->video_locked()) {
917 /* reparent the regionview into a group above all
921 ArdourCanvas::Item* rvg = rv->get_canvas_group();
922 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
923 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
924 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
925 /* move the item so that it continues to appear at the
926 same location now that its parent has changed.
928 rvg->move (rv_canvas_offset - dmg_canvas_offset);
931 /* If we have moved tracks, we'll fudge the layer delta so that the
932 region gets moved back onto layer 0 on its new track; this avoids
933 confusion when dragging regions from non-zero layers onto different
936 double this_delta_layer = delta_layer;
937 if (delta_time_axis_view != 0) {
938 this_delta_layer = - i->layer;
941 int this_delta_time_axis_view = delta_time_axis_view;
942 this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
944 int track_index = i->time_axis_view + this_delta_time_axis_view;
945 assert(track_index >= 0);
947 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
948 i->time_axis_view = track_index;
949 #ifdef DEBUG_DROPZONEDRAG
950 printf("IN THE ZONE\n");
952 assert(i->time_axis_view >= _time_axis_views.size());
953 if (current_pointer_y() >= 0) {
956 NewTrackIndexAndPosition ip;
957 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
959 /* store index of each new playlist as a negative count, starting at -1 */
961 if (pdz == playlist_dropzone_map.end()) {
964 * retain the ordering top -> bottom in the drop-zone
965 * this can be done by sorting the regions according to
966 * i->time_axis_view Y, prior to iterating over DraggingView
969 int n = playlist_dropzone_map.size() + 1;
971 /* compute where this new track (which doesn't exist yet) will live
975 ip.first = -n; /* in time axis units, negative to signify "in drop zone " */
976 ip.second = last_track_bottom_edge; /* where to place the top edge of the regionview */
978 /* How high is this region view ? */
980 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
981 ArdourCanvas::Rect bbox;
987 last_track_bottom_edge += bbox.height();
989 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), ip));
997 /* values are zero or negative, hence the use of min() */
998 biggest_drop_zone_offset = min (biggest_drop_zone_offset, dzoffset);
999 y_delta = ip.second - rv->get_canvas_group()->canvas_origin().y;
1004 /* The TimeAxisView that this region is now over */
1005 TimeAxisView* current_tv = _time_axis_views[track_index];
1007 /* Ensure it is moved from stacked -> expanded if appropriate */
1008 if (current_tv->view()->layer_display() == Stacked) {
1009 current_tv->view()->set_layer_display (Expanded);
1012 /* We're only allowed to go -ve in layer on Expanded views */
1013 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1014 this_delta_layer = - i->layer;
1018 rv->set_height (current_tv->view()->child_height ());
1020 /* Update show/hidden status as the region view may have come from a hidden track,
1021 or have moved to one.
1023 if (current_tv->hidden ()) {
1024 rv->get_canvas_group()->hide ();
1026 rv->get_canvas_group()->show ();
1029 /* Update the DraggingView */
1030 i->time_axis_view = track_index;
1031 i->layer += this_delta_layer;
1034 _editor->mouse_brush_insert_region (rv, pending_region_position);
1038 /* Get the y coordinate of the top of the track that this region is now over */
1039 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1041 /* And adjust for the layer that it should be on */
1042 StreamView* cv = current_tv->view ();
1043 switch (cv->layer_display ()) {
1047 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1050 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1054 /* need to get the parent of the regionview
1055 * canvas group and get its position in
1056 * equivalent coordinate space as the trackview
1057 * we are now dragging over.
1060 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1065 /* Now move the region view */
1066 rv->move (x_delta, y_delta);
1068 } /* foreach region */
1070 _total_x_delta += x_delta;
1072 if (x_delta != 0 && !_brushing) {
1073 show_verbose_cursor_time (_last_frame_position);
1078 /* the pointer is currently over a time axis view */
1080 if (_last_pointer_time_axis_view < 0) {
1082 /* last motion event was not over a time axis view */
1084 if (delta_time_axis_view < 0) {
1085 /* was in the drop zone, moving up */
1086 assert(current_pointer_time_axis_view >= 0);
1087 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1089 /* was in the drop zone, moving down ... not possible */
1094 /* last motion event was also over a time axis view */
1096 _last_pointer_time_axis_view += delta_time_axis_view;
1097 assert(_last_pointer_time_axis_view >= 0);
1102 /* the pointer is not over a time axis view */
1104 _last_pointer_time_axis_view = biggest_drop_zone_offset;
1107 _last_pointer_layer += delta_layer;
1111 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1113 if (_copy && first_move) {
1115 if (_x_constrained) {
1116 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1118 _editor->begin_reversible_command (Operations::region_copy);
1121 /* duplicate the regionview(s) and region(s) */
1123 list<DraggingView> new_regionviews;
1125 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1127 RegionView* rv = i->view;
1128 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1129 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1131 const boost::shared_ptr<const Region> original = rv->region();
1132 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1133 region_copy->set_position (original->position());
1134 /* need to set this so that the drop zone code can work. This doesn't
1135 actually put the region into the playlist, but just sets a weak pointer
1138 region_copy->set_playlist (original->playlist());
1142 boost::shared_ptr<AudioRegion> audioregion_copy
1143 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1145 nrv = new AudioRegionView (*arv, audioregion_copy);
1147 boost::shared_ptr<MidiRegion> midiregion_copy
1148 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1149 nrv = new MidiRegionView (*mrv, midiregion_copy);
1154 nrv->get_canvas_group()->show ();
1155 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1157 /* swap _primary to the copy */
1159 if (rv == _primary) {
1163 /* ..and deselect the one we copied */
1165 rv->set_selected (false);
1168 if (!new_regionviews.empty()) {
1170 /* reflect the fact that we are dragging the copies */
1172 _views = new_regionviews;
1174 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1177 } else if (!_copy && first_move) {
1179 if (_x_constrained) {
1180 _editor->begin_reversible_command (_("fixed time region drag"));
1182 _editor->begin_reversible_command (Operations::region_drag);
1186 RegionMotionDrag::motion (event, first_move);
1190 RegionMotionDrag::finished (GdkEvent *, bool)
1192 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1193 if (!(*i)->view()) {
1197 if ((*i)->view()->layer_display() == Expanded) {
1198 (*i)->view()->set_layer_display (Stacked);
1204 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1206 RegionMotionDrag::finished (ev, movement_occurred);
1208 if (!movement_occurred) {
1212 if (was_double_click() && !_views.empty()) {
1213 DraggingView dv = _views.front();
1214 dv.view->show_region_editor ();
1221 /* reverse this here so that we have the correct logic to finalize
1225 if (Config->get_edit_mode() == Lock) {
1226 _x_constrained = !_x_constrained;
1229 assert (!_views.empty ());
1231 /* We might have hidden region views so that they weren't visible during the drag
1232 (when they have been reparented). Now everything can be shown again, as region
1233 views are back in their track parent groups.
1235 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1236 i->view->get_canvas_group()->show ();
1239 bool const changed_position = (_last_frame_position != _primary->region()->position());
1240 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1241 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1261 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1265 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1267 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1272 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1273 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1274 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1275 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1277 rtav->set_height (original->current_height());
1281 ChanCount one_midi_port (DataType::MIDI, 1);
1282 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1283 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1284 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1286 rtav->set_height (original->current_height());
1291 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1297 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1299 RegionSelection new_views;
1300 PlaylistSet modified_playlists;
1301 RouteTimeAxisView* new_time_axis_view = 0;
1304 /* all changes were made during motion event handlers */
1306 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1310 _editor->commit_reversible_command ();
1314 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1315 PlaylistMapping playlist_mapping;
1317 /* insert the regions into their new playlists */
1318 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1320 RouteTimeAxisView* dest_rtv = 0;
1322 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1328 if (changed_position && !_x_constrained) {
1329 where = i->view->region()->position() - drag_delta;
1331 where = i->view->region()->position();
1334 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
1335 /* dragged to drop zone */
1337 PlaylistMapping::iterator pm;
1339 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1340 /* first region from this original playlist: create a new track */
1341 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1342 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1343 dest_rtv = new_time_axis_view;
1345 /* we already created a new track for regions from this playlist, use it */
1346 dest_rtv = pm->second;
1349 /* destination time axis view is the one we dragged to */
1350 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1353 if (dest_rtv != 0) {
1354 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1355 if (new_view != 0) {
1356 new_views.push_back (new_view);
1360 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1361 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1364 list<DraggingView>::const_iterator next = i;
1370 /* If we've created new regions either by copying or moving
1371 to a new track, we want to replace the old selection with the new ones
1374 if (new_views.size() > 0) {
1375 _editor->selection->set (new_views);
1378 /* write commands for the accumulated diffs for all our modified playlists */
1379 add_stateful_diff_commands_for_playlists (modified_playlists);
1381 _editor->commit_reversible_command ();
1385 RegionMoveDrag::finished_no_copy (
1386 bool const changed_position,
1387 bool const changed_tracks,
1388 framecnt_t const drag_delta
1391 RegionSelection new_views;
1392 PlaylistSet modified_playlists;
1393 PlaylistSet frozen_playlists;
1394 set<RouteTimeAxisView*> views_to_update;
1395 RouteTimeAxisView* new_time_axis_view = 0;
1398 /* all changes were made during motion event handlers */
1399 _editor->commit_reversible_command ();
1403 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1404 PlaylistMapping playlist_mapping;
1406 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1408 RegionView* rv = i->view;
1409 RouteTimeAxisView* dest_rtv = 0;
1411 if (rv->region()->locked() || rv->region()->video_locked()) {
1416 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
1417 /* dragged to drop zone */
1419 PlaylistMapping::iterator pm;
1421 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1422 /* first region from this original playlist: create a new track */
1423 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1424 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1425 dest_rtv = new_time_axis_view;
1427 /* we already created a new track for regions from this playlist, use it */
1428 dest_rtv = pm->second;
1432 /* destination time axis view is the one we dragged to */
1433 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1438 double const dest_layer = i->layer;
1440 views_to_update.insert (dest_rtv);
1444 if (changed_position && !_x_constrained) {
1445 where = rv->region()->position() - drag_delta;
1447 where = rv->region()->position();
1450 if (changed_tracks) {
1452 /* insert into new playlist */
1454 RegionView* new_view = insert_region_into_playlist (
1455 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1458 if (new_view == 0) {
1463 new_views.push_back (new_view);
1465 /* remove from old playlist */
1467 /* the region that used to be in the old playlist is not
1468 moved to the new one - we use a copy of it. as a result,
1469 any existing editor for the region should no longer be
1472 rv->hide_region_editor();
1475 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1479 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1481 /* this movement may result in a crossfade being modified, or a layering change,
1482 so we need to get undo data from the playlist as well as the region.
1485 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1487 playlist->clear_changes ();
1490 rv->region()->clear_changes ();
1493 motion on the same track. plonk the previously reparented region
1494 back to its original canvas group (its streamview).
1495 No need to do anything for copies as they are fake regions which will be deleted.
1498 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1499 rv->get_canvas_group()->set_y_position (i->initial_y);
1502 /* just change the model */
1503 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1504 playlist->set_layer (rv->region(), dest_layer);
1507 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1509 r = frozen_playlists.insert (playlist);
1512 playlist->freeze ();
1515 rv->region()->set_position (where);
1517 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1520 if (changed_tracks) {
1522 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1523 was selected in all of them, then removing it from a playlist will have removed all
1524 trace of it from _views (i.e. there were N regions selected, we removed 1,
1525 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1526 corresponding regionview, and _views is now empty).
1528 This could have invalidated any and all iterators into _views.
1530 The heuristic we use here is: if the region selection is empty, break out of the loop
1531 here. if the region selection is not empty, then restart the loop because we know that
1532 we must have removed at least the region(view) we've just been working on as well as any
1533 that we processed on previous iterations.
1535 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1536 we can just iterate.
1540 if (_views.empty()) {
1551 /* If we've created new regions either by copying or moving
1552 to a new track, we want to replace the old selection with the new ones
1555 if (new_views.size() > 0) {
1556 _editor->selection->set (new_views);
1559 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1563 /* write commands for the accumulated diffs for all our modified playlists */
1564 add_stateful_diff_commands_for_playlists (modified_playlists);
1566 _editor->commit_reversible_command ();
1568 /* We have futzed with the layering of canvas items on our streamviews.
1569 If any region changed layer, this will have resulted in the stream
1570 views being asked to set up their region views, and all will be well.
1571 If not, we might now have badly-ordered region views. Ask the StreamViews
1572 involved to sort themselves out, just in case.
1575 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1576 (*i)->view()->playlist_layered ((*i)->track ());
1580 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1581 * @param region Region to remove.
1582 * @param playlist playlist To remove from.
1583 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1584 * that clear_changes () is only called once per playlist.
1587 RegionMoveDrag::remove_region_from_playlist (
1588 boost::shared_ptr<Region> region,
1589 boost::shared_ptr<Playlist> playlist,
1590 PlaylistSet& modified_playlists
1593 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1596 playlist->clear_changes ();
1599 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1603 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1604 * clearing the playlist's diff history first if necessary.
1605 * @param region Region to insert.
1606 * @param dest_rtv Destination RouteTimeAxisView.
1607 * @param dest_layer Destination layer.
1608 * @param where Destination position.
1609 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1610 * that clear_changes () is only called once per playlist.
1611 * @return New RegionView, or 0 if no insert was performed.
1614 RegionMoveDrag::insert_region_into_playlist (
1615 boost::shared_ptr<Region> region,
1616 RouteTimeAxisView* dest_rtv,
1619 PlaylistSet& modified_playlists
1622 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1623 if (!dest_playlist) {
1627 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1628 _new_region_view = 0;
1629 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1631 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1632 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1634 dest_playlist->clear_changes ();
1637 dest_playlist->add_region (region, where);
1639 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1640 dest_playlist->set_layer (region, dest_layer);
1645 assert (_new_region_view);
1647 return _new_region_view;
1651 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1653 _new_region_view = rv;
1657 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1659 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1660 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1662 _editor->session()->add_command (c);
1671 RegionMoveDrag::aborted (bool movement_occurred)
1675 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1676 list<DraggingView>::const_iterator next = i;
1685 RegionMotionDrag::aborted (movement_occurred);
1690 RegionMotionDrag::aborted (bool)
1692 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1694 StreamView* sview = (*i)->view();
1697 if (sview->layer_display() == Expanded) {
1698 sview->set_layer_display (Stacked);
1703 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1704 RegionView* rv = i->view;
1705 TimeAxisView* tv = &(rv->get_time_axis_view ());
1706 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1708 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1709 rv->get_canvas_group()->set_y_position (0);
1711 rv->move (-_total_x_delta, 0);
1712 rv->set_height (rtv->view()->child_height ());
1716 /** @param b true to brush, otherwise false.
1717 * @param c true to make copies of the regions being moved, otherwise false.
1719 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1720 : RegionMotionDrag (e, i, p, v, b)
1723 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1726 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1727 if (rtv && rtv->is_track()) {
1728 speed = rtv->track()->speed ();
1731 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1735 RegionMoveDrag::setup_pointer_frame_offset ()
1737 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1740 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1741 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1743 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1745 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1746 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1748 _primary = v->view()->create_region_view (r, false, false);
1750 _primary->get_canvas_group()->show ();
1751 _primary->set_position (pos, 0);
1752 _views.push_back (DraggingView (_primary, this, v));
1754 _last_frame_position = pos;
1756 _item = _primary->get_canvas_group ();
1760 RegionInsertDrag::finished (GdkEvent *, bool)
1762 int pos = _views.front().time_axis_view;
1763 assert(pos >= 0 && pos < _time_axis_views.size());
1765 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1767 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1768 _primary->get_canvas_group()->set_y_position (0);
1770 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1772 _editor->begin_reversible_command (Operations::insert_region);
1773 playlist->clear_changes ();
1774 playlist->add_region (_primary->region (), _last_frame_position);
1776 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1777 if (Config->get_edit_mode() == Ripple) {
1778 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1781 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1782 _editor->commit_reversible_command ();
1790 RegionInsertDrag::aborted (bool)
1797 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1798 : RegionMoveDrag (e, i, p, v, false, false)
1800 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1803 struct RegionSelectionByPosition {
1804 bool operator() (RegionView*a, RegionView* b) {
1805 return a->region()->position () < b->region()->position();
1810 RegionSpliceDrag::motion (GdkEvent* event, bool)
1812 /* Which trackview is this ? */
1814 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1815 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1817 /* The region motion is only processed if the pointer is over
1821 if (!tv || !tv->is_track()) {
1822 /* To make sure we hide the verbose canvas cursor when the mouse is
1823 not held over an audio track.
1825 _editor->verbose_cursor()->hide ();
1828 _editor->verbose_cursor()->show ();
1833 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1839 RegionSelection copy;
1840 _editor->selection->regions.by_position(copy);
1842 framepos_t const pf = adjusted_current_frame (event);
1844 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1846 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1852 boost::shared_ptr<Playlist> playlist;
1854 if ((playlist = atv->playlist()) == 0) {
1858 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1863 if (pf < (*i)->region()->last_frame() + 1) {
1867 if (pf > (*i)->region()->first_frame()) {
1873 playlist->shuffle ((*i)->region(), dir);
1878 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1880 RegionMoveDrag::finished (event, movement_occurred);
1884 RegionSpliceDrag::aborted (bool)
1894 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1897 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1899 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1900 RegionSelection to_ripple;
1901 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1902 if ((*i)->position() >= where) {
1903 to_ripple.push_back (rtv->view()->find_view(*i));
1907 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1908 if (!exclude.contains (*i)) {
1909 // the selection has already been added to _views
1911 if (drag_in_progress) {
1912 // do the same things that RegionMotionDrag::motion does when
1913 // first_move is true, for the region views that we're adding
1914 // to _views this time
1917 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1918 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1919 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1920 rvg->reparent (_editor->_drag_motion_group);
1922 // we only need to move in the y direction
1923 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1928 _views.push_back (DraggingView (*i, this, tav));
1934 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1937 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1938 // we added all the regions after the selection
1940 std::list<DraggingView>::iterator to_erase = i++;
1941 if (!_editor->selection->regions.contains (to_erase->view)) {
1942 // restore the non-selected regions to their original playlist & positions,
1943 // and then ripple them back by the length of the regions that were dragged away
1944 // do the same things as RegionMotionDrag::aborted
1946 RegionView *rv = to_erase->view;
1947 TimeAxisView* tv = &(rv->get_time_axis_view ());
1948 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1951 // plonk them back onto their own track
1952 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1953 rv->get_canvas_group()->set_y_position (0);
1957 // move the underlying region to match the view
1958 rv->region()->set_position (rv->region()->position() + amount);
1960 // restore the view to match the underlying region's original position
1961 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1964 rv->set_height (rtv->view()->child_height ());
1965 _views.erase (to_erase);
1971 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
1973 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
1975 return allow_moves_across_tracks;
1983 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1984 : RegionMoveDrag (e, i, p, v, false, false)
1986 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1987 // compute length of selection
1988 RegionSelection selected_regions = _editor->selection->regions;
1989 selection_length = selected_regions.end_frame() - selected_regions.start();
1991 // we'll only allow dragging to another track in ripple mode if all the regions
1992 // being dragged start off on the same track
1993 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1996 exclude = new RegionList;
1997 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1998 exclude->push_back((*i)->region());
2001 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2002 RegionSelection copy;
2003 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2005 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2006 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2008 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2009 // find ripple start point on each applicable playlist
2010 RegionView *first_selected_on_this_track = NULL;
2011 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2012 if ((*i)->region()->playlist() == (*pi)) {
2013 // region is on this playlist - it's the first, because they're sorted
2014 first_selected_on_this_track = *i;
2018 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2019 add_all_after_to_views (
2020 &first_selected_on_this_track->get_time_axis_view(),
2021 first_selected_on_this_track->region()->position(),
2022 selected_regions, false);
2025 if (allow_moves_across_tracks) {
2026 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2034 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2036 /* Which trackview is this ? */
2038 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2039 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2041 /* The region motion is only processed if the pointer is over
2045 if (!tv || !tv->is_track()) {
2046 /* To make sure we hide the verbose canvas cursor when the mouse is
2047 not held over an audiotrack.
2049 _editor->verbose_cursor()->hide ();
2053 framepos_t where = adjusted_current_frame (event);
2054 assert (where >= 0);
2056 double delta = compute_x_delta (event, &after);
2058 framecnt_t amount = _editor->pixel_to_sample (delta);
2060 if (allow_moves_across_tracks) {
2061 // all the originally selected regions were on the same track
2063 framecnt_t adjust = 0;
2064 if (prev_tav && tv != prev_tav) {
2065 // dragged onto a different track
2066 // remove the unselected regions from _views, restore them to their original positions
2067 // and add the regions after the drop point on the new playlist to _views instead.
2068 // undo the effect of rippling the previous playlist, and include the effect of removing
2069 // the dragged region(s) from this track
2071 remove_unselected_from_views (prev_amount, false);
2072 // ripple previous playlist according to the regions that have been removed onto the new playlist
2073 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2076 // move just the selected regions
2077 RegionMoveDrag::motion(event, first_move);
2079 // ensure that the ripple operation on the new playlist inserts selection_length time
2080 adjust = selection_length;
2081 // ripple the new current playlist
2082 tv->playlist()->ripple (where, amount+adjust, exclude);
2084 // add regions after point where drag entered this track to subsequent ripples
2085 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2088 // motion on same track
2089 RegionMoveDrag::motion(event, first_move);
2093 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2094 prev_position = where;
2096 // selection encompasses multiple tracks - just drag
2097 // cross-track drags are forbidden
2098 RegionMoveDrag::motion(event, first_move);
2101 if (!_x_constrained) {
2102 prev_amount += amount;
2105 _last_frame_position = after;
2109 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2111 if (!movement_occurred) {
2115 if (was_double_click() && !_views.empty()) {
2116 DraggingView dv = _views.front();
2117 dv.view->show_region_editor ();
2124 _editor->begin_reversible_command(_("Ripple drag"));
2126 // remove the regions being rippled from the dragging view, updating them to
2127 // their new positions
2128 remove_unselected_from_views (prev_amount, true);
2130 if (allow_moves_across_tracks) {
2132 // if regions were dragged across tracks, we've rippled any later
2133 // regions on the track the regions were dragged off, so we need
2134 // to add the original track to the undo record
2135 orig_tav->playlist()->clear_changes();
2136 vector<Command*> cmds;
2137 orig_tav->playlist()->rdiff (cmds);
2138 _editor->session()->add_commands (cmds);
2140 if (prev_tav && prev_tav != orig_tav) {
2141 prev_tav->playlist()->clear_changes();
2142 vector<Command*> cmds;
2143 prev_tav->playlist()->rdiff (cmds);
2144 _editor->session()->add_commands (cmds);
2147 // selection spanned multiple tracks - all will need adding to undo record
2149 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2150 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2152 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2153 (*pi)->clear_changes();
2154 vector<Command*> cmds;
2155 (*pi)->rdiff (cmds);
2156 _editor->session()->add_commands (cmds);
2160 // other modified playlists are added to undo by RegionMoveDrag::finished()
2161 RegionMoveDrag::finished (event, movement_occurred);
2162 _editor->commit_reversible_command();
2166 RegionRippleDrag::aborted (bool movement_occurred)
2168 RegionMoveDrag::aborted (movement_occurred);
2173 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2175 _view (dynamic_cast<MidiTimeAxisView*> (v))
2177 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2183 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2186 _region = add_midi_region (_view);
2187 _view->playlist()->freeze ();
2190 framepos_t const f = adjusted_current_frame (event);
2191 if (f < grab_frame()) {
2192 _region->set_position (f);
2195 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2196 so that if this region is duplicated, its duplicate starts on
2197 a snap point rather than 1 frame after a snap point. Otherwise things get
2198 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2199 place snapped notes at the start of the region.
2202 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2203 _region->set_length (len < 1 ? 1 : len);
2209 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2211 if (!movement_occurred) {
2212 add_midi_region (_view);
2214 _view->playlist()->thaw ();
2219 RegionCreateDrag::aborted (bool)
2222 _view->playlist()->thaw ();
2228 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2232 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2236 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2238 Gdk::Cursor* cursor;
2239 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2241 float x_fraction = cnote->mouse_x_fraction ();
2243 if (x_fraction > 0.0 && x_fraction < 0.25) {
2244 cursor = _editor->cursors()->left_side_trim;
2247 cursor = _editor->cursors()->right_side_trim;
2251 Drag::start_grab (event, cursor);
2253 region = &cnote->region_view();
2257 if (event->motion.state & Keyboard::PrimaryModifier) {
2263 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2265 if (ms.size() > 1) {
2266 /* has to be relative, may make no sense otherwise */
2270 /* select this note; if it is already selected, preserve the existing selection,
2271 otherwise make this note the only one selected.
2273 region->note_selected (cnote, cnote->selected ());
2275 _editor->begin_reversible_command (_("resize notes"));
2277 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2278 MidiRegionSelection::iterator next;
2281 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2283 mrv->begin_resizing (at_front);
2290 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2292 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2293 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2294 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2296 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2298 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2304 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2306 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2307 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2308 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2310 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2312 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2316 _editor->commit_reversible_command ();
2320 NoteResizeDrag::aborted (bool)
2322 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2323 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2324 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2326 mrv->abort_resizing ();
2331 AVDraggingView::AVDraggingView (RegionView* v)
2334 initial_position = v->region()->position ();
2337 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2340 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2343 TrackViewList empty;
2345 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2346 std::list<RegionView*> views = rs.by_layer();
2348 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2349 RegionView* rv = (*i);
2350 if (!rv->region()->video_locked()) {
2353 _views.push_back (AVDraggingView (rv));
2358 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2360 Drag::start_grab (event);
2361 if (_editor->session() == 0) {
2365 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2366 _max_backwards_drag = (
2367 ARDOUR_UI::instance()->video_timeline->get_duration()
2368 + ARDOUR_UI::instance()->video_timeline->get_offset()
2369 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2372 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2373 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2374 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2377 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2380 Timecode::Time timecode;
2381 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2382 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);
2383 show_verbose_cursor_text (buf);
2387 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2389 if (_editor->session() == 0) {
2392 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2396 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2397 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2399 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2400 dt = - _max_backwards_drag;
2403 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2404 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2406 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2407 RegionView* rv = i->view;
2408 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2411 rv->region()->clear_changes ();
2412 rv->region()->suspend_property_changes();
2414 rv->region()->set_position(i->initial_position + dt);
2415 rv->region_changed(ARDOUR::Properties::position);
2418 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2419 Timecode::Time timecode;
2420 Timecode::Time timediff;
2422 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2423 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2424 snprintf (buf, sizeof (buf),
2425 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2426 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2427 , _("Video Start:"),
2428 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2430 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2432 show_verbose_cursor_text (buf);
2436 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2438 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2442 if (!movement_occurred || ! _editor->session()) {
2446 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2448 _editor->begin_reversible_command (_("Move Video"));
2450 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2451 ARDOUR_UI::instance()->video_timeline->save_undo();
2452 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2453 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2455 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2456 i->view->drag_end();
2457 i->view->region()->resume_property_changes ();
2459 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2462 _editor->session()->maybe_update_session_range(
2463 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2464 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2468 _editor->commit_reversible_command ();
2472 VideoTimeLineDrag::aborted (bool)
2474 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2477 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2478 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2480 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2481 i->view->region()->resume_property_changes ();
2482 i->view->region()->set_position(i->initial_position);
2486 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2487 : RegionDrag (e, i, p, v)
2488 , _preserve_fade_anchor (preserve_fade_anchor)
2489 , _jump_position_when_done (false)
2491 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2495 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2498 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2499 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2501 if (tv && tv->is_track()) {
2502 speed = tv->track()->speed();
2505 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2506 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2507 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2509 framepos_t const pf = adjusted_current_frame (event);
2511 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2512 /* Move the contents of the region around without changing the region bounds */
2513 _operation = ContentsTrim;
2514 Drag::start_grab (event, _editor->cursors()->trimmer);
2516 /* These will get overridden for a point trim.*/
2517 if (pf < (region_start + region_length/2)) {
2518 /* closer to front */
2519 _operation = StartTrim;
2521 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2522 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2524 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2528 _operation = EndTrim;
2529 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2530 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2532 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2537 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2538 _jump_position_when_done = true;
2541 switch (_operation) {
2543 show_verbose_cursor_time (region_start);
2544 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2545 i->view->trim_front_starting ();
2549 show_verbose_cursor_time (region_end);
2552 show_verbose_cursor_time (pf);
2556 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2557 i->view->region()->suspend_property_changes ();
2562 TrimDrag::motion (GdkEvent* event, bool first_move)
2564 RegionView* rv = _primary;
2567 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2568 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2569 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2570 frameoffset_t frame_delta = 0;
2572 if (tv && tv->is_track()) {
2573 speed = tv->track()->speed();
2576 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2582 switch (_operation) {
2584 trim_type = "Region start trim";
2587 trim_type = "Region end trim";
2590 trim_type = "Region content trim";
2597 _editor->begin_reversible_command (trim_type);
2599 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2600 RegionView* rv = i->view;
2601 rv->enable_display (false);
2602 rv->region()->playlist()->clear_owned_changes ();
2604 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2607 arv->temporarily_hide_envelope ();
2611 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2612 insert_result = _editor->motion_frozen_playlists.insert (pl);
2614 if (insert_result.second) {
2620 bool non_overlap_trim = false;
2622 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2623 non_overlap_trim = true;
2626 /* contstrain trim to fade length */
2627 if (_preserve_fade_anchor) {
2628 switch (_operation) {
2630 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2631 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2633 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2634 if (ar->locked()) continue;
2635 framecnt_t len = ar->fade_in()->back()->when;
2636 if (len < dt) dt = min(dt, len);
2640 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2641 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2643 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2644 if (ar->locked()) continue;
2645 framecnt_t len = ar->fade_out()->back()->when;
2646 if (len < -dt) dt = max(dt, -len);
2655 switch (_operation) {
2657 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2658 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2659 if (changed && _preserve_fade_anchor) {
2660 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2662 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2663 framecnt_t len = ar->fade_in()->back()->when;
2664 framecnt_t diff = ar->first_frame() - i->initial_position;
2665 framepos_t new_length = len - diff;
2666 i->anchored_fade_length = min (ar->length(), new_length);
2667 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2668 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2675 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2676 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2677 if (changed && _preserve_fade_anchor) {
2678 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2680 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2681 framecnt_t len = ar->fade_out()->back()->when;
2682 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2683 framepos_t new_length = len + diff;
2684 i->anchored_fade_length = min (ar->length(), new_length);
2685 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2686 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2694 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2696 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2697 i->view->move_contents (frame_delta);
2703 switch (_operation) {
2705 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2708 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2711 // show_verbose_cursor_time (frame_delta);
2718 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2720 if (movement_occurred) {
2721 motion (event, false);
2723 if (_operation == StartTrim) {
2724 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2726 /* This must happen before the region's StatefulDiffCommand is created, as it may
2727 `correct' (ahem) the region's _start from being negative to being zero. It
2728 needs to be zero in the undo record.
2730 i->view->trim_front_ending ();
2732 if (_preserve_fade_anchor) {
2733 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2735 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2736 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2737 ar->set_fade_in_length(i->anchored_fade_length);
2738 ar->set_fade_in_active(true);
2741 if (_jump_position_when_done) {
2742 i->view->region()->set_position (i->initial_position);
2745 } else if (_operation == EndTrim) {
2746 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2747 if (_preserve_fade_anchor) {
2748 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2750 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2751 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2752 ar->set_fade_out_length(i->anchored_fade_length);
2753 ar->set_fade_out_active(true);
2756 if (_jump_position_when_done) {
2757 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2762 if (!_views.empty()) {
2763 if (_operation == StartTrim) {
2764 _editor->maybe_locate_with_edit_preroll(
2765 _views.begin()->view->region()->position());
2767 if (_operation == EndTrim) {
2768 _editor->maybe_locate_with_edit_preroll(
2769 _views.begin()->view->region()->position() +
2770 _views.begin()->view->region()->length());
2774 if (!_editor->selection->selected (_primary)) {
2775 _primary->thaw_after_trim ();
2778 set<boost::shared_ptr<Playlist> > diffed_playlists;
2780 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2781 i->view->thaw_after_trim ();
2782 i->view->enable_display (true);
2784 /* Trimming one region may affect others on the playlist, so we need
2785 to get undo Commands from the whole playlist rather than just the
2786 region. Use diffed_playlists to make sure we don't diff a given
2787 playlist more than once.
2789 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2790 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2791 vector<Command*> cmds;
2793 _editor->session()->add_commands (cmds);
2794 diffed_playlists.insert (p);
2799 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2803 _editor->motion_frozen_playlists.clear ();
2804 _editor->commit_reversible_command();
2807 /* no mouse movement */
2808 _editor->point_trim (event, adjusted_current_frame (event));
2811 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2812 if (_operation == StartTrim) {
2813 i->view->trim_front_ending ();
2816 i->view->region()->resume_property_changes ();
2821 TrimDrag::aborted (bool movement_occurred)
2823 /* Our motion method is changing model state, so use the Undo system
2824 to cancel. Perhaps not ideal, as this will leave an Undo point
2825 behind which may be slightly odd from the user's point of view.
2830 if (movement_occurred) {
2834 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2835 i->view->region()->resume_property_changes ();
2840 TrimDrag::setup_pointer_frame_offset ()
2842 list<DraggingView>::iterator i = _views.begin ();
2843 while (i != _views.end() && i->view != _primary) {
2847 if (i == _views.end()) {
2851 switch (_operation) {
2853 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2856 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2863 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2867 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2868 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2873 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2875 Drag::start_grab (event, cursor);
2876 show_verbose_cursor_time (adjusted_current_frame(event));
2880 MeterMarkerDrag::setup_pointer_frame_offset ()
2882 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2886 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2888 if (!_marker->meter().movable()) {
2894 // create a dummy marker for visual representation of moving the
2895 // section, because whether its a copy or not, we're going to
2896 // leave or lose the original marker (leave if its a copy; lose if its
2897 // not, because we'll remove it from the map).
2899 MeterSection section (_marker->meter());
2901 if (!section.movable()) {
2906 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2908 _marker = new MeterMarker (
2910 *_editor->meter_group,
2911 ARDOUR_UI::config()->color ("meter marker"),
2913 *new MeterSection (_marker->meter())
2916 /* use the new marker for the grab */
2917 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2920 TempoMap& map (_editor->session()->tempo_map());
2921 /* get current state */
2922 before_state = &map.get_state();
2923 /* remove the section while we drag it */
2924 map.remove_meter (section, true);
2928 framepos_t const pf = adjusted_current_frame (event);
2930 _marker->set_position (pf);
2931 show_verbose_cursor_time (pf);
2935 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2937 if (!movement_occurred) {
2938 if (was_double_click()) {
2939 _editor->edit_meter_marker (*_marker);
2944 if (!_marker->meter().movable()) {
2948 motion (event, false);
2950 Timecode::BBT_Time when;
2952 TempoMap& map (_editor->session()->tempo_map());
2953 map.bbt_time (last_pointer_frame(), when);
2955 if (_copy == true) {
2956 _editor->begin_reversible_command (_("copy meter mark"));
2957 XMLNode &before = map.get_state();
2958 map.add_meter (_marker->meter(), when);
2959 XMLNode &after = map.get_state();
2960 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2961 _editor->commit_reversible_command ();
2964 _editor->begin_reversible_command (_("move meter mark"));
2966 /* we removed it before, so add it back now */
2968 map.add_meter (_marker->meter(), when);
2969 XMLNode &after = map.get_state();
2970 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2971 _editor->commit_reversible_command ();
2974 // delete the dummy marker we used for visual representation while moving.
2975 // a new visual marker will show up automatically.
2980 MeterMarkerDrag::aborted (bool moved)
2982 _marker->set_position (_marker->meter().frame ());
2985 TempoMap& map (_editor->session()->tempo_map());
2986 /* we removed it before, so add it back now */
2987 map.add_meter (_marker->meter(), _marker->meter().frame());
2988 // delete the dummy marker we used for visual representation while moving.
2989 // a new visual marker will show up automatically.
2994 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2998 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3000 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3005 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3007 Drag::start_grab (event, cursor);
3008 show_verbose_cursor_time (adjusted_current_frame (event));
3012 TempoMarkerDrag::setup_pointer_frame_offset ()
3014 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3018 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3020 if (!_marker->tempo().movable()) {
3026 // create a dummy marker for visual representation of moving the
3027 // section, because whether its a copy or not, we're going to
3028 // leave or lose the original marker (leave if its a copy; lose if its
3029 // not, because we'll remove it from the map).
3031 // create a dummy marker for visual representation of moving the copy.
3032 // The actual copying is not done before we reach the finish callback.
3035 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3037 TempoSection section (_marker->tempo());
3039 _marker = new TempoMarker (
3041 *_editor->tempo_group,
3042 ARDOUR_UI::config()->color ("tempo marker"),
3044 *new TempoSection (_marker->tempo())
3047 /* use the new marker for the grab */
3048 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3051 TempoMap& map (_editor->session()->tempo_map());
3052 /* get current state */
3053 before_state = &map.get_state();
3054 /* remove the section while we drag it */
3055 map.remove_tempo (section, true);
3059 framepos_t const pf = adjusted_current_frame (event);
3060 _marker->set_position (pf);
3061 show_verbose_cursor_time (pf);
3065 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3067 if (!movement_occurred) {
3068 if (was_double_click()) {
3069 _editor->edit_tempo_marker (*_marker);
3074 if (!_marker->tempo().movable()) {
3078 motion (event, false);
3080 TempoMap& map (_editor->session()->tempo_map());
3081 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3082 Timecode::BBT_Time when;
3084 map.bbt_time (beat_time, when);
3086 if (_copy == true) {
3087 _editor->begin_reversible_command (_("copy tempo mark"));
3088 XMLNode &before = map.get_state();
3089 map.add_tempo (_marker->tempo(), when);
3090 XMLNode &after = map.get_state();
3091 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3092 _editor->commit_reversible_command ();
3095 _editor->begin_reversible_command (_("move tempo mark"));
3096 /* we removed it before, so add it back now */
3097 map.add_tempo (_marker->tempo(), when);
3098 XMLNode &after = map.get_state();
3099 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3100 _editor->commit_reversible_command ();
3103 // delete the dummy marker we used for visual representation while moving.
3104 // a new visual marker will show up automatically.
3109 TempoMarkerDrag::aborted (bool moved)
3111 _marker->set_position (_marker->tempo().frame());
3113 TempoMap& map (_editor->session()->tempo_map());
3114 /* we removed it before, so add it back now */
3115 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3116 // delete the dummy marker we used for visual representation while moving.
3117 // a new visual marker will show up automatically.
3122 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3123 : Drag (e, &c.track_canvas_item(), false)
3127 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3130 /** Do all the things we do when dragging the playhead to make it look as though
3131 * we have located, without actually doing the locate (because that would cause
3132 * the diskstream buffers to be refilled, which is too slow).
3135 CursorDrag::fake_locate (framepos_t t)
3137 _editor->playhead_cursor->set_position (t);
3139 Session* s = _editor->session ();
3140 if (s->timecode_transmission_suspended ()) {
3141 framepos_t const f = _editor->playhead_cursor->current_frame ();
3142 /* This is asynchronous so it will be sent "now"
3144 s->send_mmc_locate (f);
3145 /* These are synchronous and will be sent during the next
3148 s->queue_full_time_code ();
3149 s->queue_song_position_pointer ();
3152 show_verbose_cursor_time (t);
3153 _editor->UpdateAllTransportClocks (t);
3157 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3159 Drag::start_grab (event, c);
3161 _grab_zoom = _editor->samples_per_pixel;
3163 framepos_t where = _editor->canvas_event_sample (event);
3165 _editor->snap_to_with_modifier (where, event);
3167 _editor->_dragging_playhead = true;
3169 Session* s = _editor->session ();
3171 /* grab the track canvas item as well */
3173 _cursor.track_canvas_item().grab();
3176 if (_was_rolling && _stop) {
3180 if (s->is_auditioning()) {
3181 s->cancel_audition ();
3185 if (AudioEngine::instance()->connected()) {
3187 /* do this only if we're the engine is connected
3188 * because otherwise this request will never be
3189 * serviced and we'll busy wait forever. likewise,
3190 * notice if we are disconnected while waiting for the
3191 * request to be serviced.
3194 s->request_suspend_timecode_transmission ();
3195 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3196 /* twiddle our thumbs */
3201 fake_locate (where);
3205 CursorDrag::motion (GdkEvent* event, bool)
3207 framepos_t const adjusted_frame = adjusted_current_frame (event);
3208 if (adjusted_frame != last_pointer_frame()) {
3209 fake_locate (adjusted_frame);
3214 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3216 _editor->_dragging_playhead = false;
3218 _cursor.track_canvas_item().ungrab();
3220 if (!movement_occurred && _stop) {
3224 motion (event, false);
3226 Session* s = _editor->session ();
3228 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3229 _editor->_pending_locate_request = true;
3230 s->request_resume_timecode_transmission ();
3235 CursorDrag::aborted (bool)
3237 _cursor.track_canvas_item().ungrab();
3239 if (_editor->_dragging_playhead) {
3240 _editor->session()->request_resume_timecode_transmission ();
3241 _editor->_dragging_playhead = false;
3244 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3247 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3248 : RegionDrag (e, i, p, v)
3250 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3254 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3256 Drag::start_grab (event, cursor);
3258 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3259 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3261 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3265 FadeInDrag::setup_pointer_frame_offset ()
3267 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3268 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3269 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3273 FadeInDrag::motion (GdkEvent* event, bool)
3275 framecnt_t fade_length;
3276 framepos_t const pos = adjusted_current_frame (event);
3277 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3279 if (pos < (region->position() + 64)) {
3280 fade_length = 64; // this should be a minimum defined somewhere
3281 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3282 fade_length = region->length() - region->fade_out()->back()->when - 1;
3284 fade_length = pos - region->position();
3287 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3289 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3295 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3298 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3302 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3304 if (!movement_occurred) {
3308 framecnt_t fade_length;
3310 framepos_t const pos = adjusted_current_frame (event);
3312 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3314 if (pos < (region->position() + 64)) {
3315 fade_length = 64; // this should be a minimum defined somewhere
3316 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3317 fade_length = region->length() - region->fade_out()->back()->when - 1;
3319 fade_length = pos - region->position();
3322 _editor->begin_reversible_command (_("change fade in length"));
3324 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3326 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3332 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3333 XMLNode &before = alist->get_state();
3335 tmp->audio_region()->set_fade_in_length (fade_length);
3336 tmp->audio_region()->set_fade_in_active (true);
3338 XMLNode &after = alist->get_state();
3339 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3342 _editor->commit_reversible_command ();
3346 FadeInDrag::aborted (bool)
3348 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3349 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3355 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3359 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3360 : RegionDrag (e, i, p, v)
3362 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3366 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3368 Drag::start_grab (event, cursor);
3370 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3371 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3373 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3377 FadeOutDrag::setup_pointer_frame_offset ()
3379 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3380 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3381 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3385 FadeOutDrag::motion (GdkEvent* event, bool)
3387 framecnt_t fade_length;
3389 framepos_t const pos = adjusted_current_frame (event);
3391 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3393 if (pos > (region->last_frame() - 64)) {
3394 fade_length = 64; // this should really be a minimum fade defined somewhere
3395 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3396 fade_length = region->length() - region->fade_in()->back()->when - 1;
3398 fade_length = region->last_frame() - pos;
3401 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3403 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3409 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3412 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3416 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3418 if (!movement_occurred) {
3422 framecnt_t fade_length;
3424 framepos_t const pos = adjusted_current_frame (event);
3426 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3428 if (pos > (region->last_frame() - 64)) {
3429 fade_length = 64; // this should really be a minimum fade defined somewhere
3430 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3431 fade_length = region->length() - region->fade_in()->back()->when - 1;
3433 fade_length = region->last_frame() - pos;
3436 _editor->begin_reversible_command (_("change fade out length"));
3438 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3440 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3446 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3447 XMLNode &before = alist->get_state();
3449 tmp->audio_region()->set_fade_out_length (fade_length);
3450 tmp->audio_region()->set_fade_out_active (true);
3452 XMLNode &after = alist->get_state();
3453 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3456 _editor->commit_reversible_command ();
3460 FadeOutDrag::aborted (bool)
3462 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3463 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3469 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3473 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3476 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3478 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3481 _points.push_back (ArdourCanvas::Duple (0, 0));
3482 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3485 MarkerDrag::~MarkerDrag ()
3487 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3492 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3494 location = new Location (*l);
3495 markers.push_back (m);
3500 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3502 Drag::start_grab (event, cursor);
3506 Location *location = _editor->find_location_from_marker (_marker, is_start);
3507 _editor->_dragging_edit_point = true;
3509 update_item (location);
3511 // _drag_line->show();
3512 // _line->raise_to_top();
3515 show_verbose_cursor_time (location->start());
3517 show_verbose_cursor_time (location->end());
3520 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3523 case Selection::Toggle:
3524 /* we toggle on the button release */
3526 case Selection::Set:
3527 if (!_editor->selection->selected (_marker)) {
3528 _editor->selection->set (_marker);
3531 case Selection::Extend:
3533 Locations::LocationList ll;
3534 list<Marker*> to_add;
3536 _editor->selection->markers.range (s, e);
3537 s = min (_marker->position(), s);
3538 e = max (_marker->position(), e);
3541 if (e < max_framepos) {
3544 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3545 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3546 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3549 to_add.push_back (lm->start);
3552 to_add.push_back (lm->end);
3556 if (!to_add.empty()) {
3557 _editor->selection->add (to_add);
3561 case Selection::Add:
3562 _editor->selection->add (_marker);
3566 /* Set up copies for us to manipulate during the drag
3569 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3571 Location* l = _editor->find_location_from_marker (*i, is_start);
3578 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3580 /* range: check that the other end of the range isn't
3583 CopiedLocationInfo::iterator x;
3584 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3585 if (*(*x).location == *l) {
3589 if (x == _copied_locations.end()) {
3590 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3592 (*x).markers.push_back (*i);
3593 (*x).move_both = true;
3601 MarkerDrag::setup_pointer_frame_offset ()
3604 Location *location = _editor->find_location_from_marker (_marker, is_start);
3605 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3609 MarkerDrag::motion (GdkEvent* event, bool)
3611 framecnt_t f_delta = 0;
3613 bool move_both = false;
3614 Location *real_location;
3615 Location *copy_location = 0;
3617 framepos_t const newframe = adjusted_current_frame (event);
3618 framepos_t next = newframe;
3620 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3624 CopiedLocationInfo::iterator x;
3626 /* find the marker we're dragging, and compute the delta */
3628 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3630 copy_location = (*x).location;
3632 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3634 /* this marker is represented by this
3635 * CopiedLocationMarkerInfo
3638 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3643 if (real_location->is_mark()) {
3644 f_delta = newframe - copy_location->start();
3648 switch (_marker->type()) {
3649 case Marker::SessionStart:
3650 case Marker::RangeStart:
3651 case Marker::LoopStart:
3652 case Marker::PunchIn:
3653 f_delta = newframe - copy_location->start();
3656 case Marker::SessionEnd:
3657 case Marker::RangeEnd:
3658 case Marker::LoopEnd:
3659 case Marker::PunchOut:
3660 f_delta = newframe - copy_location->end();
3663 /* what kind of marker is this ? */
3672 if (x == _copied_locations.end()) {
3673 /* hmm, impossible - we didn't find the dragged marker */
3677 /* now move them all */
3679 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3681 copy_location = x->location;
3683 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3687 if (real_location->locked()) {
3691 if (copy_location->is_mark()) {
3695 copy_location->set_start (copy_location->start() + f_delta);
3699 framepos_t new_start = copy_location->start() + f_delta;
3700 framepos_t new_end = copy_location->end() + f_delta;
3702 if (is_start) { // start-of-range marker
3704 if (move_both || (*x).move_both) {
3705 copy_location->set_start (new_start);
3706 copy_location->set_end (new_end);
3707 } else if (new_start < copy_location->end()) {
3708 copy_location->set_start (new_start);
3709 } else if (newframe > 0) {
3710 _editor->snap_to (next, RoundUpAlways, true);
3711 copy_location->set_end (next);
3712 copy_location->set_start (newframe);
3715 } else { // end marker
3717 if (move_both || (*x).move_both) {
3718 copy_location->set_end (new_end);
3719 copy_location->set_start (new_start);
3720 } else if (new_end > copy_location->start()) {
3721 copy_location->set_end (new_end);
3722 } else if (newframe > 0) {
3723 _editor->snap_to (next, RoundDownAlways, true);
3724 copy_location->set_start (next);
3725 copy_location->set_end (newframe);
3730 update_item (copy_location);
3732 /* now lookup the actual GUI items used to display this
3733 * location and move them to wherever the copy of the location
3734 * is now. This means that the logic in ARDOUR::Location is
3735 * still enforced, even though we are not (yet) modifying
3736 * the real Location itself.
3739 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3742 lm->set_position (copy_location->start(), copy_location->end());
3747 assert (!_copied_locations.empty());
3749 show_verbose_cursor_time (newframe);
3753 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3755 if (!movement_occurred) {
3757 if (was_double_click()) {
3758 _editor->rename_marker (_marker);
3762 /* just a click, do nothing but finish
3763 off the selection process
3766 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3769 case Selection::Set:
3770 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3771 _editor->selection->set (_marker);
3775 case Selection::Toggle:
3776 /* we toggle on the button release, click only */
3777 _editor->selection->toggle (_marker);
3780 case Selection::Extend:
3781 case Selection::Add:
3788 _editor->_dragging_edit_point = false;
3790 _editor->begin_reversible_command ( _("move marker") );
3791 XMLNode &before = _editor->session()->locations()->get_state();
3793 MarkerSelection::iterator i;
3794 CopiedLocationInfo::iterator x;
3797 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3798 x != _copied_locations.end() && i != _editor->selection->markers.end();
3801 Location * location = _editor->find_location_from_marker (*i, is_start);
3805 if (location->locked()) {
3809 if (location->is_mark()) {
3810 location->set_start (((*x).location)->start());
3812 location->set (((*x).location)->start(), ((*x).location)->end());
3817 XMLNode &after = _editor->session()->locations()->get_state();
3818 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3819 _editor->commit_reversible_command ();
3823 MarkerDrag::aborted (bool movement_occured)
3825 if (!movement_occured) {
3829 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3831 /* move all markers to their original location */
3834 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3837 Location * location = _editor->find_location_from_marker (*m, is_start);
3840 (*m)->set_position (is_start ? location->start() : location->end());
3847 MarkerDrag::update_item (Location*)
3852 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3854 _cumulative_x_drag (0),
3855 _cumulative_y_drag (0)
3857 if (_zero_gain_fraction < 0.0) {
3858 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3861 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3863 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3869 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3871 Drag::start_grab (event, _editor->cursors()->fader);
3873 // start the grab at the center of the control point so
3874 // the point doesn't 'jump' to the mouse after the first drag
3875 _fixed_grab_x = _point->get_x();
3876 _fixed_grab_y = _point->get_y();
3878 float const fraction = 1 - (_point->get_y() / _point->line().height());
3880 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3882 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3884 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3886 if (!_point->can_slide ()) {
3887 _x_constrained = true;
3892 ControlPointDrag::motion (GdkEvent* event, bool)
3894 double dx = _drags->current_pointer_x() - last_pointer_x();
3895 double dy = current_pointer_y() - last_pointer_y();
3897 if (event->button.state & Keyboard::SecondaryModifier) {
3902 /* coordinate in pixels relative to the start of the region (for region-based automation)
3903 or track (for track-based automation) */
3904 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3905 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3907 // calculate zero crossing point. back off by .01 to stay on the
3908 // positive side of zero
3909 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3911 // make sure we hit zero when passing through
3912 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3916 if (_x_constrained) {
3919 if (_y_constrained) {
3923 _cumulative_x_drag = cx - _fixed_grab_x;
3924 _cumulative_y_drag = cy - _fixed_grab_y;
3928 cy = min ((double) _point->line().height(), cy);
3930 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3932 if (!_x_constrained) {
3933 _editor->snap_to_with_modifier (cx_frames, event);
3936 cx_frames = min (cx_frames, _point->line().maximum_time());
3938 float const fraction = 1.0 - (cy / _point->line().height());
3940 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3942 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3946 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3948 if (!movement_occurred) {
3952 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3953 _editor->reset_point_selection ();
3957 motion (event, false);
3960 _point->line().end_drag (_pushing, _final_index);
3961 _editor->commit_reversible_command ();
3965 ControlPointDrag::aborted (bool)
3967 _point->line().reset ();
3971 ControlPointDrag::active (Editing::MouseMode m)
3973 if (m == Editing::MouseDraw) {
3974 /* always active in mouse draw */
3978 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3979 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3982 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3985 _cumulative_y_drag (0)
3987 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3991 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3993 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3996 _item = &_line->grab_item ();
3998 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3999 origin, and ditto for y.
4002 double cx = event->button.x;
4003 double cy = event->button.y;
4005 _line->parent_group().canvas_to_item (cx, cy);
4007 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4012 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4013 /* no adjacent points */
4017 Drag::start_grab (event, _editor->cursors()->fader);
4019 /* store grab start in parent frame */
4024 double fraction = 1.0 - (cy / _line->height());
4026 _line->start_drag_line (before, after, fraction);
4028 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4032 LineDrag::motion (GdkEvent* event, bool)
4034 double dy = current_pointer_y() - last_pointer_y();
4036 if (event->button.state & Keyboard::SecondaryModifier) {
4040 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4042 _cumulative_y_drag = cy - _fixed_grab_y;
4045 cy = min ((double) _line->height(), cy);
4047 double const fraction = 1.0 - (cy / _line->height());
4050 /* we are ignoring x position for this drag, so we can just pass in anything */
4051 _line->drag_motion (0, fraction, true, false, ignored);
4053 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4057 LineDrag::finished (GdkEvent* event, bool movement_occured)
4059 if (movement_occured) {
4060 motion (event, false);
4061 _line->end_drag (false, 0);
4063 /* add a new control point on the line */
4065 AutomationTimeAxisView* atv;
4067 _line->end_drag (false, 0);
4069 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4070 framepos_t where = _editor->window_event_sample (event, 0, 0);
4071 atv->add_automation_event (event, where, event->button.y, false);
4075 _editor->commit_reversible_command ();
4079 LineDrag::aborted (bool)
4084 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4087 _cumulative_x_drag (0)
4089 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4093 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4095 Drag::start_grab (event);
4097 _line = reinterpret_cast<Line*> (_item);
4100 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4102 double cx = event->button.x;
4103 double cy = event->button.y;
4105 _item->parent()->canvas_to_item (cx, cy);
4107 /* store grab start in parent frame */
4108 _region_view_grab_x = cx;
4110 _before = *(float*) _item->get_data ("position");
4112 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4114 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4118 FeatureLineDrag::motion (GdkEvent*, bool)
4120 double dx = _drags->current_pointer_x() - last_pointer_x();
4122 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4124 _cumulative_x_drag += dx;
4126 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4135 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4137 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4139 float *pos = new float;
4142 _line->set_data ("position", pos);
4148 FeatureLineDrag::finished (GdkEvent*, bool)
4150 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4151 _arv->update_transient(_before, _before);
4155 FeatureLineDrag::aborted (bool)
4160 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4162 , _vertical_only (false)
4164 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4168 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4170 Drag::start_grab (event);
4171 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4175 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4182 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4184 framepos_t grab = grab_frame ();
4185 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4186 _editor->snap_to_with_modifier (grab, event);
4188 grab = raw_grab_frame ();
4191 /* base start and end on initial click position */
4201 if (current_pointer_y() < grab_y()) {
4202 y1 = current_pointer_y();
4205 y2 = current_pointer_y();
4209 if (start != end || y1 != y2) {
4211 double x1 = _editor->sample_to_pixel (start);
4212 double x2 = _editor->sample_to_pixel (end);
4213 const double min_dimension = 2.0;
4215 if (_vertical_only) {
4216 /* fixed 10 pixel width */
4220 x2 = min (x1 - min_dimension, x2);
4222 x2 = max (x1 + min_dimension, x2);
4227 y2 = min (y1 - min_dimension, y2);
4229 y2 = max (y1 + min_dimension, y2);
4232 /* translate rect into item space and set */
4234 ArdourCanvas::Rect r (x1, y1, x2, y2);
4236 /* this drag is a _trackview_only == true drag, so the y1 and
4237 * y2 (computed using current_pointer_y() and grab_y()) will be
4238 * relative to the top of the trackview group). The
4239 * rubberband rect has the same parent/scroll offset as the
4240 * the trackview group, so we can use the "r" rect directly
4241 * to set the shape of the rubberband.
4244 _editor->rubberband_rect->set (r);
4245 _editor->rubberband_rect->show();
4246 _editor->rubberband_rect->raise_to_top();
4248 show_verbose_cursor_time (pf);
4250 do_select_things (event, true);
4255 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4259 framepos_t grab = grab_frame ();
4260 framepos_t lpf = last_pointer_frame ();
4262 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4263 grab = raw_grab_frame ();
4264 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4278 if (current_pointer_y() < grab_y()) {
4279 y1 = current_pointer_y();
4282 y2 = current_pointer_y();
4286 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4290 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4292 if (movement_occurred) {
4294 motion (event, false);
4295 do_select_things (event, false);
4301 bool do_deselect = true;
4302 MidiTimeAxisView* mtv;
4304 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4306 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4307 /* nothing selected */
4308 add_midi_region (mtv);
4309 do_deselect = false;
4313 /* do not deselect if Primary or Tertiary (toggle-select or
4314 * extend-select are pressed.
4317 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4318 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4325 _editor->rubberband_rect->hide();
4329 RubberbandSelectDrag::aborted (bool)
4331 _editor->rubberband_rect->hide ();
4334 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4335 : RegionDrag (e, i, p, v)
4337 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4341 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4343 Drag::start_grab (event, cursor);
4345 show_verbose_cursor_time (adjusted_current_frame (event));
4349 TimeFXDrag::motion (GdkEvent* event, bool)
4351 RegionView* rv = _primary;
4352 StreamView* cv = rv->get_time_axis_view().view ();
4354 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4355 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4356 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4358 framepos_t const pf = adjusted_current_frame (event);
4360 if (pf > rv->region()->position()) {
4361 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4364 show_verbose_cursor_time (pf);
4368 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4370 _primary->get_time_axis_view().hide_timestretch ();
4372 if (!movement_occurred) {
4376 if (last_pointer_frame() < _primary->region()->position()) {
4377 /* backwards drag of the left edge - not usable */
4381 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4383 float percentage = (double) newlen / (double) _primary->region()->length();
4385 #ifndef USE_RUBBERBAND
4386 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4387 if (_primary->region()->data_type() == DataType::AUDIO) {
4388 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4392 if (!_editor->get_selection().regions.empty()) {
4393 /* primary will already be included in the selection, and edit
4394 group shared editing will propagate selection across
4395 equivalent regions, so just use the current region
4399 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4400 error << _("An error occurred while executing time stretch operation") << endmsg;
4406 TimeFXDrag::aborted (bool)
4408 _primary->get_time_axis_view().hide_timestretch ();
4411 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4414 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4418 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4420 Drag::start_grab (event);
4424 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4426 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4430 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4432 if (movement_occurred && _editor->session()) {
4433 /* make sure we stop */
4434 _editor->session()->request_transport_speed (0.0);
4439 ScrubDrag::aborted (bool)
4444 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4448 , _original_pointer_time_axis (-1)
4449 , _time_selection_at_start (!_editor->get_selection().time.empty())
4451 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4453 if (_time_selection_at_start) {
4454 start_at_start = _editor->get_selection().time.start();
4455 end_at_start = _editor->get_selection().time.end_frame();
4460 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4462 if (_editor->session() == 0) {
4466 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4468 switch (_operation) {
4469 case CreateSelection:
4470 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4475 cursor = _editor->cursors()->selector;
4476 Drag::start_grab (event, cursor);
4479 case SelectionStartTrim:
4480 if (_editor->clicked_axisview) {
4481 _editor->clicked_axisview->order_selection_trims (_item, true);
4483 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4486 case SelectionEndTrim:
4487 if (_editor->clicked_axisview) {
4488 _editor->clicked_axisview->order_selection_trims (_item, false);
4490 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4494 Drag::start_grab (event, cursor);
4497 case SelectionExtend:
4498 Drag::start_grab (event, cursor);
4502 if (_operation == SelectionMove) {
4503 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4505 show_verbose_cursor_time (adjusted_current_frame (event));
4508 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4512 SelectionDrag::setup_pointer_frame_offset ()
4514 switch (_operation) {
4515 case CreateSelection:
4516 _pointer_frame_offset = 0;
4519 case SelectionStartTrim:
4521 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4524 case SelectionEndTrim:
4525 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4528 case SelectionExtend:
4534 SelectionDrag::motion (GdkEvent* event, bool first_move)
4536 framepos_t start = 0;
4538 framecnt_t length = 0;
4539 framecnt_t distance = 0;
4541 framepos_t const pending_position = adjusted_current_frame (event);
4543 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4547 switch (_operation) {
4548 case CreateSelection:
4550 framepos_t grab = grab_frame ();
4553 grab = adjusted_current_frame (event, false);
4554 if (grab < pending_position) {
4555 _editor->snap_to (grab, RoundDownMaybe);
4557 _editor->snap_to (grab, RoundUpMaybe);
4561 if (pending_position < grab) {
4562 start = pending_position;
4565 end = pending_position;
4569 /* first drag: Either add to the selection
4570 or create a new selection
4577 /* adding to the selection */
4578 _editor->set_selected_track_as_side_effect (Selection::Add);
4579 _editor->clicked_selection = _editor->selection->add (start, end);
4586 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4587 _editor->set_selected_track_as_side_effect (Selection::Set);
4590 _editor->clicked_selection = _editor->selection->set (start, end);
4594 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4595 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4596 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4598 _editor->selection->add (atest);
4602 /* select all tracks within the rectangle that we've marked out so far */
4603 TrackViewList new_selection;
4604 TrackViewList& all_tracks (_editor->track_views);
4606 ArdourCanvas::Coord const top = grab_y();
4607 ArdourCanvas::Coord const bottom = current_pointer_y();
4609 if (top >= 0 && bottom >= 0) {
4611 //first, find the tracks that are covered in the y range selection
4612 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4613 if ((*i)->covered_by_y_range (top, bottom)) {
4614 new_selection.push_back (*i);
4618 //now find any tracks that are GROUPED with the tracks we selected
4619 TrackViewList grouped_add = new_selection;
4620 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4621 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4622 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4623 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4624 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4625 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4626 grouped_add.push_back (*j);
4631 //now compare our list with the current selection, and add or remove as necessary
4632 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4633 TrackViewList tracks_to_add;
4634 TrackViewList tracks_to_remove;
4635 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4636 if ( !_editor->selection->tracks.contains ( *i ) )
4637 tracks_to_add.push_back ( *i );
4638 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4639 if ( !grouped_add.contains ( *i ) )
4640 tracks_to_remove.push_back ( *i );
4641 _editor->selection->add(tracks_to_add);
4642 _editor->selection->remove(tracks_to_remove);
4648 case SelectionStartTrim:
4650 start = _editor->selection->time[_editor->clicked_selection].start;
4651 end = _editor->selection->time[_editor->clicked_selection].end;
4653 if (pending_position > end) {
4656 start = pending_position;
4660 case SelectionEndTrim:
4662 start = _editor->selection->time[_editor->clicked_selection].start;
4663 end = _editor->selection->time[_editor->clicked_selection].end;
4665 if (pending_position < start) {
4668 end = pending_position;
4675 start = _editor->selection->time[_editor->clicked_selection].start;
4676 end = _editor->selection->time[_editor->clicked_selection].end;
4678 length = end - start;
4679 distance = pending_position - start;
4680 start = pending_position;
4681 _editor->snap_to (start);
4683 end = start + length;
4687 case SelectionExtend:
4692 switch (_operation) {
4694 if (_time_selection_at_start) {
4695 _editor->selection->move_time (distance);
4699 _editor->selection->replace (_editor->clicked_selection, start, end);
4703 if (_operation == SelectionMove) {
4704 show_verbose_cursor_time(start);
4706 show_verbose_cursor_time(pending_position);
4711 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4713 Session* s = _editor->session();
4715 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4716 if (movement_occurred) {
4717 motion (event, false);
4718 /* XXX this is not object-oriented programming at all. ick */
4719 if (_editor->selection->time.consolidate()) {
4720 _editor->selection->TimeChanged ();
4723 /* XXX what if its a music time selection? */
4725 if ( s->get_play_range() && s->transport_rolling() ) {
4726 s->request_play_range (&_editor->selection->time, true);
4728 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4729 if (_operation == SelectionEndTrim)
4730 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4732 s->request_locate (_editor->get_selection().time.start());
4738 /* just a click, no pointer movement.
4741 if (_operation == SelectionExtend) {
4742 if (_time_selection_at_start) {
4743 framepos_t pos = adjusted_current_frame (event, false);
4744 framepos_t start = min (pos, start_at_start);
4745 framepos_t end = max (pos, end_at_start);
4746 _editor->selection->set (start, end);
4749 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4750 if (_editor->clicked_selection) {
4751 _editor->selection->remove (_editor->clicked_selection);
4754 if (!_editor->clicked_selection) {
4755 _editor->selection->clear_time();
4760 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4761 _editor->selection->set (_editor->clicked_axisview);
4764 if (s && s->get_play_range () && s->transport_rolling()) {
4765 s->request_stop (false, false);
4770 _editor->stop_canvas_autoscroll ();
4771 _editor->clicked_selection = 0;
4772 _editor->commit_reversible_selection_op ();
4776 SelectionDrag::aborted (bool)
4781 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4782 : Drag (e, i, false),
4786 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4788 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4789 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4790 physical_screen_height (_editor->get_window())));
4791 _drag_rect->hide ();
4793 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4794 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4797 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4799 /* normal canvas items will be cleaned up when their parent group is deleted. But
4800 this item is created as the child of a long-lived parent group, and so we
4801 need to explicitly delete it.
4807 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4809 if (_editor->session() == 0) {
4813 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4815 if (!_editor->temp_location) {
4816 _editor->temp_location = new Location (*_editor->session());
4819 switch (_operation) {
4820 case CreateSkipMarker:
4821 case CreateRangeMarker:
4822 case CreateTransportMarker:
4823 case CreateCDMarker:
4825 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4830 cursor = _editor->cursors()->selector;
4834 Drag::start_grab (event, cursor);
4836 show_verbose_cursor_time (adjusted_current_frame (event));
4840 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4842 framepos_t start = 0;
4844 ArdourCanvas::Rectangle *crect;
4846 switch (_operation) {
4847 case CreateSkipMarker:
4848 crect = _editor->range_bar_drag_rect;
4850 case CreateRangeMarker:
4851 crect = _editor->range_bar_drag_rect;
4853 case CreateTransportMarker:
4854 crect = _editor->transport_bar_drag_rect;
4856 case CreateCDMarker:
4857 crect = _editor->cd_marker_bar_drag_rect;
4860 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4865 framepos_t const pf = adjusted_current_frame (event);
4867 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4868 framepos_t grab = grab_frame ();
4869 _editor->snap_to (grab);
4871 if (pf < grab_frame()) {
4879 /* first drag: Either add to the selection
4880 or create a new selection.
4885 _editor->temp_location->set (start, end);
4889 update_item (_editor->temp_location);
4891 //_drag_rect->raise_to_top();
4897 _editor->temp_location->set (start, end);
4899 double x1 = _editor->sample_to_pixel (start);
4900 double x2 = _editor->sample_to_pixel (end);
4904 update_item (_editor->temp_location);
4907 show_verbose_cursor_time (pf);
4912 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4914 Location * newloc = 0;
4918 if (movement_occurred) {
4919 motion (event, false);
4922 switch (_operation) {
4923 case CreateSkipMarker:
4924 case CreateRangeMarker:
4925 case CreateCDMarker:
4927 XMLNode &before = _editor->session()->locations()->get_state();
4928 if (_operation == CreateSkipMarker) {
4929 _editor->begin_reversible_command (_("new skip marker"));
4930 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4931 flags = Location::IsRangeMarker | Location::IsSkip;
4932 _editor->range_bar_drag_rect->hide();
4933 } else if (_operation == CreateCDMarker) {
4934 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4935 _editor->begin_reversible_command (_("new CD marker"));
4936 flags = Location::IsRangeMarker | Location::IsCDMarker;
4937 _editor->cd_marker_bar_drag_rect->hide();
4939 _editor->begin_reversible_command (_("new skip marker"));
4940 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4941 flags = Location::IsRangeMarker;
4942 _editor->range_bar_drag_rect->hide();
4944 newloc = new Location (
4945 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4948 _editor->session()->locations()->add (newloc, true);
4949 XMLNode &after = _editor->session()->locations()->get_state();
4950 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4951 _editor->commit_reversible_command ();
4955 case CreateTransportMarker:
4956 // popup menu to pick loop or punch
4957 _editor->new_transport_marker_context_menu (&event->button, _item);
4963 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4965 if (_operation == CreateTransportMarker) {
4967 /* didn't drag, so just locate */
4969 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4971 } else if (_operation == CreateCDMarker) {
4973 /* didn't drag, but mark is already created so do
4976 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4981 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4983 if (end == max_framepos) {
4984 end = _editor->session()->current_end_frame ();
4987 if (start == max_framepos) {
4988 start = _editor->session()->current_start_frame ();
4991 switch (_editor->mouse_mode) {
4993 /* find the two markers on either side and then make the selection from it */
4994 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4998 /* find the two markers on either side of the click and make the range out of it */
4999 _editor->selection->set (start, end);
5008 _editor->stop_canvas_autoscroll ();
5012 RangeMarkerBarDrag::aborted (bool movement_occured)
5014 if (movement_occured) {
5015 _drag_rect->hide ();
5020 RangeMarkerBarDrag::update_item (Location* location)
5022 double const x1 = _editor->sample_to_pixel (location->start());
5023 double const x2 = _editor->sample_to_pixel (location->end());
5025 _drag_rect->set_x0 (x1);
5026 _drag_rect->set_x1 (x2);
5029 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5031 , _cumulative_dx (0)
5032 , _cumulative_dy (0)
5034 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5036 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5038 _region = &_primary->region_view ();
5039 _note_height = _region->midi_stream_view()->note_height ();
5043 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5045 Drag::start_grab (event);
5047 if (!(_was_selected = _primary->selected())) {
5049 /* tertiary-click means extend selection - we'll do that on button release,
5050 so don't add it here, because otherwise we make it hard to figure
5051 out the "extend-to" range.
5054 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5057 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5060 _region->note_selected (_primary, true);
5062 _region->unique_select (_primary);
5065 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5066 _editor->commit_reversible_selection_op();
5071 /** @return Current total drag x change in frames */
5073 NoteDrag::total_dx () const
5076 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5078 /* primary note time */
5079 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5081 /* new time of the primary note in session frames */
5082 frameoffset_t st = n + dx;
5084 framepos_t const rp = _region->region()->position ();
5086 /* prevent the note being dragged earlier than the region's position */
5089 /* snap and return corresponding delta */
5090 return _region->snap_frame_to_frame (st - rp) + rp - n;
5093 /** @return Current total drag y change in note number */
5095 NoteDrag::total_dy () const
5097 MidiStreamView* msv = _region->midi_stream_view ();
5098 double const y = _region->midi_view()->y_position ();
5099 /* new current note */
5100 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5102 n = max (msv->lowest_note(), n);
5103 n = min (msv->highest_note(), n);
5104 /* and work out delta */
5105 return n - msv->y_to_note (grab_y() - y);
5109 NoteDrag::motion (GdkEvent *, bool)
5111 /* Total change in x and y since the start of the drag */
5112 frameoffset_t const dx = total_dx ();
5113 int8_t const dy = total_dy ();
5115 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5116 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5117 double const tdy = -dy * _note_height - _cumulative_dy;
5120 _cumulative_dx += tdx;
5121 _cumulative_dy += tdy;
5123 int8_t note_delta = total_dy();
5125 _region->move_selection (tdx, tdy, note_delta);
5127 /* the new note value may be the same as the old one, but we
5128 * don't know what that means because the selection may have
5129 * involved more than one note and we might be doing something
5130 * odd with them. so show the note value anyway, always.
5134 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5136 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5137 (int) floor ((double)new_note));
5139 show_verbose_cursor_text (buf);
5144 NoteDrag::finished (GdkEvent* ev, bool moved)
5147 /* no motion - select note */
5149 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5150 _editor->current_mouse_mode() == Editing::MouseDraw) {
5152 bool changed = false;
5154 if (_was_selected) {
5155 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5157 _region->note_deselected (_primary);
5161 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5162 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5164 if (!extend && !add && _region->selection_size() > 1) {
5165 _region->unique_select (_primary);
5167 } else if (extend) {
5168 _region->note_selected (_primary, true, true);
5171 /* it was added during button press */
5176 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5177 _editor->commit_reversible_selection_op();
5181 _region->note_dropped (_primary, total_dx(), total_dy());
5186 NoteDrag::aborted (bool)
5191 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5192 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5193 : Drag (editor, atv->base_item ())
5195 , _y_origin (atv->y_position())
5196 , _nothing_to_drag (false)
5198 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5199 setup (atv->lines ());
5202 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5203 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5204 : Drag (editor, rv->get_canvas_group ())
5206 , _y_origin (rv->get_time_axis_view().y_position())
5207 , _nothing_to_drag (false)
5210 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5212 list<boost::shared_ptr<AutomationLine> > lines;
5214 AudioRegionView* audio_view;
5215 AutomationRegionView* automation_view;
5216 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5217 lines.push_back (audio_view->get_gain_line ());
5218 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5219 lines.push_back (automation_view->line ());
5222 error << _("Automation range drag created for invalid region type") << endmsg;
5228 /** @param lines AutomationLines to drag.
5229 * @param offset Offset from the session start to the points in the AutomationLines.
5232 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5234 /* find the lines that overlap the ranges being dragged */
5235 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5236 while (i != lines.end ()) {
5237 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5240 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5242 /* check this range against all the AudioRanges that we are using */
5243 list<AudioRange>::const_iterator k = _ranges.begin ();
5244 while (k != _ranges.end()) {
5245 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5251 /* add it to our list if it overlaps at all */
5252 if (k != _ranges.end()) {
5257 _lines.push_back (n);
5263 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5267 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5269 return 1.0 - ((global_y - _y_origin) / line->height());
5273 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5275 const double v = list->eval(x);
5276 return _integral ? rint(v) : v;
5280 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5282 Drag::start_grab (event, cursor);
5284 /* Get line states before we start changing things */
5285 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5286 i->state = &i->line->get_state ();
5287 i->original_fraction = y_fraction (i->line, current_pointer_y());
5290 if (_ranges.empty()) {
5292 /* No selected time ranges: drag all points */
5293 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5294 uint32_t const N = i->line->npoints ();
5295 for (uint32_t j = 0; j < N; ++j) {
5296 i->points.push_back (i->line->nth (j));
5302 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5304 framecnt_t const half = (i->start + i->end) / 2;
5306 /* find the line that this audio range starts in */
5307 list<Line>::iterator j = _lines.begin();
5308 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5312 if (j != _lines.end()) {
5313 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5315 /* j is the line that this audio range starts in; fade into it;
5316 64 samples length plucked out of thin air.
5319 framepos_t a = i->start + 64;
5324 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5325 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5327 the_list->editor_add (p, value (the_list, p));
5328 the_list->editor_add (q, value (the_list, q));
5331 /* same thing for the end */
5334 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5338 if (j != _lines.end()) {
5339 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5341 /* j is the line that this audio range starts in; fade out of it;
5342 64 samples length plucked out of thin air.
5345 framepos_t b = i->end - 64;
5350 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5351 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5353 the_list->editor_add (p, value (the_list, p));
5354 the_list->editor_add (q, value (the_list, q));
5358 _nothing_to_drag = true;
5360 /* Find all the points that should be dragged and put them in the relevant
5361 points lists in the Line structs.
5364 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5366 uint32_t const N = i->line->npoints ();
5367 for (uint32_t j = 0; j < N; ++j) {
5369 /* here's a control point on this line */
5370 ControlPoint* p = i->line->nth (j);
5371 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5373 /* see if it's inside a range */
5374 list<AudioRange>::const_iterator k = _ranges.begin ();
5375 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5379 if (k != _ranges.end()) {
5380 /* dragging this point */
5381 _nothing_to_drag = false;
5382 i->points.push_back (p);
5388 if (_nothing_to_drag) {
5392 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5393 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5398 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5400 if (_nothing_to_drag) {
5404 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5405 float const f = y_fraction (l->line, current_pointer_y());
5406 /* we are ignoring x position for this drag, so we can just pass in anything */
5408 l->line->drag_motion (0, f, true, false, ignored);
5409 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5414 AutomationRangeDrag::finished (GdkEvent* event, bool)
5416 if (_nothing_to_drag) {
5420 motion (event, false);
5421 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5422 i->line->end_drag (false, 0);
5425 _editor->commit_reversible_command ();
5429 AutomationRangeDrag::aborted (bool)
5431 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5436 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5438 , initial_time_axis_view (itav)
5440 /* note that time_axis_view may be null if the regionview was created
5441 * as part of a copy operation.
5443 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5444 layer = v->region()->layer ();
5445 initial_y = v->get_canvas_group()->position().y;
5446 initial_playlist = v->region()->playlist ();
5447 initial_position = v->region()->position ();
5448 initial_end = v->region()->position () + v->region()->length ();
5451 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5452 : Drag (e, i->canvas_item ())
5455 , _cumulative_dx (0)
5457 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5458 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5463 PatchChangeDrag::motion (GdkEvent* ev, bool)
5465 framepos_t f = adjusted_current_frame (ev);
5466 boost::shared_ptr<Region> r = _region_view->region ();
5467 f = max (f, r->position ());
5468 f = min (f, r->last_frame ());
5470 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5471 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5472 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5473 _cumulative_dx = dxu;
5477 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5479 if (!movement_occurred) {
5483 boost::shared_ptr<Region> r (_region_view->region ());
5484 framepos_t f = adjusted_current_frame (ev);
5485 f = max (f, r->position ());
5486 f = min (f, r->last_frame ());
5488 _region_view->move_patch_change (
5490 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5495 PatchChangeDrag::aborted (bool)
5497 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5501 PatchChangeDrag::setup_pointer_frame_offset ()
5503 boost::shared_ptr<Region> region = _region_view->region ();
5504 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5507 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5508 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5515 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5517 _region_view->update_drag_selection (
5519 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5523 MidiRubberbandSelectDrag::deselect_things ()
5528 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5529 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5532 _vertical_only = true;
5536 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5538 double const y = _region_view->midi_view()->y_position ();
5540 y1 = max (0.0, y1 - y);
5541 y2 = max (0.0, y2 - y);
5543 _region_view->update_vertical_drag_selection (
5546 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5551 MidiVerticalSelectDrag::deselect_things ()
5556 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5557 : RubberbandSelectDrag (e, i)
5563 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5565 if (drag_in_progress) {
5566 /* We just want to select things at the end of the drag, not during it */
5570 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5572 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5574 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5576 _editor->commit_reversible_selection_op ();
5580 EditorRubberbandSelectDrag::deselect_things ()
5582 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5584 _editor->selection->clear_tracks();
5585 _editor->selection->clear_regions();
5586 _editor->selection->clear_points ();
5587 _editor->selection->clear_lines ();
5588 _editor->selection->clear_midi_notes ();
5590 _editor->commit_reversible_selection_op();
5593 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5598 _note[0] = _note[1] = 0;
5601 NoteCreateDrag::~NoteCreateDrag ()
5607 NoteCreateDrag::grid_frames (framepos_t t) const
5610 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5612 grid_beats = Evoral::Beats(1);
5615 return _region_view->region_beats_to_region_frames (grid_beats);
5619 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5621 Drag::start_grab (event, cursor);
5623 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5625 framepos_t pf = _drags->current_pointer_frame ();
5626 framecnt_t const g = grid_frames (pf);
5628 /* Hack so that we always snap to the note that we are over, instead of snapping
5629 to the next one if we're more than halfway through the one we're over.
5631 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5635 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5636 _note[1] = _note[0];
5638 MidiStreamView* sv = _region_view->midi_stream_view ();
5639 double const x = _editor->sample_to_pixel (_note[0]);
5640 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5642 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5643 _drag_rect->set_outline_all ();
5644 _drag_rect->set_outline_color (0xffffff99);
5645 _drag_rect->set_fill_color (0xffffff66);
5649 NoteCreateDrag::motion (GdkEvent* event, bool)
5651 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5652 double const x0 = _editor->sample_to_pixel (_note[0]);
5653 double const x1 = _editor->sample_to_pixel (_note[1]);
5654 _drag_rect->set_x0 (std::min(x0, x1));
5655 _drag_rect->set_x1 (std::max(x0, x1));
5659 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5661 if (!had_movement) {
5665 framepos_t const start = min (_note[0], _note[1]);
5666 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5668 framecnt_t const g = grid_frames (start);
5669 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5671 if (_editor->snap_mode() == SnapNormal && length < g) {
5675 Evoral::Beats length_beats = max (
5676 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5678 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5682 NoteCreateDrag::y_to_region (double y) const
5685 _region_view->get_canvas_group()->canvas_to_item (x, y);
5690 NoteCreateDrag::aborted (bool)
5695 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5700 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5704 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5706 Drag::start_grab (event, cursor);
5710 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5716 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5719 distance = _drags->current_pointer_x() - grab_x();
5720 len = ar->fade_in()->back()->when;
5722 distance = grab_x() - _drags->current_pointer_x();
5723 len = ar->fade_out()->back()->when;
5726 /* how long should it be ? */
5728 new_length = len + _editor->pixel_to_sample (distance);
5730 /* now check with the region that this is legal */
5732 new_length = ar->verify_xfade_bounds (new_length, start);
5735 arv->reset_fade_in_shape_width (ar, new_length);
5737 arv->reset_fade_out_shape_width (ar, new_length);
5742 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5748 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5751 distance = _drags->current_pointer_x() - grab_x();
5752 len = ar->fade_in()->back()->when;
5754 distance = grab_x() - _drags->current_pointer_x();
5755 len = ar->fade_out()->back()->when;
5758 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5760 _editor->begin_reversible_command ("xfade trim");
5761 ar->playlist()->clear_owned_changes ();
5764 ar->set_fade_in_length (new_length);
5766 ar->set_fade_out_length (new_length);
5769 /* Adjusting the xfade may affect other regions in the playlist, so we need
5770 to get undo Commands from the whole playlist rather than just the
5774 vector<Command*> cmds;
5775 ar->playlist()->rdiff (cmds);
5776 _editor->session()->add_commands (cmds);
5777 _editor->commit_reversible_command ();
5782 CrossfadeEdgeDrag::aborted (bool)
5785 // arv->redraw_start_xfade ();
5787 // arv->redraw_end_xfade ();
5791 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5792 : Drag (e, item, true)
5793 , line (new EditorCursor (*e))
5795 line->set_position (pos);
5799 RegionCutDrag::~RegionCutDrag ()
5805 RegionCutDrag::motion (GdkEvent*, bool)
5807 framepos_t where = _drags->current_pointer_frame();
5808 _editor->snap_to (where);
5810 line->set_position (where);
5814 RegionCutDrag::finished (GdkEvent*, bool)
5816 _editor->get_track_canvas()->canvas()->re_enter();
5818 framepos_t pos = _drags->current_pointer_frame();
5822 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5828 _editor->split_regions_at (pos, rs);
5832 RegionCutDrag::aborted (bool)