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 /** check constraints for this drag.
408 * Note that the current convention is to use "contains" for
409 * key modifiers during motion and "equals" when initiating a drag.
410 * In this case we haven't moved yet, so "equals" applies here.
412 if (Config->get_edit_mode() != Lock) {
413 if (event->motion.state & Gdk::BUTTON2_MASK) {
414 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
415 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
416 _x_constrained = false;
417 _y_constrained = true;
419 _x_constrained = true;
420 _y_constrained = false;
422 } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
423 // if dragging normally, the motion is constrained to the first direction of movement.
424 if (_initially_vertical) {
425 _x_constrained = true;
426 _y_constrained = false;
428 _x_constrained = false;
429 _y_constrained = true;
433 if (event->button.state & Gdk::BUTTON2_MASK) {
434 _x_constrained = false;
436 _x_constrained = true;
438 _y_constrained = false;
442 if (!from_autoscroll) {
443 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
446 if (!_editor->autoscroll_active() || from_autoscroll) {
449 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
451 motion (event, first_move && !_starting_point_passed);
453 if (first_move && !_starting_point_passed) {
454 _starting_point_passed = true;
457 _last_pointer_x = _drags->current_pointer_x ();
458 _last_pointer_y = current_pointer_y ();
459 _last_pointer_frame = adjusted_current_frame (event);
469 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
477 aborted (_move_threshold_passed);
479 _editor->stop_canvas_autoscroll ();
480 _editor->verbose_cursor()->hide ();
484 Drag::show_verbose_cursor_time (framepos_t frame)
486 _editor->verbose_cursor()->set_time (frame);
487 _editor->verbose_cursor()->show ();
491 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
493 _editor->verbose_cursor()->set_duration (start, end);
494 _editor->verbose_cursor()->show ();
498 Drag::show_verbose_cursor_text (string const & text)
500 _editor->verbose_cursor()->set (text);
501 _editor->verbose_cursor()->show ();
504 boost::shared_ptr<Region>
505 Drag::add_midi_region (MidiTimeAxisView* view)
507 if (_editor->session()) {
508 const TempoMap& map (_editor->session()->tempo_map());
509 framecnt_t pos = grab_frame();
510 const Meter& m = map.meter_at (pos);
511 /* not that the frame rate used here can be affected by pull up/down which
514 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
515 return view->add_region (grab_frame(), len, true);
518 return boost::shared_ptr<Region>();
521 struct EditorOrderTimeAxisViewSorter {
522 bool operator() (TimeAxisView* a, TimeAxisView* b) {
523 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
524 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
526 return ra->route()->order_key () < rb->route()->order_key ();
530 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
535 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
537 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
538 as some of the regions we are dragging may be on such tracks.
541 TrackViewList track_views = _editor->track_views;
542 track_views.sort (EditorOrderTimeAxisViewSorter ());
544 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
545 _time_axis_views.push_back (*i);
547 TimeAxisView::Children children_list = (*i)->get_child_list ();
548 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
549 _time_axis_views.push_back (j->get());
553 /* the list of views can be empty at this point if this is a region list-insert drag
556 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
557 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
560 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
564 RegionDrag::region_going_away (RegionView* v)
566 list<DraggingView>::iterator i = _views.begin ();
567 while (i != _views.end() && i->view != v) {
571 if (i != _views.end()) {
576 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
577 * or -1 if it is not found.
580 RegionDrag::find_time_axis_view (TimeAxisView* t) const
583 int const N = _time_axis_views.size ();
584 while (i < N && _time_axis_views[i] != t) {
588 if (_time_axis_views[i] != t) {
595 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
596 : RegionDrag (e, i, p, v)
599 , _last_pointer_time_axis_view (0)
600 , _last_pointer_layer (0)
605 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
609 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
611 Drag::start_grab (event, cursor);
612 setup_snap_delta (_last_frame_position);
614 show_verbose_cursor_time (_last_frame_position);
616 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
618 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
619 assert(_last_pointer_time_axis_view >= 0);
620 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
624 /* cross track dragging seems broken here. disabled for now. */
625 _y_constrained = true;
630 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
632 /* compute the amount of pointer motion in frames, and where
633 the region would be if we moved it by that much.
635 *pending_region_position = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
637 framepos_t sync_frame;
638 framecnt_t sync_offset;
641 sync_offset = _primary->region()->sync_offset (sync_dir);
643 /* we don't handle a sync point that lies before zero.
645 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
647 sync_frame = *pending_region_position + (sync_dir * sync_offset);
649 _editor->snap_to_with_modifier (sync_frame, event);
651 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - snap_delta (event->button.state);
654 *pending_region_position = _last_frame_position;
657 if (*pending_region_position > max_framepos - _primary->region()->length()) {
658 *pending_region_position = _last_frame_position;
663 bool const x_move_allowed = !_x_constrained;
665 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
667 /* x movement since last time (in pixels) */
668 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
670 /* total x movement */
671 framecnt_t total_dx = *pending_region_position;
672 if (regions_came_from_canvas()) {
673 total_dx = total_dx - grab_frame ();
676 /* check that no regions have gone off the start of the session */
677 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
678 if ((i->view->region()->position() + total_dx) < 0) {
680 *pending_region_position = _last_frame_position;
691 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
697 const int tavsize = _time_axis_views.size();
698 const int dt = delta > 0 ? +1 : -1;
700 int target = start + delta - skip;
702 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
703 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
705 while (current >= 0 && current != target) {
707 if (current < 0 && dt < 0) {
710 if (current >= tavsize && dt > 0) {
713 if (current < 0 || current >= tavsize) {
717 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
718 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
722 if (distance_only && current == start + delta) {
730 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
732 if (_y_constrained) {
736 const int tavsize = _time_axis_views.size();
737 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
738 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
739 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
741 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
742 /* already in the drop zone */
743 if (delta_track >= 0) {
744 /* downward motion - OK if others are still not in the dropzone */
753 } else if (n >= tavsize) {
754 /* downward motion into drop zone. That's fine. */
758 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
759 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
760 /* not a track, or the wrong type */
764 double const l = i->layer + delta_layer;
766 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
767 mode to allow the user to place a region below another on layer 0.
769 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
770 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
771 If it has, the layers will be munged later anyway, so it's ok.
777 /* all regions being dragged are ok with this change */
781 struct DraggingViewSorter {
782 bool operator() (const DraggingView& a, const DraggingView& b) {
783 return a.time_axis_view < b.time_axis_view;
788 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
790 double delta_layer = 0;
791 int delta_time_axis_view = 0;
792 int current_pointer_time_axis_view = -1;
794 assert (!_views.empty ());
796 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
798 /* Find the TimeAxisView that the pointer is now over */
799 const double cur_y = current_pointer_y ();
800 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
801 TimeAxisView* tv = r.first;
803 if (!tv && cur_y < 0) {
804 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
808 /* find drop-zone y-position */
809 Coord last_track_bottom_edge;
810 last_track_bottom_edge = 0;
811 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
812 if (!(*t)->hidden()) {
813 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
818 if (tv && tv->view()) {
819 /* the mouse is over a track */
820 double layer = r.second;
822 if (first_move && tv->view()->layer_display() == Stacked) {
823 tv->view()->set_layer_display (Expanded);
826 /* Here's the current pointer position in terms of time axis view and layer */
827 current_pointer_time_axis_view = find_time_axis_view (tv);
828 assert(current_pointer_time_axis_view >= 0);
830 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
832 /* Work out the change in y */
834 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
835 if (!rtv || !rtv->is_track()) {
836 /* ignore busses early on. we can't move any regions on them */
837 } else if (_last_pointer_time_axis_view < 0) {
838 /* Was in the drop-zone, now over a track.
839 * Hence it must be an upward move (from the bottom)
841 * track_index is still -1, so delta must be set to
842 * move up the correct number of tracks from the bottom.
844 * This is necessary because steps may be skipped if
845 * the bottom-most track is not a valid target and/or
846 * if there are hidden tracks at the bottom.
847 * Hence the initial offset (_ddropzone) as well as the
848 * last valid pointer position (_pdropzone) need to be
849 * taken into account.
851 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
853 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
856 /* TODO needs adjustment per DraggingView,
858 * e.g. select one region on the top-layer of a track
859 * and one region which is at the bottom-layer of another track
862 * Indicated drop-zones and layering is wrong.
863 * and may infer additional layers on the target-track
864 * (depending how many layers the original track had).
866 * Or select two regions (different layers) on a same track,
867 * move across a non-layer track.. -> layering info is lost.
868 * on drop either of the regions may be on top.
870 * Proposed solution: screw it :) well,
871 * don't use delta_layer, use an absolute value
872 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
873 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
874 * 3) iterate over all DraggingView, find the one that is over the track with most layers
875 * 4) proportionally scale layer to layers available on target
877 delta_layer = current_pointer_layer - _last_pointer_layer;
880 /* for automation lanes, there is a TimeAxisView but no ->view()
881 * if (!tv) -> dropzone
883 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
884 /* Moving into the drop-zone.. */
885 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
886 /* delta_time_axis_view may not be sufficient to move into the DZ
887 * the mouse may enter it, but it may not be a valid move due to
890 * -> remember the delta needed to move into the dropzone
892 _ddropzone = delta_time_axis_view;
893 /* ..but subtract hidden tracks (or routes) at the bottom.
894 * we silently move mover them
896 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
897 - _time_axis_views.size();
899 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
900 /* move around inside the zone.
901 * This allows to move further down until all regions are in the zone.
903 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
904 assert(ptr_y >= last_track_bottom_edge);
905 assert(_ddropzone > 0);
907 /* calculate mouse position in 'tracks' below last track. */
908 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
909 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
911 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
913 delta_time_axis_view = dzpos - _pdropzone;
914 } else if (dzpos < _pdropzone && _ndropzone > 0) {
915 // move up inside the DZ
916 delta_time_axis_view = dzpos - _pdropzone;
920 /* Work out the change in x */
921 framepos_t pending_region_position;
922 double const x_delta = compute_x_delta (event, &pending_region_position);
923 _last_frame_position = pending_region_position;
925 /* calculate hidden tracks in current y-axis delta */
927 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
928 /* The mouse is more than one track below the dropzone.
929 * distance calculation is not needed (and would not work, either
930 * because the dropzone is "packed").
932 * Except when [partially] moving regions out of dropzone in a large step.
933 * (the mouse may or may not remain in the DZ)
934 * Hidden tracks at the bottom of the TAV need to be skipped.
936 * This also handles the case if the mouse entered the DZ
937 * in a large step (exessive delta), either due to fast-movement,
938 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
940 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
941 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
943 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
944 -_time_axis_views.size() - dt;
947 else if (_last_pointer_time_axis_view < 0) {
948 /* Moving out of the zone. Check for hidden tracks at the bottom. */
949 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
950 -_time_axis_views.size() - delta_time_axis_view;
952 /* calculate hidden tracks that are skipped by the pointer movement */
953 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
954 - _last_pointer_time_axis_view
955 - delta_time_axis_view;
958 /* Verify change in y */
959 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
960 /* this y movement is not allowed, so do no y movement this time */
961 delta_time_axis_view = 0;
966 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
967 /* haven't reached next snap point, and we're not switching
968 trackviews nor layers. nothing to do.
973 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
974 PlaylistDropzoneMap playlist_dropzone_map;
975 _ndropzone = 0; // number of elements currently in the dropzone
978 /* sort views by time_axis.
979 * This retains track order in the dropzone, regardless
980 * of actual selection order
982 _views.sort (DraggingViewSorter());
984 /* count number of distinct tracks of all regions
985 * being dragged, used for dropzone.
988 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
989 if (i->time_axis_view != prev_track) {
990 prev_track = i->time_axis_view;
996 _views.back().time_axis_view -
997 _views.front().time_axis_view;
999 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1000 - _views.back().time_axis_view;
1002 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1006 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1008 RegionView* rv = i->view;
1013 if (rv->region()->locked() || rv->region()->video_locked()) {
1020 /* reparent the regionview into a group above all
1024 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1025 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1026 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1027 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1028 /* move the item so that it continues to appear at the
1029 same location now that its parent has changed.
1031 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1034 /* If we have moved tracks, we'll fudge the layer delta so that the
1035 region gets moved back onto layer 0 on its new track; this avoids
1036 confusion when dragging regions from non-zero layers onto different
1039 double this_delta_layer = delta_layer;
1040 if (delta_time_axis_view != 0) {
1041 this_delta_layer = - i->layer;
1044 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1046 int track_index = i->time_axis_view + this_delta_time_axis_view;
1047 assert(track_index >= 0);
1049 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1050 /* Track is in the Dropzone */
1052 i->time_axis_view = track_index;
1053 assert(i->time_axis_view >= (int) _time_axis_views.size());
1056 double yposition = 0;
1057 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1058 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1061 /* store index of each new playlist as a negative count, starting at -1 */
1063 if (pdz == playlist_dropzone_map.end()) {
1064 /* compute where this new track (which doesn't exist yet) will live
1067 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1069 /* How high is this region view ? */
1071 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1072 ArdourCanvas::Rect bbox;
1075 bbox = obbox.get ();
1078 last_track_bottom_edge += bbox.height();
1080 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1083 yposition = pdz->second;
1086 /* values are zero or negative, hence the use of min() */
1087 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1092 /* The TimeAxisView that this region is now over */
1093 TimeAxisView* current_tv = _time_axis_views[track_index];
1095 /* Ensure it is moved from stacked -> expanded if appropriate */
1096 if (current_tv->view()->layer_display() == Stacked) {
1097 current_tv->view()->set_layer_display (Expanded);
1100 /* We're only allowed to go -ve in layer on Expanded views */
1101 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1102 this_delta_layer = - i->layer;
1106 rv->set_height (current_tv->view()->child_height ());
1108 /* Update show/hidden status as the region view may have come from a hidden track,
1109 or have moved to one.
1111 if (current_tv->hidden ()) {
1112 rv->get_canvas_group()->hide ();
1114 rv->get_canvas_group()->show ();
1117 /* Update the DraggingView */
1118 i->time_axis_view = track_index;
1119 i->layer += this_delta_layer;
1122 _editor->mouse_brush_insert_region (rv, pending_region_position);
1126 /* Get the y coordinate of the top of the track that this region is now over */
1127 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1129 /* And adjust for the layer that it should be on */
1130 StreamView* cv = current_tv->view ();
1131 switch (cv->layer_display ()) {
1135 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1138 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1142 /* need to get the parent of the regionview
1143 * canvas group and get its position in
1144 * equivalent coordinate space as the trackview
1145 * we are now dragging over.
1148 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1153 /* Now move the region view */
1154 rv->move (x_delta, y_delta);
1156 } /* foreach region */
1158 _total_x_delta += x_delta;
1160 if (x_delta != 0 && !_brushing) {
1161 show_verbose_cursor_time (_last_frame_position);
1164 /* keep track of pointer movement */
1166 /* the pointer is currently over a time axis view */
1168 if (_last_pointer_time_axis_view < 0) {
1169 /* last motion event was not over a time axis view
1170 * or last y-movement out of the dropzone was not valid
1173 if (delta_time_axis_view < 0) {
1174 /* in the drop zone, moving up */
1176 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1177 * We do not use negative _last_pointer_time_axis_view because
1178 * the dropzone is "packed" (the actual track offset is ignored)
1180 * As opposed to the actual number
1181 * of elements in the dropzone (_ndropzone)
1182 * _pdropzone is not constrained. This is necessary
1183 * to allow moving multiple regions with y-distance
1186 * There can be 0 elements in the dropzone,
1187 * even though the drag-pointer is inside the DZ.
1190 * [ Audio-track, Midi-track, Audio-track, DZ ]
1191 * move regions from both audio tracks at the same time into the
1192 * DZ by grabbing the region in the bottom track.
1194 assert(current_pointer_time_axis_view >= 0);
1195 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1199 /* only move out of the zone if the movement is OK */
1200 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1201 assert(delta_time_axis_view < 0);
1202 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1203 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1204 * the current position can be calculated as follows:
1206 // a well placed oofus attack can still throw this off.
1207 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1208 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1211 /* last motion event was also over a time axis view */
1212 _last_pointer_time_axis_view += delta_time_axis_view;
1213 assert(_last_pointer_time_axis_view >= 0);
1218 /* the pointer is not over a time axis view */
1219 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1220 _pdropzone += delta_time_axis_view - delta_skip;
1221 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1224 _last_pointer_layer += delta_layer;
1228 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1230 if (_copy && first_move) {
1231 if (_x_constrained && !_brushing) {
1232 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1233 } else if (!_brushing) {
1234 _editor->begin_reversible_command (Operations::region_copy);
1235 } else if (_brushing) {
1236 _editor->begin_reversible_command (Operations::drag_region_brush);
1238 /* duplicate the regionview(s) and region(s) */
1240 list<DraggingView> new_regionviews;
1242 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1244 RegionView* rv = i->view;
1245 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1246 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1248 const boost::shared_ptr<const Region> original = rv->region();
1249 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1250 region_copy->set_position (original->position());
1251 /* need to set this so that the drop zone code can work. This doesn't
1252 actually put the region into the playlist, but just sets a weak pointer
1255 region_copy->set_playlist (original->playlist());
1259 boost::shared_ptr<AudioRegion> audioregion_copy
1260 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1262 nrv = new AudioRegionView (*arv, audioregion_copy);
1264 boost::shared_ptr<MidiRegion> midiregion_copy
1265 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1266 nrv = new MidiRegionView (*mrv, midiregion_copy);
1271 nrv->get_canvas_group()->show ();
1272 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1274 /* swap _primary to the copy */
1276 if (rv == _primary) {
1280 /* ..and deselect the one we copied */
1282 rv->set_selected (false);
1285 if (!new_regionviews.empty()) {
1287 /* reflect the fact that we are dragging the copies */
1289 _views = new_regionviews;
1291 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1294 } else if (!_copy && first_move) {
1295 if (_x_constrained && !_brushing) {
1296 _editor->begin_reversible_command (_("fixed time region drag"));
1297 } else if (!_brushing) {
1298 _editor->begin_reversible_command (Operations::region_drag);
1299 } else if (_brushing) {
1300 _editor->begin_reversible_command (Operations::drag_region_brush);
1303 RegionMotionDrag::motion (event, first_move);
1307 RegionMotionDrag::finished (GdkEvent *, bool)
1309 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1310 if (!(*i)->view()) {
1314 if ((*i)->view()->layer_display() == Expanded) {
1315 (*i)->view()->set_layer_display (Stacked);
1321 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1323 RegionMotionDrag::finished (ev, movement_occurred);
1325 if (!movement_occurred) {
1329 if (was_double_click() && !_views.empty()) {
1330 DraggingView dv = _views.front();
1331 dv.view->show_region_editor ();
1338 assert (!_views.empty ());
1340 /* We might have hidden region views so that they weren't visible during the drag
1341 (when they have been reparented). Now everything can be shown again, as region
1342 views are back in their track parent groups.
1344 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1345 i->view->get_canvas_group()->show ();
1348 bool const changed_position = (_last_frame_position != _primary->region()->position());
1349 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1350 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1370 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1374 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1376 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1381 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1382 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1383 uint32_t output_chan = region->n_channels();
1384 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1385 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1387 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1388 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1390 rtav->set_height (original->current_height());
1394 ChanCount one_midi_port (DataType::MIDI, 1);
1395 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1396 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1397 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1399 rtav->set_height (original->current_height());
1404 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1410 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1412 RegionSelection new_views;
1413 PlaylistSet modified_playlists;
1414 RouteTimeAxisView* new_time_axis_view = 0;
1417 /* all changes were made during motion event handlers */
1419 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1423 _editor->commit_reversible_command ();
1427 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1428 PlaylistMapping playlist_mapping;
1430 /* insert the regions into their new playlists */
1431 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1433 RouteTimeAxisView* dest_rtv = 0;
1435 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1441 if (changed_position && !_x_constrained) {
1442 where = i->view->region()->position() - drag_delta;
1444 where = i->view->region()->position();
1447 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1448 /* dragged to drop zone */
1450 PlaylistMapping::iterator pm;
1452 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1453 /* first region from this original playlist: create a new track */
1454 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1455 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1456 dest_rtv = new_time_axis_view;
1458 /* we already created a new track for regions from this playlist, use it */
1459 dest_rtv = pm->second;
1462 /* destination time axis view is the one we dragged to */
1463 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1466 if (dest_rtv != 0) {
1467 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1468 if (new_view != 0) {
1469 new_views.push_back (new_view);
1473 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1474 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1477 list<DraggingView>::const_iterator next = i;
1483 /* If we've created new regions either by copying or moving
1484 to a new track, we want to replace the old selection with the new ones
1487 if (new_views.size() > 0) {
1488 _editor->selection->set (new_views);
1491 /* write commands for the accumulated diffs for all our modified playlists */
1492 add_stateful_diff_commands_for_playlists (modified_playlists);
1494 _editor->commit_reversible_command ();
1498 RegionMoveDrag::finished_no_copy (
1499 bool const changed_position,
1500 bool const changed_tracks,
1501 framecnt_t const drag_delta
1504 RegionSelection new_views;
1505 PlaylistSet modified_playlists;
1506 PlaylistSet frozen_playlists;
1507 set<RouteTimeAxisView*> views_to_update;
1508 RouteTimeAxisView* new_time_axis_view = 0;
1510 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1511 PlaylistMapping playlist_mapping;
1513 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1515 RegionView* rv = i->view;
1516 RouteTimeAxisView* dest_rtv = 0;
1518 if (rv->region()->locked() || rv->region()->video_locked()) {
1523 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1524 /* dragged to drop zone */
1526 PlaylistMapping::iterator pm;
1528 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1529 /* first region from this original playlist: create a new track */
1530 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1531 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1532 dest_rtv = new_time_axis_view;
1534 /* we already created a new track for regions from this playlist, use it */
1535 dest_rtv = pm->second;
1539 /* destination time axis view is the one we dragged to */
1540 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1545 double const dest_layer = i->layer;
1547 views_to_update.insert (dest_rtv);
1551 if (changed_position && !_x_constrained) {
1552 where = rv->region()->position() - drag_delta;
1554 where = rv->region()->position();
1557 if (changed_tracks) {
1559 /* insert into new playlist */
1561 RegionView* new_view = insert_region_into_playlist (
1562 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1565 if (new_view == 0) {
1570 new_views.push_back (new_view);
1572 /* remove from old playlist */
1574 /* the region that used to be in the old playlist is not
1575 moved to the new one - we use a copy of it. as a result,
1576 any existing editor for the region should no longer be
1579 rv->hide_region_editor();
1582 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1586 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1588 /* this movement may result in a crossfade being modified, or a layering change,
1589 so we need to get undo data from the playlist as well as the region.
1592 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1594 playlist->clear_changes ();
1597 rv->region()->clear_changes ();
1600 motion on the same track. plonk the previously reparented region
1601 back to its original canvas group (its streamview).
1602 No need to do anything for copies as they are fake regions which will be deleted.
1605 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1606 rv->get_canvas_group()->set_y_position (i->initial_y);
1609 /* just change the model */
1610 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1611 playlist->set_layer (rv->region(), dest_layer);
1614 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1616 r = frozen_playlists.insert (playlist);
1619 playlist->freeze ();
1622 rv->region()->set_position (where);
1623 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1626 if (changed_tracks) {
1628 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1629 was selected in all of them, then removing it from a playlist will have removed all
1630 trace of it from _views (i.e. there were N regions selected, we removed 1,
1631 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1632 corresponding regionview, and _views is now empty).
1634 This could have invalidated any and all iterators into _views.
1636 The heuristic we use here is: if the region selection is empty, break out of the loop
1637 here. if the region selection is not empty, then restart the loop because we know that
1638 we must have removed at least the region(view) we've just been working on as well as any
1639 that we processed on previous iterations.
1641 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1642 we can just iterate.
1646 if (_views.empty()) {
1657 /* If we've created new regions either by copying or moving
1658 to a new track, we want to replace the old selection with the new ones
1661 if (new_views.size() > 0) {
1662 _editor->selection->set (new_views);
1665 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1669 /* write commands for the accumulated diffs for all our modified playlists */
1670 add_stateful_diff_commands_for_playlists (modified_playlists);
1671 /* applies to _brushing */
1672 _editor->commit_reversible_command ();
1674 /* We have futzed with the layering of canvas items on our streamviews.
1675 If any region changed layer, this will have resulted in the stream
1676 views being asked to set up their region views, and all will be well.
1677 If not, we might now have badly-ordered region views. Ask the StreamViews
1678 involved to sort themselves out, just in case.
1681 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1682 (*i)->view()->playlist_layered ((*i)->track ());
1686 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1687 * @param region Region to remove.
1688 * @param playlist playlist To remove from.
1689 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1690 * that clear_changes () is only called once per playlist.
1693 RegionMoveDrag::remove_region_from_playlist (
1694 boost::shared_ptr<Region> region,
1695 boost::shared_ptr<Playlist> playlist,
1696 PlaylistSet& modified_playlists
1699 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1702 playlist->clear_changes ();
1705 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1709 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1710 * clearing the playlist's diff history first if necessary.
1711 * @param region Region to insert.
1712 * @param dest_rtv Destination RouteTimeAxisView.
1713 * @param dest_layer Destination layer.
1714 * @param where Destination position.
1715 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1716 * that clear_changes () is only called once per playlist.
1717 * @return New RegionView, or 0 if no insert was performed.
1720 RegionMoveDrag::insert_region_into_playlist (
1721 boost::shared_ptr<Region> region,
1722 RouteTimeAxisView* dest_rtv,
1725 PlaylistSet& modified_playlists
1728 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1729 if (!dest_playlist) {
1733 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1734 _new_region_view = 0;
1735 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1737 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1738 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1740 dest_playlist->clear_changes ();
1743 dest_playlist->add_region (region, where);
1745 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1746 dest_playlist->set_layer (region, dest_layer);
1751 assert (_new_region_view);
1753 return _new_region_view;
1757 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1759 _new_region_view = rv;
1763 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1765 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1766 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1768 _editor->session()->add_command (c);
1777 RegionMoveDrag::aborted (bool movement_occurred)
1781 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1782 list<DraggingView>::const_iterator next = i;
1791 RegionMotionDrag::aborted (movement_occurred);
1796 RegionMotionDrag::aborted (bool)
1798 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1800 StreamView* sview = (*i)->view();
1803 if (sview->layer_display() == Expanded) {
1804 sview->set_layer_display (Stacked);
1809 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1810 RegionView* rv = i->view;
1811 TimeAxisView* tv = &(rv->get_time_axis_view ());
1812 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1814 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1815 rv->get_canvas_group()->set_y_position (0);
1817 rv->move (-_total_x_delta, 0);
1818 rv->set_height (rtv->view()->child_height ());
1822 /** @param b true to brush, otherwise false.
1823 * @param c true to make copies of the regions being moved, otherwise false.
1825 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1826 : RegionMotionDrag (e, i, p, v, b)
1829 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1832 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1833 if (rtv && rtv->is_track()) {
1834 speed = rtv->track()->speed ();
1837 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1841 RegionMoveDrag::setup_pointer_frame_offset ()
1843 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1846 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1847 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1849 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1851 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1852 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1854 _primary = v->view()->create_region_view (r, false, false);
1856 _primary->get_canvas_group()->show ();
1857 _primary->set_position (pos, 0);
1858 _views.push_back (DraggingView (_primary, this, v));
1860 _last_frame_position = pos;
1862 _item = _primary->get_canvas_group ();
1866 RegionInsertDrag::finished (GdkEvent *, bool)
1868 int pos = _views.front().time_axis_view;
1869 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1871 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1873 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1874 _primary->get_canvas_group()->set_y_position (0);
1876 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1878 _editor->begin_reversible_command (Operations::insert_region);
1879 playlist->clear_changes ();
1880 playlist->add_region (_primary->region (), _last_frame_position);
1882 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1883 if (Config->get_edit_mode() == Ripple) {
1884 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1887 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1888 _editor->commit_reversible_command ();
1896 RegionInsertDrag::aborted (bool)
1903 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1904 : RegionMoveDrag (e, i, p, v, false, false)
1906 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1909 struct RegionSelectionByPosition {
1910 bool operator() (RegionView*a, RegionView* b) {
1911 return a->region()->position () < b->region()->position();
1916 RegionSpliceDrag::motion (GdkEvent* event, bool)
1918 /* Which trackview is this ? */
1920 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1921 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1923 /* The region motion is only processed if the pointer is over
1927 if (!tv || !tv->is_track()) {
1928 /* To make sure we hide the verbose canvas cursor when the mouse is
1929 not held over an audio track.
1931 _editor->verbose_cursor()->hide ();
1934 _editor->verbose_cursor()->show ();
1939 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1945 RegionSelection copy;
1946 _editor->selection->regions.by_position(copy);
1948 framepos_t const pf = adjusted_current_frame (event);
1950 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1952 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1958 boost::shared_ptr<Playlist> playlist;
1960 if ((playlist = atv->playlist()) == 0) {
1964 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1969 if (pf < (*i)->region()->last_frame() + 1) {
1973 if (pf > (*i)->region()->first_frame()) {
1979 playlist->shuffle ((*i)->region(), dir);
1984 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1986 RegionMoveDrag::finished (event, movement_occurred);
1990 RegionSpliceDrag::aborted (bool)
2000 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2003 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2005 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2006 RegionSelection to_ripple;
2007 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2008 if ((*i)->position() >= where) {
2009 to_ripple.push_back (rtv->view()->find_view(*i));
2013 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2014 if (!exclude.contains (*i)) {
2015 // the selection has already been added to _views
2017 if (drag_in_progress) {
2018 // do the same things that RegionMotionDrag::motion does when
2019 // first_move is true, for the region views that we're adding
2020 // to _views this time
2023 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2024 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2025 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2026 rvg->reparent (_editor->_drag_motion_group);
2028 // we only need to move in the y direction
2029 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2034 _views.push_back (DraggingView (*i, this, tav));
2040 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2043 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2044 // we added all the regions after the selection
2046 std::list<DraggingView>::iterator to_erase = i++;
2047 if (!_editor->selection->regions.contains (to_erase->view)) {
2048 // restore the non-selected regions to their original playlist & positions,
2049 // and then ripple them back by the length of the regions that were dragged away
2050 // do the same things as RegionMotionDrag::aborted
2052 RegionView *rv = to_erase->view;
2053 TimeAxisView* tv = &(rv->get_time_axis_view ());
2054 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2057 // plonk them back onto their own track
2058 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2059 rv->get_canvas_group()->set_y_position (0);
2063 // move the underlying region to match the view
2064 rv->region()->set_position (rv->region()->position() + amount);
2066 // restore the view to match the underlying region's original position
2067 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2070 rv->set_height (rtv->view()->child_height ());
2071 _views.erase (to_erase);
2077 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2079 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2081 return allow_moves_across_tracks;
2089 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2090 : RegionMoveDrag (e, i, p, v, false, false)
2092 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2093 // compute length of selection
2094 RegionSelection selected_regions = _editor->selection->regions;
2095 selection_length = selected_regions.end_frame() - selected_regions.start();
2097 // we'll only allow dragging to another track in ripple mode if all the regions
2098 // being dragged start off on the same track
2099 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2102 exclude = new RegionList;
2103 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2104 exclude->push_back((*i)->region());
2107 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2108 RegionSelection copy;
2109 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2111 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2112 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2114 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2115 // find ripple start point on each applicable playlist
2116 RegionView *first_selected_on_this_track = NULL;
2117 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2118 if ((*i)->region()->playlist() == (*pi)) {
2119 // region is on this playlist - it's the first, because they're sorted
2120 first_selected_on_this_track = *i;
2124 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2125 add_all_after_to_views (
2126 &first_selected_on_this_track->get_time_axis_view(),
2127 first_selected_on_this_track->region()->position(),
2128 selected_regions, false);
2131 if (allow_moves_across_tracks) {
2132 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2140 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2142 /* Which trackview is this ? */
2144 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2145 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2147 /* The region motion is only processed if the pointer is over
2151 if (!tv || !tv->is_track()) {
2152 /* To make sure we hide the verbose canvas cursor when the mouse is
2153 not held over an audiotrack.
2155 _editor->verbose_cursor()->hide ();
2159 framepos_t where = adjusted_current_frame (event);
2160 assert (where >= 0);
2162 double delta = compute_x_delta (event, &after);
2164 framecnt_t amount = _editor->pixel_to_sample (delta);
2166 if (allow_moves_across_tracks) {
2167 // all the originally selected regions were on the same track
2169 framecnt_t adjust = 0;
2170 if (prev_tav && tv != prev_tav) {
2171 // dragged onto a different track
2172 // remove the unselected regions from _views, restore them to their original positions
2173 // and add the regions after the drop point on the new playlist to _views instead.
2174 // undo the effect of rippling the previous playlist, and include the effect of removing
2175 // the dragged region(s) from this track
2177 remove_unselected_from_views (prev_amount, false);
2178 // ripple previous playlist according to the regions that have been removed onto the new playlist
2179 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2182 // move just the selected regions
2183 RegionMoveDrag::motion(event, first_move);
2185 // ensure that the ripple operation on the new playlist inserts selection_length time
2186 adjust = selection_length;
2187 // ripple the new current playlist
2188 tv->playlist()->ripple (where, amount+adjust, exclude);
2190 // add regions after point where drag entered this track to subsequent ripples
2191 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2194 // motion on same track
2195 RegionMoveDrag::motion(event, first_move);
2199 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2200 prev_position = where;
2202 // selection encompasses multiple tracks - just drag
2203 // cross-track drags are forbidden
2204 RegionMoveDrag::motion(event, first_move);
2207 if (!_x_constrained) {
2208 prev_amount += amount;
2211 _last_frame_position = after;
2215 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2217 if (!movement_occurred) {
2221 if (was_double_click() && !_views.empty()) {
2222 DraggingView dv = _views.front();
2223 dv.view->show_region_editor ();
2230 _editor->begin_reversible_command(_("Ripple drag"));
2232 // remove the regions being rippled from the dragging view, updating them to
2233 // their new positions
2234 remove_unselected_from_views (prev_amount, true);
2236 if (allow_moves_across_tracks) {
2238 // if regions were dragged across tracks, we've rippled any later
2239 // regions on the track the regions were dragged off, so we need
2240 // to add the original track to the undo record
2241 orig_tav->playlist()->clear_changes();
2242 vector<Command*> cmds;
2243 orig_tav->playlist()->rdiff (cmds);
2244 _editor->session()->add_commands (cmds);
2246 if (prev_tav && prev_tav != orig_tav) {
2247 prev_tav->playlist()->clear_changes();
2248 vector<Command*> cmds;
2249 prev_tav->playlist()->rdiff (cmds);
2250 _editor->session()->add_commands (cmds);
2253 // selection spanned multiple tracks - all will need adding to undo record
2255 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2256 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2258 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2259 (*pi)->clear_changes();
2260 vector<Command*> cmds;
2261 (*pi)->rdiff (cmds);
2262 _editor->session()->add_commands (cmds);
2266 // other modified playlists are added to undo by RegionMoveDrag::finished()
2267 RegionMoveDrag::finished (event, movement_occurred);
2268 _editor->commit_reversible_command();
2272 RegionRippleDrag::aborted (bool movement_occurred)
2274 RegionMoveDrag::aborted (movement_occurred);
2279 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2281 _view (dynamic_cast<MidiTimeAxisView*> (v))
2283 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2289 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2292 _region = add_midi_region (_view);
2293 _view->playlist()->freeze ();
2296 framepos_t const f = adjusted_current_frame (event);
2297 if (f < grab_frame()) {
2298 _region->set_position (f);
2301 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2302 so that if this region is duplicated, its duplicate starts on
2303 a snap point rather than 1 frame after a snap point. Otherwise things get
2304 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2305 place snapped notes at the start of the region.
2308 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2309 _region->set_length (len < 1 ? 1 : len);
2315 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2317 if (!movement_occurred) {
2318 add_midi_region (_view);
2320 _view->playlist()->thaw ();
2325 RegionCreateDrag::aborted (bool)
2328 _view->playlist()->thaw ();
2334 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2339 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2343 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2345 Gdk::Cursor* cursor;
2346 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2348 float x_fraction = cnote->mouse_x_fraction ();
2350 if (x_fraction > 0.0 && x_fraction < 0.25) {
2351 cursor = _editor->cursors()->left_side_trim;
2354 cursor = _editor->cursors()->right_side_trim;
2358 Drag::start_grab (event, cursor);
2360 region = &cnote->region_view();
2363 temp = region->snap_to_pixel (cnote->x0 (), true);
2364 _snap_delta = temp - cnote->x0 ();
2368 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2374 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2376 if (ms.size() > 1) {
2377 /* has to be relative, may make no sense otherwise */
2381 /* select this note; if it is already selected, preserve the existing selection,
2382 otherwise make this note the only one selected.
2384 region->note_selected (cnote, cnote->selected ());
2386 _editor->begin_reversible_command (_("resize notes"));
2388 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2389 MidiRegionSelection::iterator next;
2392 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2394 mrv->begin_resizing (at_front);
2401 NoteResizeDrag::motion (GdkEvent* event, bool /*first_move*/)
2403 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2404 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2405 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2407 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2411 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2413 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2414 if (_editor->snap_mode () != SnapOff) {
2418 if (_editor->snap_mode () == SnapOff) {
2420 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2421 if (apply_snap_delta) {
2427 if (apply_snap_delta) {
2431 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2437 NoteResizeDrag::finished (GdkEvent* event, bool /*movement_occurred*/)
2439 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2440 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2441 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2443 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2446 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2448 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2449 if (_editor->snap_mode () != SnapOff) {
2453 if (_editor->snap_mode () == SnapOff) {
2455 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2456 if (apply_snap_delta) {
2461 if (apply_snap_delta) {
2464 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2468 _editor->commit_reversible_command ();
2472 NoteResizeDrag::aborted (bool)
2474 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2475 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2476 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2478 mrv->abort_resizing ();
2483 AVDraggingView::AVDraggingView (RegionView* v)
2486 initial_position = v->region()->position ();
2489 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2492 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2495 TrackViewList empty;
2497 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2498 std::list<RegionView*> views = rs.by_layer();
2500 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2501 RegionView* rv = (*i);
2502 if (!rv->region()->video_locked()) {
2505 _views.push_back (AVDraggingView (rv));
2510 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2512 Drag::start_grab (event);
2513 if (_editor->session() == 0) {
2517 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2518 _max_backwards_drag = (
2519 ARDOUR_UI::instance()->video_timeline->get_duration()
2520 + ARDOUR_UI::instance()->video_timeline->get_offset()
2521 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2524 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2525 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2526 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2529 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2532 Timecode::Time timecode;
2533 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2534 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);
2535 show_verbose_cursor_text (buf);
2539 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2541 if (_editor->session() == 0) {
2544 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2548 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2549 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2551 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2552 dt = - _max_backwards_drag;
2555 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2556 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2558 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2559 RegionView* rv = i->view;
2560 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2563 rv->region()->clear_changes ();
2564 rv->region()->suspend_property_changes();
2566 rv->region()->set_position(i->initial_position + dt);
2567 rv->region_changed(ARDOUR::Properties::position);
2570 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2571 Timecode::Time timecode;
2572 Timecode::Time timediff;
2574 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2575 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2576 snprintf (buf, sizeof (buf),
2577 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2578 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2579 , _("Video Start:"),
2580 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2582 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2584 show_verbose_cursor_text (buf);
2588 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2590 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2594 if (!movement_occurred || ! _editor->session()) {
2598 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2600 _editor->begin_reversible_command (_("Move Video"));
2602 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2603 ARDOUR_UI::instance()->video_timeline->save_undo();
2604 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2605 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2607 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2608 i->view->drag_end();
2609 i->view->region()->resume_property_changes ();
2611 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2614 _editor->session()->maybe_update_session_range(
2615 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2616 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2620 _editor->commit_reversible_command ();
2624 VideoTimeLineDrag::aborted (bool)
2626 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2629 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2630 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2632 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2633 i->view->region()->resume_property_changes ();
2634 i->view->region()->set_position(i->initial_position);
2638 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2639 : RegionDrag (e, i, p, v)
2640 , _preserve_fade_anchor (preserve_fade_anchor)
2641 , _jump_position_when_done (false)
2643 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2647 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2650 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2651 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2653 if (tv && tv->is_track()) {
2654 speed = tv->track()->speed();
2657 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2658 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2659 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2661 framepos_t const pf = adjusted_current_frame (event);
2662 setup_snap_delta (region_start);
2664 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2665 /* Move the contents of the region around without changing the region bounds */
2666 _operation = ContentsTrim;
2667 Drag::start_grab (event, _editor->cursors()->trimmer);
2669 /* These will get overridden for a point trim.*/
2670 if (pf < (region_start + region_length/2)) {
2671 /* closer to front */
2672 _operation = StartTrim;
2673 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2674 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2676 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2680 _operation = EndTrim;
2681 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2682 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2684 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2688 /* jump trim disabled for now
2689 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2690 _jump_position_when_done = true;
2694 switch (_operation) {
2696 show_verbose_cursor_time (region_start);
2697 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2698 i->view->trim_front_starting ();
2702 show_verbose_cursor_duration (region_start, region_end);
2705 show_verbose_cursor_time (pf);
2709 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2710 i->view->region()->suspend_property_changes ();
2715 TrimDrag::motion (GdkEvent* event, bool first_move)
2717 RegionView* rv = _primary;
2720 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2721 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2722 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2723 frameoffset_t frame_delta = 0;
2725 if (tv && tv->is_track()) {
2726 speed = tv->track()->speed();
2728 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2729 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2735 switch (_operation) {
2737 trim_type = "Region start trim";
2740 trim_type = "Region end trim";
2743 trim_type = "Region content trim";
2750 _editor->begin_reversible_command (trim_type);
2752 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2753 RegionView* rv = i->view;
2754 rv->enable_display (false);
2755 rv->region()->playlist()->clear_owned_changes ();
2757 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2760 arv->temporarily_hide_envelope ();
2764 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2765 insert_result = _editor->motion_frozen_playlists.insert (pl);
2767 if (insert_result.second) {
2773 bool non_overlap_trim = false;
2775 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2776 non_overlap_trim = true;
2779 /* contstrain trim to fade length */
2780 if (_preserve_fade_anchor) {
2781 switch (_operation) {
2783 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2784 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2786 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2787 if (ar->locked()) continue;
2788 framecnt_t len = ar->fade_in()->back()->when;
2789 if (len < dt) dt = min(dt, len);
2793 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2794 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2796 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2797 if (ar->locked()) continue;
2798 framecnt_t len = ar->fade_out()->back()->when;
2799 if (len < -dt) dt = max(dt, -len);
2808 switch (_operation) {
2810 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2811 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2812 if (changed && _preserve_fade_anchor) {
2813 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2815 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2816 framecnt_t len = ar->fade_in()->back()->when;
2817 framecnt_t diff = ar->first_frame() - i->initial_position;
2818 framepos_t new_length = len - diff;
2819 i->anchored_fade_length = min (ar->length(), new_length);
2820 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2821 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2828 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2829 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2830 if (changed && _preserve_fade_anchor) {
2831 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2833 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2834 framecnt_t len = ar->fade_out()->back()->when;
2835 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2836 framepos_t new_length = len + diff;
2837 i->anchored_fade_length = min (ar->length(), new_length);
2838 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2839 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2847 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2849 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2850 i->view->move_contents (frame_delta);
2856 switch (_operation) {
2858 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2861 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2864 // show_verbose_cursor_time (frame_delta);
2870 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2872 if (movement_occurred) {
2873 motion (event, false);
2875 if (_operation == StartTrim) {
2876 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2878 /* This must happen before the region's StatefulDiffCommand is created, as it may
2879 `correct' (ahem) the region's _start from being negative to being zero. It
2880 needs to be zero in the undo record.
2882 i->view->trim_front_ending ();
2884 if (_preserve_fade_anchor) {
2885 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2887 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2888 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2889 ar->set_fade_in_length(i->anchored_fade_length);
2890 ar->set_fade_in_active(true);
2893 if (_jump_position_when_done) {
2894 i->view->region()->set_position (i->initial_position);
2897 } else if (_operation == EndTrim) {
2898 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2899 if (_preserve_fade_anchor) {
2900 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2902 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2903 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2904 ar->set_fade_out_length(i->anchored_fade_length);
2905 ar->set_fade_out_active(true);
2908 if (_jump_position_when_done) {
2909 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2914 if (!_views.empty()) {
2915 if (_operation == StartTrim) {
2916 _editor->maybe_locate_with_edit_preroll(
2917 _views.begin()->view->region()->position());
2919 if (_operation == EndTrim) {
2920 _editor->maybe_locate_with_edit_preroll(
2921 _views.begin()->view->region()->position() +
2922 _views.begin()->view->region()->length());
2926 if (!_editor->selection->selected (_primary)) {
2927 _primary->thaw_after_trim ();
2930 set<boost::shared_ptr<Playlist> > diffed_playlists;
2932 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2933 i->view->thaw_after_trim ();
2934 i->view->enable_display (true);
2936 /* Trimming one region may affect others on the playlist, so we need
2937 to get undo Commands from the whole playlist rather than just the
2938 region. Use diffed_playlists to make sure we don't diff a given
2939 playlist more than once.
2941 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2942 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2943 vector<Command*> cmds;
2945 _editor->session()->add_commands (cmds);
2946 diffed_playlists.insert (p);
2951 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2955 _editor->motion_frozen_playlists.clear ();
2956 _editor->commit_reversible_command();
2959 /* no mouse movement */
2960 _editor->point_trim (event, adjusted_current_frame (event));
2963 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2964 if (_operation == StartTrim) {
2965 i->view->trim_front_ending ();
2968 i->view->region()->resume_property_changes ();
2973 TrimDrag::aborted (bool movement_occurred)
2975 /* Our motion method is changing model state, so use the Undo system
2976 to cancel. Perhaps not ideal, as this will leave an Undo point
2977 behind which may be slightly odd from the user's point of view.
2982 if (movement_occurred) {
2986 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2987 i->view->region()->resume_property_changes ();
2992 TrimDrag::setup_pointer_frame_offset ()
2994 list<DraggingView>::iterator i = _views.begin ();
2995 while (i != _views.end() && i->view != _primary) {
2999 if (i == _views.end()) {
3003 switch (_operation) {
3005 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3008 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3015 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3019 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3020 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3025 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3027 Drag::start_grab (event, cursor);
3028 show_verbose_cursor_time (adjusted_current_frame(event));
3032 MeterMarkerDrag::setup_pointer_frame_offset ()
3034 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3038 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3040 if (!_marker->meter().movable()) {
3046 // create a dummy marker for visual representation of moving the
3047 // section, because whether its a copy or not, we're going to
3048 // leave or lose the original marker (leave if its a copy; lose if its
3049 // not, because we'll remove it from the map).
3051 MeterSection section (_marker->meter());
3053 if (!section.movable()) {
3058 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3060 _marker = new MeterMarker (
3062 *_editor->meter_group,
3063 ARDOUR_UI::config()->color ("meter marker"),
3065 *new MeterSection (_marker->meter())
3068 /* use the new marker for the grab */
3069 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3072 TempoMap& map (_editor->session()->tempo_map());
3073 /* get current state */
3074 before_state = &map.get_state();
3075 /* remove the section while we drag it */
3076 map.remove_meter (section, true);
3080 framepos_t const pf = adjusted_current_frame (event);
3082 _marker->set_position (pf);
3083 show_verbose_cursor_time (pf);
3087 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3089 if (!movement_occurred) {
3090 if (was_double_click()) {
3091 _editor->edit_meter_marker (*_marker);
3096 if (!_marker->meter().movable()) {
3100 motion (event, false);
3102 Timecode::BBT_Time when;
3104 TempoMap& map (_editor->session()->tempo_map());
3105 map.bbt_time (last_pointer_frame(), when);
3107 if (_copy == true) {
3108 _editor->begin_reversible_command (_("copy meter mark"));
3109 XMLNode &before = map.get_state();
3110 map.add_meter (_marker->meter(), when);
3111 XMLNode &after = map.get_state();
3112 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3113 _editor->commit_reversible_command ();
3116 _editor->begin_reversible_command (_("move meter mark"));
3118 /* we removed it before, so add it back now */
3120 map.add_meter (_marker->meter(), when);
3121 XMLNode &after = map.get_state();
3122 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3123 _editor->commit_reversible_command ();
3126 // delete the dummy marker we used for visual representation while moving.
3127 // a new visual marker will show up automatically.
3132 MeterMarkerDrag::aborted (bool moved)
3134 _marker->set_position (_marker->meter().frame ());
3137 TempoMap& map (_editor->session()->tempo_map());
3138 /* we removed it before, so add it back now */
3139 map.add_meter (_marker->meter(), _marker->meter().frame());
3140 // delete the dummy marker we used for visual representation while moving.
3141 // a new visual marker will show up automatically.
3146 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3150 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3152 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3157 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3159 Drag::start_grab (event, cursor);
3160 show_verbose_cursor_time (adjusted_current_frame (event));
3164 TempoMarkerDrag::setup_pointer_frame_offset ()
3166 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3170 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3172 if (!_marker->tempo().movable()) {
3178 // create a dummy marker for visual representation of moving the
3179 // section, because whether its a copy or not, we're going to
3180 // leave or lose the original marker (leave if its a copy; lose if its
3181 // not, because we'll remove it from the map).
3183 // create a dummy marker for visual representation of moving the copy.
3184 // The actual copying is not done before we reach the finish callback.
3187 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3189 TempoSection section (_marker->tempo());
3191 _marker = new TempoMarker (
3193 *_editor->tempo_group,
3194 ARDOUR_UI::config()->color ("tempo marker"),
3196 *new TempoSection (_marker->tempo())
3199 /* use the new marker for the grab */
3200 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3203 TempoMap& map (_editor->session()->tempo_map());
3204 /* get current state */
3205 before_state = &map.get_state();
3206 /* remove the section while we drag it */
3207 map.remove_tempo (section, true);
3211 framepos_t const pf = adjusted_current_frame (event);
3212 _marker->set_position (pf);
3213 show_verbose_cursor_time (pf);
3217 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3219 if (!movement_occurred) {
3220 if (was_double_click()) {
3221 _editor->edit_tempo_marker (*_marker);
3226 if (!_marker->tempo().movable()) {
3230 motion (event, false);
3232 TempoMap& map (_editor->session()->tempo_map());
3233 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3234 Timecode::BBT_Time when;
3236 map.bbt_time (beat_time, when);
3238 if (_copy == true) {
3239 _editor->begin_reversible_command (_("copy tempo mark"));
3240 XMLNode &before = map.get_state();
3241 map.add_tempo (_marker->tempo(), when);
3242 XMLNode &after = map.get_state();
3243 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3244 _editor->commit_reversible_command ();
3247 _editor->begin_reversible_command (_("move tempo mark"));
3248 /* we removed it before, so add it back now */
3249 map.add_tempo (_marker->tempo(), when);
3250 XMLNode &after = map.get_state();
3251 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3252 _editor->commit_reversible_command ();
3255 // delete the dummy marker we used for visual representation while moving.
3256 // a new visual marker will show up automatically.
3261 TempoMarkerDrag::aborted (bool moved)
3263 _marker->set_position (_marker->tempo().frame());
3265 TempoMap& map (_editor->session()->tempo_map());
3266 /* we removed it before, so add it back now */
3267 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3268 // delete the dummy marker we used for visual representation while moving.
3269 // a new visual marker will show up automatically.
3274 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3275 : Drag (e, &c.track_canvas_item(), false)
3279 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3282 /** Do all the things we do when dragging the playhead to make it look as though
3283 * we have located, without actually doing the locate (because that would cause
3284 * the diskstream buffers to be refilled, which is too slow).
3287 CursorDrag::fake_locate (framepos_t t)
3289 _editor->playhead_cursor->set_position (t);
3291 Session* s = _editor->session ();
3292 if (s->timecode_transmission_suspended ()) {
3293 framepos_t const f = _editor->playhead_cursor->current_frame ();
3294 /* This is asynchronous so it will be sent "now"
3296 s->send_mmc_locate (f);
3297 /* These are synchronous and will be sent during the next
3300 s->queue_full_time_code ();
3301 s->queue_song_position_pointer ();
3304 show_verbose_cursor_time (t);
3305 _editor->UpdateAllTransportClocks (t);
3309 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3311 Drag::start_grab (event, c);
3312 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3314 _grab_zoom = _editor->samples_per_pixel;
3316 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3318 _editor->snap_to_with_modifier (where, event);
3320 _editor->_dragging_playhead = true;
3322 Session* s = _editor->session ();
3324 /* grab the track canvas item as well */
3326 _cursor.track_canvas_item().grab();
3329 if (_was_rolling && _stop) {
3333 if (s->is_auditioning()) {
3334 s->cancel_audition ();
3338 if (AudioEngine::instance()->connected()) {
3340 /* do this only if we're the engine is connected
3341 * because otherwise this request will never be
3342 * serviced and we'll busy wait forever. likewise,
3343 * notice if we are disconnected while waiting for the
3344 * request to be serviced.
3347 s->request_suspend_timecode_transmission ();
3348 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3349 /* twiddle our thumbs */
3354 fake_locate (where - snap_delta (event->button.state));
3358 CursorDrag::motion (GdkEvent* event, bool)
3360 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3361 _editor->snap_to_with_modifier (where, event);
3362 if (where != last_pointer_frame()) {
3363 fake_locate (where - snap_delta (event->button.state));
3368 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3370 _editor->_dragging_playhead = false;
3372 _cursor.track_canvas_item().ungrab();
3374 if (!movement_occurred && _stop) {
3378 motion (event, false);
3380 Session* s = _editor->session ();
3382 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3383 _editor->_pending_locate_request = true;
3384 s->request_resume_timecode_transmission ();
3389 CursorDrag::aborted (bool)
3391 _cursor.track_canvas_item().ungrab();
3393 if (_editor->_dragging_playhead) {
3394 _editor->session()->request_resume_timecode_transmission ();
3395 _editor->_dragging_playhead = false;
3398 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3401 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3402 : RegionDrag (e, i, p, v)
3404 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3408 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3410 Drag::start_grab (event, cursor);
3412 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3413 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3414 setup_snap_delta (r->position ());
3416 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3420 FadeInDrag::setup_pointer_frame_offset ()
3422 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3423 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3424 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3428 FadeInDrag::motion (GdkEvent* event, bool)
3430 framecnt_t fade_length;
3432 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3433 _editor->snap_to_with_modifier (pos, event);
3434 pos -= snap_delta (event->button.state);
3436 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3438 if (pos < (region->position() + 64)) {
3439 fade_length = 64; // this should be a minimum defined somewhere
3440 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3441 fade_length = region->length() - region->fade_out()->back()->when - 1;
3443 fade_length = pos - region->position();
3446 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3448 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3454 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3457 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3461 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3463 if (!movement_occurred) {
3467 framecnt_t fade_length;
3468 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3469 _editor->snap_to_with_modifier (pos, event);
3470 pos -= snap_delta (event->button.state);
3472 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3474 if (pos < (region->position() + 64)) {
3475 fade_length = 64; // this should be a minimum defined somewhere
3476 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3477 fade_length = region->length() - region->fade_out()->back()->when - 1;
3479 fade_length = pos - region->position();
3482 bool in_command = false;
3484 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3486 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3492 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3493 XMLNode &before = alist->get_state();
3495 tmp->audio_region()->set_fade_in_length (fade_length);
3496 tmp->audio_region()->set_fade_in_active (true);
3499 _editor->begin_reversible_command (_("change fade in length"));
3502 XMLNode &after = alist->get_state();
3503 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3507 _editor->commit_reversible_command ();
3512 FadeInDrag::aborted (bool)
3514 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3515 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3521 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3525 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3526 : RegionDrag (e, i, p, v)
3528 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3532 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3534 Drag::start_grab (event, cursor);
3536 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3537 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3538 setup_snap_delta (r->last_frame ());
3540 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3544 FadeOutDrag::setup_pointer_frame_offset ()
3546 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3547 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3548 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3552 FadeOutDrag::motion (GdkEvent* event, bool)
3554 framecnt_t fade_length;
3556 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3557 _editor->snap_to_with_modifier (pos, event);
3558 pos -= snap_delta (event->button.state);
3560 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3562 if (pos > (region->last_frame() - 64)) {
3563 fade_length = 64; // this should really be a minimum fade defined somewhere
3564 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3565 fade_length = region->length() - region->fade_in()->back()->when - 1;
3567 fade_length = region->last_frame() - pos;
3570 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3572 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3578 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3581 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3585 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3587 if (!movement_occurred) {
3591 framecnt_t fade_length;
3593 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3594 _editor->snap_to_with_modifier (pos, event);
3595 pos -= snap_delta (event->button.state);
3597 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3599 if (pos > (region->last_frame() - 64)) {
3600 fade_length = 64; // this should really be a minimum fade defined somewhere
3601 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3602 fade_length = region->length() - region->fade_in()->back()->when - 1;
3604 fade_length = region->last_frame() - pos;
3607 bool in_command = false;
3609 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3611 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3617 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3618 XMLNode &before = alist->get_state();
3620 tmp->audio_region()->set_fade_out_length (fade_length);
3621 tmp->audio_region()->set_fade_out_active (true);
3624 _editor->begin_reversible_command (_("change fade out length"));
3627 XMLNode &after = alist->get_state();
3628 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3632 _editor->commit_reversible_command ();
3637 FadeOutDrag::aborted (bool)
3639 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3640 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3646 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3650 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3653 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3655 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3658 _points.push_back (ArdourCanvas::Duple (0, 0));
3659 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3662 MarkerDrag::~MarkerDrag ()
3664 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3669 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3671 location = new Location (*l);
3672 markers.push_back (m);
3677 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3679 Drag::start_grab (event, cursor);
3683 Location *location = _editor->find_location_from_marker (_marker, is_start);
3684 _editor->_dragging_edit_point = true;
3686 update_item (location);
3688 // _drag_line->show();
3689 // _line->raise_to_top();
3692 show_verbose_cursor_time (location->start());
3694 show_verbose_cursor_time (location->end());
3697 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3700 case Selection::Toggle:
3701 /* we toggle on the button release */
3703 case Selection::Set:
3704 if (!_editor->selection->selected (_marker)) {
3705 _editor->selection->set (_marker);
3708 case Selection::Extend:
3710 Locations::LocationList ll;
3711 list<Marker*> to_add;
3713 _editor->selection->markers.range (s, e);
3714 s = min (_marker->position(), s);
3715 e = max (_marker->position(), e);
3718 if (e < max_framepos) {
3721 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3722 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3723 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3726 to_add.push_back (lm->start);
3729 to_add.push_back (lm->end);
3733 if (!to_add.empty()) {
3734 _editor->selection->add (to_add);
3738 case Selection::Add:
3739 _editor->selection->add (_marker);
3743 /* Set up copies for us to manipulate during the drag
3746 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3748 Location* l = _editor->find_location_from_marker (*i, is_start);
3755 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3757 /* range: check that the other end of the range isn't
3760 CopiedLocationInfo::iterator x;
3761 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3762 if (*(*x).location == *l) {
3766 if (x == _copied_locations.end()) {
3767 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3769 (*x).markers.push_back (*i);
3770 (*x).move_both = true;
3778 MarkerDrag::setup_pointer_frame_offset ()
3781 Location *location = _editor->find_location_from_marker (_marker, is_start);
3782 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3786 MarkerDrag::motion (GdkEvent* event, bool)
3788 framecnt_t f_delta = 0;
3790 bool move_both = false;
3791 Location *real_location;
3792 Location *copy_location = 0;
3794 framepos_t const newframe = adjusted_current_frame (event);
3795 framepos_t next = newframe;
3797 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3801 CopiedLocationInfo::iterator x;
3803 /* find the marker we're dragging, and compute the delta */
3805 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3807 copy_location = (*x).location;
3809 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3811 /* this marker is represented by this
3812 * CopiedLocationMarkerInfo
3815 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3820 if (real_location->is_mark()) {
3821 f_delta = newframe - copy_location->start();
3825 switch (_marker->type()) {
3826 case Marker::SessionStart:
3827 case Marker::RangeStart:
3828 case Marker::LoopStart:
3829 case Marker::PunchIn:
3830 f_delta = newframe - copy_location->start();
3833 case Marker::SessionEnd:
3834 case Marker::RangeEnd:
3835 case Marker::LoopEnd:
3836 case Marker::PunchOut:
3837 f_delta = newframe - copy_location->end();
3840 /* what kind of marker is this ? */
3849 if (x == _copied_locations.end()) {
3850 /* hmm, impossible - we didn't find the dragged marker */
3854 /* now move them all */
3856 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3858 copy_location = x->location;
3860 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3864 if (real_location->locked()) {
3868 if (copy_location->is_mark()) {
3872 copy_location->set_start (copy_location->start() + f_delta);
3876 framepos_t new_start = copy_location->start() + f_delta;
3877 framepos_t new_end = copy_location->end() + f_delta;
3879 if (is_start) { // start-of-range marker
3881 if (move_both || (*x).move_both) {
3882 copy_location->set_start (new_start);
3883 copy_location->set_end (new_end);
3884 } else if (new_start < copy_location->end()) {
3885 copy_location->set_start (new_start);
3886 } else if (newframe > 0) {
3887 _editor->snap_to (next, RoundUpAlways, true);
3888 copy_location->set_end (next);
3889 copy_location->set_start (newframe);
3892 } else { // end marker
3894 if (move_both || (*x).move_both) {
3895 copy_location->set_end (new_end);
3896 copy_location->set_start (new_start);
3897 } else if (new_end > copy_location->start()) {
3898 copy_location->set_end (new_end);
3899 } else if (newframe > 0) {
3900 _editor->snap_to (next, RoundDownAlways, true);
3901 copy_location->set_start (next);
3902 copy_location->set_end (newframe);
3907 update_item (copy_location);
3909 /* now lookup the actual GUI items used to display this
3910 * location and move them to wherever the copy of the location
3911 * is now. This means that the logic in ARDOUR::Location is
3912 * still enforced, even though we are not (yet) modifying
3913 * the real Location itself.
3916 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3919 lm->set_position (copy_location->start(), copy_location->end());
3924 assert (!_copied_locations.empty());
3926 show_verbose_cursor_time (newframe);
3930 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3932 if (!movement_occurred) {
3934 if (was_double_click()) {
3935 _editor->rename_marker (_marker);
3939 /* just a click, do nothing but finish
3940 off the selection process
3943 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3946 case Selection::Set:
3947 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3948 _editor->selection->set (_marker);
3952 case Selection::Toggle:
3953 /* we toggle on the button release, click only */
3954 _editor->selection->toggle (_marker);
3957 case Selection::Extend:
3958 case Selection::Add:
3965 _editor->_dragging_edit_point = false;
3967 XMLNode &before = _editor->session()->locations()->get_state();
3968 bool in_command = false;
3970 MarkerSelection::iterator i;
3971 CopiedLocationInfo::iterator x;
3974 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3975 x != _copied_locations.end() && i != _editor->selection->markers.end();
3978 Location * location = _editor->find_location_from_marker (*i, is_start);
3982 if (location->locked()) {
3986 _editor->begin_reversible_command ( _("move marker") );
3989 if (location->is_mark()) {
3990 location->set_start (((*x).location)->start());
3992 location->set (((*x).location)->start(), ((*x).location)->end());
3998 XMLNode &after = _editor->session()->locations()->get_state();
3999 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4000 _editor->commit_reversible_command ();
4005 MarkerDrag::aborted (bool movement_occured)
4007 if (!movement_occured) {
4011 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4013 /* move all markers to their original location */
4016 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4019 Location * location = _editor->find_location_from_marker (*m, is_start);
4022 (*m)->set_position (is_start ? location->start() : location->end());
4029 MarkerDrag::update_item (Location*)
4034 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4036 _cumulative_x_drag (0),
4037 _cumulative_y_drag (0)
4038 , _first_move (true)
4040 if (_zero_gain_fraction < 0.0) {
4041 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4044 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4046 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4052 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4054 Drag::start_grab (event, _editor->cursors()->fader);
4056 // start the grab at the center of the control point so
4057 // the point doesn't 'jump' to the mouse after the first drag
4058 _fixed_grab_x = _point->get_x();
4059 _fixed_grab_y = _point->get_y();
4061 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4062 setup_snap_delta (pos);
4064 float const fraction = 1 - (_point->get_y() / _point->line().height());
4065 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4067 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4069 if (!_point->can_slide ()) {
4070 _x_constrained = true;
4075 ControlPointDrag::motion (GdkEvent* event, bool)
4077 double dx = _drags->current_pointer_x() - last_pointer_x();
4078 double dy = current_pointer_y() - last_pointer_y();
4080 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4085 /* coordinate in pixels relative to the start of the region (for region-based automation)
4086 or track (for track-based automation) */
4087 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4088 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4090 // calculate zero crossing point. back off by .01 to stay on the
4091 // positive side of zero
4092 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4094 // make sure we hit zero when passing through
4095 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4099 if (_x_constrained) {
4102 if (_y_constrained) {
4106 _cumulative_x_drag = cx - _fixed_grab_x;
4107 _cumulative_y_drag = cy - _fixed_grab_y;
4111 cy = min ((double) _point->line().height(), cy);
4113 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4115 if (!_x_constrained) {
4116 _editor->snap_to_with_modifier (cx_frames, event);
4119 cx_frames -= snap_delta (event->button.state);
4120 cx_frames = min (cx_frames, _point->line().maximum_time());
4122 float const fraction = 1.0 - (cy / _point->line().height());
4125 _editor->begin_reversible_command (_("automation event move"));
4126 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4127 _first_move = false;
4130 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4132 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4136 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4138 if (!movement_occurred) {
4141 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4142 _editor->reset_point_selection ();
4146 motion (event, false);
4150 _point->line().end_drag (_pushing, _final_index);
4151 _editor->commit_reversible_command ();
4156 ControlPointDrag::aborted (bool)
4158 _point->line().reset ();
4162 ControlPointDrag::active (Editing::MouseMode m)
4164 if (m == Editing::MouseDraw) {
4165 /* always active in mouse draw */
4169 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4170 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4173 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4176 , _cumulative_y_drag (0)
4178 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4182 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4184 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4187 _item = &_line->grab_item ();
4189 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4190 origin, and ditto for y.
4193 double cx = event->button.x;
4194 double cy = event->button.y;
4196 _line->parent_group().canvas_to_item (cx, cy);
4198 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4203 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4204 /* no adjacent points */
4208 Drag::start_grab (event, _editor->cursors()->fader);
4210 /* store grab start in parent frame */
4215 double fraction = 1.0 - (cy / _line->height());
4216 _editor->begin_reversible_command (_("automation range move"));
4217 _line->start_drag_line (before, after, fraction);
4219 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4223 LineDrag::motion (GdkEvent* event, bool)
4225 double dy = current_pointer_y() - last_pointer_y();
4227 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4231 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4233 _cumulative_y_drag = cy - _fixed_grab_y;
4236 cy = min ((double) _line->height(), cy);
4238 double const fraction = 1.0 - (cy / _line->height());
4241 /* we are ignoring x position for this drag, so we can just pass in anything */
4242 _line->drag_motion (0, fraction, true, false, ignored);
4244 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4248 LineDrag::finished (GdkEvent* event, bool movement_occured)
4250 if (movement_occured) {
4251 motion (event, false);
4252 _line->end_drag (false, 0);
4254 /* add a new control point on the line */
4256 AutomationTimeAxisView* atv;
4258 _line->end_drag (false, 0);
4260 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4261 framepos_t where = _editor->window_event_sample (event, 0, 0);
4262 atv->add_automation_event (event, where, event->button.y, false);
4266 _editor->commit_reversible_command ();
4270 LineDrag::aborted (bool)
4275 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4278 _cumulative_x_drag (0)
4280 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4284 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4286 Drag::start_grab (event);
4288 _line = reinterpret_cast<Line*> (_item);
4291 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4293 double cx = event->button.x;
4294 double cy = event->button.y;
4296 _item->parent()->canvas_to_item (cx, cy);
4298 /* store grab start in parent frame */
4299 _region_view_grab_x = cx;
4301 _before = *(float*) _item->get_data ("position");
4303 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4305 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4309 FeatureLineDrag::motion (GdkEvent*, bool)
4311 double dx = _drags->current_pointer_x() - last_pointer_x();
4313 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4315 _cumulative_x_drag += dx;
4317 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4326 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4328 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4330 float *pos = new float;
4333 _line->set_data ("position", pos);
4339 FeatureLineDrag::finished (GdkEvent*, bool)
4341 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4342 _arv->update_transient(_before, _before);
4346 FeatureLineDrag::aborted (bool)
4351 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4353 , _vertical_only (false)
4355 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4359 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4361 Drag::start_grab (event);
4362 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4366 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4373 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4375 framepos_t grab = grab_frame ();
4376 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4377 _editor->snap_to_with_modifier (grab, event);
4379 grab = raw_grab_frame ();
4382 /* base start and end on initial click position */
4392 if (current_pointer_y() < grab_y()) {
4393 y1 = current_pointer_y();
4396 y2 = current_pointer_y();
4400 if (start != end || y1 != y2) {
4402 double x1 = _editor->sample_to_pixel (start);
4403 double x2 = _editor->sample_to_pixel (end);
4404 const double min_dimension = 2.0;
4406 if (_vertical_only) {
4407 /* fixed 10 pixel width */
4411 x2 = min (x1 - min_dimension, x2);
4413 x2 = max (x1 + min_dimension, x2);
4418 y2 = min (y1 - min_dimension, y2);
4420 y2 = max (y1 + min_dimension, y2);
4423 /* translate rect into item space and set */
4425 ArdourCanvas::Rect r (x1, y1, x2, y2);
4427 /* this drag is a _trackview_only == true drag, so the y1 and
4428 * y2 (computed using current_pointer_y() and grab_y()) will be
4429 * relative to the top of the trackview group). The
4430 * rubberband rect has the same parent/scroll offset as the
4431 * the trackview group, so we can use the "r" rect directly
4432 * to set the shape of the rubberband.
4435 _editor->rubberband_rect->set (r);
4436 _editor->rubberband_rect->show();
4437 _editor->rubberband_rect->raise_to_top();
4439 show_verbose_cursor_time (pf);
4441 do_select_things (event, true);
4446 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4450 framepos_t grab = grab_frame ();
4451 framepos_t lpf = last_pointer_frame ();
4453 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4454 grab = raw_grab_frame ();
4455 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4469 if (current_pointer_y() < grab_y()) {
4470 y1 = current_pointer_y();
4473 y2 = current_pointer_y();
4477 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4481 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4483 if (movement_occurred) {
4485 motion (event, false);
4486 do_select_things (event, false);
4492 bool do_deselect = true;
4493 MidiTimeAxisView* mtv;
4495 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4497 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4498 /* nothing selected */
4499 add_midi_region (mtv);
4500 do_deselect = false;
4504 /* do not deselect if Primary or Tertiary (toggle-select or
4505 * extend-select are pressed.
4508 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4509 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4516 _editor->rubberband_rect->hide();
4520 RubberbandSelectDrag::aborted (bool)
4522 _editor->rubberband_rect->hide ();
4525 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4526 : RegionDrag (e, i, p, v)
4528 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4532 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4534 Drag::start_grab (event, cursor);
4536 _editor->get_selection().add (_primary);
4538 framepos_t where = _primary->region()->position();
4539 setup_snap_delta (where);
4541 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4545 TimeFXDrag::motion (GdkEvent* event, bool)
4547 RegionView* rv = _primary;
4548 StreamView* cv = rv->get_time_axis_view().view ();
4550 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4551 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4552 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4553 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4554 _editor->snap_to_with_modifier (pf, event);
4555 pf -= snap_delta (event->button.state);
4557 if (pf > rv->region()->position()) {
4558 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4561 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4565 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4567 _primary->get_time_axis_view().hide_timestretch ();
4569 if (!movement_occurred) {
4573 if (last_pointer_frame() < _primary->region()->position()) {
4574 /* backwards drag of the left edge - not usable */
4578 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4580 float percentage = (double) newlen / (double) _primary->region()->length();
4582 #ifndef USE_RUBBERBAND
4583 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4584 if (_primary->region()->data_type() == DataType::AUDIO) {
4585 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4589 if (!_editor->get_selection().regions.empty()) {
4590 /* primary will already be included in the selection, and edit
4591 group shared editing will propagate selection across
4592 equivalent regions, so just use the current region
4596 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4597 error << _("An error occurred while executing time stretch operation") << endmsg;
4603 TimeFXDrag::aborted (bool)
4605 _primary->get_time_axis_view().hide_timestretch ();
4608 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4611 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4615 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4617 Drag::start_grab (event);
4621 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4623 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4627 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4629 if (movement_occurred && _editor->session()) {
4630 /* make sure we stop */
4631 _editor->session()->request_transport_speed (0.0);
4636 ScrubDrag::aborted (bool)
4641 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4645 , _time_selection_at_start (!_editor->get_selection().time.empty())
4647 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4649 if (_time_selection_at_start) {
4650 start_at_start = _editor->get_selection().time.start();
4651 end_at_start = _editor->get_selection().time.end_frame();
4656 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4658 if (_editor->session() == 0) {
4662 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4664 switch (_operation) {
4665 case CreateSelection:
4666 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4671 cursor = _editor->cursors()->selector;
4672 Drag::start_grab (event, cursor);
4675 case SelectionStartTrim:
4676 if (_editor->clicked_axisview) {
4677 _editor->clicked_axisview->order_selection_trims (_item, true);
4679 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4682 case SelectionEndTrim:
4683 if (_editor->clicked_axisview) {
4684 _editor->clicked_axisview->order_selection_trims (_item, false);
4686 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4690 Drag::start_grab (event, cursor);
4693 case SelectionExtend:
4694 Drag::start_grab (event, cursor);
4698 if (_operation == SelectionMove) {
4699 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4701 show_verbose_cursor_time (adjusted_current_frame (event));
4706 SelectionDrag::setup_pointer_frame_offset ()
4708 switch (_operation) {
4709 case CreateSelection:
4710 _pointer_frame_offset = 0;
4713 case SelectionStartTrim:
4715 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4718 case SelectionEndTrim:
4719 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4722 case SelectionExtend:
4728 SelectionDrag::motion (GdkEvent* event, bool first_move)
4730 framepos_t start = 0;
4732 framecnt_t length = 0;
4733 framecnt_t distance = 0;
4735 framepos_t const pending_position = adjusted_current_frame (event);
4737 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4741 switch (_operation) {
4742 case CreateSelection:
4744 framepos_t grab = grab_frame ();
4747 grab = adjusted_current_frame (event, false);
4748 if (grab < pending_position) {
4749 _editor->snap_to (grab, RoundDownMaybe);
4751 _editor->snap_to (grab, RoundUpMaybe);
4755 if (pending_position < grab) {
4756 start = pending_position;
4759 end = pending_position;
4763 /* first drag: Either add to the selection
4764 or create a new selection
4771 /* adding to the selection */
4772 _editor->set_selected_track_as_side_effect (Selection::Add);
4773 _editor->clicked_selection = _editor->selection->add (start, end);
4780 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4781 _editor->set_selected_track_as_side_effect (Selection::Set);
4784 _editor->clicked_selection = _editor->selection->set (start, end);
4788 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4789 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4790 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4792 _editor->selection->add (atest);
4796 /* select all tracks within the rectangle that we've marked out so far */
4797 TrackViewList new_selection;
4798 TrackViewList& all_tracks (_editor->track_views);
4800 ArdourCanvas::Coord const top = grab_y();
4801 ArdourCanvas::Coord const bottom = current_pointer_y();
4803 if (top >= 0 && bottom >= 0) {
4805 //first, find the tracks that are covered in the y range selection
4806 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4807 if ((*i)->covered_by_y_range (top, bottom)) {
4808 new_selection.push_back (*i);
4812 //now find any tracks that are GROUPED with the tracks we selected
4813 TrackViewList grouped_add = new_selection;
4814 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4815 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4816 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4817 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4818 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4819 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4820 grouped_add.push_back (*j);
4825 //now compare our list with the current selection, and add or remove as necessary
4826 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4827 TrackViewList tracks_to_add;
4828 TrackViewList tracks_to_remove;
4829 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4830 if ( !_editor->selection->tracks.contains ( *i ) )
4831 tracks_to_add.push_back ( *i );
4832 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4833 if ( !grouped_add.contains ( *i ) )
4834 tracks_to_remove.push_back ( *i );
4835 _editor->selection->add(tracks_to_add);
4836 _editor->selection->remove(tracks_to_remove);
4842 case SelectionStartTrim:
4844 start = _editor->selection->time[_editor->clicked_selection].start;
4845 end = _editor->selection->time[_editor->clicked_selection].end;
4847 if (pending_position > end) {
4850 start = pending_position;
4854 case SelectionEndTrim:
4856 start = _editor->selection->time[_editor->clicked_selection].start;
4857 end = _editor->selection->time[_editor->clicked_selection].end;
4859 if (pending_position < start) {
4862 end = pending_position;
4869 start = _editor->selection->time[_editor->clicked_selection].start;
4870 end = _editor->selection->time[_editor->clicked_selection].end;
4872 length = end - start;
4873 distance = pending_position - start;
4874 start = pending_position;
4875 _editor->snap_to (start);
4877 end = start + length;
4881 case SelectionExtend:
4886 switch (_operation) {
4888 if (_time_selection_at_start) {
4889 _editor->selection->move_time (distance);
4893 _editor->selection->replace (_editor->clicked_selection, start, end);
4897 if (_operation == SelectionMove) {
4898 show_verbose_cursor_time(start);
4900 show_verbose_cursor_time(pending_position);
4905 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4907 Session* s = _editor->session();
4909 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4910 if (movement_occurred) {
4911 motion (event, false);
4912 /* XXX this is not object-oriented programming at all. ick */
4913 if (_editor->selection->time.consolidate()) {
4914 _editor->selection->TimeChanged ();
4917 /* XXX what if its a music time selection? */
4919 if ( s->get_play_range() && s->transport_rolling() ) {
4920 s->request_play_range (&_editor->selection->time, true);
4922 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4923 if (_operation == SelectionEndTrim)
4924 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4926 s->request_locate (_editor->get_selection().time.start());
4932 /* just a click, no pointer movement.
4935 if (_operation == SelectionExtend) {
4936 if (_time_selection_at_start) {
4937 framepos_t pos = adjusted_current_frame (event, false);
4938 framepos_t start = min (pos, start_at_start);
4939 framepos_t end = max (pos, end_at_start);
4940 _editor->selection->set (start, end);
4943 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4944 if (_editor->clicked_selection) {
4945 _editor->selection->remove (_editor->clicked_selection);
4948 if (!_editor->clicked_selection) {
4949 _editor->selection->clear_time();
4954 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4955 _editor->selection->set (_editor->clicked_axisview);
4958 if (s && s->get_play_range () && s->transport_rolling()) {
4959 s->request_stop (false, false);
4964 _editor->stop_canvas_autoscroll ();
4965 _editor->clicked_selection = 0;
4966 _editor->commit_reversible_selection_op ();
4970 SelectionDrag::aborted (bool)
4975 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4976 : Drag (e, i, false),
4980 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4982 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4983 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4984 physical_screen_height (_editor->get_window())));
4985 _drag_rect->hide ();
4987 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4988 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4991 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4993 /* normal canvas items will be cleaned up when their parent group is deleted. But
4994 this item is created as the child of a long-lived parent group, and so we
4995 need to explicitly delete it.
5001 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5003 if (_editor->session() == 0) {
5007 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5009 if (!_editor->temp_location) {
5010 _editor->temp_location = new Location (*_editor->session());
5013 switch (_operation) {
5014 case CreateSkipMarker:
5015 case CreateRangeMarker:
5016 case CreateTransportMarker:
5017 case CreateCDMarker:
5019 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5024 cursor = _editor->cursors()->selector;
5028 Drag::start_grab (event, cursor);
5030 show_verbose_cursor_time (adjusted_current_frame (event));
5034 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5036 framepos_t start = 0;
5038 ArdourCanvas::Rectangle *crect;
5040 switch (_operation) {
5041 case CreateSkipMarker:
5042 crect = _editor->range_bar_drag_rect;
5044 case CreateRangeMarker:
5045 crect = _editor->range_bar_drag_rect;
5047 case CreateTransportMarker:
5048 crect = _editor->transport_bar_drag_rect;
5050 case CreateCDMarker:
5051 crect = _editor->cd_marker_bar_drag_rect;
5054 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5059 framepos_t const pf = adjusted_current_frame (event);
5061 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5062 framepos_t grab = grab_frame ();
5063 _editor->snap_to (grab);
5065 if (pf < grab_frame()) {
5073 /* first drag: Either add to the selection
5074 or create a new selection.
5079 _editor->temp_location->set (start, end);
5083 update_item (_editor->temp_location);
5085 //_drag_rect->raise_to_top();
5091 _editor->temp_location->set (start, end);
5093 double x1 = _editor->sample_to_pixel (start);
5094 double x2 = _editor->sample_to_pixel (end);
5098 update_item (_editor->temp_location);
5101 show_verbose_cursor_time (pf);
5106 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5108 Location * newloc = 0;
5112 if (movement_occurred) {
5113 motion (event, false);
5116 switch (_operation) {
5117 case CreateSkipMarker:
5118 case CreateRangeMarker:
5119 case CreateCDMarker:
5121 XMLNode &before = _editor->session()->locations()->get_state();
5122 if (_operation == CreateSkipMarker) {
5123 _editor->begin_reversible_command (_("new skip marker"));
5124 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5125 flags = Location::IsRangeMarker | Location::IsSkip;
5126 _editor->range_bar_drag_rect->hide();
5127 } else if (_operation == CreateCDMarker) {
5128 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5129 _editor->begin_reversible_command (_("new CD marker"));
5130 flags = Location::IsRangeMarker | Location::IsCDMarker;
5131 _editor->cd_marker_bar_drag_rect->hide();
5133 _editor->begin_reversible_command (_("new skip marker"));
5134 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5135 flags = Location::IsRangeMarker;
5136 _editor->range_bar_drag_rect->hide();
5138 newloc = new Location (
5139 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5142 _editor->session()->locations()->add (newloc, true);
5143 XMLNode &after = _editor->session()->locations()->get_state();
5144 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5145 _editor->commit_reversible_command ();
5149 case CreateTransportMarker:
5150 // popup menu to pick loop or punch
5151 _editor->new_transport_marker_context_menu (&event->button, _item);
5157 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5159 if (_operation == CreateTransportMarker) {
5161 /* didn't drag, so just locate */
5163 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5165 } else if (_operation == CreateCDMarker) {
5167 /* didn't drag, but mark is already created so do
5170 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5175 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5177 if (end == max_framepos) {
5178 end = _editor->session()->current_end_frame ();
5181 if (start == max_framepos) {
5182 start = _editor->session()->current_start_frame ();
5185 switch (_editor->mouse_mode) {
5187 /* find the two markers on either side and then make the selection from it */
5188 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5192 /* find the two markers on either side of the click and make the range out of it */
5193 _editor->selection->set (start, end);
5202 _editor->stop_canvas_autoscroll ();
5206 RangeMarkerBarDrag::aborted (bool movement_occured)
5208 if (movement_occured) {
5209 _drag_rect->hide ();
5214 RangeMarkerBarDrag::update_item (Location* location)
5216 double const x1 = _editor->sample_to_pixel (location->start());
5217 double const x2 = _editor->sample_to_pixel (location->end());
5219 _drag_rect->set_x0 (x1);
5220 _drag_rect->set_x1 (x2);
5223 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5225 , _cumulative_dx (0)
5226 , _cumulative_dy (0)
5228 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5230 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5232 _region = &_primary->region_view ();
5233 _note_height = _region->midi_stream_view()->note_height ();
5237 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5239 Drag::start_grab (event);
5240 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5242 if (!(_was_selected = _primary->selected())) {
5244 /* tertiary-click means extend selection - we'll do that on button release,
5245 so don't add it here, because otherwise we make it hard to figure
5246 out the "extend-to" range.
5249 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5252 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5255 _region->note_selected (_primary, true);
5257 _region->unique_select (_primary);
5260 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5261 _editor->commit_reversible_selection_op();
5266 /** @return Current total drag x change in frames */
5268 NoteDrag::total_dx (const guint state) const
5271 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5273 /* primary note time */
5274 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5276 /* new time of the primary note in session frames */
5277 frameoffset_t st = n + dx + snap_delta (state);
5279 framepos_t const rp = _region->region()->position ();
5281 /* prevent the note being dragged earlier than the region's position */
5284 /* possibly snap and return corresponding delta */
5288 if (ArdourKeyboard::indicates_snap (state)) {
5289 if (_editor->snap_mode () != SnapOff) {
5293 if (_editor->snap_mode () == SnapOff) {
5295 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5296 if (ArdourKeyboard::indicates_snap_delta (state)) {
5304 ret = _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5306 ret = st - n - snap_delta (state);
5311 /** @return Current total drag y change in note number */
5313 NoteDrag::total_dy () const
5315 MidiStreamView* msv = _region->midi_stream_view ();
5316 double const y = _region->midi_view()->y_position ();
5317 /* new current note */
5318 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5320 n = max (msv->lowest_note(), n);
5321 n = min (msv->highest_note(), n);
5322 /* and work out delta */
5323 return n - msv->y_to_note (grab_y() - y);
5327 NoteDrag::motion (GdkEvent * event, bool)
5329 /* Total change in x and y since the start of the drag */
5330 frameoffset_t const dx = total_dx (event->button.state);
5331 int8_t const dy = total_dy ();
5333 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5334 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5335 double const tdy = -dy * _note_height - _cumulative_dy;
5338 _cumulative_dx += tdx;
5339 _cumulative_dy += tdy;
5341 int8_t note_delta = total_dy();
5343 _region->move_selection (tdx, tdy, note_delta);
5345 /* the new note value may be the same as the old one, but we
5346 * don't know what that means because the selection may have
5347 * involved more than one note and we might be doing something
5348 * odd with them. so show the note value anyway, always.
5352 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5354 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5355 (int) floor ((double)new_note));
5357 show_verbose_cursor_text (buf);
5362 NoteDrag::finished (GdkEvent* ev, bool moved)
5365 /* no motion - select note */
5367 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5368 _editor->current_mouse_mode() == Editing::MouseDraw) {
5370 bool changed = false;
5372 if (_was_selected) {
5373 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5375 _region->note_deselected (_primary);
5379 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5380 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5382 if (!extend && !add && _region->selection_size() > 1) {
5383 _region->unique_select (_primary);
5385 } else if (extend) {
5386 _region->note_selected (_primary, true, true);
5389 /* it was added during button press */
5394 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5395 _editor->commit_reversible_selection_op();
5399 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5404 NoteDrag::aborted (bool)
5409 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5410 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5411 : Drag (editor, atv->base_item ())
5413 , _y_origin (atv->y_position())
5414 , _nothing_to_drag (false)
5416 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5417 setup (atv->lines ());
5420 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5421 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5422 : Drag (editor, rv->get_canvas_group ())
5424 , _y_origin (rv->get_time_axis_view().y_position())
5425 , _nothing_to_drag (false)
5428 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5430 list<boost::shared_ptr<AutomationLine> > lines;
5432 AudioRegionView* audio_view;
5433 AutomationRegionView* automation_view;
5434 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5435 lines.push_back (audio_view->get_gain_line ());
5436 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5437 lines.push_back (automation_view->line ());
5440 error << _("Automation range drag created for invalid region type") << endmsg;
5446 /** @param lines AutomationLines to drag.
5447 * @param offset Offset from the session start to the points in the AutomationLines.
5450 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5452 /* find the lines that overlap the ranges being dragged */
5453 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5454 while (i != lines.end ()) {
5455 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5458 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5460 /* check this range against all the AudioRanges that we are using */
5461 list<AudioRange>::const_iterator k = _ranges.begin ();
5462 while (k != _ranges.end()) {
5463 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5469 /* add it to our list if it overlaps at all */
5470 if (k != _ranges.end()) {
5475 _lines.push_back (n);
5481 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5485 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5487 return 1.0 - ((global_y - _y_origin) / line->height());
5491 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5493 const double v = list->eval(x);
5494 return _integral ? rint(v) : v;
5498 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5500 Drag::start_grab (event, cursor);
5502 /* Get line states before we start changing things */
5503 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5504 i->state = &i->line->get_state ();
5505 i->original_fraction = y_fraction (i->line, current_pointer_y());
5508 if (_ranges.empty()) {
5510 /* No selected time ranges: drag all points */
5511 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5512 uint32_t const N = i->line->npoints ();
5513 for (uint32_t j = 0; j < N; ++j) {
5514 i->points.push_back (i->line->nth (j));
5520 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5522 framecnt_t const half = (i->start + i->end) / 2;
5524 /* find the line that this audio range starts in */
5525 list<Line>::iterator j = _lines.begin();
5526 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5530 if (j != _lines.end()) {
5531 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5533 /* j is the line that this audio range starts in; fade into it;
5534 64 samples length plucked out of thin air.
5537 framepos_t a = i->start + 64;
5542 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5543 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5545 the_list->editor_add (p, value (the_list, p), false);
5546 the_list->editor_add (q, value (the_list, q), false);
5549 /* same thing for the end */
5552 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5556 if (j != _lines.end()) {
5557 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5559 /* j is the line that this audio range starts in; fade out of it;
5560 64 samples length plucked out of thin air.
5563 framepos_t b = i->end - 64;
5568 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5569 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5571 the_list->editor_add (p, value (the_list, p), false);
5572 the_list->editor_add (q, value (the_list, q), false);
5576 _nothing_to_drag = true;
5578 /* Find all the points that should be dragged and put them in the relevant
5579 points lists in the Line structs.
5582 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5584 uint32_t const N = i->line->npoints ();
5585 for (uint32_t j = 0; j < N; ++j) {
5587 /* here's a control point on this line */
5588 ControlPoint* p = i->line->nth (j);
5589 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5591 /* see if it's inside a range */
5592 list<AudioRange>::const_iterator k = _ranges.begin ();
5593 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5597 if (k != _ranges.end()) {
5598 /* dragging this point */
5599 _nothing_to_drag = false;
5600 i->points.push_back (p);
5606 if (_nothing_to_drag) {
5609 _editor->begin_reversible_command (_("automation range move"));
5610 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5611 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5616 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5618 if (_nothing_to_drag) {
5622 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5623 float const f = y_fraction (l->line, current_pointer_y());
5624 /* we are ignoring x position for this drag, so we can just pass in anything */
5626 l->line->drag_motion (0, f, true, false, ignored);
5627 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5632 AutomationRangeDrag::finished (GdkEvent* event, bool)
5634 if (_nothing_to_drag) {
5638 motion (event, false);
5639 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5640 i->line->end_drag (false, 0);
5643 _editor->commit_reversible_command ();
5647 AutomationRangeDrag::aborted (bool)
5649 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5654 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5656 , initial_time_axis_view (itav)
5658 /* note that time_axis_view may be null if the regionview was created
5659 * as part of a copy operation.
5661 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5662 layer = v->region()->layer ();
5663 initial_y = v->get_canvas_group()->position().y;
5664 initial_playlist = v->region()->playlist ();
5665 initial_position = v->region()->position ();
5666 initial_end = v->region()->position () + v->region()->length ();
5669 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5670 : Drag (e, i->canvas_item ())
5673 , _cumulative_dx (0)
5675 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5676 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5681 PatchChangeDrag::motion (GdkEvent* ev, bool)
5683 framepos_t f = adjusted_current_frame (ev);
5684 boost::shared_ptr<Region> r = _region_view->region ();
5685 f = max (f, r->position ());
5686 f = min (f, r->last_frame ());
5688 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5689 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5690 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5691 _cumulative_dx = dxu;
5695 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5697 if (!movement_occurred) {
5701 boost::shared_ptr<Region> r (_region_view->region ());
5702 framepos_t f = adjusted_current_frame (ev);
5703 f = max (f, r->position ());
5704 f = min (f, r->last_frame ());
5706 _region_view->move_patch_change (
5708 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5713 PatchChangeDrag::aborted (bool)
5715 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5719 PatchChangeDrag::setup_pointer_frame_offset ()
5721 boost::shared_ptr<Region> region = _region_view->region ();
5722 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5725 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5726 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5733 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5735 _region_view->update_drag_selection (
5737 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5741 MidiRubberbandSelectDrag::deselect_things ()
5746 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5747 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5750 _vertical_only = true;
5754 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5756 double const y = _region_view->midi_view()->y_position ();
5758 y1 = max (0.0, y1 - y);
5759 y2 = max (0.0, y2 - y);
5761 _region_view->update_vertical_drag_selection (
5764 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5769 MidiVerticalSelectDrag::deselect_things ()
5774 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5775 : RubberbandSelectDrag (e, i)
5781 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5783 if (drag_in_progress) {
5784 /* We just want to select things at the end of the drag, not during it */
5788 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5790 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5792 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5794 _editor->commit_reversible_selection_op ();
5798 EditorRubberbandSelectDrag::deselect_things ()
5800 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5802 _editor->selection->clear_tracks();
5803 _editor->selection->clear_regions();
5804 _editor->selection->clear_points ();
5805 _editor->selection->clear_lines ();
5806 _editor->selection->clear_midi_notes ();
5808 _editor->commit_reversible_selection_op();
5811 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5816 _note[0] = _note[1] = 0;
5819 NoteCreateDrag::~NoteCreateDrag ()
5825 NoteCreateDrag::grid_frames (framepos_t t) const
5828 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5830 grid_beats = Evoral::Beats(1);
5833 return _region_view->region_beats_to_region_frames (grid_beats);
5837 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5839 Drag::start_grab (event, cursor);
5841 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5843 framepos_t pf = _drags->current_pointer_frame ();
5844 framecnt_t const g = grid_frames (pf);
5846 /* Hack so that we always snap to the note that we are over, instead of snapping
5847 to the next one if we're more than halfway through the one we're over.
5849 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5853 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5854 _note[1] = _note[0];
5856 MidiStreamView* sv = _region_view->midi_stream_view ();
5857 double const x = _editor->sample_to_pixel (_note[0]);
5858 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5860 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5861 _drag_rect->set_outline_all ();
5862 _drag_rect->set_outline_color (0xffffff99);
5863 _drag_rect->set_fill_color (0xffffff66);
5867 NoteCreateDrag::motion (GdkEvent* event, bool)
5869 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5870 double const x0 = _editor->sample_to_pixel (_note[0]);
5871 double const x1 = _editor->sample_to_pixel (_note[1]);
5872 _drag_rect->set_x0 (std::min(x0, x1));
5873 _drag_rect->set_x1 (std::max(x0, x1));
5877 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5879 if (!had_movement) {
5883 framepos_t const start = min (_note[0], _note[1]);
5884 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5886 framecnt_t const g = grid_frames (start);
5887 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5889 if (_editor->snap_mode() == SnapNormal && length < g) {
5893 Evoral::Beats length_beats = max (
5894 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5896 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5900 NoteCreateDrag::y_to_region (double y) const
5903 _region_view->get_canvas_group()->canvas_to_item (x, y);
5908 NoteCreateDrag::aborted (bool)
5913 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5918 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5922 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5924 Drag::start_grab (event, cursor);
5928 CrossfadeEdgeDrag::motion (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 /* how long should it be ? */
5946 new_length = len + _editor->pixel_to_sample (distance);
5948 /* now check with the region that this is legal */
5950 new_length = ar->verify_xfade_bounds (new_length, start);
5953 arv->reset_fade_in_shape_width (ar, new_length);
5955 arv->reset_fade_out_shape_width (ar, new_length);
5960 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5966 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5969 distance = _drags->current_pointer_x() - grab_x();
5970 len = ar->fade_in()->back()->when;
5972 distance = grab_x() - _drags->current_pointer_x();
5973 len = ar->fade_out()->back()->when;
5976 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5978 _editor->begin_reversible_command ("xfade trim");
5979 ar->playlist()->clear_owned_changes ();
5982 ar->set_fade_in_length (new_length);
5984 ar->set_fade_out_length (new_length);
5987 /* Adjusting the xfade may affect other regions in the playlist, so we need
5988 to get undo Commands from the whole playlist rather than just the
5992 vector<Command*> cmds;
5993 ar->playlist()->rdiff (cmds);
5994 _editor->session()->add_commands (cmds);
5995 _editor->commit_reversible_command ();
6000 CrossfadeEdgeDrag::aborted (bool)
6003 // arv->redraw_start_xfade ();
6005 // arv->redraw_end_xfade ();
6009 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6010 : Drag (e, item, true)
6011 , line (new EditorCursor (*e))
6013 line->set_position (pos);
6017 RegionCutDrag::~RegionCutDrag ()
6023 RegionCutDrag::motion (GdkEvent*, bool)
6025 framepos_t where = _drags->current_pointer_frame();
6026 _editor->snap_to (where);
6028 line->set_position (where);
6032 RegionCutDrag::finished (GdkEvent*, bool)
6034 _editor->get_track_canvas()->canvas()->re_enter();
6036 framepos_t pos = _drags->current_pointer_frame();
6040 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6046 _editor->split_regions_at (pos, rs);
6050 RegionCutDrag::aborted (bool)