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 , _x_constrained (false)
222 , _y_constrained (false)
223 , _trackview_only (trackview_only)
224 , _move_threshold_passed (false)
225 , _starting_point_passed (false)
226 , _initially_vertical (false)
227 , _was_double_click (false)
228 , _raw_grab_frame (0)
230 , _last_pointer_frame (0)
237 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
243 _cursor_ctx = CursorContext::create (*_editor, cursor);
245 _cursor_ctx->change (cursor);
252 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
255 /* we set up x/y dragging constraints on first move */
257 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
259 setup_pointer_frame_offset ();
260 _grab_frame = adjusted_frame (_raw_grab_frame, event);
261 _last_pointer_frame = _grab_frame;
262 _last_pointer_x = _grab_x;
264 if (_trackview_only) {
265 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
268 _last_pointer_y = _grab_y;
272 if (!_editor->cursors()->is_invalid (cursor)) {
273 /* CAIROCANVAS need a variant here that passes *cursor */
274 _cursor_ctx = CursorContext::create (*_editor, cursor);
277 if (_editor->session() && _editor->session()->transport_rolling()) {
280 _was_rolling = false;
283 switch (_editor->snap_type()) {
284 case SnapToRegionStart:
285 case SnapToRegionEnd:
286 case SnapToRegionSync:
287 case SnapToRegionBoundary:
288 _editor->build_region_boundary_cache ();
295 /** Call to end a drag `successfully'. Ungrabs item and calls
296 * subclass' finished() method.
298 * @param event GDK event, or 0.
299 * @return true if some movement occurred, otherwise false.
302 Drag::end_grab (GdkEvent* event)
304 _editor->stop_canvas_autoscroll ();
308 finished (event, _move_threshold_passed);
310 _editor->verbose_cursor()->hide ();
313 return _move_threshold_passed;
317 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
321 if (f > _pointer_frame_offset) {
322 pos = f - _pointer_frame_offset;
326 _editor->snap_to_with_modifier (pos, event);
333 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
335 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
339 Drag::snap_delta (guint state) const
341 if (ArdourKeyboard::indicates_snap_delta (state)) {
349 Drag::current_pointer_x() const
351 return _drags->current_pointer_x ();
355 Drag::current_pointer_y () const
357 if (!_trackview_only) {
358 return _drags->current_pointer_y ();
361 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
365 Drag::setup_snap_delta (framepos_t pos)
367 framepos_t temp = pos;
368 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
369 _snap_delta = temp - pos;
373 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
375 /* check to see if we have moved in any way that matters since the last motion event */
376 if (_move_threshold_passed &&
377 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
378 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
382 pair<framecnt_t, int> const threshold = move_threshold ();
384 bool const old_move_threshold_passed = _move_threshold_passed;
386 if (!_move_threshold_passed) {
388 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
389 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
391 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
394 if (active (_editor->mouse_mode) && _move_threshold_passed) {
396 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
398 if (old_move_threshold_passed != _move_threshold_passed) {
402 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
403 _initially_vertical = true;
405 _initially_vertical = false;
407 if (Config->get_edit_mode() != Lock) {
408 if (event->motion.state & Gdk::BUTTON2_MASK) {
409 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
410 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
411 _x_constrained = false;
412 _y_constrained = true;
414 _x_constrained = true;
415 _y_constrained = false;
417 } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
418 // if dragging normally, the motion is constrained to the first direction of movement.
419 if (_initially_vertical) {
420 _x_constrained = true;
421 _y_constrained = false;
423 _x_constrained = false;
424 _y_constrained = true;
428 if (event->button.state & Gdk::BUTTON2_MASK) {
429 _x_constrained = false;
431 _x_constrained = true;
433 _y_constrained = false;
437 if (!from_autoscroll) {
438 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
441 if (!_editor->autoscroll_active() || from_autoscroll) {
444 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
446 motion (event, first_move && !_starting_point_passed);
448 if (first_move && !_starting_point_passed) {
449 _starting_point_passed = true;
452 _last_pointer_x = _drags->current_pointer_x ();
453 _last_pointer_y = current_pointer_y ();
454 _last_pointer_frame = adjusted_current_frame (event);
464 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
472 aborted (_move_threshold_passed);
474 _editor->stop_canvas_autoscroll ();
475 _editor->verbose_cursor()->hide ();
479 Drag::show_verbose_cursor_time (framepos_t frame)
481 _editor->verbose_cursor()->set_time (frame);
482 _editor->verbose_cursor()->show ();
486 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
488 _editor->verbose_cursor()->set_duration (start, end);
489 _editor->verbose_cursor()->show ();
493 Drag::show_verbose_cursor_text (string const & text)
495 _editor->verbose_cursor()->set (text);
496 _editor->verbose_cursor()->show ();
499 boost::shared_ptr<Region>
500 Drag::add_midi_region (MidiTimeAxisView* view)
502 if (_editor->session()) {
503 const TempoMap& map (_editor->session()->tempo_map());
504 framecnt_t pos = grab_frame();
505 const Meter& m = map.meter_at (pos);
506 /* not that the frame rate used here can be affected by pull up/down which
509 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
510 return view->add_region (grab_frame(), len, true);
513 return boost::shared_ptr<Region>();
516 struct EditorOrderTimeAxisViewSorter {
517 bool operator() (TimeAxisView* a, TimeAxisView* b) {
518 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
519 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
521 return ra->route()->order_key () < rb->route()->order_key ();
525 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
530 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
532 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
533 as some of the regions we are dragging may be on such tracks.
536 TrackViewList track_views = _editor->track_views;
537 track_views.sort (EditorOrderTimeAxisViewSorter ());
539 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
540 _time_axis_views.push_back (*i);
542 TimeAxisView::Children children_list = (*i)->get_child_list ();
543 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
544 _time_axis_views.push_back (j->get());
548 /* the list of views can be empty at this point if this is a region list-insert drag
551 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
552 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
555 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
559 RegionDrag::region_going_away (RegionView* v)
561 list<DraggingView>::iterator i = _views.begin ();
562 while (i != _views.end() && i->view != v) {
566 if (i != _views.end()) {
571 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
572 * or -1 if it is not found.
575 RegionDrag::find_time_axis_view (TimeAxisView* t) const
578 int const N = _time_axis_views.size ();
579 while (i < N && _time_axis_views[i] != t) {
583 if (_time_axis_views[i] != t) {
590 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
591 : RegionDrag (e, i, p, v)
594 , _last_pointer_time_axis_view (0)
595 , _last_pointer_layer (0)
600 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
604 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
606 Drag::start_grab (event, cursor);
607 setup_snap_delta (_last_frame_position);
609 show_verbose_cursor_time (_last_frame_position);
611 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
613 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
614 assert(_last_pointer_time_axis_view >= 0);
615 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
620 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
622 /* compute the amount of pointer motion in frames, and where
623 the region would be if we moved it by that much.
625 *pending_region_position = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
627 framepos_t sync_frame;
628 framecnt_t sync_offset;
631 sync_offset = _primary->region()->sync_offset (sync_dir);
633 /* we don't handle a sync point that lies before zero.
635 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
637 sync_frame = *pending_region_position + (sync_dir * sync_offset);
639 _editor->snap_to_with_modifier (sync_frame, event);
641 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - snap_delta (event->button.state);
644 *pending_region_position = _last_frame_position;
647 if (*pending_region_position > max_framepos - _primary->region()->length()) {
648 *pending_region_position = _last_frame_position;
653 bool const x_move_allowed = !_x_constrained;
655 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
657 /* x movement since last time (in pixels) */
658 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
660 /* total x movement */
661 framecnt_t total_dx = *pending_region_position;
662 if (regions_came_from_canvas()) {
663 total_dx = total_dx - grab_frame ();
666 /* check that no regions have gone off the start of the session */
667 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
668 if ((i->view->region()->position() + total_dx) < 0) {
670 *pending_region_position = _last_frame_position;
681 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
687 const int tavsize = _time_axis_views.size();
688 const int dt = delta > 0 ? +1 : -1;
690 int target = start + delta - skip;
692 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
693 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
695 while (current >= 0 && current != target) {
697 if (current < 0 && dt < 0) {
700 if (current >= tavsize && dt > 0) {
703 if (current < 0 || current >= tavsize) {
707 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
708 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
712 if (distance_only && current == start + delta) {
720 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
722 if (_y_constrained) {
726 const int tavsize = _time_axis_views.size();
727 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
728 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
729 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
731 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
732 /* already in the drop zone */
733 if (delta_track >= 0) {
734 /* downward motion - OK if others are still not in the dropzone */
743 } else if (n >= tavsize) {
744 /* downward motion into drop zone. That's fine. */
748 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
749 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
750 /* not a track, or the wrong type */
754 double const l = i->layer + delta_layer;
756 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
757 mode to allow the user to place a region below another on layer 0.
759 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
760 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
761 If it has, the layers will be munged later anyway, so it's ok.
767 /* all regions being dragged are ok with this change */
771 struct DraggingViewSorter {
772 bool operator() (const DraggingView& a, const DraggingView& b) {
773 return a.time_axis_view < b.time_axis_view;
778 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
780 double delta_layer = 0;
781 int delta_time_axis_view = 0;
782 int current_pointer_time_axis_view = -1;
784 assert (!_views.empty ());
786 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
788 /* Find the TimeAxisView that the pointer is now over */
789 const double cur_y = current_pointer_y ();
790 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
791 TimeAxisView* tv = r.first;
793 if (!tv && cur_y < 0) {
794 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
798 /* find drop-zone y-position */
799 Coord last_track_bottom_edge;
800 last_track_bottom_edge = 0;
801 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
802 if (!(*t)->hidden()) {
803 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
808 if (tv && tv->view()) {
809 /* the mouse is over a track */
810 double layer = r.second;
812 if (first_move && tv->view()->layer_display() == Stacked) {
813 tv->view()->set_layer_display (Expanded);
816 /* Here's the current pointer position in terms of time axis view and layer */
817 current_pointer_time_axis_view = find_time_axis_view (tv);
818 assert(current_pointer_time_axis_view >= 0);
820 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
822 /* Work out the change in y */
824 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
825 if (!rtv || !rtv->is_track()) {
826 /* ignore busses early on. we can't move any regions on them */
827 } else if (_last_pointer_time_axis_view < 0) {
828 /* Was in the drop-zone, now over a track.
829 * Hence it must be an upward move (from the bottom)
831 * track_index is still -1, so delta must be set to
832 * move up the correct number of tracks from the bottom.
834 * This is necessary because steps may be skipped if
835 * the bottom-most track is not a valid target and/or
836 * if there are hidden tracks at the bottom.
837 * Hence the initial offset (_ddropzone) as well as the
838 * last valid pointer position (_pdropzone) need to be
839 * taken into account.
841 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
843 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
846 /* TODO needs adjustment per DraggingView,
848 * e.g. select one region on the top-layer of a track
849 * and one region which is at the bottom-layer of another track
852 * Indicated drop-zones and layering is wrong.
853 * and may infer additional layers on the target-track
854 * (depending how many layers the original track had).
856 * Or select two regions (different layers) on a same track,
857 * move across a non-layer track.. -> layering info is lost.
858 * on drop either of the regions may be on top.
860 * Proposed solution: screw it :) well,
861 * don't use delta_layer, use an absolute value
862 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
863 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
864 * 3) iterate over all DraggingView, find the one that is over the track with most layers
865 * 4) proportionally scale layer to layers available on target
867 delta_layer = current_pointer_layer - _last_pointer_layer;
870 /* for automation lanes, there is a TimeAxisView but no ->view()
871 * if (!tv) -> dropzone
873 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
874 /* Moving into the drop-zone.. */
875 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
876 /* delta_time_axis_view may not be sufficient to move into the DZ
877 * the mouse may enter it, but it may not be a valid move due to
880 * -> remember the delta needed to move into the dropzone
882 _ddropzone = delta_time_axis_view;
883 /* ..but subtract hidden tracks (or routes) at the bottom.
884 * we silently move mover them
886 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
887 - _time_axis_views.size();
889 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
890 /* move around inside the zone.
891 * This allows to move further down until all regions are in the zone.
893 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
894 assert(ptr_y >= last_track_bottom_edge);
895 assert(_ddropzone > 0);
897 /* calculate mouse position in 'tracks' below last track. */
898 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
899 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
901 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
903 delta_time_axis_view = dzpos - _pdropzone;
904 } else if (dzpos < _pdropzone && _ndropzone > 0) {
905 // move up inside the DZ
906 delta_time_axis_view = dzpos - _pdropzone;
910 /* Work out the change in x */
911 framepos_t pending_region_position;
912 double const x_delta = compute_x_delta (event, &pending_region_position);
913 _last_frame_position = pending_region_position;
915 /* calculate hidden tracks in current y-axis delta */
917 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
918 /* The mouse is more than one track below the dropzone.
919 * distance calculation is not needed (and would not work, either
920 * because the dropzone is "packed").
922 * Except when [partially] moving regions out of dropzone in a large step.
923 * (the mouse may or may not remain in the DZ)
924 * Hidden tracks at the bottom of the TAV need to be skipped.
926 * This also handles the case if the mouse entered the DZ
927 * in a large step (exessive delta), either due to fast-movement,
928 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
930 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
931 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
933 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
934 -_time_axis_views.size() - dt;
937 else if (_last_pointer_time_axis_view < 0) {
938 /* Moving out of the zone. Check for hidden tracks at the bottom. */
939 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
940 -_time_axis_views.size() - delta_time_axis_view;
942 /* calculate hidden tracks that are skipped by the pointer movement */
943 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
944 - _last_pointer_time_axis_view
945 - delta_time_axis_view;
948 /* Verify change in y */
949 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
950 /* this y movement is not allowed, so do no y movement this time */
951 delta_time_axis_view = 0;
956 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
957 /* haven't reached next snap point, and we're not switching
958 trackviews nor layers. nothing to do.
963 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
964 PlaylistDropzoneMap playlist_dropzone_map;
965 _ndropzone = 0; // number of elements currently in the dropzone
968 /* sort views by time_axis.
969 * This retains track order in the dropzone, regardless
970 * of actual selection order
972 _views.sort (DraggingViewSorter());
974 /* count number of distinct tracks of all regions
975 * being dragged, used for dropzone.
978 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
979 if (i->time_axis_view != prev_track) {
980 prev_track = i->time_axis_view;
986 _views.back().time_axis_view -
987 _views.front().time_axis_view;
989 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
990 - _views.back().time_axis_view;
992 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
996 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
998 RegionView* rv = i->view;
1003 if (rv->region()->locked() || rv->region()->video_locked()) {
1010 /* reparent the regionview into a group above all
1014 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1015 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1016 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1017 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1018 /* move the item so that it continues to appear at the
1019 same location now that its parent has changed.
1021 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1024 /* If we have moved tracks, we'll fudge the layer delta so that the
1025 region gets moved back onto layer 0 on its new track; this avoids
1026 confusion when dragging regions from non-zero layers onto different
1029 double this_delta_layer = delta_layer;
1030 if (delta_time_axis_view != 0) {
1031 this_delta_layer = - i->layer;
1034 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1036 int track_index = i->time_axis_view + this_delta_time_axis_view;
1037 assert(track_index >= 0);
1039 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1040 /* Track is in the Dropzone */
1042 i->time_axis_view = track_index;
1043 assert(i->time_axis_view >= (int) _time_axis_views.size());
1046 double yposition = 0;
1047 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1048 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1051 /* store index of each new playlist as a negative count, starting at -1 */
1053 if (pdz == playlist_dropzone_map.end()) {
1054 /* compute where this new track (which doesn't exist yet) will live
1057 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1059 /* How high is this region view ? */
1061 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1062 ArdourCanvas::Rect bbox;
1065 bbox = obbox.get ();
1068 last_track_bottom_edge += bbox.height();
1070 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1073 yposition = pdz->second;
1076 /* values are zero or negative, hence the use of min() */
1077 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1082 /* The TimeAxisView that this region is now over */
1083 TimeAxisView* current_tv = _time_axis_views[track_index];
1085 /* Ensure it is moved from stacked -> expanded if appropriate */
1086 if (current_tv->view()->layer_display() == Stacked) {
1087 current_tv->view()->set_layer_display (Expanded);
1090 /* We're only allowed to go -ve in layer on Expanded views */
1091 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1092 this_delta_layer = - i->layer;
1096 rv->set_height (current_tv->view()->child_height ());
1098 /* Update show/hidden status as the region view may have come from a hidden track,
1099 or have moved to one.
1101 if (current_tv->hidden ()) {
1102 rv->get_canvas_group()->hide ();
1104 rv->get_canvas_group()->show ();
1107 /* Update the DraggingView */
1108 i->time_axis_view = track_index;
1109 i->layer += this_delta_layer;
1112 _editor->mouse_brush_insert_region (rv, pending_region_position);
1116 /* Get the y coordinate of the top of the track that this region is now over */
1117 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1119 /* And adjust for the layer that it should be on */
1120 StreamView* cv = current_tv->view ();
1121 switch (cv->layer_display ()) {
1125 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1128 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1132 /* need to get the parent of the regionview
1133 * canvas group and get its position in
1134 * equivalent coordinate space as the trackview
1135 * we are now dragging over.
1138 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1143 /* Now move the region view */
1144 rv->move (x_delta, y_delta);
1146 } /* foreach region */
1148 _total_x_delta += x_delta;
1150 if (x_delta != 0 && !_brushing) {
1151 show_verbose_cursor_time (_last_frame_position);
1154 /* keep track of pointer movement */
1156 /* the pointer is currently over a time axis view */
1158 if (_last_pointer_time_axis_view < 0) {
1159 /* last motion event was not over a time axis view
1160 * or last y-movement out of the dropzone was not valid
1163 if (delta_time_axis_view < 0) {
1164 /* in the drop zone, moving up */
1166 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1167 * We do not use negative _last_pointer_time_axis_view because
1168 * the dropzone is "packed" (the actual track offset is ignored)
1170 * As opposed to the actual number
1171 * of elements in the dropzone (_ndropzone)
1172 * _pdropzone is not constrained. This is necessary
1173 * to allow moving multiple regions with y-distance
1176 * There can be 0 elements in the dropzone,
1177 * even though the drag-pointer is inside the DZ.
1180 * [ Audio-track, Midi-track, Audio-track, DZ ]
1181 * move regions from both audio tracks at the same time into the
1182 * DZ by grabbing the region in the bottom track.
1184 assert(current_pointer_time_axis_view >= 0);
1185 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1189 /* only move out of the zone if the movement is OK */
1190 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1191 assert(delta_time_axis_view < 0);
1192 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1193 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1194 * the current position can be calculated as follows:
1196 // a well placed oofus attack can still throw this off.
1197 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1198 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1201 /* last motion event was also over a time axis view */
1202 _last_pointer_time_axis_view += delta_time_axis_view;
1203 assert(_last_pointer_time_axis_view >= 0);
1208 /* the pointer is not over a time axis view */
1209 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1210 _pdropzone += delta_time_axis_view - delta_skip;
1211 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1214 _last_pointer_layer += delta_layer;
1218 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1220 if (_copy && first_move) {
1222 if (_x_constrained) {
1223 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1225 _editor->begin_reversible_command (Operations::region_copy);
1228 /* duplicate the regionview(s) and region(s) */
1230 list<DraggingView> new_regionviews;
1232 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1234 RegionView* rv = i->view;
1235 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1236 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1238 const boost::shared_ptr<const Region> original = rv->region();
1239 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1240 region_copy->set_position (original->position());
1241 /* need to set this so that the drop zone code can work. This doesn't
1242 actually put the region into the playlist, but just sets a weak pointer
1245 region_copy->set_playlist (original->playlist());
1249 boost::shared_ptr<AudioRegion> audioregion_copy
1250 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1252 nrv = new AudioRegionView (*arv, audioregion_copy);
1254 boost::shared_ptr<MidiRegion> midiregion_copy
1255 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1256 nrv = new MidiRegionView (*mrv, midiregion_copy);
1261 nrv->get_canvas_group()->show ();
1262 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1264 /* swap _primary to the copy */
1266 if (rv == _primary) {
1270 /* ..and deselect the one we copied */
1272 rv->set_selected (false);
1275 if (!new_regionviews.empty()) {
1277 /* reflect the fact that we are dragging the copies */
1279 _views = new_regionviews;
1281 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1284 } else if (!_copy && first_move) {
1286 if (_x_constrained) {
1287 _editor->begin_reversible_command (_("fixed time region drag"));
1289 _editor->begin_reversible_command (Operations::region_drag);
1293 RegionMotionDrag::motion (event, first_move);
1297 RegionMotionDrag::finished (GdkEvent *, bool)
1299 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1300 if (!(*i)->view()) {
1304 if ((*i)->view()->layer_display() == Expanded) {
1305 (*i)->view()->set_layer_display (Stacked);
1311 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1313 RegionMotionDrag::finished (ev, movement_occurred);
1315 if (!movement_occurred) {
1319 if (was_double_click() && !_views.empty()) {
1320 DraggingView dv = _views.front();
1321 dv.view->show_region_editor ();
1328 assert (!_views.empty ());
1330 /* We might have hidden region views so that they weren't visible during the drag
1331 (when they have been reparented). Now everything can be shown again, as region
1332 views are back in their track parent groups.
1334 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1335 i->view->get_canvas_group()->show ();
1338 bool const changed_position = (_last_frame_position != _primary->region()->position());
1339 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1340 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1360 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1364 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1366 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1371 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1372 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1373 uint32_t output_chan = region->n_channels();
1374 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1375 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1377 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1378 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1380 rtav->set_height (original->current_height());
1384 ChanCount one_midi_port (DataType::MIDI, 1);
1385 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1386 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1387 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1389 rtav->set_height (original->current_height());
1394 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1400 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1402 RegionSelection new_views;
1403 PlaylistSet modified_playlists;
1404 RouteTimeAxisView* new_time_axis_view = 0;
1407 /* all changes were made during motion event handlers */
1409 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1413 _editor->commit_reversible_command ();
1417 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1418 PlaylistMapping playlist_mapping;
1420 /* insert the regions into their new playlists */
1421 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1423 RouteTimeAxisView* dest_rtv = 0;
1425 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1431 if (changed_position && !_x_constrained) {
1432 where = i->view->region()->position() - drag_delta;
1434 where = i->view->region()->position();
1437 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1438 /* dragged to drop zone */
1440 PlaylistMapping::iterator pm;
1442 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1443 /* first region from this original playlist: create a new track */
1444 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1445 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1446 dest_rtv = new_time_axis_view;
1448 /* we already created a new track for regions from this playlist, use it */
1449 dest_rtv = pm->second;
1452 /* destination time axis view is the one we dragged to */
1453 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1456 if (dest_rtv != 0) {
1457 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1458 if (new_view != 0) {
1459 new_views.push_back (new_view);
1463 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1464 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1467 list<DraggingView>::const_iterator next = i;
1473 /* If we've created new regions either by copying or moving
1474 to a new track, we want to replace the old selection with the new ones
1477 if (new_views.size() > 0) {
1478 _editor->selection->set (new_views);
1481 /* write commands for the accumulated diffs for all our modified playlists */
1482 add_stateful_diff_commands_for_playlists (modified_playlists);
1484 _editor->commit_reversible_command ();
1488 RegionMoveDrag::finished_no_copy (
1489 bool const changed_position,
1490 bool const changed_tracks,
1491 framecnt_t const drag_delta
1494 RegionSelection new_views;
1495 PlaylistSet modified_playlists;
1496 PlaylistSet frozen_playlists;
1497 set<RouteTimeAxisView*> views_to_update;
1498 RouteTimeAxisView* new_time_axis_view = 0;
1501 /* all changes were made during motion event handlers */
1502 _editor->commit_reversible_command ();
1506 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1507 PlaylistMapping playlist_mapping;
1509 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1511 RegionView* rv = i->view;
1512 RouteTimeAxisView* dest_rtv = 0;
1514 if (rv->region()->locked() || rv->region()->video_locked()) {
1519 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1520 /* dragged to drop zone */
1522 PlaylistMapping::iterator pm;
1524 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1525 /* first region from this original playlist: create a new track */
1526 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1527 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1528 dest_rtv = new_time_axis_view;
1530 /* we already created a new track for regions from this playlist, use it */
1531 dest_rtv = pm->second;
1535 /* destination time axis view is the one we dragged to */
1536 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1541 double const dest_layer = i->layer;
1543 views_to_update.insert (dest_rtv);
1547 if (changed_position && !_x_constrained) {
1548 where = rv->region()->position() - drag_delta;
1550 where = rv->region()->position();
1553 if (changed_tracks) {
1555 /* insert into new playlist */
1557 RegionView* new_view = insert_region_into_playlist (
1558 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1561 if (new_view == 0) {
1566 new_views.push_back (new_view);
1568 /* remove from old playlist */
1570 /* the region that used to be in the old playlist is not
1571 moved to the new one - we use a copy of it. as a result,
1572 any existing editor for the region should no longer be
1575 rv->hide_region_editor();
1578 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1582 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1584 /* this movement may result in a crossfade being modified, or a layering change,
1585 so we need to get undo data from the playlist as well as the region.
1588 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1590 playlist->clear_changes ();
1593 rv->region()->clear_changes ();
1596 motion on the same track. plonk the previously reparented region
1597 back to its original canvas group (its streamview).
1598 No need to do anything for copies as they are fake regions which will be deleted.
1601 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1602 rv->get_canvas_group()->set_y_position (i->initial_y);
1605 /* just change the model */
1606 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1607 playlist->set_layer (rv->region(), dest_layer);
1610 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1612 r = frozen_playlists.insert (playlist);
1615 playlist->freeze ();
1618 rv->region()->set_position (where);
1620 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1623 if (changed_tracks) {
1625 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1626 was selected in all of them, then removing it from a playlist will have removed all
1627 trace of it from _views (i.e. there were N regions selected, we removed 1,
1628 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1629 corresponding regionview, and _views is now empty).
1631 This could have invalidated any and all iterators into _views.
1633 The heuristic we use here is: if the region selection is empty, break out of the loop
1634 here. if the region selection is not empty, then restart the loop because we know that
1635 we must have removed at least the region(view) we've just been working on as well as any
1636 that we processed on previous iterations.
1638 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1639 we can just iterate.
1643 if (_views.empty()) {
1654 /* If we've created new regions either by copying or moving
1655 to a new track, we want to replace the old selection with the new ones
1658 if (new_views.size() > 0) {
1659 _editor->selection->set (new_views);
1662 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1666 /* write commands for the accumulated diffs for all our modified playlists */
1667 add_stateful_diff_commands_for_playlists (modified_playlists);
1669 _editor->commit_reversible_command ();
1671 /* We have futzed with the layering of canvas items on our streamviews.
1672 If any region changed layer, this will have resulted in the stream
1673 views being asked to set up their region views, and all will be well.
1674 If not, we might now have badly-ordered region views. Ask the StreamViews
1675 involved to sort themselves out, just in case.
1678 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1679 (*i)->view()->playlist_layered ((*i)->track ());
1683 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1684 * @param region Region to remove.
1685 * @param playlist playlist To remove from.
1686 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1687 * that clear_changes () is only called once per playlist.
1690 RegionMoveDrag::remove_region_from_playlist (
1691 boost::shared_ptr<Region> region,
1692 boost::shared_ptr<Playlist> playlist,
1693 PlaylistSet& modified_playlists
1696 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1699 playlist->clear_changes ();
1702 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1706 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1707 * clearing the playlist's diff history first if necessary.
1708 * @param region Region to insert.
1709 * @param dest_rtv Destination RouteTimeAxisView.
1710 * @param dest_layer Destination layer.
1711 * @param where Destination position.
1712 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1713 * that clear_changes () is only called once per playlist.
1714 * @return New RegionView, or 0 if no insert was performed.
1717 RegionMoveDrag::insert_region_into_playlist (
1718 boost::shared_ptr<Region> region,
1719 RouteTimeAxisView* dest_rtv,
1722 PlaylistSet& modified_playlists
1725 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1726 if (!dest_playlist) {
1730 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1731 _new_region_view = 0;
1732 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1734 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1735 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1737 dest_playlist->clear_changes ();
1740 dest_playlist->add_region (region, where);
1742 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1743 dest_playlist->set_layer (region, dest_layer);
1748 assert (_new_region_view);
1750 return _new_region_view;
1754 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1756 _new_region_view = rv;
1760 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1762 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1763 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1765 _editor->session()->add_command (c);
1774 RegionMoveDrag::aborted (bool movement_occurred)
1778 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1779 list<DraggingView>::const_iterator next = i;
1788 RegionMotionDrag::aborted (movement_occurred);
1793 RegionMotionDrag::aborted (bool)
1795 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1797 StreamView* sview = (*i)->view();
1800 if (sview->layer_display() == Expanded) {
1801 sview->set_layer_display (Stacked);
1806 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1807 RegionView* rv = i->view;
1808 TimeAxisView* tv = &(rv->get_time_axis_view ());
1809 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1811 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1812 rv->get_canvas_group()->set_y_position (0);
1814 rv->move (-_total_x_delta, 0);
1815 rv->set_height (rtv->view()->child_height ());
1819 /** @param b true to brush, otherwise false.
1820 * @param c true to make copies of the regions being moved, otherwise false.
1822 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1823 : RegionMotionDrag (e, i, p, v, b)
1826 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1829 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1830 if (rtv && rtv->is_track()) {
1831 speed = rtv->track()->speed ();
1834 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1838 RegionMoveDrag::setup_pointer_frame_offset ()
1840 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1843 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1844 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1846 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1848 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1849 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1851 _primary = v->view()->create_region_view (r, false, false);
1853 _primary->get_canvas_group()->show ();
1854 _primary->set_position (pos, 0);
1855 _views.push_back (DraggingView (_primary, this, v));
1857 _last_frame_position = pos;
1859 _item = _primary->get_canvas_group ();
1863 RegionInsertDrag::finished (GdkEvent *, bool)
1865 int pos = _views.front().time_axis_view;
1866 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1868 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1870 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1871 _primary->get_canvas_group()->set_y_position (0);
1873 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1875 _editor->begin_reversible_command (Operations::insert_region);
1876 playlist->clear_changes ();
1877 playlist->add_region (_primary->region (), _last_frame_position);
1879 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1880 if (Config->get_edit_mode() == Ripple) {
1881 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1884 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1885 _editor->commit_reversible_command ();
1893 RegionInsertDrag::aborted (bool)
1900 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1901 : RegionMoveDrag (e, i, p, v, false, false)
1903 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1906 struct RegionSelectionByPosition {
1907 bool operator() (RegionView*a, RegionView* b) {
1908 return a->region()->position () < b->region()->position();
1913 RegionSpliceDrag::motion (GdkEvent* event, bool)
1915 /* Which trackview is this ? */
1917 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1918 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1920 /* The region motion is only processed if the pointer is over
1924 if (!tv || !tv->is_track()) {
1925 /* To make sure we hide the verbose canvas cursor when the mouse is
1926 not held over an audio track.
1928 _editor->verbose_cursor()->hide ();
1931 _editor->verbose_cursor()->show ();
1936 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1942 RegionSelection copy;
1943 _editor->selection->regions.by_position(copy);
1945 framepos_t const pf = adjusted_current_frame (event);
1947 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1949 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1955 boost::shared_ptr<Playlist> playlist;
1957 if ((playlist = atv->playlist()) == 0) {
1961 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1966 if (pf < (*i)->region()->last_frame() + 1) {
1970 if (pf > (*i)->region()->first_frame()) {
1976 playlist->shuffle ((*i)->region(), dir);
1981 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1983 RegionMoveDrag::finished (event, movement_occurred);
1987 RegionSpliceDrag::aborted (bool)
1997 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2000 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2002 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2003 RegionSelection to_ripple;
2004 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2005 if ((*i)->position() >= where) {
2006 to_ripple.push_back (rtv->view()->find_view(*i));
2010 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2011 if (!exclude.contains (*i)) {
2012 // the selection has already been added to _views
2014 if (drag_in_progress) {
2015 // do the same things that RegionMotionDrag::motion does when
2016 // first_move is true, for the region views that we're adding
2017 // to _views this time
2020 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2021 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2022 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2023 rvg->reparent (_editor->_drag_motion_group);
2025 // we only need to move in the y direction
2026 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2031 _views.push_back (DraggingView (*i, this, tav));
2037 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2040 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2041 // we added all the regions after the selection
2043 std::list<DraggingView>::iterator to_erase = i++;
2044 if (!_editor->selection->regions.contains (to_erase->view)) {
2045 // restore the non-selected regions to their original playlist & positions,
2046 // and then ripple them back by the length of the regions that were dragged away
2047 // do the same things as RegionMotionDrag::aborted
2049 RegionView *rv = to_erase->view;
2050 TimeAxisView* tv = &(rv->get_time_axis_view ());
2051 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2054 // plonk them back onto their own track
2055 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2056 rv->get_canvas_group()->set_y_position (0);
2060 // move the underlying region to match the view
2061 rv->region()->set_position (rv->region()->position() + amount);
2063 // restore the view to match the underlying region's original position
2064 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2067 rv->set_height (rtv->view()->child_height ());
2068 _views.erase (to_erase);
2074 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2076 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2078 return allow_moves_across_tracks;
2086 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2087 : RegionMoveDrag (e, i, p, v, false, false)
2089 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2090 // compute length of selection
2091 RegionSelection selected_regions = _editor->selection->regions;
2092 selection_length = selected_regions.end_frame() - selected_regions.start();
2094 // we'll only allow dragging to another track in ripple mode if all the regions
2095 // being dragged start off on the same track
2096 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2099 exclude = new RegionList;
2100 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2101 exclude->push_back((*i)->region());
2104 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2105 RegionSelection copy;
2106 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2108 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2109 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2111 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2112 // find ripple start point on each applicable playlist
2113 RegionView *first_selected_on_this_track = NULL;
2114 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2115 if ((*i)->region()->playlist() == (*pi)) {
2116 // region is on this playlist - it's the first, because they're sorted
2117 first_selected_on_this_track = *i;
2121 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2122 add_all_after_to_views (
2123 &first_selected_on_this_track->get_time_axis_view(),
2124 first_selected_on_this_track->region()->position(),
2125 selected_regions, false);
2128 if (allow_moves_across_tracks) {
2129 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2137 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2139 /* Which trackview is this ? */
2141 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2142 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2144 /* The region motion is only processed if the pointer is over
2148 if (!tv || !tv->is_track()) {
2149 /* To make sure we hide the verbose canvas cursor when the mouse is
2150 not held over an audiotrack.
2152 _editor->verbose_cursor()->hide ();
2156 framepos_t where = adjusted_current_frame (event);
2157 assert (where >= 0);
2159 double delta = compute_x_delta (event, &after);
2161 framecnt_t amount = _editor->pixel_to_sample (delta);
2163 if (allow_moves_across_tracks) {
2164 // all the originally selected regions were on the same track
2166 framecnt_t adjust = 0;
2167 if (prev_tav && tv != prev_tav) {
2168 // dragged onto a different track
2169 // remove the unselected regions from _views, restore them to their original positions
2170 // and add the regions after the drop point on the new playlist to _views instead.
2171 // undo the effect of rippling the previous playlist, and include the effect of removing
2172 // the dragged region(s) from this track
2174 remove_unselected_from_views (prev_amount, false);
2175 // ripple previous playlist according to the regions that have been removed onto the new playlist
2176 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2179 // move just the selected regions
2180 RegionMoveDrag::motion(event, first_move);
2182 // ensure that the ripple operation on the new playlist inserts selection_length time
2183 adjust = selection_length;
2184 // ripple the new current playlist
2185 tv->playlist()->ripple (where, amount+adjust, exclude);
2187 // add regions after point where drag entered this track to subsequent ripples
2188 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2191 // motion on same track
2192 RegionMoveDrag::motion(event, first_move);
2196 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2197 prev_position = where;
2199 // selection encompasses multiple tracks - just drag
2200 // cross-track drags are forbidden
2201 RegionMoveDrag::motion(event, first_move);
2204 if (!_x_constrained) {
2205 prev_amount += amount;
2208 _last_frame_position = after;
2212 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2214 if (!movement_occurred) {
2218 if (was_double_click() && !_views.empty()) {
2219 DraggingView dv = _views.front();
2220 dv.view->show_region_editor ();
2227 _editor->begin_reversible_command(_("Ripple drag"));
2229 // remove the regions being rippled from the dragging view, updating them to
2230 // their new positions
2231 remove_unselected_from_views (prev_amount, true);
2233 if (allow_moves_across_tracks) {
2235 // if regions were dragged across tracks, we've rippled any later
2236 // regions on the track the regions were dragged off, so we need
2237 // to add the original track to the undo record
2238 orig_tav->playlist()->clear_changes();
2239 vector<Command*> cmds;
2240 orig_tav->playlist()->rdiff (cmds);
2241 _editor->session()->add_commands (cmds);
2243 if (prev_tav && prev_tav != orig_tav) {
2244 prev_tav->playlist()->clear_changes();
2245 vector<Command*> cmds;
2246 prev_tav->playlist()->rdiff (cmds);
2247 _editor->session()->add_commands (cmds);
2250 // selection spanned multiple tracks - all will need adding to undo record
2252 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2253 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2255 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2256 (*pi)->clear_changes();
2257 vector<Command*> cmds;
2258 (*pi)->rdiff (cmds);
2259 _editor->session()->add_commands (cmds);
2263 // other modified playlists are added to undo by RegionMoveDrag::finished()
2264 RegionMoveDrag::finished (event, movement_occurred);
2265 _editor->commit_reversible_command();
2269 RegionRippleDrag::aborted (bool movement_occurred)
2271 RegionMoveDrag::aborted (movement_occurred);
2276 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2278 _view (dynamic_cast<MidiTimeAxisView*> (v))
2280 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2286 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2289 _region = add_midi_region (_view);
2290 _view->playlist()->freeze ();
2293 framepos_t const f = adjusted_current_frame (event);
2294 if (f < grab_frame()) {
2295 _region->set_position (f);
2298 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2299 so that if this region is duplicated, its duplicate starts on
2300 a snap point rather than 1 frame after a snap point. Otherwise things get
2301 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2302 place snapped notes at the start of the region.
2305 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2306 _region->set_length (len < 1 ? 1 : len);
2312 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2314 if (!movement_occurred) {
2315 add_midi_region (_view);
2317 _view->playlist()->thaw ();
2322 RegionCreateDrag::aborted (bool)
2325 _view->playlist()->thaw ();
2331 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2336 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2340 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2342 Gdk::Cursor* cursor;
2343 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2345 float x_fraction = cnote->mouse_x_fraction ();
2347 if (x_fraction > 0.0 && x_fraction < 0.25) {
2348 cursor = _editor->cursors()->left_side_trim;
2351 cursor = _editor->cursors()->right_side_trim;
2355 Drag::start_grab (event, cursor);
2357 region = &cnote->region_view();
2360 temp = region->snap_to_pixel (cnote->x0 (), true);
2361 _snap_delta = temp - cnote->x0 ();
2365 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2371 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2373 if (ms.size() > 1) {
2374 /* has to be relative, may make no sense otherwise */
2378 /* select this note; if it is already selected, preserve the existing selection,
2379 otherwise make this note the only one selected.
2381 region->note_selected (cnote, cnote->selected ());
2383 _editor->begin_reversible_command (_("resize notes"));
2385 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2386 MidiRegionSelection::iterator next;
2389 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2391 mrv->begin_resizing (at_front);
2398 NoteResizeDrag::motion (GdkEvent* event, bool /*first_move*/)
2400 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2401 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2402 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2404 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2408 bool apply_snap_delta = !ArdourKeyboard::indicates_snap_delta (event->button.state);
2410 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2411 if (_editor->snap_mode () != SnapOff) {
2415 if (_editor->snap_mode () == SnapOff) {
2417 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2418 if (!apply_snap_delta) {
2424 if (apply_snap_delta) {
2428 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2434 NoteResizeDrag::finished (GdkEvent* event, bool /*movement_occurred*/)
2436 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2437 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2438 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2440 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2443 bool apply_snap_delta = !ArdourKeyboard::indicates_snap_delta (event->button.state);
2445 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2446 if (_editor->snap_mode () != SnapOff) {
2450 if (_editor->snap_mode () == SnapOff) {
2452 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2453 if (!apply_snap_delta) {
2458 if (apply_snap_delta) {
2461 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2465 _editor->commit_reversible_command ();
2469 NoteResizeDrag::aborted (bool)
2471 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2472 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2473 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2475 mrv->abort_resizing ();
2480 AVDraggingView::AVDraggingView (RegionView* v)
2483 initial_position = v->region()->position ();
2486 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2489 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2492 TrackViewList empty;
2494 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2495 std::list<RegionView*> views = rs.by_layer();
2497 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2498 RegionView* rv = (*i);
2499 if (!rv->region()->video_locked()) {
2502 _views.push_back (AVDraggingView (rv));
2507 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2509 Drag::start_grab (event);
2510 if (_editor->session() == 0) {
2514 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2515 _max_backwards_drag = (
2516 ARDOUR_UI::instance()->video_timeline->get_duration()
2517 + ARDOUR_UI::instance()->video_timeline->get_offset()
2518 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2521 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2522 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2523 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2526 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2529 Timecode::Time timecode;
2530 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2531 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);
2532 show_verbose_cursor_text (buf);
2536 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2538 if (_editor->session() == 0) {
2541 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2545 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2546 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2548 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2549 dt = - _max_backwards_drag;
2552 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2553 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2555 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2556 RegionView* rv = i->view;
2557 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2560 rv->region()->clear_changes ();
2561 rv->region()->suspend_property_changes();
2563 rv->region()->set_position(i->initial_position + dt);
2564 rv->region_changed(ARDOUR::Properties::position);
2567 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2568 Timecode::Time timecode;
2569 Timecode::Time timediff;
2571 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2572 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2573 snprintf (buf, sizeof (buf),
2574 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2575 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2576 , _("Video Start:"),
2577 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2579 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2581 show_verbose_cursor_text (buf);
2585 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2587 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2591 if (!movement_occurred || ! _editor->session()) {
2595 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2597 _editor->begin_reversible_command (_("Move Video"));
2599 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2600 ARDOUR_UI::instance()->video_timeline->save_undo();
2601 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2602 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2604 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2605 i->view->drag_end();
2606 i->view->region()->resume_property_changes ();
2608 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2611 _editor->session()->maybe_update_session_range(
2612 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2613 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2617 _editor->commit_reversible_command ();
2621 VideoTimeLineDrag::aborted (bool)
2623 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2626 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2627 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2629 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2630 i->view->region()->resume_property_changes ();
2631 i->view->region()->set_position(i->initial_position);
2635 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2636 : RegionDrag (e, i, p, v)
2637 , _preserve_fade_anchor (preserve_fade_anchor)
2638 , _jump_position_when_done (false)
2640 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2644 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2647 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2648 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2650 if (tv && tv->is_track()) {
2651 speed = tv->track()->speed();
2654 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2655 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2656 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2658 framepos_t const pf = adjusted_current_frame (event);
2659 setup_snap_delta (region_start);
2661 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2662 /* Move the contents of the region around without changing the region bounds */
2663 _operation = ContentsTrim;
2664 Drag::start_grab (event, _editor->cursors()->trimmer);
2666 /* These will get overridden for a point trim.*/
2667 if (pf < (region_start + region_length/2)) {
2668 /* closer to front */
2669 _operation = StartTrim;
2670 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2671 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2673 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2677 _operation = EndTrim;
2678 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2679 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2681 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2685 /* jump trim disabled for now
2686 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2687 _jump_position_when_done = true;
2691 switch (_operation) {
2693 show_verbose_cursor_time (region_start);
2694 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2695 i->view->trim_front_starting ();
2699 show_verbose_cursor_duration (region_start, region_end);
2702 show_verbose_cursor_time (pf);
2706 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2707 i->view->region()->suspend_property_changes ();
2712 TrimDrag::motion (GdkEvent* event, bool first_move)
2714 RegionView* rv = _primary;
2717 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2718 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2719 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2720 frameoffset_t frame_delta = 0;
2722 if (tv && tv->is_track()) {
2723 speed = tv->track()->speed();
2725 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2726 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2732 switch (_operation) {
2734 trim_type = "Region start trim";
2737 trim_type = "Region end trim";
2740 trim_type = "Region content trim";
2747 _editor->begin_reversible_command (trim_type);
2749 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2750 RegionView* rv = i->view;
2751 rv->enable_display (false);
2752 rv->region()->playlist()->clear_owned_changes ();
2754 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2757 arv->temporarily_hide_envelope ();
2761 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2762 insert_result = _editor->motion_frozen_playlists.insert (pl);
2764 if (insert_result.second) {
2770 bool non_overlap_trim = false;
2772 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2773 non_overlap_trim = true;
2776 /* contstrain trim to fade length */
2777 if (_preserve_fade_anchor) {
2778 switch (_operation) {
2780 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2781 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2783 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2784 if (ar->locked()) continue;
2785 framecnt_t len = ar->fade_in()->back()->when;
2786 if (len < dt) dt = min(dt, len);
2790 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2791 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2793 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2794 if (ar->locked()) continue;
2795 framecnt_t len = ar->fade_out()->back()->when;
2796 if (len < -dt) dt = max(dt, -len);
2805 switch (_operation) {
2807 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2808 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2809 if (changed && _preserve_fade_anchor) {
2810 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2812 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2813 framecnt_t len = ar->fade_in()->back()->when;
2814 framecnt_t diff = ar->first_frame() - i->initial_position;
2815 framepos_t new_length = len - diff;
2816 i->anchored_fade_length = min (ar->length(), new_length);
2817 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2818 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2825 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2826 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2827 if (changed && _preserve_fade_anchor) {
2828 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2830 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2831 framecnt_t len = ar->fade_out()->back()->when;
2832 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2833 framepos_t new_length = len + diff;
2834 i->anchored_fade_length = min (ar->length(), new_length);
2835 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2836 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2844 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2846 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2847 i->view->move_contents (frame_delta);
2853 switch (_operation) {
2855 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2858 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2861 // show_verbose_cursor_time (frame_delta);
2867 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2869 if (movement_occurred) {
2870 motion (event, false);
2872 if (_operation == StartTrim) {
2873 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2875 /* This must happen before the region's StatefulDiffCommand is created, as it may
2876 `correct' (ahem) the region's _start from being negative to being zero. It
2877 needs to be zero in the undo record.
2879 i->view->trim_front_ending ();
2881 if (_preserve_fade_anchor) {
2882 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2884 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2885 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2886 ar->set_fade_in_length(i->anchored_fade_length);
2887 ar->set_fade_in_active(true);
2890 if (_jump_position_when_done) {
2891 i->view->region()->set_position (i->initial_position);
2894 } else if (_operation == EndTrim) {
2895 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2896 if (_preserve_fade_anchor) {
2897 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2899 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2900 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2901 ar->set_fade_out_length(i->anchored_fade_length);
2902 ar->set_fade_out_active(true);
2905 if (_jump_position_when_done) {
2906 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2911 if (!_views.empty()) {
2912 if (_operation == StartTrim) {
2913 _editor->maybe_locate_with_edit_preroll(
2914 _views.begin()->view->region()->position());
2916 if (_operation == EndTrim) {
2917 _editor->maybe_locate_with_edit_preroll(
2918 _views.begin()->view->region()->position() +
2919 _views.begin()->view->region()->length());
2923 if (!_editor->selection->selected (_primary)) {
2924 _primary->thaw_after_trim ();
2927 set<boost::shared_ptr<Playlist> > diffed_playlists;
2929 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2930 i->view->thaw_after_trim ();
2931 i->view->enable_display (true);
2933 /* Trimming one region may affect others on the playlist, so we need
2934 to get undo Commands from the whole playlist rather than just the
2935 region. Use diffed_playlists to make sure we don't diff a given
2936 playlist more than once.
2938 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2939 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2940 vector<Command*> cmds;
2942 _editor->session()->add_commands (cmds);
2943 diffed_playlists.insert (p);
2948 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2952 _editor->motion_frozen_playlists.clear ();
2953 _editor->commit_reversible_command();
2956 /* no mouse movement */
2957 _editor->point_trim (event, adjusted_current_frame (event));
2960 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2961 if (_operation == StartTrim) {
2962 i->view->trim_front_ending ();
2965 i->view->region()->resume_property_changes ();
2970 TrimDrag::aborted (bool movement_occurred)
2972 /* Our motion method is changing model state, so use the Undo system
2973 to cancel. Perhaps not ideal, as this will leave an Undo point
2974 behind which may be slightly odd from the user's point of view.
2979 if (movement_occurred) {
2983 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2984 i->view->region()->resume_property_changes ();
2989 TrimDrag::setup_pointer_frame_offset ()
2991 list<DraggingView>::iterator i = _views.begin ();
2992 while (i != _views.end() && i->view != _primary) {
2996 if (i == _views.end()) {
3000 switch (_operation) {
3002 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3005 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3012 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3016 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3017 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3022 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3024 Drag::start_grab (event, cursor);
3025 show_verbose_cursor_time (adjusted_current_frame(event));
3029 MeterMarkerDrag::setup_pointer_frame_offset ()
3031 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3035 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3037 if (!_marker->meter().movable()) {
3043 // create a dummy marker for visual representation of moving the
3044 // section, because whether its a copy or not, we're going to
3045 // leave or lose the original marker (leave if its a copy; lose if its
3046 // not, because we'll remove it from the map).
3048 MeterSection section (_marker->meter());
3050 if (!section.movable()) {
3055 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3057 _marker = new MeterMarker (
3059 *_editor->meter_group,
3060 ARDOUR_UI::config()->color ("meter marker"),
3062 *new MeterSection (_marker->meter())
3065 /* use the new marker for the grab */
3066 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3069 TempoMap& map (_editor->session()->tempo_map());
3070 /* get current state */
3071 before_state = &map.get_state();
3072 /* remove the section while we drag it */
3073 map.remove_meter (section, true);
3077 framepos_t const pf = adjusted_current_frame (event);
3079 _marker->set_position (pf);
3080 show_verbose_cursor_time (pf);
3084 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3086 if (!movement_occurred) {
3087 if (was_double_click()) {
3088 _editor->edit_meter_marker (*_marker);
3093 if (!_marker->meter().movable()) {
3097 motion (event, false);
3099 Timecode::BBT_Time when;
3101 TempoMap& map (_editor->session()->tempo_map());
3102 map.bbt_time (last_pointer_frame(), when);
3104 if (_copy == true) {
3105 _editor->begin_reversible_command (_("copy meter mark"));
3106 XMLNode &before = map.get_state();
3107 map.add_meter (_marker->meter(), when);
3108 XMLNode &after = map.get_state();
3109 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3110 _editor->commit_reversible_command ();
3113 _editor->begin_reversible_command (_("move meter mark"));
3115 /* we removed it before, so add it back now */
3117 map.add_meter (_marker->meter(), when);
3118 XMLNode &after = map.get_state();
3119 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3120 _editor->commit_reversible_command ();
3123 // delete the dummy marker we used for visual representation while moving.
3124 // a new visual marker will show up automatically.
3129 MeterMarkerDrag::aborted (bool moved)
3131 _marker->set_position (_marker->meter().frame ());
3134 TempoMap& map (_editor->session()->tempo_map());
3135 /* we removed it before, so add it back now */
3136 map.add_meter (_marker->meter(), _marker->meter().frame());
3137 // delete the dummy marker we used for visual representation while moving.
3138 // a new visual marker will show up automatically.
3143 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3147 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3149 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3154 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3156 Drag::start_grab (event, cursor);
3157 show_verbose_cursor_time (adjusted_current_frame (event));
3161 TempoMarkerDrag::setup_pointer_frame_offset ()
3163 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3167 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3169 if (!_marker->tempo().movable()) {
3175 // create a dummy marker for visual representation of moving the
3176 // section, because whether its a copy or not, we're going to
3177 // leave or lose the original marker (leave if its a copy; lose if its
3178 // not, because we'll remove it from the map).
3180 // create a dummy marker for visual representation of moving the copy.
3181 // The actual copying is not done before we reach the finish callback.
3184 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3186 TempoSection section (_marker->tempo());
3188 _marker = new TempoMarker (
3190 *_editor->tempo_group,
3191 ARDOUR_UI::config()->color ("tempo marker"),
3193 *new TempoSection (_marker->tempo())
3196 /* use the new marker for the grab */
3197 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3200 TempoMap& map (_editor->session()->tempo_map());
3201 /* get current state */
3202 before_state = &map.get_state();
3203 /* remove the section while we drag it */
3204 map.remove_tempo (section, true);
3208 framepos_t const pf = adjusted_current_frame (event);
3209 _marker->set_position (pf);
3210 show_verbose_cursor_time (pf);
3214 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3216 if (!movement_occurred) {
3217 if (was_double_click()) {
3218 _editor->edit_tempo_marker (*_marker);
3223 if (!_marker->tempo().movable()) {
3227 motion (event, false);
3229 TempoMap& map (_editor->session()->tempo_map());
3230 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3231 Timecode::BBT_Time when;
3233 map.bbt_time (beat_time, when);
3235 if (_copy == true) {
3236 _editor->begin_reversible_command (_("copy tempo mark"));
3237 XMLNode &before = map.get_state();
3238 map.add_tempo (_marker->tempo(), when);
3239 XMLNode &after = map.get_state();
3240 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3241 _editor->commit_reversible_command ();
3244 _editor->begin_reversible_command (_("move tempo mark"));
3245 /* we removed it before, so add it back now */
3246 map.add_tempo (_marker->tempo(), when);
3247 XMLNode &after = map.get_state();
3248 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3249 _editor->commit_reversible_command ();
3252 // delete the dummy marker we used for visual representation while moving.
3253 // a new visual marker will show up automatically.
3258 TempoMarkerDrag::aborted (bool moved)
3260 _marker->set_position (_marker->tempo().frame());
3262 TempoMap& map (_editor->session()->tempo_map());
3263 /* we removed it before, so add it back now */
3264 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3265 // delete the dummy marker we used for visual representation while moving.
3266 // a new visual marker will show up automatically.
3271 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3272 : Drag (e, &c.track_canvas_item(), false)
3276 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3279 /** Do all the things we do when dragging the playhead to make it look as though
3280 * we have located, without actually doing the locate (because that would cause
3281 * the diskstream buffers to be refilled, which is too slow).
3284 CursorDrag::fake_locate (framepos_t t)
3286 _editor->playhead_cursor->set_position (t);
3288 Session* s = _editor->session ();
3289 if (s->timecode_transmission_suspended ()) {
3290 framepos_t const f = _editor->playhead_cursor->current_frame ();
3291 /* This is asynchronous so it will be sent "now"
3293 s->send_mmc_locate (f);
3294 /* These are synchronous and will be sent during the next
3297 s->queue_full_time_code ();
3298 s->queue_song_position_pointer ();
3301 show_verbose_cursor_time (t);
3302 _editor->UpdateAllTransportClocks (t);
3306 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3308 Drag::start_grab (event, c);
3309 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3311 _grab_zoom = _editor->samples_per_pixel;
3313 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3315 _editor->snap_to_with_modifier (where, event);
3317 _editor->_dragging_playhead = true;
3319 Session* s = _editor->session ();
3321 /* grab the track canvas item as well */
3323 _cursor.track_canvas_item().grab();
3326 if (_was_rolling && _stop) {
3330 if (s->is_auditioning()) {
3331 s->cancel_audition ();
3335 if (AudioEngine::instance()->connected()) {
3337 /* do this only if we're the engine is connected
3338 * because otherwise this request will never be
3339 * serviced and we'll busy wait forever. likewise,
3340 * notice if we are disconnected while waiting for the
3341 * request to be serviced.
3344 s->request_suspend_timecode_transmission ();
3345 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3346 /* twiddle our thumbs */
3351 fake_locate (where - snap_delta (event->button.state));
3355 CursorDrag::motion (GdkEvent* event, bool)
3357 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3358 _editor->snap_to_with_modifier (where, event);
3359 if (where != last_pointer_frame()) {
3360 fake_locate (where - snap_delta (event->button.state));
3365 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3367 _editor->_dragging_playhead = false;
3369 _cursor.track_canvas_item().ungrab();
3371 if (!movement_occurred && _stop) {
3375 motion (event, false);
3377 Session* s = _editor->session ();
3379 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3380 _editor->_pending_locate_request = true;
3381 s->request_resume_timecode_transmission ();
3386 CursorDrag::aborted (bool)
3388 _cursor.track_canvas_item().ungrab();
3390 if (_editor->_dragging_playhead) {
3391 _editor->session()->request_resume_timecode_transmission ();
3392 _editor->_dragging_playhead = false;
3395 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3398 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3399 : RegionDrag (e, i, p, v)
3401 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3405 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3407 Drag::start_grab (event, cursor);
3409 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3410 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3411 setup_snap_delta (r->position ());
3413 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3417 FadeInDrag::setup_pointer_frame_offset ()
3419 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3420 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3421 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3425 FadeInDrag::motion (GdkEvent* event, bool)
3427 framecnt_t fade_length;
3429 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3430 _editor->snap_to_with_modifier (pos, event);
3431 pos -= snap_delta (event->button.state);
3433 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3435 if (pos < (region->position() + 64)) {
3436 fade_length = 64; // this should be a minimum defined somewhere
3437 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3438 fade_length = region->length() - region->fade_out()->back()->when - 1;
3440 fade_length = pos - region->position();
3443 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3445 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3451 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3454 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3458 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3460 if (!movement_occurred) {
3464 framecnt_t fade_length;
3465 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3466 _editor->snap_to_with_modifier (pos, event);
3467 pos -= snap_delta (event->button.state);
3469 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3471 if (pos < (region->position() + 64)) {
3472 fade_length = 64; // this should be a minimum defined somewhere
3473 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3474 fade_length = region->length() - region->fade_out()->back()->when - 1;
3476 fade_length = pos - region->position();
3479 _editor->begin_reversible_command (_("change fade in length"));
3481 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3483 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3489 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3490 XMLNode &before = alist->get_state();
3492 tmp->audio_region()->set_fade_in_length (fade_length);
3493 tmp->audio_region()->set_fade_in_active (true);
3495 XMLNode &after = alist->get_state();
3496 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3499 _editor->commit_reversible_command ();
3503 FadeInDrag::aborted (bool)
3505 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3506 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3512 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3516 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3517 : RegionDrag (e, i, p, v)
3519 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3523 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3525 Drag::start_grab (event, cursor);
3527 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3528 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3529 setup_snap_delta (r->last_frame ());
3531 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3535 FadeOutDrag::setup_pointer_frame_offset ()
3537 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3538 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3539 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3543 FadeOutDrag::motion (GdkEvent* event, bool)
3545 framecnt_t fade_length;
3547 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3548 _editor->snap_to_with_modifier (pos, event);
3549 pos -= snap_delta (event->button.state);
3551 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3553 if (pos > (region->last_frame() - 64)) {
3554 fade_length = 64; // this should really be a minimum fade defined somewhere
3555 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3556 fade_length = region->length() - region->fade_in()->back()->when - 1;
3558 fade_length = region->last_frame() - pos;
3561 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3563 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3569 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3572 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3576 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3578 if (!movement_occurred) {
3582 framecnt_t fade_length;
3584 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3585 _editor->snap_to_with_modifier (pos, event);
3586 pos -= snap_delta (event->button.state);
3588 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3590 if (pos > (region->last_frame() - 64)) {
3591 fade_length = 64; // this should really be a minimum fade defined somewhere
3592 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3593 fade_length = region->length() - region->fade_in()->back()->when - 1;
3595 fade_length = region->last_frame() - pos;
3598 _editor->begin_reversible_command (_("change fade out length"));
3600 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3602 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3608 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3609 XMLNode &before = alist->get_state();
3611 tmp->audio_region()->set_fade_out_length (fade_length);
3612 tmp->audio_region()->set_fade_out_active (true);
3614 XMLNode &after = alist->get_state();
3615 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3618 _editor->commit_reversible_command ();
3622 FadeOutDrag::aborted (bool)
3624 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3625 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3631 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3635 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3638 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3640 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3643 _points.push_back (ArdourCanvas::Duple (0, 0));
3644 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3647 MarkerDrag::~MarkerDrag ()
3649 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3654 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3656 location = new Location (*l);
3657 markers.push_back (m);
3662 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3664 Drag::start_grab (event, cursor);
3668 Location *location = _editor->find_location_from_marker (_marker, is_start);
3669 _editor->_dragging_edit_point = true;
3671 update_item (location);
3673 // _drag_line->show();
3674 // _line->raise_to_top();
3677 show_verbose_cursor_time (location->start());
3679 show_verbose_cursor_time (location->end());
3682 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3685 case Selection::Toggle:
3686 /* we toggle on the button release */
3688 case Selection::Set:
3689 if (!_editor->selection->selected (_marker)) {
3690 _editor->selection->set (_marker);
3693 case Selection::Extend:
3695 Locations::LocationList ll;
3696 list<Marker*> to_add;
3698 _editor->selection->markers.range (s, e);
3699 s = min (_marker->position(), s);
3700 e = max (_marker->position(), e);
3703 if (e < max_framepos) {
3706 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3707 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3708 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3711 to_add.push_back (lm->start);
3714 to_add.push_back (lm->end);
3718 if (!to_add.empty()) {
3719 _editor->selection->add (to_add);
3723 case Selection::Add:
3724 _editor->selection->add (_marker);
3728 /* Set up copies for us to manipulate during the drag
3731 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3733 Location* l = _editor->find_location_from_marker (*i, is_start);
3740 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3742 /* range: check that the other end of the range isn't
3745 CopiedLocationInfo::iterator x;
3746 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3747 if (*(*x).location == *l) {
3751 if (x == _copied_locations.end()) {
3752 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3754 (*x).markers.push_back (*i);
3755 (*x).move_both = true;
3763 MarkerDrag::setup_pointer_frame_offset ()
3766 Location *location = _editor->find_location_from_marker (_marker, is_start);
3767 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3771 MarkerDrag::motion (GdkEvent* event, bool)
3773 framecnt_t f_delta = 0;
3775 bool move_both = false;
3776 Location *real_location;
3777 Location *copy_location = 0;
3779 framepos_t const newframe = adjusted_current_frame (event);
3780 framepos_t next = newframe;
3782 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3786 CopiedLocationInfo::iterator x;
3788 /* find the marker we're dragging, and compute the delta */
3790 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3792 copy_location = (*x).location;
3794 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3796 /* this marker is represented by this
3797 * CopiedLocationMarkerInfo
3800 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3805 if (real_location->is_mark()) {
3806 f_delta = newframe - copy_location->start();
3810 switch (_marker->type()) {
3811 case Marker::SessionStart:
3812 case Marker::RangeStart:
3813 case Marker::LoopStart:
3814 case Marker::PunchIn:
3815 f_delta = newframe - copy_location->start();
3818 case Marker::SessionEnd:
3819 case Marker::RangeEnd:
3820 case Marker::LoopEnd:
3821 case Marker::PunchOut:
3822 f_delta = newframe - copy_location->end();
3825 /* what kind of marker is this ? */
3834 if (x == _copied_locations.end()) {
3835 /* hmm, impossible - we didn't find the dragged marker */
3839 /* now move them all */
3841 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3843 copy_location = x->location;
3845 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3849 if (real_location->locked()) {
3853 if (copy_location->is_mark()) {
3857 copy_location->set_start (copy_location->start() + f_delta);
3861 framepos_t new_start = copy_location->start() + f_delta;
3862 framepos_t new_end = copy_location->end() + f_delta;
3864 if (is_start) { // start-of-range marker
3866 if (move_both || (*x).move_both) {
3867 copy_location->set_start (new_start);
3868 copy_location->set_end (new_end);
3869 } else if (new_start < copy_location->end()) {
3870 copy_location->set_start (new_start);
3871 } else if (newframe > 0) {
3872 _editor->snap_to (next, RoundUpAlways, true);
3873 copy_location->set_end (next);
3874 copy_location->set_start (newframe);
3877 } else { // end marker
3879 if (move_both || (*x).move_both) {
3880 copy_location->set_end (new_end);
3881 copy_location->set_start (new_start);
3882 } else if (new_end > copy_location->start()) {
3883 copy_location->set_end (new_end);
3884 } else if (newframe > 0) {
3885 _editor->snap_to (next, RoundDownAlways, true);
3886 copy_location->set_start (next);
3887 copy_location->set_end (newframe);
3892 update_item (copy_location);
3894 /* now lookup the actual GUI items used to display this
3895 * location and move them to wherever the copy of the location
3896 * is now. This means that the logic in ARDOUR::Location is
3897 * still enforced, even though we are not (yet) modifying
3898 * the real Location itself.
3901 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3904 lm->set_position (copy_location->start(), copy_location->end());
3909 assert (!_copied_locations.empty());
3911 show_verbose_cursor_time (newframe);
3915 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3917 if (!movement_occurred) {
3919 if (was_double_click()) {
3920 _editor->rename_marker (_marker);
3924 /* just a click, do nothing but finish
3925 off the selection process
3928 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3931 case Selection::Set:
3932 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3933 _editor->selection->set (_marker);
3937 case Selection::Toggle:
3938 /* we toggle on the button release, click only */
3939 _editor->selection->toggle (_marker);
3942 case Selection::Extend:
3943 case Selection::Add:
3950 _editor->_dragging_edit_point = false;
3952 _editor->begin_reversible_command ( _("move marker") );
3953 XMLNode &before = _editor->session()->locations()->get_state();
3955 MarkerSelection::iterator i;
3956 CopiedLocationInfo::iterator x;
3959 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3960 x != _copied_locations.end() && i != _editor->selection->markers.end();
3963 Location * location = _editor->find_location_from_marker (*i, is_start);
3967 if (location->locked()) {
3971 if (location->is_mark()) {
3972 location->set_start (((*x).location)->start());
3974 location->set (((*x).location)->start(), ((*x).location)->end());
3979 XMLNode &after = _editor->session()->locations()->get_state();
3980 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3981 _editor->commit_reversible_command ();
3985 MarkerDrag::aborted (bool movement_occured)
3987 if (!movement_occured) {
3991 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3993 /* move all markers to their original location */
3996 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3999 Location * location = _editor->find_location_from_marker (*m, is_start);
4002 (*m)->set_position (is_start ? location->start() : location->end());
4009 MarkerDrag::update_item (Location*)
4014 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4016 _cumulative_x_drag (0),
4017 _cumulative_y_drag (0)
4019 if (_zero_gain_fraction < 0.0) {
4020 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4023 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4025 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4031 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4033 Drag::start_grab (event, _editor->cursors()->fader);
4035 // start the grab at the center of the control point so
4036 // the point doesn't 'jump' to the mouse after the first drag
4037 _fixed_grab_x = _point->get_x();
4038 _fixed_grab_y = _point->get_y();
4040 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4041 setup_snap_delta (pos);
4043 float const fraction = 1 - (_point->get_y() / _point->line().height());
4045 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4047 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4049 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4051 if (!_point->can_slide ()) {
4052 _x_constrained = true;
4057 ControlPointDrag::motion (GdkEvent* event, bool)
4059 double dx = _drags->current_pointer_x() - last_pointer_x();
4060 double dy = current_pointer_y() - last_pointer_y();
4062 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4067 /* coordinate in pixels relative to the start of the region (for region-based automation)
4068 or track (for track-based automation) */
4069 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4070 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4072 // calculate zero crossing point. back off by .01 to stay on the
4073 // positive side of zero
4074 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4076 // make sure we hit zero when passing through
4077 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4081 if (_x_constrained) {
4084 if (_y_constrained) {
4088 _cumulative_x_drag = cx - _fixed_grab_x;
4089 _cumulative_y_drag = cy - _fixed_grab_y;
4093 cy = min ((double) _point->line().height(), cy);
4095 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4097 if (!_x_constrained) {
4098 _editor->snap_to_with_modifier (cx_frames, event);
4101 cx_frames -= snap_delta (event->button.state);
4102 cx_frames = min (cx_frames, _point->line().maximum_time());
4104 float const fraction = 1.0 - (cy / _point->line().height());
4106 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4108 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4112 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4114 if (!movement_occurred) {
4117 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4118 _editor->reset_point_selection ();
4122 motion (event, false);
4125 _point->line().end_drag (_pushing, _final_index);
4126 _editor->commit_reversible_command ();
4130 ControlPointDrag::aborted (bool)
4132 _point->line().reset ();
4136 ControlPointDrag::active (Editing::MouseMode m)
4138 if (m == Editing::MouseDraw) {
4139 /* always active in mouse draw */
4143 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4144 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4147 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4150 , _cumulative_y_drag (0)
4152 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4156 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4158 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4161 _item = &_line->grab_item ();
4163 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4164 origin, and ditto for y.
4167 double cx = event->button.x;
4168 double cy = event->button.y;
4170 _line->parent_group().canvas_to_item (cx, cy);
4172 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4177 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4178 /* no adjacent points */
4182 Drag::start_grab (event, _editor->cursors()->fader);
4184 /* store grab start in parent frame */
4189 double fraction = 1.0 - (cy / _line->height());
4191 _line->start_drag_line (before, after, fraction);
4193 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4197 LineDrag::motion (GdkEvent* event, bool)
4199 double dy = current_pointer_y() - last_pointer_y();
4201 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4205 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4207 _cumulative_y_drag = cy - _fixed_grab_y;
4210 cy = min ((double) _line->height(), cy);
4212 double const fraction = 1.0 - (cy / _line->height());
4215 /* we are ignoring x position for this drag, so we can just pass in anything */
4216 _line->drag_motion (0, fraction, true, false, ignored);
4218 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4222 LineDrag::finished (GdkEvent* event, bool movement_occured)
4224 if (movement_occured) {
4225 motion (event, false);
4226 _line->end_drag (false, 0);
4228 /* add a new control point on the line */
4230 AutomationTimeAxisView* atv;
4232 _line->end_drag (false, 0);
4234 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4235 framepos_t where = _editor->window_event_sample (event, 0, 0);
4236 atv->add_automation_event (event, where, event->button.y, false);
4240 _editor->commit_reversible_command ();
4244 LineDrag::aborted (bool)
4249 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4252 _cumulative_x_drag (0)
4254 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4258 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4260 Drag::start_grab (event);
4262 _line = reinterpret_cast<Line*> (_item);
4265 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4267 double cx = event->button.x;
4268 double cy = event->button.y;
4270 _item->parent()->canvas_to_item (cx, cy);
4272 /* store grab start in parent frame */
4273 _region_view_grab_x = cx;
4275 _before = *(float*) _item->get_data ("position");
4277 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4279 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4283 FeatureLineDrag::motion (GdkEvent*, bool)
4285 double dx = _drags->current_pointer_x() - last_pointer_x();
4287 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4289 _cumulative_x_drag += dx;
4291 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4300 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4302 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4304 float *pos = new float;
4307 _line->set_data ("position", pos);
4313 FeatureLineDrag::finished (GdkEvent*, bool)
4315 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4316 _arv->update_transient(_before, _before);
4320 FeatureLineDrag::aborted (bool)
4325 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4327 , _vertical_only (false)
4329 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4333 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4335 Drag::start_grab (event);
4336 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4340 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4347 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4349 framepos_t grab = grab_frame ();
4350 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4351 _editor->snap_to_with_modifier (grab, event);
4353 grab = raw_grab_frame ();
4356 /* base start and end on initial click position */
4366 if (current_pointer_y() < grab_y()) {
4367 y1 = current_pointer_y();
4370 y2 = current_pointer_y();
4374 if (start != end || y1 != y2) {
4376 double x1 = _editor->sample_to_pixel (start);
4377 double x2 = _editor->sample_to_pixel (end);
4378 const double min_dimension = 2.0;
4380 if (_vertical_only) {
4381 /* fixed 10 pixel width */
4385 x2 = min (x1 - min_dimension, x2);
4387 x2 = max (x1 + min_dimension, x2);
4392 y2 = min (y1 - min_dimension, y2);
4394 y2 = max (y1 + min_dimension, y2);
4397 /* translate rect into item space and set */
4399 ArdourCanvas::Rect r (x1, y1, x2, y2);
4401 /* this drag is a _trackview_only == true drag, so the y1 and
4402 * y2 (computed using current_pointer_y() and grab_y()) will be
4403 * relative to the top of the trackview group). The
4404 * rubberband rect has the same parent/scroll offset as the
4405 * the trackview group, so we can use the "r" rect directly
4406 * to set the shape of the rubberband.
4409 _editor->rubberband_rect->set (r);
4410 _editor->rubberband_rect->show();
4411 _editor->rubberband_rect->raise_to_top();
4413 show_verbose_cursor_time (pf);
4415 do_select_things (event, true);
4420 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4424 framepos_t grab = grab_frame ();
4425 framepos_t lpf = last_pointer_frame ();
4427 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4428 grab = raw_grab_frame ();
4429 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4443 if (current_pointer_y() < grab_y()) {
4444 y1 = current_pointer_y();
4447 y2 = current_pointer_y();
4451 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4455 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4457 if (movement_occurred) {
4459 motion (event, false);
4460 do_select_things (event, false);
4466 bool do_deselect = true;
4467 MidiTimeAxisView* mtv;
4469 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4471 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4472 /* nothing selected */
4473 add_midi_region (mtv);
4474 do_deselect = false;
4478 /* do not deselect if Primary or Tertiary (toggle-select or
4479 * extend-select are pressed.
4482 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4483 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4490 _editor->rubberband_rect->hide();
4494 RubberbandSelectDrag::aborted (bool)
4496 _editor->rubberband_rect->hide ();
4499 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4500 : RegionDrag (e, i, p, v)
4502 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4506 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4508 Drag::start_grab (event, cursor);
4510 _editor->get_selection().add (_primary);
4512 framepos_t where = _primary->region()->position();
4513 setup_snap_delta (where);
4515 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4519 TimeFXDrag::motion (GdkEvent* event, bool)
4521 RegionView* rv = _primary;
4522 StreamView* cv = rv->get_time_axis_view().view ();
4524 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4525 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4526 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4527 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4528 _editor->snap_to_with_modifier (pf, event);
4529 pf -= snap_delta (event->button.state);
4531 if (pf > rv->region()->position()) {
4532 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4535 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4539 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4541 _primary->get_time_axis_view().hide_timestretch ();
4543 if (!movement_occurred) {
4547 if (last_pointer_frame() < _primary->region()->position()) {
4548 /* backwards drag of the left edge - not usable */
4552 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4554 float percentage = (double) newlen / (double) _primary->region()->length();
4556 #ifndef USE_RUBBERBAND
4557 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4558 if (_primary->region()->data_type() == DataType::AUDIO) {
4559 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4563 if (!_editor->get_selection().regions.empty()) {
4564 /* primary will already be included in the selection, and edit
4565 group shared editing will propagate selection across
4566 equivalent regions, so just use the current region
4570 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4571 error << _("An error occurred while executing time stretch operation") << endmsg;
4577 TimeFXDrag::aborted (bool)
4579 _primary->get_time_axis_view().hide_timestretch ();
4582 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4585 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4589 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4591 Drag::start_grab (event);
4595 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4597 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4601 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4603 if (movement_occurred && _editor->session()) {
4604 /* make sure we stop */
4605 _editor->session()->request_transport_speed (0.0);
4610 ScrubDrag::aborted (bool)
4615 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4619 , _time_selection_at_start (!_editor->get_selection().time.empty())
4621 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4623 if (_time_selection_at_start) {
4624 start_at_start = _editor->get_selection().time.start();
4625 end_at_start = _editor->get_selection().time.end_frame();
4630 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4632 if (_editor->session() == 0) {
4636 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4638 switch (_operation) {
4639 case CreateSelection:
4640 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4645 cursor = _editor->cursors()->selector;
4646 Drag::start_grab (event, cursor);
4649 case SelectionStartTrim:
4650 if (_editor->clicked_axisview) {
4651 _editor->clicked_axisview->order_selection_trims (_item, true);
4653 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4656 case SelectionEndTrim:
4657 if (_editor->clicked_axisview) {
4658 _editor->clicked_axisview->order_selection_trims (_item, false);
4660 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4664 Drag::start_grab (event, cursor);
4667 case SelectionExtend:
4668 Drag::start_grab (event, cursor);
4672 if (_operation == SelectionMove) {
4673 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4675 show_verbose_cursor_time (adjusted_current_frame (event));
4680 SelectionDrag::setup_pointer_frame_offset ()
4682 switch (_operation) {
4683 case CreateSelection:
4684 _pointer_frame_offset = 0;
4687 case SelectionStartTrim:
4689 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4692 case SelectionEndTrim:
4693 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4696 case SelectionExtend:
4702 SelectionDrag::motion (GdkEvent* event, bool first_move)
4704 framepos_t start = 0;
4706 framecnt_t length = 0;
4707 framecnt_t distance = 0;
4709 framepos_t const pending_position = adjusted_current_frame (event);
4711 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4715 switch (_operation) {
4716 case CreateSelection:
4718 framepos_t grab = grab_frame ();
4721 grab = adjusted_current_frame (event, false);
4722 if (grab < pending_position) {
4723 _editor->snap_to (grab, RoundDownMaybe);
4725 _editor->snap_to (grab, RoundUpMaybe);
4729 if (pending_position < grab) {
4730 start = pending_position;
4733 end = pending_position;
4737 /* first drag: Either add to the selection
4738 or create a new selection
4745 /* adding to the selection */
4746 _editor->set_selected_track_as_side_effect (Selection::Add);
4747 _editor->clicked_selection = _editor->selection->add (start, end);
4754 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4755 _editor->set_selected_track_as_side_effect (Selection::Set);
4758 _editor->clicked_selection = _editor->selection->set (start, end);
4762 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4763 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4764 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4766 _editor->selection->add (atest);
4770 /* select all tracks within the rectangle that we've marked out so far */
4771 TrackViewList new_selection;
4772 TrackViewList& all_tracks (_editor->track_views);
4774 ArdourCanvas::Coord const top = grab_y();
4775 ArdourCanvas::Coord const bottom = current_pointer_y();
4777 if (top >= 0 && bottom >= 0) {
4779 //first, find the tracks that are covered in the y range selection
4780 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4781 if ((*i)->covered_by_y_range (top, bottom)) {
4782 new_selection.push_back (*i);
4786 //now find any tracks that are GROUPED with the tracks we selected
4787 TrackViewList grouped_add = new_selection;
4788 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4789 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4790 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4791 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4792 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4793 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4794 grouped_add.push_back (*j);
4799 //now compare our list with the current selection, and add or remove as necessary
4800 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4801 TrackViewList tracks_to_add;
4802 TrackViewList tracks_to_remove;
4803 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4804 if ( !_editor->selection->tracks.contains ( *i ) )
4805 tracks_to_add.push_back ( *i );
4806 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4807 if ( !grouped_add.contains ( *i ) )
4808 tracks_to_remove.push_back ( *i );
4809 _editor->selection->add(tracks_to_add);
4810 _editor->selection->remove(tracks_to_remove);
4816 case SelectionStartTrim:
4818 start = _editor->selection->time[_editor->clicked_selection].start;
4819 end = _editor->selection->time[_editor->clicked_selection].end;
4821 if (pending_position > end) {
4824 start = pending_position;
4828 case SelectionEndTrim:
4830 start = _editor->selection->time[_editor->clicked_selection].start;
4831 end = _editor->selection->time[_editor->clicked_selection].end;
4833 if (pending_position < start) {
4836 end = pending_position;
4843 start = _editor->selection->time[_editor->clicked_selection].start;
4844 end = _editor->selection->time[_editor->clicked_selection].end;
4846 length = end - start;
4847 distance = pending_position - start;
4848 start = pending_position;
4849 _editor->snap_to (start);
4851 end = start + length;
4855 case SelectionExtend:
4860 switch (_operation) {
4862 if (_time_selection_at_start) {
4863 _editor->selection->move_time (distance);
4867 _editor->selection->replace (_editor->clicked_selection, start, end);
4871 if (_operation == SelectionMove) {
4872 show_verbose_cursor_time(start);
4874 show_verbose_cursor_time(pending_position);
4879 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4881 Session* s = _editor->session();
4883 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4884 if (movement_occurred) {
4885 motion (event, false);
4886 /* XXX this is not object-oriented programming at all. ick */
4887 if (_editor->selection->time.consolidate()) {
4888 _editor->selection->TimeChanged ();
4891 /* XXX what if its a music time selection? */
4893 if ( s->get_play_range() && s->transport_rolling() ) {
4894 s->request_play_range (&_editor->selection->time, true);
4896 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4897 if (_operation == SelectionEndTrim)
4898 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4900 s->request_locate (_editor->get_selection().time.start());
4906 /* just a click, no pointer movement.
4909 if (_operation == SelectionExtend) {
4910 if (_time_selection_at_start) {
4911 framepos_t pos = adjusted_current_frame (event, false);
4912 framepos_t start = min (pos, start_at_start);
4913 framepos_t end = max (pos, end_at_start);
4914 _editor->selection->set (start, end);
4917 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4918 if (_editor->clicked_selection) {
4919 _editor->selection->remove (_editor->clicked_selection);
4922 if (!_editor->clicked_selection) {
4923 _editor->selection->clear_time();
4928 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4929 _editor->selection->set (_editor->clicked_axisview);
4932 if (s && s->get_play_range () && s->transport_rolling()) {
4933 s->request_stop (false, false);
4938 _editor->stop_canvas_autoscroll ();
4939 _editor->clicked_selection = 0;
4940 _editor->commit_reversible_selection_op ();
4944 SelectionDrag::aborted (bool)
4949 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4950 : Drag (e, i, false),
4954 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4956 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4957 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4958 physical_screen_height (_editor->get_window())));
4959 _drag_rect->hide ();
4961 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4962 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4965 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4967 /* normal canvas items will be cleaned up when their parent group is deleted. But
4968 this item is created as the child of a long-lived parent group, and so we
4969 need to explicitly delete it.
4975 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4977 if (_editor->session() == 0) {
4981 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4983 if (!_editor->temp_location) {
4984 _editor->temp_location = new Location (*_editor->session());
4987 switch (_operation) {
4988 case CreateSkipMarker:
4989 case CreateRangeMarker:
4990 case CreateTransportMarker:
4991 case CreateCDMarker:
4993 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4998 cursor = _editor->cursors()->selector;
5002 Drag::start_grab (event, cursor);
5004 show_verbose_cursor_time (adjusted_current_frame (event));
5008 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5010 framepos_t start = 0;
5012 ArdourCanvas::Rectangle *crect;
5014 switch (_operation) {
5015 case CreateSkipMarker:
5016 crect = _editor->range_bar_drag_rect;
5018 case CreateRangeMarker:
5019 crect = _editor->range_bar_drag_rect;
5021 case CreateTransportMarker:
5022 crect = _editor->transport_bar_drag_rect;
5024 case CreateCDMarker:
5025 crect = _editor->cd_marker_bar_drag_rect;
5028 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5033 framepos_t const pf = adjusted_current_frame (event);
5035 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5036 framepos_t grab = grab_frame ();
5037 _editor->snap_to (grab);
5039 if (pf < grab_frame()) {
5047 /* first drag: Either add to the selection
5048 or create a new selection.
5053 _editor->temp_location->set (start, end);
5057 update_item (_editor->temp_location);
5059 //_drag_rect->raise_to_top();
5065 _editor->temp_location->set (start, end);
5067 double x1 = _editor->sample_to_pixel (start);
5068 double x2 = _editor->sample_to_pixel (end);
5072 update_item (_editor->temp_location);
5075 show_verbose_cursor_time (pf);
5080 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5082 Location * newloc = 0;
5086 if (movement_occurred) {
5087 motion (event, false);
5090 switch (_operation) {
5091 case CreateSkipMarker:
5092 case CreateRangeMarker:
5093 case CreateCDMarker:
5095 XMLNode &before = _editor->session()->locations()->get_state();
5096 if (_operation == CreateSkipMarker) {
5097 _editor->begin_reversible_command (_("new skip marker"));
5098 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5099 flags = Location::IsRangeMarker | Location::IsSkip;
5100 _editor->range_bar_drag_rect->hide();
5101 } else if (_operation == CreateCDMarker) {
5102 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5103 _editor->begin_reversible_command (_("new CD marker"));
5104 flags = Location::IsRangeMarker | Location::IsCDMarker;
5105 _editor->cd_marker_bar_drag_rect->hide();
5107 _editor->begin_reversible_command (_("new skip marker"));
5108 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5109 flags = Location::IsRangeMarker;
5110 _editor->range_bar_drag_rect->hide();
5112 newloc = new Location (
5113 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5116 _editor->session()->locations()->add (newloc, true);
5117 XMLNode &after = _editor->session()->locations()->get_state();
5118 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5119 _editor->commit_reversible_command ();
5123 case CreateTransportMarker:
5124 // popup menu to pick loop or punch
5125 _editor->new_transport_marker_context_menu (&event->button, _item);
5131 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5133 if (_operation == CreateTransportMarker) {
5135 /* didn't drag, so just locate */
5137 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5139 } else if (_operation == CreateCDMarker) {
5141 /* didn't drag, but mark is already created so do
5144 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5149 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5151 if (end == max_framepos) {
5152 end = _editor->session()->current_end_frame ();
5155 if (start == max_framepos) {
5156 start = _editor->session()->current_start_frame ();
5159 switch (_editor->mouse_mode) {
5161 /* find the two markers on either side and then make the selection from it */
5162 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5166 /* find the two markers on either side of the click and make the range out of it */
5167 _editor->selection->set (start, end);
5176 _editor->stop_canvas_autoscroll ();
5180 RangeMarkerBarDrag::aborted (bool movement_occured)
5182 if (movement_occured) {
5183 _drag_rect->hide ();
5188 RangeMarkerBarDrag::update_item (Location* location)
5190 double const x1 = _editor->sample_to_pixel (location->start());
5191 double const x2 = _editor->sample_to_pixel (location->end());
5193 _drag_rect->set_x0 (x1);
5194 _drag_rect->set_x1 (x2);
5197 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5199 , _cumulative_dx (0)
5200 , _cumulative_dy (0)
5202 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5204 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5206 _region = &_primary->region_view ();
5207 _note_height = _region->midi_stream_view()->note_height ();
5211 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5213 Drag::start_grab (event);
5214 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5216 if (!(_was_selected = _primary->selected())) {
5218 /* tertiary-click means extend selection - we'll do that on button release,
5219 so don't add it here, because otherwise we make it hard to figure
5220 out the "extend-to" range.
5223 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5226 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5229 _region->note_selected (_primary, true);
5231 _region->unique_select (_primary);
5234 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5235 _editor->commit_reversible_selection_op();
5240 /** @return Current total drag x change in frames */
5242 NoteDrag::total_dx (const guint state) const
5245 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5247 /* primary note time */
5248 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5250 /* new time of the primary note in session frames */
5251 frameoffset_t st = n + dx + snap_delta (state);
5253 framepos_t const rp = _region->region()->position ();
5255 /* prevent the note being dragged earlier than the region's position */
5258 /* possibly snap and return corresponding delta */
5262 if (ArdourKeyboard::indicates_snap (state)) {
5263 if (_editor->snap_mode () != SnapOff) {
5267 if (_editor->snap_mode () == SnapOff) {
5269 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5270 if (ArdourKeyboard::indicates_snap_delta (state)) {
5276 return _region->snap_frame_to_frame (st - rp, snap) + rp - n - snap_delta (state);
5279 /** @return Current total drag y change in note number */
5281 NoteDrag::total_dy () const
5283 MidiStreamView* msv = _region->midi_stream_view ();
5284 double const y = _region->midi_view()->y_position ();
5285 /* new current note */
5286 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5288 n = max (msv->lowest_note(), n);
5289 n = min (msv->highest_note(), n);
5290 /* and work out delta */
5291 return n - msv->y_to_note (grab_y() - y);
5295 NoteDrag::motion (GdkEvent * event, bool)
5297 /* Total change in x and y since the start of the drag */
5298 frameoffset_t const dx = total_dx (event->button.state);
5299 int8_t const dy = total_dy ();
5301 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5302 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5303 double const tdy = -dy * _note_height - _cumulative_dy;
5306 _cumulative_dx += tdx;
5307 _cumulative_dy += tdy;
5309 int8_t note_delta = total_dy();
5311 _region->move_selection (tdx, tdy, note_delta);
5313 /* the new note value may be the same as the old one, but we
5314 * don't know what that means because the selection may have
5315 * involved more than one note and we might be doing something
5316 * odd with them. so show the note value anyway, always.
5320 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5322 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5323 (int) floor ((double)new_note));
5325 show_verbose_cursor_text (buf);
5330 NoteDrag::finished (GdkEvent* ev, bool moved)
5333 /* no motion - select note */
5335 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5336 _editor->current_mouse_mode() == Editing::MouseDraw) {
5338 bool changed = false;
5340 if (_was_selected) {
5341 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5343 _region->note_deselected (_primary);
5347 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5348 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5350 if (!extend && !add && _region->selection_size() > 1) {
5351 _region->unique_select (_primary);
5353 } else if (extend) {
5354 _region->note_selected (_primary, true, true);
5357 /* it was added during button press */
5362 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5363 _editor->commit_reversible_selection_op();
5367 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5372 NoteDrag::aborted (bool)
5377 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5378 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5379 : Drag (editor, atv->base_item ())
5381 , _y_origin (atv->y_position())
5382 , _nothing_to_drag (false)
5384 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5385 setup (atv->lines ());
5388 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5389 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5390 : Drag (editor, rv->get_canvas_group ())
5392 , _y_origin (rv->get_time_axis_view().y_position())
5393 , _nothing_to_drag (false)
5396 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5398 list<boost::shared_ptr<AutomationLine> > lines;
5400 AudioRegionView* audio_view;
5401 AutomationRegionView* automation_view;
5402 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5403 lines.push_back (audio_view->get_gain_line ());
5404 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5405 lines.push_back (automation_view->line ());
5408 error << _("Automation range drag created for invalid region type") << endmsg;
5414 /** @param lines AutomationLines to drag.
5415 * @param offset Offset from the session start to the points in the AutomationLines.
5418 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5420 /* find the lines that overlap the ranges being dragged */
5421 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5422 while (i != lines.end ()) {
5423 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5426 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5428 /* check this range against all the AudioRanges that we are using */
5429 list<AudioRange>::const_iterator k = _ranges.begin ();
5430 while (k != _ranges.end()) {
5431 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5437 /* add it to our list if it overlaps at all */
5438 if (k != _ranges.end()) {
5443 _lines.push_back (n);
5449 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5453 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5455 return 1.0 - ((global_y - _y_origin) / line->height());
5459 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5461 const double v = list->eval(x);
5462 return _integral ? rint(v) : v;
5466 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5468 Drag::start_grab (event, cursor);
5470 /* Get line states before we start changing things */
5471 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5472 i->state = &i->line->get_state ();
5473 i->original_fraction = y_fraction (i->line, current_pointer_y());
5476 if (_ranges.empty()) {
5478 /* No selected time ranges: drag all points */
5479 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5480 uint32_t const N = i->line->npoints ();
5481 for (uint32_t j = 0; j < N; ++j) {
5482 i->points.push_back (i->line->nth (j));
5488 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5490 framecnt_t const half = (i->start + i->end) / 2;
5492 /* find the line that this audio range starts in */
5493 list<Line>::iterator j = _lines.begin();
5494 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5498 if (j != _lines.end()) {
5499 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5501 /* j is the line that this audio range starts in; fade into it;
5502 64 samples length plucked out of thin air.
5505 framepos_t a = i->start + 64;
5510 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5511 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5513 the_list->editor_add (p, value (the_list, p));
5514 the_list->editor_add (q, value (the_list, q));
5517 /* same thing for the end */
5520 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5524 if (j != _lines.end()) {
5525 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5527 /* j is the line that this audio range starts in; fade out of it;
5528 64 samples length plucked out of thin air.
5531 framepos_t b = i->end - 64;
5536 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5537 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5539 the_list->editor_add (p, value (the_list, p));
5540 the_list->editor_add (q, value (the_list, q));
5544 _nothing_to_drag = true;
5546 /* Find all the points that should be dragged and put them in the relevant
5547 points lists in the Line structs.
5550 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5552 uint32_t const N = i->line->npoints ();
5553 for (uint32_t j = 0; j < N; ++j) {
5555 /* here's a control point on this line */
5556 ControlPoint* p = i->line->nth (j);
5557 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5559 /* see if it's inside a range */
5560 list<AudioRange>::const_iterator k = _ranges.begin ();
5561 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5565 if (k != _ranges.end()) {
5566 /* dragging this point */
5567 _nothing_to_drag = false;
5568 i->points.push_back (p);
5574 if (_nothing_to_drag) {
5578 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5579 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5584 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5586 if (_nothing_to_drag) {
5590 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5591 float const f = y_fraction (l->line, current_pointer_y());
5592 /* we are ignoring x position for this drag, so we can just pass in anything */
5594 l->line->drag_motion (0, f, true, false, ignored);
5595 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5600 AutomationRangeDrag::finished (GdkEvent* event, bool)
5602 if (_nothing_to_drag) {
5606 motion (event, false);
5607 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5608 i->line->end_drag (false, 0);
5611 _editor->commit_reversible_command ();
5615 AutomationRangeDrag::aborted (bool)
5617 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5622 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5624 , initial_time_axis_view (itav)
5626 /* note that time_axis_view may be null if the regionview was created
5627 * as part of a copy operation.
5629 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5630 layer = v->region()->layer ();
5631 initial_y = v->get_canvas_group()->position().y;
5632 initial_playlist = v->region()->playlist ();
5633 initial_position = v->region()->position ();
5634 initial_end = v->region()->position () + v->region()->length ();
5637 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5638 : Drag (e, i->canvas_item ())
5641 , _cumulative_dx (0)
5643 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5644 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5649 PatchChangeDrag::motion (GdkEvent* ev, bool)
5651 framepos_t f = adjusted_current_frame (ev);
5652 boost::shared_ptr<Region> r = _region_view->region ();
5653 f = max (f, r->position ());
5654 f = min (f, r->last_frame ());
5656 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5657 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5658 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5659 _cumulative_dx = dxu;
5663 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5665 if (!movement_occurred) {
5669 boost::shared_ptr<Region> r (_region_view->region ());
5670 framepos_t f = adjusted_current_frame (ev);
5671 f = max (f, r->position ());
5672 f = min (f, r->last_frame ());
5674 _region_view->move_patch_change (
5676 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5681 PatchChangeDrag::aborted (bool)
5683 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5687 PatchChangeDrag::setup_pointer_frame_offset ()
5689 boost::shared_ptr<Region> region = _region_view->region ();
5690 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5693 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5694 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5701 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5703 _region_view->update_drag_selection (
5705 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5709 MidiRubberbandSelectDrag::deselect_things ()
5714 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5715 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5718 _vertical_only = true;
5722 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5724 double const y = _region_view->midi_view()->y_position ();
5726 y1 = max (0.0, y1 - y);
5727 y2 = max (0.0, y2 - y);
5729 _region_view->update_vertical_drag_selection (
5732 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5737 MidiVerticalSelectDrag::deselect_things ()
5742 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5743 : RubberbandSelectDrag (e, i)
5749 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5751 if (drag_in_progress) {
5752 /* We just want to select things at the end of the drag, not during it */
5756 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5758 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5760 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5762 _editor->commit_reversible_selection_op ();
5766 EditorRubberbandSelectDrag::deselect_things ()
5768 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5770 _editor->selection->clear_tracks();
5771 _editor->selection->clear_regions();
5772 _editor->selection->clear_points ();
5773 _editor->selection->clear_lines ();
5774 _editor->selection->clear_midi_notes ();
5776 _editor->commit_reversible_selection_op();
5779 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5784 _note[0] = _note[1] = 0;
5787 NoteCreateDrag::~NoteCreateDrag ()
5793 NoteCreateDrag::grid_frames (framepos_t t) const
5796 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5798 grid_beats = Evoral::Beats(1);
5801 return _region_view->region_beats_to_region_frames (grid_beats);
5805 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5807 Drag::start_grab (event, cursor);
5809 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5811 framepos_t pf = _drags->current_pointer_frame ();
5812 framecnt_t const g = grid_frames (pf);
5814 /* Hack so that we always snap to the note that we are over, instead of snapping
5815 to the next one if we're more than halfway through the one we're over.
5817 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5821 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5822 _note[1] = _note[0];
5824 MidiStreamView* sv = _region_view->midi_stream_view ();
5825 double const x = _editor->sample_to_pixel (_note[0]);
5826 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5828 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5829 _drag_rect->set_outline_all ();
5830 _drag_rect->set_outline_color (0xffffff99);
5831 _drag_rect->set_fill_color (0xffffff66);
5835 NoteCreateDrag::motion (GdkEvent* event, bool)
5837 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5838 double const x0 = _editor->sample_to_pixel (_note[0]);
5839 double const x1 = _editor->sample_to_pixel (_note[1]);
5840 _drag_rect->set_x0 (std::min(x0, x1));
5841 _drag_rect->set_x1 (std::max(x0, x1));
5845 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5847 if (!had_movement) {
5851 framepos_t const start = min (_note[0], _note[1]);
5852 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5854 framecnt_t const g = grid_frames (start);
5855 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5857 if (_editor->snap_mode() == SnapNormal && length < g) {
5861 Evoral::Beats length_beats = max (
5862 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5864 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5868 NoteCreateDrag::y_to_region (double y) const
5871 _region_view->get_canvas_group()->canvas_to_item (x, y);
5876 NoteCreateDrag::aborted (bool)
5881 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5886 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5890 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5892 Drag::start_grab (event, cursor);
5896 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5902 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5905 distance = _drags->current_pointer_x() - grab_x();
5906 len = ar->fade_in()->back()->when;
5908 distance = grab_x() - _drags->current_pointer_x();
5909 len = ar->fade_out()->back()->when;
5912 /* how long should it be ? */
5914 new_length = len + _editor->pixel_to_sample (distance);
5916 /* now check with the region that this is legal */
5918 new_length = ar->verify_xfade_bounds (new_length, start);
5921 arv->reset_fade_in_shape_width (ar, new_length);
5923 arv->reset_fade_out_shape_width (ar, new_length);
5928 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5934 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5937 distance = _drags->current_pointer_x() - grab_x();
5938 len = ar->fade_in()->back()->when;
5940 distance = grab_x() - _drags->current_pointer_x();
5941 len = ar->fade_out()->back()->when;
5944 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5946 _editor->begin_reversible_command ("xfade trim");
5947 ar->playlist()->clear_owned_changes ();
5950 ar->set_fade_in_length (new_length);
5952 ar->set_fade_out_length (new_length);
5955 /* Adjusting the xfade may affect other regions in the playlist, so we need
5956 to get undo Commands from the whole playlist rather than just the
5960 vector<Command*> cmds;
5961 ar->playlist()->rdiff (cmds);
5962 _editor->session()->add_commands (cmds);
5963 _editor->commit_reversible_command ();
5968 CrossfadeEdgeDrag::aborted (bool)
5971 // arv->redraw_start_xfade ();
5973 // arv->redraw_end_xfade ();
5977 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5978 : Drag (e, item, true)
5979 , line (new EditorCursor (*e))
5981 line->set_position (pos);
5985 RegionCutDrag::~RegionCutDrag ()
5991 RegionCutDrag::motion (GdkEvent*, bool)
5993 framepos_t where = _drags->current_pointer_frame();
5994 _editor->snap_to (where);
5996 line->set_position (where);
6000 RegionCutDrag::finished (GdkEvent*, bool)
6002 _editor->get_track_canvas()->canvas()->re_enter();
6004 framepos_t pos = _drags->current_pointer_frame();
6008 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6014 _editor->split_regions_at (pos, rs);
6018 RegionCutDrag::aborted (bool)